1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-store-tree-model.h>
36 #include <tny-gtk-header-list-model.h>
37 #include <tny-folder.h>
38 #include <tny-folder-store-observer.h>
39 #include <tny-account-store.h>
40 #include <tny-account.h>
41 #include <tny-folder.h>
42 #include <tny-camel-folder.h>
43 #include <tny-simple-list.h>
44 #include <modest-tny-account.h>
45 #include <modest-tny-folder.h>
46 #include <modest-tny-local-folders-account.h>
47 #include <modest-tny-outbox-account.h>
48 #include <modest-marshal.h>
49 #include <modest-icon-names.h>
50 #include <modest-tny-account-store.h>
51 #include <modest-text-utils.h>
52 #include <modest-runtime.h>
53 #include "modest-folder-view.h"
54 #include <modest-dnd.h>
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
59 /* 'private'/'protected' functions */
60 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
61 static void modest_folder_view_init (ModestFolderView *obj);
62 static void modest_folder_view_finalize (GObject *obj);
64 static void tny_account_store_view_init (gpointer g,
67 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
68 TnyAccountStore *account_store);
70 static void on_selection_changed (GtkTreeSelection *sel, gpointer data);
72 static void on_account_removed (TnyAccountStore *self,
76 static void on_account_inserted (TnyAccountStore *self,
80 static void on_account_changed (TnyAccountStore *self,
84 static gint cmp_rows (GtkTreeModel *tree_model,
89 static gboolean filter_row (GtkTreeModel *model,
93 static gboolean on_key_pressed (GtkWidget *self,
97 static void on_configuration_key_changed (ModestConf* conf,
99 ModestConfEvent event,
100 ModestConfNotificationId notification_id,
101 ModestFolderView *self);
104 static void on_drag_data_get (GtkWidget *widget,
105 GdkDragContext *context,
106 GtkSelectionData *selection_data,
111 static void on_drag_data_received (GtkWidget *widget,
112 GdkDragContext *context,
115 GtkSelectionData *selection_data,
120 static gboolean on_drag_motion (GtkWidget *widget,
121 GdkDragContext *context,
127 static void expand_root_items (ModestFolderView *self);
129 static gint expand_row_timeout (gpointer data);
131 static void setup_drag_and_drop (GtkTreeView *self);
133 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
136 static void _clear_hidding_filter (ModestFolderView *folder_view);
138 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
141 ModestFolderView *self);
144 FOLDER_SELECTION_CHANGED_SIGNAL,
145 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
149 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
150 struct _ModestFolderViewPrivate {
151 TnyAccountStore *account_store;
152 TnyFolderStore *cur_folder_store;
154 TnyFolder *folder_to_select; /* folder to select after the next update */
156 ModestConfNotificationId notification_id;
158 gulong changed_signal;
159 gulong account_inserted_signal;
160 gulong account_removed_signal;
161 gulong account_changed_signal;
162 gulong conf_key_signal;
164 /* not unref this object, its a singlenton */
165 ModestEmailClipboard *clipboard;
167 /* Filter tree model */
171 TnyFolderStoreQuery *query;
172 guint timer_expander;
174 gchar *local_account_name;
175 gchar *visible_account_id;
176 ModestFolderViewStyle style;
178 gboolean reselect; /* we use this to force a reselection of the INBOX */
179 gboolean show_non_move;
181 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
182 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
183 MODEST_TYPE_FOLDER_VIEW, \
184 ModestFolderViewPrivate))
186 static GObjectClass *parent_class = NULL;
188 static guint signals[LAST_SIGNAL] = {0};
191 modest_folder_view_get_type (void)
193 static GType my_type = 0;
195 static const GTypeInfo my_info = {
196 sizeof(ModestFolderViewClass),
197 NULL, /* base init */
198 NULL, /* base finalize */
199 (GClassInitFunc) modest_folder_view_class_init,
200 NULL, /* class finalize */
201 NULL, /* class data */
202 sizeof(ModestFolderView),
204 (GInstanceInitFunc) modest_folder_view_init,
208 static const GInterfaceInfo tny_account_store_view_info = {
209 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
210 NULL, /* interface_finalize */
211 NULL /* interface_data */
215 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
219 g_type_add_interface_static (my_type,
220 TNY_TYPE_ACCOUNT_STORE_VIEW,
221 &tny_account_store_view_info);
227 modest_folder_view_class_init (ModestFolderViewClass *klass)
229 GObjectClass *gobject_class;
230 gobject_class = (GObjectClass*) klass;
232 parent_class = g_type_class_peek_parent (klass);
233 gobject_class->finalize = modest_folder_view_finalize;
235 g_type_class_add_private (gobject_class,
236 sizeof(ModestFolderViewPrivate));
238 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
239 g_signal_new ("folder_selection_changed",
240 G_TYPE_FROM_CLASS (gobject_class),
242 G_STRUCT_OFFSET (ModestFolderViewClass,
243 folder_selection_changed),
245 modest_marshal_VOID__POINTER_BOOLEAN,
246 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
249 * This signal is emitted whenever the currently selected
250 * folder display name is computed. Note that the name could
251 * be different to the folder name, because we could append
252 * the unread messages count to the folder name to build the
253 * folder display name
255 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
256 g_signal_new ("folder-display-name-changed",
257 G_TYPE_FROM_CLASS (gobject_class),
259 G_STRUCT_OFFSET (ModestFolderViewClass,
260 folder_display_name_changed),
262 g_cclosure_marshal_VOID__STRING,
263 G_TYPE_NONE, 1, G_TYPE_STRING);
270 ModestFolderView *self;
271 gchar *previous_name;
272 } GetMmcAccountNameData;
276 text_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
277 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
279 ModestFolderViewPrivate *priv;
284 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
285 GObject *instance = NULL;
287 g_return_if_fail (column);
288 g_return_if_fail (tree_model);
289 g_return_if_fail (iter != NULL);
291 gtk_tree_model_get (tree_model, iter,
292 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
293 TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
294 TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
295 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
296 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
298 rendobj = G_OBJECT(renderer);
308 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
309 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
311 gchar *item_name = NULL;
312 gint item_weight = 400;
314 if (type != TNY_FOLDER_TYPE_ROOT) {
317 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
318 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
319 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
320 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
322 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
326 /* Select the number to show: the unread or unsent messages */
327 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
332 /* Use bold font style if there are unread or unset messages */
334 item_name = g_strdup_printf ("%s (%d)", fname, number);
337 item_name = g_strdup (fname);
341 } else if (TNY_IS_ACCOUNT (instance)) {
342 /* If it's a server account */
343 if (modest_tny_account_is_virtual_local_folders (
344 TNY_ACCOUNT (instance))) {
345 item_name = g_strdup (priv->local_account_name);
347 } else if (modest_tny_account_is_memory_card_account (
348 TNY_ACCOUNT (instance))) {
349 /* fname is only correct when the items are first
350 * added to the model, not when the account is
351 * changed later, so get the name from the account
353 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
356 item_name = g_strdup (fname);
362 item_name = g_strdup ("unknown");
364 if (item_name && item_weight) {
365 /* Set the name in the treeview cell: */
366 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
368 /* Notify display name observers */
369 /* TODO: What listens for this signal, and how can it use only the new name? */
370 if (G_OBJECT (priv->cur_folder_store) == instance) {
371 g_signal_emit (G_OBJECT(self),
372 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
379 g_object_unref (G_OBJECT (instance));
384 icon_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
385 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
387 GObject *rendobj = NULL, *instance = NULL;
388 GdkPixbuf *pixbuf = NULL;
389 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
390 const gchar *account_id = NULL;
391 gboolean has_children;
393 rendobj = G_OBJECT(renderer);
394 gtk_tree_model_get (tree_model, iter,
395 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
396 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
398 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
403 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
404 /* We include the MERGE type here because it's used to create
405 the local OUTBOX folder */
406 if (type == TNY_FOLDER_TYPE_NORMAL ||
407 type == TNY_FOLDER_TYPE_UNKNOWN) {
408 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
412 case TNY_FOLDER_TYPE_ROOT:
413 if (TNY_IS_ACCOUNT (instance)) {
415 if (modest_tny_account_is_virtual_local_folders (
416 TNY_ACCOUNT (instance))) {
417 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
420 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
422 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
423 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
425 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
429 case TNY_FOLDER_TYPE_INBOX:
430 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
432 case TNY_FOLDER_TYPE_OUTBOX:
433 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
435 case TNY_FOLDER_TYPE_JUNK:
436 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
438 case TNY_FOLDER_TYPE_SENT:
439 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
441 case TNY_FOLDER_TYPE_TRASH:
442 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
444 case TNY_FOLDER_TYPE_DRAFTS:
445 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
447 case TNY_FOLDER_TYPE_NORMAL:
449 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
453 g_object_unref (G_OBJECT (instance));
456 g_object_set (rendobj, "pixbuf", pixbuf, NULL);
457 if (has_children && (pixbuf != NULL)) {
458 GdkPixbuf *open_pixbuf, *closed_pixbuf;
459 GdkPixbuf *open_emblem, *closed_emblem;
460 open_pixbuf = gdk_pixbuf_copy (pixbuf);
461 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
462 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
463 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
466 gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0,
467 MIN (gdk_pixbuf_get_width (open_emblem),
468 gdk_pixbuf_get_width (open_pixbuf)),
469 MIN (gdk_pixbuf_get_height (open_emblem),
470 gdk_pixbuf_get_height (open_pixbuf)),
471 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
472 g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
473 g_object_unref (open_emblem);
476 gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0,
477 MIN (gdk_pixbuf_get_width (closed_emblem),
478 gdk_pixbuf_get_width (closed_pixbuf)),
479 MIN (gdk_pixbuf_get_height (closed_emblem),
480 gdk_pixbuf_get_height (closed_pixbuf)),
481 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
482 g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
483 g_object_unref (closed_emblem);
486 g_object_unref (closed_pixbuf);
488 g_object_unref (open_pixbuf);
492 g_object_unref (pixbuf);
496 add_columns (GtkWidget *treeview)
498 GtkTreeViewColumn *column;
499 GtkCellRenderer *renderer;
500 GtkTreeSelection *sel;
503 column = gtk_tree_view_column_new ();
505 /* Set icon and text render function */
506 renderer = gtk_cell_renderer_pixbuf_new();
507 gtk_tree_view_column_pack_start (column, renderer, FALSE);
508 gtk_tree_view_column_set_cell_data_func(column, renderer,
509 icon_cell_data, treeview, NULL);
511 renderer = gtk_cell_renderer_text_new();
512 gtk_tree_view_column_pack_start (column, renderer, FALSE);
513 gtk_tree_view_column_set_cell_data_func(column, renderer,
514 text_cell_data, treeview, NULL);
516 /* Set selection mode */
517 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
518 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
520 /* Set treeview appearance */
521 gtk_tree_view_column_set_spacing (column, 2);
522 gtk_tree_view_column_set_resizable (column, TRUE);
523 gtk_tree_view_column_set_fixed_width (column, TRUE);
524 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
525 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
528 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
532 modest_folder_view_init (ModestFolderView *obj)
534 ModestFolderViewPrivate *priv;
537 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
539 priv->timer_expander = 0;
540 priv->account_store = NULL;
542 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
543 priv->cur_folder_store = NULL;
544 priv->visible_account_id = NULL;
545 priv->folder_to_select = NULL;
547 /* Initialize the local account name */
548 conf = modest_runtime_get_conf();
549 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
551 /* Init email clipboard */
552 priv->clipboard = modest_runtime_get_email_clipboard ();
553 priv->hidding_ids = NULL;
554 priv->n_selected = 0;
555 priv->reselect = FALSE;
556 priv->show_non_move = TRUE;
559 add_columns (GTK_WIDGET (obj));
561 /* Setup drag and drop */
562 setup_drag_and_drop (GTK_TREE_VIEW(obj));
564 /* Connect signals */
565 g_signal_connect (G_OBJECT (obj),
567 G_CALLBACK (on_key_pressed), NULL);
570 * Track changes in the local account name (in the device it
571 * will be the device name)
573 priv->notification_id = modest_conf_listen_to_namespace (conf,
574 MODEST_CONF_NAMESPACE);
575 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
577 G_CALLBACK(on_configuration_key_changed),
582 tny_account_store_view_init (gpointer g, gpointer iface_data)
584 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
586 klass->set_account_store_func = modest_folder_view_set_account_store;
592 modest_folder_view_finalize (GObject *obj)
594 ModestFolderViewPrivate *priv;
595 GtkTreeSelection *sel;
597 g_return_if_fail (obj);
599 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
601 if (priv->notification_id) {
602 modest_conf_forget_namespace (modest_runtime_get_conf (),
603 MODEST_CONF_NAMESPACE,
604 priv->notification_id);
607 if (priv->timer_expander != 0) {
608 g_source_remove (priv->timer_expander);
609 priv->timer_expander = 0;
612 if (priv->account_store) {
613 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
614 priv->account_inserted_signal);
615 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
616 priv->account_removed_signal);
617 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
618 priv->account_changed_signal);
619 g_object_unref (G_OBJECT(priv->account_store));
620 priv->account_store = NULL;
624 g_object_unref (G_OBJECT (priv->query));
628 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
629 if (priv->folder_to_select) {
630 g_object_unref (G_OBJECT(priv->folder_to_select));
631 priv->folder_to_select = NULL;
634 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
636 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
638 g_free (priv->local_account_name);
639 g_free (priv->visible_account_id);
641 if (priv->conf_key_signal) {
642 g_signal_handler_disconnect (modest_runtime_get_conf (),
643 priv->conf_key_signal);
644 priv->conf_key_signal = 0;
647 if (priv->cur_folder_store) {
648 if (TNY_IS_FOLDER(priv->cur_folder_store))
649 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
650 /* FALSE --> expunge the message */
652 g_object_unref (priv->cur_folder_store);
653 priv->cur_folder_store = NULL;
656 /* Clear hidding array created by cut operation */
657 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
659 G_OBJECT_CLASS(parent_class)->finalize (obj);
664 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
666 ModestFolderViewPrivate *priv;
669 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
670 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
672 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
673 device = tny_account_store_get_device (account_store);
675 if (G_UNLIKELY (priv->account_store)) {
677 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
678 priv->account_inserted_signal))
679 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
680 priv->account_inserted_signal);
681 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
682 priv->account_removed_signal))
683 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
684 priv->account_removed_signal);
685 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
686 priv->account_changed_signal))
687 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
688 priv->account_changed_signal);
689 g_object_unref (G_OBJECT (priv->account_store));
692 priv->account_store = g_object_ref (G_OBJECT (account_store));
694 priv->account_removed_signal =
695 g_signal_connect (G_OBJECT(account_store), "account_removed",
696 G_CALLBACK (on_account_removed), self);
698 priv->account_inserted_signal =
699 g_signal_connect (G_OBJECT(account_store), "account_inserted",
700 G_CALLBACK (on_account_inserted), self);
702 priv->account_changed_signal =
703 g_signal_connect (G_OBJECT(account_store), "account_changed",
704 G_CALLBACK (on_account_changed), self);
706 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
708 g_object_unref (G_OBJECT (device));
712 on_account_inserted (TnyAccountStore *account_store,
716 ModestFolderViewPrivate *priv;
717 GtkTreeModel *sort_model, *filter_model;
719 /* Ignore transport account insertions, we're not showing them
720 in the folder view */
721 if (TNY_IS_TRANSPORT_ACCOUNT (account))
724 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
726 /* If we're adding a new account, and there is no previous
727 one, we need to select the visible server account */
728 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
729 !priv->visible_account_id)
730 modest_widget_memory_restore (modest_runtime_get_conf(),
731 G_OBJECT (user_data),
732 MODEST_CONF_FOLDER_VIEW_KEY);
734 /* Get the inner model */
735 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
736 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
738 /* Insert the account in the model */
739 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
745 on_account_changed (TnyAccountStore *account_store,
746 TnyAccount *tny_account,
750 ModestFolderViewPrivate *priv;
751 GtkTreeModel *sort_model, *filter_model;
753 /* Ignore transport account insertions, we're not showing them
754 in the folder view */
755 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
758 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
760 /* Get the inner model */
761 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
762 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
764 /* Remove the account from the model */
765 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
766 G_OBJECT (tny_account));
768 /* Insert the account in the model */
769 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
770 G_OBJECT (tny_account));
776 on_account_removed (TnyAccountStore *account_store,
780 ModestFolderView *self = NULL;
781 ModestFolderViewPrivate *priv;
782 GtkTreeModel *sort_model, *filter_model;
783 GtkTreeSelection *sel = NULL;
785 /* Ignore transport account removals, we're not showing them
786 in the folder view */
787 if (TNY_IS_TRANSPORT_ACCOUNT (account))
790 self = MODEST_FOLDER_VIEW (user_data);
791 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
793 /* Invalidate the cur_folder_store only if the selected folder
794 belongs to the account that is being removed */
795 if (priv->cur_folder_store) {
796 TnyAccount *selected_folder_account = NULL;
798 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
799 selected_folder_account =
800 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
802 selected_folder_account =
803 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
806 if (selected_folder_account == account) {
807 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
808 gtk_tree_selection_unselect_all (sel);
810 g_object_unref (selected_folder_account);
813 /* Invalidate row to select only if the folder to select
814 belongs to the account that is being removed*/
815 if (priv->folder_to_select) {
816 TnyAccount *folder_to_select_account = NULL;
818 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
819 if (folder_to_select_account == account) {
820 /* modest_folder_view_disable_next_folder_selection (self); */
821 g_object_unref (priv->folder_to_select);
822 priv->folder_to_select = NULL;
824 g_object_unref (folder_to_select_account);
827 /* Remove the account from the model */
828 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
829 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
830 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
833 /* If the removed account is the currently viewed one then
834 clear the configuration value. The new visible account will be the default account */
835 if (priv->visible_account_id &&
836 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
838 /* Clear the current visible account_id */
839 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
841 /* Call the restore method, this will set the new visible account */
842 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
843 MODEST_CONF_FOLDER_VIEW_KEY);
846 /* Select the INBOX */
847 modest_folder_view_select_first_inbox_or_local (self);
851 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
853 GtkTreeViewColumn *col;
855 g_return_if_fail (self);
857 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
859 g_printerr ("modest: failed get column for title\n");
863 gtk_tree_view_column_set_title (col, title);
864 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
869 modest_folder_view_on_map (ModestFolderView *self,
870 GdkEventExpose *event,
873 ModestFolderViewPrivate *priv;
875 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
877 /* This won't happen often */
878 if (G_UNLIKELY (priv->reselect)) {
879 /* Select the first inbox or the local account if not found */
881 /* TODO: this could cause a lock at startup, so we
882 comment it for the moment. We know that this will
883 be a bug, because the INBOX is not selected, but we
884 need to rewrite some parts of Modest to avoid the
885 deathlock situation */
886 /* TODO: check if this is still the case */
887 priv->reselect = FALSE;
888 modest_folder_view_select_first_inbox_or_local (self);
889 /* Notify the display name observers */
890 g_signal_emit (G_OBJECT(self),
891 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
895 expand_root_items (self);
901 modest_folder_view_new (TnyFolderStoreQuery *query)
904 ModestFolderViewPrivate *priv;
905 GtkTreeSelection *sel;
907 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
908 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
911 priv->query = g_object_ref (query);
913 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
914 priv->changed_signal = g_signal_connect (sel, "changed",
915 G_CALLBACK (on_selection_changed), self);
917 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
919 return GTK_WIDGET(self);
922 /* this feels dirty; any other way to expand all the root items? */
924 expand_root_items (ModestFolderView *self)
927 path = gtk_tree_path_new_first ();
929 /* all folders should have child items, so.. */
930 while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
931 gtk_tree_path_next (path);
933 gtk_tree_path_free (path);
937 * We use this function to implement the
938 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
939 * account in this case, and the local folders.
942 filter_row (GtkTreeModel *model,
946 ModestFolderViewPrivate *priv;
947 gboolean retval = TRUE;
948 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
949 GObject *instance = NULL;
950 const gchar *id = NULL;
952 gboolean found = FALSE;
953 gboolean cleared = FALSE;
955 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
956 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
958 gtk_tree_model_get (model, iter,
959 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
960 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
963 /* Do not show if there is no instance, this could indeed
964 happen when the model is being modified while it's being
965 drawn. This could occur for example when moving folders
970 if (type == TNY_FOLDER_TYPE_ROOT) {
971 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
972 account instead of a folder. */
973 if (TNY_IS_ACCOUNT (instance)) {
974 TnyAccount *acc = TNY_ACCOUNT (instance);
975 const gchar *account_id = tny_account_get_id (acc);
977 /* If it isn't a special folder,
978 * don't show it unless it is the visible account: */
979 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
980 !modest_tny_account_is_virtual_local_folders (acc) &&
981 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
983 /* Show only the visible account id */
984 if (priv->visible_account_id) {
985 if (strcmp (account_id, priv->visible_account_id))
992 /* Never show these to the user. They are merged into one folder
993 * in the local-folders account instead: */
994 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
999 /* Check hiding (if necessary) */
1000 cleared = modest_email_clipboard_cleared (priv->clipboard);
1001 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1002 id = tny_folder_get_id (TNY_FOLDER(instance));
1003 if (priv->hidding_ids != NULL)
1004 for (i=0; i < priv->n_selected && !found; i++)
1005 if (priv->hidding_ids[i] != NULL && id != NULL)
1006 found = (!strcmp (priv->hidding_ids[i], id));
1012 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1013 folder as no message can be move there according to UI specs */
1014 if (!priv->show_non_move)
1018 case TNY_FOLDER_TYPE_OUTBOX:
1019 case TNY_FOLDER_TYPE_SENT:
1020 case TNY_FOLDER_TYPE_DRAFTS:
1023 case TNY_FOLDER_TYPE_UNKNOWN:
1024 case TNY_FOLDER_TYPE_NORMAL:
1025 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1026 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1027 || type == TNY_FOLDER_TYPE_DRAFTS)
1038 g_object_unref (instance);
1045 modest_folder_view_update_model (ModestFolderView *self,
1046 TnyAccountStore *account_store)
1048 ModestFolderViewPrivate *priv;
1049 GtkTreeModel *model /* , *old_model */;
1050 /* TnyAccount *local_account; */
1051 TnyList *model_as_list;
1053 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1054 g_return_val_if_fail (account_store, FALSE);
1056 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1058 /* Notify that there is no folder selected */
1059 g_signal_emit (G_OBJECT(self),
1060 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1062 if (priv->cur_folder_store) {
1063 g_object_unref (priv->cur_folder_store);
1064 priv->cur_folder_store = NULL;
1067 /* FIXME: the local accounts are not shown when the query
1068 selects only the subscribed folders. */
1069 /* model = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1070 model = tny_gtk_folder_store_tree_model_new (NULL);
1072 /* Deal with the model via its TnyList Interface,
1073 * filling the TnyList via a get_accounts() call: */
1074 model_as_list = TNY_LIST(model);
1076 /* Get the accounts: */
1077 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1079 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1080 g_object_unref (model_as_list);
1081 model_as_list = NULL;
1083 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1085 sortable = gtk_tree_model_sort_new_with_model (model);
1086 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1087 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1088 GTK_SORT_ASCENDING);
1089 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1090 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1091 cmp_rows, NULL, NULL);
1093 /* Create filter model */
1094 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1095 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1101 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1102 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1103 (GCallback) on_row_inserted_maybe_select_folder, self);
1106 g_object_unref (model);
1107 g_object_unref (filter_model);
1108 g_object_unref (sortable);
1110 /* Force a reselection of the INBOX next time the widget is shown */
1111 priv->reselect = TRUE;
1118 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1120 GtkTreeModel *model = NULL;
1121 TnyFolderStore *folder = NULL;
1123 ModestFolderView *tree_view = NULL;
1124 ModestFolderViewPrivate *priv = NULL;
1125 gboolean selected = FALSE;
1127 g_return_if_fail (sel);
1128 g_return_if_fail (user_data);
1130 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1132 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1134 /* Notify the display name observers */
1135 g_signal_emit (G_OBJECT(user_data),
1136 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1139 tree_view = MODEST_FOLDER_VIEW (user_data);
1142 gtk_tree_model_get (model, &iter,
1143 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1146 /* If the folder is the same do not notify */
1147 if (priv->cur_folder_store == folder && folder) {
1148 g_object_unref (folder);
1153 /* Current folder was unselected */
1154 if (priv->cur_folder_store) {
1155 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1156 priv->cur_folder_store, FALSE);
1158 if (TNY_IS_FOLDER(priv->cur_folder_store))
1159 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1160 FALSE, NULL, NULL, NULL);
1162 /* FALSE --> don't expunge the messages */
1164 g_object_unref (priv->cur_folder_store);
1165 priv->cur_folder_store = NULL;
1168 /* New current references */
1169 priv->cur_folder_store = folder;
1171 /* New folder has been selected */
1172 g_signal_emit (G_OBJECT(tree_view),
1173 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1174 0, priv->cur_folder_store, TRUE);
1178 modest_folder_view_get_selected (ModestFolderView *self)
1180 ModestFolderViewPrivate *priv;
1182 g_return_val_if_fail (self, NULL);
1184 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1185 if (priv->cur_folder_store)
1186 g_object_ref (priv->cur_folder_store);
1188 return priv->cur_folder_store;
1192 get_cmp_rows_type_pos (GObject *folder)
1194 /* Remote accounts -> Local account -> MMC account .*/
1197 if (TNY_IS_ACCOUNT (folder) &&
1198 modest_tny_account_is_virtual_local_folders (
1199 TNY_ACCOUNT (folder))) {
1201 } else if (TNY_IS_ACCOUNT (folder)) {
1202 TnyAccount *account = TNY_ACCOUNT (folder);
1203 const gchar *account_id = tny_account_get_id (account);
1204 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1210 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1211 return -1; /* Should never happen */
1216 get_cmp_subfolder_type_pos (TnyFolderType t)
1218 /* Inbox, Outbox, Drafts, Sent, User */
1222 case TNY_FOLDER_TYPE_INBOX:
1225 case TNY_FOLDER_TYPE_OUTBOX:
1228 case TNY_FOLDER_TYPE_DRAFTS:
1231 case TNY_FOLDER_TYPE_SENT:
1240 * This function orders the mail accounts according to these rules:
1241 * 1st - remote accounts
1242 * 2nd - local account
1246 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1250 gchar *name1 = NULL;
1251 gchar *name2 = NULL;
1252 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1253 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1254 GObject *folder1 = NULL;
1255 GObject *folder2 = NULL;
1257 gtk_tree_model_get (tree_model, iter1,
1258 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1259 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1260 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1262 gtk_tree_model_get (tree_model, iter2,
1263 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1264 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1265 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1268 /* Return if we get no folder. This could happen when folder
1269 operations are happening. The model is updated after the
1270 folder copy/move actually occurs, so there could be
1271 situations where the model to be drawn is not correct */
1272 if (!folder1 || !folder2)
1275 if (type == TNY_FOLDER_TYPE_ROOT) {
1276 /* Compare the types, so that
1277 * Remote accounts -> Local account -> MMC account .*/
1278 const gint pos1 = get_cmp_rows_type_pos (folder1);
1279 const gint pos2 = get_cmp_rows_type_pos (folder2);
1280 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1281 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1284 else if (pos1 > pos2)
1287 /* Compare items of the same type: */
1289 TnyAccount *account1 = NULL;
1290 if (TNY_IS_ACCOUNT (folder1))
1291 account1 = TNY_ACCOUNT (folder1);
1293 TnyAccount *account2 = NULL;
1294 if (TNY_IS_ACCOUNT (folder2))
1295 account2 = TNY_ACCOUNT (folder2);
1297 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1298 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1300 if (!account_id && !account_id2) {
1302 } else if (!account_id) {
1304 } else if (!account_id2) {
1306 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1309 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1313 gint cmp1 = 0, cmp2 = 0;
1314 /* get the parent to know if it's a local folder */
1317 gboolean has_parent;
1318 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1320 GObject *parent_folder;
1321 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1322 gtk_tree_model_get (tree_model, &parent,
1323 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1324 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1326 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1327 TNY_IS_ACCOUNT (parent_folder) &&
1328 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1329 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1330 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1332 g_object_unref (parent_folder);
1335 /* if they are not local folders */
1337 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1338 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1342 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1344 cmp = (cmp1 - cmp2);
1349 g_object_unref(G_OBJECT(folder1));
1351 g_object_unref(G_OBJECT(folder2));
1359 /*****************************************************************************/
1360 /* DRAG and DROP stuff */
1361 /*****************************************************************************/
1364 * This function fills the #GtkSelectionData with the row and the
1365 * model that has been dragged. It's called when this widget is a
1366 * source for dnd after the event drop happened
1369 on_drag_data_get (GtkWidget *widget,
1370 GdkDragContext *context,
1371 GtkSelectionData *selection_data,
1376 GtkTreeSelection *selection;
1377 GtkTreeModel *model;
1379 GtkTreePath *source_row;
1381 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1382 gtk_tree_selection_get_selected (selection, &model, &iter);
1383 source_row = gtk_tree_model_get_path (model, &iter);
1385 gtk_tree_set_row_drag_data (selection_data,
1389 gtk_tree_path_free (source_row);
1392 typedef struct _DndHelper {
1393 gboolean delete_source;
1394 GtkTreePath *source_row;
1395 GdkDragContext *context;
1401 * This function is the callback of the
1402 * modest_mail_operation_xfer_msgs () and
1403 * modest_mail_operation_xfer_folder() calls. We check here if the
1404 * message/folder was correctly asynchronously transferred. The reason
1405 * to use the same callback is that the code is the same, it only has
1406 * to check that the operation went fine and then finalize the drag
1410 on_progress_changed (ModestMailOperation *mail_op,
1411 ModestMailOperationState *state,
1417 helper = (DndHelper *) user_data;
1419 if (!state->finished)
1422 if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1428 /* Notify the drag source. Never call delete, the monitor will
1429 do the job if needed */
1430 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1432 /* Free the helper */
1433 gtk_tree_path_free (helper->source_row);
1434 g_slice_free (DndHelper, helper);
1438 /* get the folder for the row the treepath refers to. */
1439 /* folder must be unref'd */
1441 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1444 TnyFolder *folder = NULL;
1446 if (gtk_tree_model_get_iter (model,&iter, path))
1447 gtk_tree_model_get (model, &iter,
1448 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1454 show_banner_move_target_error ()
1456 ModestWindow *main_window;
1458 main_window = modest_window_mgr_get_main_window(
1459 modest_runtime_get_window_mgr());
1461 modest_platform_information_banner(GTK_WIDGET(main_window),
1462 NULL, _("mail_in_ui_folder_move_target_error"));
1466 * This function is used by drag_data_received_cb to manage drag and
1467 * drop of a header, i.e, and drag from the header view to the folder
1471 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1472 GtkTreeModel *dest_model,
1473 GtkTreePath *dest_row,
1476 TnyList *headers = NULL;
1477 TnyHeader *header = NULL;
1478 TnyFolder *folder = NULL;
1479 ModestMailOperation *mail_op = NULL;
1480 GtkTreeIter source_iter;
1481 ModestWindowMgr *mgr = NULL; /*no need for unref*/
1482 ModestWindow *main_win = NULL; /*no need for unref*/
1484 g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1485 g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1486 g_return_if_fail (dest_row);
1487 g_return_if_fail (helper);
1490 gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1491 gtk_tree_model_get (source_model, &source_iter,
1492 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1494 if (!TNY_IS_HEADER(header)) {
1495 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1499 /* Check if the selected message is in msg-view. If it is than
1500 * do not enable drag&drop on that. */
1501 mgr = modest_runtime_get_window_mgr ();
1502 if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1506 folder = tree_path_to_folder (dest_model, dest_row);
1507 if (!TNY_IS_FOLDER(folder)) {
1508 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1509 show_banner_move_target_error();
1512 if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1513 g_debug ("folder rules: cannot write to that folder");
1517 headers = tny_simple_list_new ();
1518 tny_list_append (headers, G_OBJECT (header));
1520 main_win = modest_window_mgr_get_main_window(mgr);
1521 if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1522 == GTK_RESPONSE_CANCEL)
1525 /* Transfer message */
1526 mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1528 modest_ui_actions_move_folder_error_handler,
1530 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1532 g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1533 G_CALLBACK (on_progress_changed), helper);
1535 modest_mail_operation_xfer_msgs (mail_op,
1538 helper->delete_source,
1543 if (G_IS_OBJECT(mail_op))
1544 g_object_unref (G_OBJECT (mail_op));
1545 if (G_IS_OBJECT(header))
1546 g_object_unref (G_OBJECT (header));
1547 if (G_IS_OBJECT(folder))
1548 g_object_unref (G_OBJECT (folder));
1549 if (G_IS_OBJECT(headers))
1550 g_object_unref (headers);
1554 * This function is used by drag_data_received_cb to manage drag and
1555 * drop of a folder, i.e, and drag from the folder view to the same
1559 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
1560 GtkTreeModel *dest_model,
1561 GtkTreePath *dest_row,
1562 GtkSelectionData *selection_data,
1565 ModestMailOperation *mail_op = NULL;
1566 GtkTreeIter dest_iter, iter;
1567 TnyFolderStore *dest_folder = NULL;
1568 TnyFolder *folder = NULL;
1569 gboolean forbidden = FALSE;
1572 /* check the folder rules for the destination */
1573 folder = tree_path_to_folder (dest_model, dest_row);
1574 if (TNY_IS_FOLDER(folder)) {
1575 ModestTnyFolderRules rules =
1576 modest_tny_folder_get_rules (folder);
1577 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1580 g_debug ("folder rules: cannot write to that folder");
1581 } else if (TNY_IS_FOLDER_STORE(folder)){
1582 /* enable local root as destination for folders */
1583 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1584 && TNY_IS_ACCOUNT (folder))
1587 g_object_unref (folder);
1590 /* check the folder rules for the source */
1591 folder = tree_path_to_folder (source_model, helper->source_row);
1592 if (TNY_IS_FOLDER(folder)) {
1593 ModestTnyFolderRules rules =
1594 modest_tny_folder_get_rules (folder);
1595 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1597 g_debug ("folder rules: cannot move that folder");
1600 g_object_unref (folder);
1604 /* Check if the drag is possible */
1605 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1606 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1607 gtk_tree_path_free (helper->source_row);
1608 g_slice_free (DndHelper, helper);
1613 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1614 gtk_tree_model_get (dest_model, &dest_iter,
1615 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1617 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1618 gtk_tree_model_get (source_model, &iter,
1619 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1622 /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1623 if (modest_platform_connect_and_wait_if_network_folderstore (
1624 NULL, dest_folder) &&
1625 modest_platform_connect_and_wait_if_network_folderstore (
1626 NULL, TNY_FOLDER_STORE (folder))) {
1627 /* Do the mail operation */
1628 mail_op = modest_mail_operation_new_with_error_handling (
1629 MODEST_MAIL_OPERATION_TYPE_RECEIVE,
1631 modest_ui_actions_move_folder_error_handler,
1633 modest_mail_operation_queue_add (
1634 modest_runtime_get_mail_operation_queue (),
1639 G_CALLBACK (on_progress_changed),
1642 modest_mail_operation_xfer_folder (mail_op,
1645 helper->delete_source,
1649 g_object_unref (G_OBJECT (mail_op));
1653 g_object_unref (G_OBJECT (dest_folder));
1654 g_object_unref (G_OBJECT (folder));
1658 * This function receives the data set by the "drag-data-get" signal
1659 * handler. This information comes within the #GtkSelectionData. This
1660 * function will manage both the drags of folders of the treeview and
1661 * drags of headers of the header view widget.
1664 on_drag_data_received (GtkWidget *widget,
1665 GdkDragContext *context,
1668 GtkSelectionData *selection_data,
1673 GtkWidget *source_widget;
1674 GtkTreeModel *dest_model, *source_model;
1675 GtkTreePath *source_row, *dest_row;
1676 GtkTreeViewDropPosition pos;
1677 gboolean success = FALSE, delete_source = FALSE;
1678 DndHelper *helper = NULL;
1680 /* Do not allow further process */
1681 g_signal_stop_emission_by_name (widget, "drag-data-received");
1682 source_widget = gtk_drag_get_source_widget (context);
1684 /* Get the action */
1685 if (context->action == GDK_ACTION_MOVE) {
1686 delete_source = TRUE;
1688 /* Notify that there is no folder selected. We need to
1689 do this in order to update the headers view (and
1690 its monitors, because when moving, the old folder
1691 won't longer exist. We can not wait for the end of
1692 the operation, because the operation won't start if
1693 the folder is in use */
1694 if (source_widget == widget) {
1695 ModestFolderViewPrivate *priv;
1697 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1698 if (priv->cur_folder_store) {
1699 g_object_unref (priv->cur_folder_store);
1700 priv->cur_folder_store = NULL;
1703 g_signal_emit (G_OBJECT (widget),
1704 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1708 /* Check if the get_data failed */
1709 if (selection_data == NULL || selection_data->length < 0)
1710 gtk_drag_finish (context, success, FALSE, time);
1712 /* Get the models */
1713 gtk_tree_get_row_drag_data (selection_data,
1717 /* Select the destination model */
1718 if (source_widget == widget) {
1719 dest_model = source_model;
1721 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1724 /* Get the path to the destination row. Can not call
1725 gtk_tree_view_get_drag_dest_row() because the source row
1726 is not selected anymore */
1727 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1730 /* Only allow drops IN other rows */
1731 if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1732 gtk_drag_finish (context, success, FALSE, time);
1734 /* Create the helper */
1735 helper = g_slice_new0 (DndHelper);
1736 helper->delete_source = delete_source;
1737 helper->source_row = gtk_tree_path_copy (source_row);
1738 helper->context = context;
1739 helper->time = time;
1741 /* Drags from the header view */
1742 if (source_widget != widget) {
1744 drag_and_drop_from_header_view (source_model,
1751 drag_and_drop_from_folder_view (source_model,
1759 gtk_tree_path_free (source_row);
1760 gtk_tree_path_free (dest_row);
1764 * We define a "drag-drop" signal handler because we do not want to
1765 * use the default one, because the default one always calls
1766 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1767 * signal handler, because there we have all the information available
1768 * to know if the dnd was a success or not.
1771 drag_drop_cb (GtkWidget *widget,
1772 GdkDragContext *context,
1780 if (!context->targets)
1783 /* Check if we're dragging a folder row */
1784 target = gtk_drag_dest_find_target (widget, context, NULL);
1786 /* Request the data from the source. */
1787 gtk_drag_get_data(widget, context, target, time);
1793 * This function expands a node of a tree view if it's not expanded
1794 * yet. Not sure why it needs the threads stuff, but gtk+`example code
1795 * does that, so that's why they're here.
1798 expand_row_timeout (gpointer data)
1800 GtkTreeView *tree_view = data;
1801 GtkTreePath *dest_path = NULL;
1802 GtkTreeViewDropPosition pos;
1803 gboolean result = FALSE;
1805 GDK_THREADS_ENTER ();
1807 gtk_tree_view_get_drag_dest_row (tree_view,
1812 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1813 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1814 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1815 gtk_tree_path_free (dest_path);
1819 gtk_tree_path_free (dest_path);
1824 GDK_THREADS_LEAVE ();
1830 * This function is called whenever the pointer is moved over a widget
1831 * while dragging some data. It installs a timeout that will expand a
1832 * node of the treeview if not expanded yet. This function also calls
1833 * gdk_drag_status in order to set the suggested action that will be
1834 * used by the "drag-data-received" signal handler to know if we
1835 * should do a move or just a copy of the data.
1838 on_drag_motion (GtkWidget *widget,
1839 GdkDragContext *context,
1845 GtkTreeViewDropPosition pos;
1846 GtkTreePath *dest_row;
1847 ModestFolderViewPrivate *priv;
1848 GdkDragAction suggested_action;
1849 gboolean valid_location = FALSE;
1851 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1853 if (priv->timer_expander != 0) {
1854 g_source_remove (priv->timer_expander);
1855 priv->timer_expander = 0;
1858 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1863 /* Do not allow drops between folders */
1865 pos == GTK_TREE_VIEW_DROP_BEFORE ||
1866 pos == GTK_TREE_VIEW_DROP_AFTER) {
1867 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1868 gdk_drag_status(context, 0, time);
1869 valid_location = FALSE;
1872 valid_location = TRUE;
1875 /* Expand the selected row after 1/2 second */
1876 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1877 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1878 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1881 /* Select the desired action. By default we pick MOVE */
1882 suggested_action = GDK_ACTION_MOVE;
1884 if (context->actions == GDK_ACTION_COPY)
1885 gdk_drag_status(context, GDK_ACTION_COPY, time);
1886 else if (context->actions == GDK_ACTION_MOVE)
1887 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1888 else if (context->actions & suggested_action)
1889 gdk_drag_status(context, suggested_action, time);
1891 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1895 gtk_tree_path_free (dest_row);
1896 g_signal_stop_emission_by_name (widget, "drag-motion");
1897 return valid_location;
1901 /* Folder view drag types */
1902 const GtkTargetEntry folder_view_drag_types[] =
1904 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1905 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1909 * This function sets the treeview as a source and a target for dnd
1910 * events. It also connects all the requirede signals.
1913 setup_drag_and_drop (GtkTreeView *self)
1915 /* Set up the folder view as a dnd destination. Set only the
1916 highlight flag, otherwise gtk will have a different
1918 gtk_drag_dest_set (GTK_WIDGET (self),
1919 GTK_DEST_DEFAULT_HIGHLIGHT,
1920 folder_view_drag_types,
1921 G_N_ELEMENTS (folder_view_drag_types),
1922 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1924 g_signal_connect (G_OBJECT (self),
1925 "drag_data_received",
1926 G_CALLBACK (on_drag_data_received),
1930 /* Set up the treeview as a dnd source */
1931 gtk_drag_source_set (GTK_WIDGET (self),
1933 folder_view_drag_types,
1934 G_N_ELEMENTS (folder_view_drag_types),
1935 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1937 g_signal_connect (G_OBJECT (self),
1939 G_CALLBACK (on_drag_motion),
1942 g_signal_connect (G_OBJECT (self),
1944 G_CALLBACK (on_drag_data_get),
1947 g_signal_connect (G_OBJECT (self),
1949 G_CALLBACK (drag_drop_cb),
1954 * This function manages the navigation through the folders using the
1955 * keyboard or the hardware keys in the device
1958 on_key_pressed (GtkWidget *self,
1962 GtkTreeSelection *selection;
1964 GtkTreeModel *model;
1965 gboolean retval = FALSE;
1967 /* Up and Down are automatically managed by the treeview */
1968 if (event->keyval == GDK_Return) {
1969 /* Expand/Collapse the selected row */
1970 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1971 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1974 path = gtk_tree_model_get_path (model, &iter);
1976 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1977 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1979 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1980 gtk_tree_path_free (path);
1982 /* No further processing */
1990 * We listen to the changes in the local folder account name key,
1991 * because we want to show the right name in the view. The local
1992 * folder account name corresponds to the device name in the Maemo
1993 * version. We do this because we do not want to query gconf on each
1994 * tree view refresh. It's better to cache it and change whenever
1998 on_configuration_key_changed (ModestConf* conf,
2000 ModestConfEvent event,
2001 ModestConfNotificationId id,
2002 ModestFolderView *self)
2004 ModestFolderViewPrivate *priv;
2007 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2008 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2010 /* Do not listen for changes in other namespaces */
2011 if (priv->notification_id != id)
2014 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2015 g_free (priv->local_account_name);
2017 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2018 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2020 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2021 MODEST_CONF_DEVICE_NAME, NULL);
2023 /* Force a redraw */
2024 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2025 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2026 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2027 gtk_tree_view_column_queue_resize (tree_column);
2033 modest_folder_view_set_style (ModestFolderView *self,
2034 ModestFolderViewStyle style)
2036 ModestFolderViewPrivate *priv;
2038 g_return_if_fail (self);
2040 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2042 priv->style = style;
2046 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2047 const gchar *account_id)
2049 ModestFolderViewPrivate *priv;
2050 GtkTreeModel *model;
2052 g_return_if_fail (self);
2054 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2056 /* This will be used by the filter_row callback,
2057 * to decided which rows to show: */
2058 if (priv->visible_account_id) {
2059 g_free (priv->visible_account_id);
2060 priv->visible_account_id = NULL;
2063 priv->visible_account_id = g_strdup (account_id);
2066 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2067 if (GTK_IS_TREE_MODEL_FILTER (model))
2068 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2070 /* Save settings to gconf */
2071 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2072 MODEST_CONF_FOLDER_VIEW_KEY);
2076 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2078 ModestFolderViewPrivate *priv;
2080 g_return_val_if_fail (self, NULL);
2082 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2084 return (const gchar *) priv->visible_account_id;
2088 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2092 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2094 gtk_tree_model_get (model, iter,
2095 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2098 gboolean result = FALSE;
2099 if (type == TNY_FOLDER_TYPE_INBOX) {
2103 *inbox_iter = *iter;
2107 if (gtk_tree_model_iter_children (model, &child, iter)) {
2108 if (find_inbox_iter (model, &child, inbox_iter))
2112 } while (gtk_tree_model_iter_next (model, iter));
2121 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2123 GtkTreeModel *model;
2124 GtkTreeIter iter, inbox_iter;
2125 GtkTreeSelection *sel;
2126 GtkTreePath *path = NULL;
2128 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2132 expand_root_items (self);
2133 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2135 gtk_tree_model_get_iter_first (model, &iter);
2137 if (find_inbox_iter (model, &iter, &inbox_iter))
2138 path = gtk_tree_model_get_path (model, &inbox_iter);
2140 path = gtk_tree_path_new_first ();
2142 /* Select the row and free */
2143 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2144 gtk_tree_path_free (path);
2147 gtk_widget_grab_focus (GTK_WIDGET(self));
2153 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2158 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2159 TnyFolder* a_folder;
2162 gtk_tree_model_get (model, iter,
2163 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2164 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2165 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2169 if (folder == a_folder) {
2170 g_object_unref (a_folder);
2171 *folder_iter = *iter;
2174 g_object_unref (a_folder);
2176 if (gtk_tree_model_iter_children (model, &child, iter)) {
2177 if (find_folder_iter (model, &child, folder_iter, folder))
2181 } while (gtk_tree_model_iter_next (model, iter));
2188 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2189 ModestFolderView *self)
2191 ModestFolderViewPrivate *priv = NULL;
2192 GtkTreeSelection *sel;
2194 if (!MODEST_IS_FOLDER_VIEW(self))
2197 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2199 if (priv->folder_to_select) {
2201 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2204 path = gtk_tree_model_get_path (tree_model, iter);
2205 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2207 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2209 gtk_tree_selection_select_iter (sel, iter);
2210 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2212 gtk_tree_path_free (path);
2217 modest_folder_view_disable_next_folder_selection (self);
2218 /* g_object_unref (priv->folder_to_select); */
2219 /* priv->folder_to_select = NULL; */
2225 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2227 ModestFolderViewPrivate *priv = NULL;
2229 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2230 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2232 if (priv->folder_to_select)
2233 g_object_unref(priv->folder_to_select);
2235 priv->folder_to_select = NULL;
2239 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2240 gboolean after_change)
2242 GtkTreeModel *model;
2243 GtkTreeIter iter, folder_iter;
2244 GtkTreeSelection *sel;
2245 ModestFolderViewPrivate *priv = NULL;
2247 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2248 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2250 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2254 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2255 gtk_tree_selection_unselect_all (sel);
2257 if (priv->folder_to_select)
2258 g_object_unref(priv->folder_to_select);
2259 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2263 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2268 gtk_tree_model_get_iter_first (model, &iter);
2269 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2272 path = gtk_tree_model_get_path (model, &folder_iter);
2273 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2275 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2276 gtk_tree_selection_select_iter (sel, &folder_iter);
2277 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2279 gtk_tree_path_free (path);
2287 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2289 /* Copy selection */
2290 _clipboard_set_selected_data (folder_view, FALSE);
2294 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2296 ModestFolderViewPrivate *priv = NULL;
2297 GtkTreeModel *model = NULL;
2298 const gchar **hidding = NULL;
2299 guint i, n_selected;
2301 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2302 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2304 /* Copy selection */
2305 if (!_clipboard_set_selected_data (folder_view, TRUE))
2308 /* Get hidding ids */
2309 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2311 /* Clear hidding array created by previous cut operation */
2312 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2314 /* Copy hidding array */
2315 priv->n_selected = n_selected;
2316 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2317 for (i=0; i < n_selected; i++)
2318 priv->hidding_ids[i] = g_strdup(hidding[i]);
2320 /* Hide cut folders */
2321 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2322 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2326 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2327 ModestFolderView *folder_view_dst)
2329 GtkTreeModel *filter_model = NULL;
2330 GtkTreeModel *model = NULL;
2331 GtkTreeModel *new_filter_model = NULL;
2333 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2334 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2337 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2338 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2340 /* Build new filter model */
2341 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2342 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2346 /* Set copied model */
2347 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2350 g_object_unref (new_filter_model);
2354 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2357 GtkTreeModel *model = NULL;
2358 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2359 priv->show_non_move = show;
2360 /* modest_folder_view_update_model(folder_view, */
2361 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2363 /* Hide special folders */
2364 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2365 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2366 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2370 /* Returns FALSE if it did not selected anything */
2372 _clipboard_set_selected_data (ModestFolderView *folder_view,
2375 ModestFolderViewPrivate *priv = NULL;
2376 TnyFolderStore *folder = NULL;
2377 gboolean retval = FALSE;
2379 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2380 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2382 /* Set selected data on clipboard */
2383 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2384 folder = modest_folder_view_get_selected (folder_view);
2386 /* Do not allow to select an account */
2387 if (TNY_IS_FOLDER (folder)) {
2388 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2393 g_object_unref (folder);
2399 _clear_hidding_filter (ModestFolderView *folder_view)
2401 ModestFolderViewPrivate *priv;
2404 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2405 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2407 if (priv->hidding_ids != NULL) {
2408 for (i=0; i < priv->n_selected; i++)
2409 g_free (priv->hidding_ids[i]);
2410 g_free(priv->hidding_ids);