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 <tny-camel-account.h>
45 #include <modest-tny-account.h>
46 #include <modest-tny-folder.h>
47 #include <modest-tny-local-folders-account.h>
48 #include <modest-tny-outbox-account.h>
49 #include <modest-marshal.h>
50 #include <modest-icon-names.h>
51 #include <modest-tny-account-store.h>
52 #include <modest-text-utils.h>
53 #include <modest-runtime.h>
54 #include "modest-folder-view.h"
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
58 #include "modest-dnd.h"
59 #include "widgets/modest-window.h"
61 /* Folder view drag types */
62 const GtkTargetEntry folder_view_drag_types[] =
64 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
65 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
68 /* 'private'/'protected' functions */
69 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
70 static void modest_folder_view_init (ModestFolderView *obj);
71 static void modest_folder_view_finalize (GObject *obj);
73 static void tny_account_store_view_init (gpointer g,
76 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
77 TnyAccountStore *account_store);
79 static void on_selection_changed (GtkTreeSelection *sel,
82 static void on_account_removed (TnyAccountStore *self,
86 static void on_account_inserted (TnyAccountStore *self,
90 static void on_account_changed (TnyAccountStore *self,
94 static gint cmp_rows (GtkTreeModel *tree_model,
99 static gboolean filter_row (GtkTreeModel *model,
103 static gboolean on_key_pressed (GtkWidget *self,
107 static void on_configuration_key_changed (ModestConf* conf,
109 ModestConfEvent event,
110 ModestConfNotificationId notification_id,
111 ModestFolderView *self);
114 static void on_drag_data_get (GtkWidget *widget,
115 GdkDragContext *context,
116 GtkSelectionData *selection_data,
121 static void on_drag_data_received (GtkWidget *widget,
122 GdkDragContext *context,
125 GtkSelectionData *selection_data,
130 static gboolean on_drag_motion (GtkWidget *widget,
131 GdkDragContext *context,
137 static void expand_root_items (ModestFolderView *self);
139 static gint expand_row_timeout (gpointer data);
141 static void setup_drag_and_drop (GtkTreeView *self);
143 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
146 static void _clear_hidding_filter (ModestFolderView *folder_view);
148 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
151 ModestFolderView *self);
153 static void on_display_name_changed (ModestAccountMgr *self,
154 const gchar *account,
158 FOLDER_SELECTION_CHANGED_SIGNAL,
159 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
163 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
164 struct _ModestFolderViewPrivate {
165 TnyAccountStore *account_store;
166 TnyFolderStore *cur_folder_store;
168 TnyFolder *folder_to_select; /* folder to select after the next update */
170 gulong changed_signal;
171 gulong account_inserted_signal;
172 gulong account_removed_signal;
173 gulong account_changed_signal;
174 gulong conf_key_signal;
175 gulong display_name_changed_signal;
177 /* not unref this object, its a singlenton */
178 ModestEmailClipboard *clipboard;
180 /* Filter tree model */
184 TnyFolderStoreQuery *query;
185 guint timer_expander;
187 gchar *local_account_name;
188 gchar *visible_account_id;
189 ModestFolderViewStyle style;
191 gboolean reselect; /* we use this to force a reselection of the INBOX */
192 gboolean show_non_move;
193 gboolean reexpand; /* next time we expose, we'll expand all root folders */
195 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
196 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
197 MODEST_TYPE_FOLDER_VIEW, \
198 ModestFolderViewPrivate))
200 static GObjectClass *parent_class = NULL;
202 static guint signals[LAST_SIGNAL] = {0};
205 modest_folder_view_get_type (void)
207 static GType my_type = 0;
209 static const GTypeInfo my_info = {
210 sizeof(ModestFolderViewClass),
211 NULL, /* base init */
212 NULL, /* base finalize */
213 (GClassInitFunc) modest_folder_view_class_init,
214 NULL, /* class finalize */
215 NULL, /* class data */
216 sizeof(ModestFolderView),
218 (GInstanceInitFunc) modest_folder_view_init,
222 static const GInterfaceInfo tny_account_store_view_info = {
223 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
224 NULL, /* interface_finalize */
225 NULL /* interface_data */
229 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
233 g_type_add_interface_static (my_type,
234 TNY_TYPE_ACCOUNT_STORE_VIEW,
235 &tny_account_store_view_info);
241 modest_folder_view_class_init (ModestFolderViewClass *klass)
243 GObjectClass *gobject_class;
244 gobject_class = (GObjectClass*) klass;
246 parent_class = g_type_class_peek_parent (klass);
247 gobject_class->finalize = modest_folder_view_finalize;
249 g_type_class_add_private (gobject_class,
250 sizeof(ModestFolderViewPrivate));
252 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
253 g_signal_new ("folder_selection_changed",
254 G_TYPE_FROM_CLASS (gobject_class),
256 G_STRUCT_OFFSET (ModestFolderViewClass,
257 folder_selection_changed),
259 modest_marshal_VOID__POINTER_BOOLEAN,
260 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
263 * This signal is emitted whenever the currently selected
264 * folder display name is computed. Note that the name could
265 * be different to the folder name, because we could append
266 * the unread messages count to the folder name to build the
267 * folder display name
269 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
270 g_signal_new ("folder-display-name-changed",
271 G_TYPE_FROM_CLASS (gobject_class),
273 G_STRUCT_OFFSET (ModestFolderViewClass,
274 folder_display_name_changed),
276 g_cclosure_marshal_VOID__STRING,
277 G_TYPE_NONE, 1, G_TYPE_STRING);
280 /* Simplify checks for NULLs: */
282 strings_are_equal (const gchar *a, const gchar *b)
288 return (strcmp (a, b) == 0);
295 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
297 GObject *instance = NULL;
299 gtk_tree_model_get (model, iter,
300 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
304 return FALSE; /* keep walking */
306 if (!TNY_IS_ACCOUNT (instance)) {
307 g_object_unref (instance);
308 return FALSE; /* keep walking */
311 /* Check if this is the looked-for account: */
312 TnyAccount *this_account = TNY_ACCOUNT (instance);
313 TnyAccount *account = TNY_ACCOUNT (data);
315 const gchar *this_account_id = tny_account_get_id(this_account);
316 const gchar *account_id = tny_account_get_id(account);
317 g_object_unref (instance);
320 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
321 if (strings_are_equal(this_account_id, account_id)) {
322 /* Tell the model that the data has changed, so that
323 * it calls the cell_data_func callbacks again: */
324 /* TODO: This does not seem to actually cause the new string to be shown: */
325 gtk_tree_model_row_changed (model, path, iter);
327 return TRUE; /* stop walking */
330 return FALSE; /* keep walking */
335 ModestFolderView *self;
336 gchar *previous_name;
337 } GetMmcAccountNameData;
340 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
342 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
344 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
346 if (!strings_are_equal (
347 tny_account_get_name(TNY_ACCOUNT(account)),
348 data->previous_name)) {
350 /* Tell the model that the data has changed, so that
351 * it calls the cell_data_func callbacks again: */
352 ModestFolderView *self = data->self;
353 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
355 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
358 g_free (data->previous_name);
359 g_slice_free (GetMmcAccountNameData, data);
363 text_cell_data (GtkTreeViewColumn *column,
364 GtkCellRenderer *renderer,
365 GtkTreeModel *tree_model,
369 ModestFolderViewPrivate *priv;
370 GObject *rendobj = (GObject *) renderer;
372 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
373 GObject *instance = NULL;
375 gtk_tree_model_get (tree_model, iter,
376 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
377 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
378 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
389 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
390 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
392 gchar *item_name = NULL;
393 gint item_weight = 400;
395 if (type != TNY_FOLDER_TYPE_ROOT) {
398 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
399 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
400 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
401 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
403 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
407 /* note: we cannot reliably get the counts from the tree model, we need
408 * to use explicit calls on tny_folder for some reason.
410 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
411 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
412 (type == TNY_FOLDER_TYPE_OUTBOX) ||
413 (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
414 number = tny_folder_get_all_count (TNY_FOLDER(instance));
416 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
418 /* Use bold font style if there are unread or unset messages */
420 item_name = g_strdup_printf ("%s (%d)", fname, number);
423 item_name = g_strdup (fname);
427 } else if (TNY_IS_ACCOUNT (instance)) {
428 /* If it's a server account */
429 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
430 item_name = g_strdup (priv->local_account_name);
432 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
433 /* fname is only correct when the items are first
434 * added to the model, not when the account is
435 * changed later, so get the name from the account
437 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
440 item_name = g_strdup (fname);
446 item_name = g_strdup ("unknown");
448 if (item_name && item_weight) {
449 /* Set the name in the treeview cell: */
450 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
452 /* Notify display name observers */
453 /* TODO: What listens for this signal, and how can it use only the new name? */
454 if (((GObject *) priv->cur_folder_store) == instance) {
455 g_signal_emit (G_OBJECT(self),
456 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
463 /* If it is a Memory card account, make sure that we have the correct name.
464 * This function will be trigerred again when the name has been retrieved: */
465 if (TNY_IS_STORE_ACCOUNT (instance) &&
466 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
468 /* Get the account name asynchronously: */
469 GetMmcAccountNameData *callback_data =
470 g_slice_new0(GetMmcAccountNameData);
471 callback_data->self = self;
473 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
475 callback_data->previous_name = g_strdup (name);
477 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
478 on_get_mmc_account_name, callback_data);
481 g_object_unref (G_OBJECT (instance));
488 GdkPixbuf *pixbuf_open;
489 GdkPixbuf *pixbuf_close;
494 get_folder_icons (TnyFolderType type, GObject *instance)
496 GdkPixbuf *pixbuf = NULL;
497 GdkPixbuf *pixbuf_open = NULL;
498 GdkPixbuf *pixbuf_close = NULL;
499 ThreePixbufs *retval = g_slice_new (ThreePixbufs);
501 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
502 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
503 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
504 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
505 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
507 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
508 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
509 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
510 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
511 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
513 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
514 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
515 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
516 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
517 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
520 /* MERGE is not needed anymore as the folder now has the correct type jschmid */
521 /* We include the MERGE type here because it's used to create
522 the local OUTBOX folder */
523 if (type == TNY_FOLDER_TYPE_NORMAL ||
524 type == TNY_FOLDER_TYPE_UNKNOWN) {
525 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
529 case TNY_FOLDER_TYPE_INVALID:
530 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
533 case TNY_FOLDER_TYPE_ROOT:
534 if (TNY_IS_ACCOUNT (instance)) {
536 if (modest_tny_account_is_virtual_local_folders (
537 TNY_ACCOUNT (instance))) {
540 avirt_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS));
542 if (!avirt_pixbuf_open) {
543 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
544 avirt_pixbuf_open = gdk_pixbuf_copy (avirt_pixbuf);
545 gdk_pixbuf_composite (emblem, avirt_pixbuf_open, 0, 0,
546 MIN (gdk_pixbuf_get_width (emblem),
547 gdk_pixbuf_get_width (avirt_pixbuf_open)),
548 MIN (gdk_pixbuf_get_height (emblem),
549 gdk_pixbuf_get_height (avirt_pixbuf_open)),
550 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
551 g_object_unref (emblem);
554 if (!avirt_pixbuf_close) {
555 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
556 avirt_pixbuf_close = gdk_pixbuf_copy (avirt_pixbuf);
557 gdk_pixbuf_composite (emblem, avirt_pixbuf_close, 0, 0,
558 MIN (gdk_pixbuf_get_width (emblem),
559 gdk_pixbuf_get_width (avirt_pixbuf_close)),
560 MIN (gdk_pixbuf_get_height (emblem),
561 gdk_pixbuf_get_height (avirt_pixbuf_close)),
562 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
563 g_object_unref (emblem);
567 pixbuf = g_object_ref (avirt_pixbuf);
568 pixbuf_open = g_object_ref (avirt_pixbuf_open);
569 pixbuf_close = g_object_ref (avirt_pixbuf_close);
573 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
575 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
577 ammc_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_MMC));
579 if (!ammc_pixbuf_open) {
580 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
581 ammc_pixbuf_open = gdk_pixbuf_copy (ammc_pixbuf);
582 gdk_pixbuf_composite (emblem, ammc_pixbuf_open, 0, 0,
583 MIN (gdk_pixbuf_get_width (emblem),
584 gdk_pixbuf_get_width (ammc_pixbuf_open)),
585 MIN (gdk_pixbuf_get_height (emblem),
586 gdk_pixbuf_get_height (ammc_pixbuf_open)),
587 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
588 g_object_unref (emblem);
591 if (!ammc_pixbuf_close) {
592 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
593 ammc_pixbuf_close = gdk_pixbuf_copy (ammc_pixbuf);
594 gdk_pixbuf_composite (emblem, ammc_pixbuf_close, 0, 0,
595 MIN (gdk_pixbuf_get_width (emblem),
596 gdk_pixbuf_get_width (ammc_pixbuf_close)),
597 MIN (gdk_pixbuf_get_height (emblem),
598 gdk_pixbuf_get_height (ammc_pixbuf_close)),
599 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
600 g_object_unref (emblem);
604 pixbuf = g_object_ref (ammc_pixbuf);
605 pixbuf_open = g_object_ref (ammc_pixbuf_open);
606 pixbuf_close = g_object_ref (ammc_pixbuf_close);
611 anorm_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT));
613 if (!anorm_pixbuf_open) {
614 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
615 anorm_pixbuf_open = gdk_pixbuf_copy (anorm_pixbuf);
616 gdk_pixbuf_composite (emblem, anorm_pixbuf_open, 0, 0,
617 MIN (gdk_pixbuf_get_width (emblem),
618 gdk_pixbuf_get_width (anorm_pixbuf_open)),
619 MIN (gdk_pixbuf_get_height (emblem),
620 gdk_pixbuf_get_height (anorm_pixbuf_open)),
621 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
622 g_object_unref (emblem);
625 if (!anorm_pixbuf_close) {
626 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
627 anorm_pixbuf_close = gdk_pixbuf_copy (anorm_pixbuf);
628 gdk_pixbuf_composite (emblem, anorm_pixbuf_close, 0, 0,
629 MIN (gdk_pixbuf_get_width (emblem),
630 gdk_pixbuf_get_width (anorm_pixbuf_close)),
631 MIN (gdk_pixbuf_get_height (emblem),
632 gdk_pixbuf_get_height (anorm_pixbuf_close)),
633 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
634 g_object_unref (emblem);
638 pixbuf = g_object_ref (anorm_pixbuf);
639 pixbuf_open = g_object_ref (anorm_pixbuf_open);
640 pixbuf_close = g_object_ref (anorm_pixbuf_close);
646 case TNY_FOLDER_TYPE_INBOX:
649 inbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX));
651 if (!inbox_pixbuf_open) {
652 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
653 inbox_pixbuf_open = gdk_pixbuf_copy (inbox_pixbuf);
654 gdk_pixbuf_composite (emblem, inbox_pixbuf_open, 0, 0,
655 MIN (gdk_pixbuf_get_width (emblem),
656 gdk_pixbuf_get_width (inbox_pixbuf_open)),
657 MIN (gdk_pixbuf_get_height (emblem),
658 gdk_pixbuf_get_height (inbox_pixbuf_open)),
659 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
660 g_object_unref (emblem);
663 if (!inbox_pixbuf_close) {
664 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
665 inbox_pixbuf_close = gdk_pixbuf_copy (inbox_pixbuf);
666 gdk_pixbuf_composite (emblem, inbox_pixbuf_close, 0, 0,
667 MIN (gdk_pixbuf_get_width (emblem),
668 gdk_pixbuf_get_width (inbox_pixbuf_close)),
669 MIN (gdk_pixbuf_get_height (emblem),
670 gdk_pixbuf_get_height (inbox_pixbuf_close)),
671 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
672 g_object_unref (emblem);
676 pixbuf = g_object_ref (inbox_pixbuf);
677 pixbuf_open = g_object_ref (inbox_pixbuf_open);
678 pixbuf_close = g_object_ref (inbox_pixbuf_close);
681 case TNY_FOLDER_TYPE_OUTBOX:
683 outbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX));
685 if (!outbox_pixbuf_open) {
686 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
687 outbox_pixbuf_open = gdk_pixbuf_copy (outbox_pixbuf);
688 gdk_pixbuf_composite (emblem, outbox_pixbuf_open, 0, 0,
689 MIN (gdk_pixbuf_get_width (emblem),
690 gdk_pixbuf_get_width (outbox_pixbuf_open)),
691 MIN (gdk_pixbuf_get_height (emblem),
692 gdk_pixbuf_get_height (outbox_pixbuf_open)),
693 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
694 g_object_unref (emblem);
697 if (!outbox_pixbuf_close) {
698 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
699 outbox_pixbuf_close = gdk_pixbuf_copy (outbox_pixbuf);
700 gdk_pixbuf_composite (emblem, outbox_pixbuf_close, 0, 0,
701 MIN (gdk_pixbuf_get_width (emblem),
702 gdk_pixbuf_get_width (outbox_pixbuf_close)),
703 MIN (gdk_pixbuf_get_height (emblem),
704 gdk_pixbuf_get_height (outbox_pixbuf_close)),
705 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
706 g_object_unref (emblem);
710 pixbuf = g_object_ref (outbox_pixbuf);
711 pixbuf_open = g_object_ref (outbox_pixbuf_open);
712 pixbuf_close = g_object_ref (outbox_pixbuf_close);
715 case TNY_FOLDER_TYPE_JUNK:
717 junk_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK));
719 if (!junk_pixbuf_open) {
720 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
721 junk_pixbuf_open = gdk_pixbuf_copy (junk_pixbuf);
722 gdk_pixbuf_composite (emblem, junk_pixbuf_open, 0, 0,
723 MIN (gdk_pixbuf_get_width (emblem),
724 gdk_pixbuf_get_width (junk_pixbuf_open)),
725 MIN (gdk_pixbuf_get_height (emblem),
726 gdk_pixbuf_get_height (junk_pixbuf_open)),
727 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
728 g_object_unref (emblem);
731 if (!junk_pixbuf_close) {
732 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
733 junk_pixbuf_close = gdk_pixbuf_copy (junk_pixbuf);
734 gdk_pixbuf_composite (emblem, junk_pixbuf_close, 0, 0,
735 MIN (gdk_pixbuf_get_width (emblem),
736 gdk_pixbuf_get_width (junk_pixbuf_close)),
737 MIN (gdk_pixbuf_get_height (emblem),
738 gdk_pixbuf_get_height (junk_pixbuf_close)),
739 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
740 g_object_unref (emblem);
744 pixbuf = g_object_ref (junk_pixbuf);
745 pixbuf_open = g_object_ref (junk_pixbuf_open);
746 pixbuf_close = g_object_ref (junk_pixbuf_close);
749 case TNY_FOLDER_TYPE_SENT:
751 sent_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_SENT));
753 if (!sent_pixbuf_open) {
754 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
755 sent_pixbuf_open = gdk_pixbuf_copy (sent_pixbuf);
756 gdk_pixbuf_composite (emblem, sent_pixbuf_open, 0, 0,
757 MIN (gdk_pixbuf_get_width (emblem),
758 gdk_pixbuf_get_width (sent_pixbuf_open)),
759 MIN (gdk_pixbuf_get_height (emblem),
760 gdk_pixbuf_get_height (sent_pixbuf_open)),
761 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
762 g_object_unref (emblem);
765 if (!sent_pixbuf_close) {
766 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
767 sent_pixbuf_close = gdk_pixbuf_copy (sent_pixbuf);
768 gdk_pixbuf_composite (emblem, sent_pixbuf_close, 0, 0,
769 MIN (gdk_pixbuf_get_width (emblem),
770 gdk_pixbuf_get_width (sent_pixbuf_close)),
771 MIN (gdk_pixbuf_get_height (emblem),
772 gdk_pixbuf_get_height (sent_pixbuf_close)),
773 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
774 g_object_unref (emblem);
778 pixbuf = g_object_ref (sent_pixbuf);
779 pixbuf_open = g_object_ref (sent_pixbuf_open);
780 pixbuf_close = g_object_ref (sent_pixbuf_close);
783 case TNY_FOLDER_TYPE_TRASH:
785 trash_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH));
787 if (!trash_pixbuf_open) {
788 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
789 trash_pixbuf_open = gdk_pixbuf_copy (trash_pixbuf);
790 gdk_pixbuf_composite (emblem, trash_pixbuf_open, 0, 0,
791 MIN (gdk_pixbuf_get_width (emblem),
792 gdk_pixbuf_get_width (trash_pixbuf_open)),
793 MIN (gdk_pixbuf_get_height (emblem),
794 gdk_pixbuf_get_height (trash_pixbuf_open)),
795 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
796 g_object_unref (emblem);
799 if (!trash_pixbuf_close) {
800 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
801 trash_pixbuf_close = gdk_pixbuf_copy (trash_pixbuf);
802 gdk_pixbuf_composite (emblem, trash_pixbuf_close, 0, 0,
803 MIN (gdk_pixbuf_get_width (emblem),
804 gdk_pixbuf_get_width (trash_pixbuf_close)),
805 MIN (gdk_pixbuf_get_height (emblem),
806 gdk_pixbuf_get_height (trash_pixbuf_close)),
807 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
808 g_object_unref (emblem);
812 pixbuf = g_object_ref (trash_pixbuf);
813 pixbuf_open = g_object_ref (trash_pixbuf_open);
814 pixbuf_close = g_object_ref (trash_pixbuf_close);
817 case TNY_FOLDER_TYPE_DRAFTS:
819 draft_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS));
821 if (!draft_pixbuf_open) {
822 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
823 draft_pixbuf_open = gdk_pixbuf_copy (draft_pixbuf);
824 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0,
825 MIN (gdk_pixbuf_get_width (emblem),
826 gdk_pixbuf_get_width (draft_pixbuf_open)),
827 MIN (gdk_pixbuf_get_height (emblem),
828 gdk_pixbuf_get_height (draft_pixbuf_open)),
829 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
830 g_object_unref (emblem);
833 if (!draft_pixbuf_close) {
834 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
835 draft_pixbuf_close = gdk_pixbuf_copy (draft_pixbuf);
836 gdk_pixbuf_composite (emblem, draft_pixbuf_close, 0, 0,
837 MIN (gdk_pixbuf_get_width (emblem),
838 gdk_pixbuf_get_width (draft_pixbuf_close)),
839 MIN (gdk_pixbuf_get_height (emblem),
840 gdk_pixbuf_get_height (draft_pixbuf_close)),
841 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
842 g_object_unref (emblem);
846 pixbuf = g_object_ref (draft_pixbuf);
847 pixbuf_open = g_object_ref (draft_pixbuf_open);
848 pixbuf_close = g_object_ref (draft_pixbuf_close);
851 case TNY_FOLDER_TYPE_NORMAL:
854 normal_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL));
856 if (!normal_pixbuf_open) {
857 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
858 normal_pixbuf_open = gdk_pixbuf_copy (normal_pixbuf);
859 gdk_pixbuf_composite (emblem, normal_pixbuf_open, 0, 0,
860 MIN (gdk_pixbuf_get_width (emblem),
861 gdk_pixbuf_get_width (normal_pixbuf_open)),
862 MIN (gdk_pixbuf_get_height (emblem),
863 gdk_pixbuf_get_height (normal_pixbuf_open)),
864 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
865 g_object_unref (emblem);
868 if (!normal_pixbuf_close) {
869 GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
870 normal_pixbuf_close = gdk_pixbuf_copy (normal_pixbuf);
871 gdk_pixbuf_composite (emblem, normal_pixbuf_close, 0, 0,
872 MIN (gdk_pixbuf_get_width (emblem),
873 gdk_pixbuf_get_width (normal_pixbuf_close)),
874 MIN (gdk_pixbuf_get_height (emblem),
875 gdk_pixbuf_get_height (normal_pixbuf_close)),
876 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
877 g_object_unref (emblem);
881 pixbuf = g_object_ref (normal_pixbuf);
882 pixbuf_open = g_object_ref (normal_pixbuf_open);
883 pixbuf_close = g_object_ref (normal_pixbuf_close);
889 retval->pixbuf = pixbuf;
890 retval->pixbuf_open = pixbuf_open;
891 retval->pixbuf_close = pixbuf_close;
898 free_pixbufs (ThreePixbufs *pixbufs)
900 g_object_unref (pixbufs->pixbuf);
901 g_object_unref (pixbufs->pixbuf_open);
902 g_object_unref (pixbufs->pixbuf_close);
903 g_slice_free (ThreePixbufs, pixbufs);
907 icon_cell_data (GtkTreeViewColumn *column,
908 GtkCellRenderer *renderer,
909 GtkTreeModel *tree_model,
913 GObject *rendobj = NULL, *instance = NULL;
914 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
915 gboolean has_children;
916 ThreePixbufs *pixbufs;
918 rendobj = (GObject *) renderer;
920 gtk_tree_model_get (tree_model, iter,
921 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
922 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
928 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
929 pixbufs = get_folder_icons (type, instance);
930 g_object_unref (instance);
933 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
936 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
937 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
940 free_pixbufs (pixbufs);
946 add_columns (GtkWidget *treeview)
948 GtkTreeViewColumn *column;
949 GtkCellRenderer *renderer;
950 GtkTreeSelection *sel;
953 column = gtk_tree_view_column_new ();
955 /* Set icon and text render function */
956 renderer = gtk_cell_renderer_pixbuf_new();
957 gtk_tree_view_column_pack_start (column, renderer, FALSE);
958 gtk_tree_view_column_set_cell_data_func(column, renderer,
959 icon_cell_data, treeview, NULL);
961 renderer = gtk_cell_renderer_text_new();
962 g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END,
963 "ellipsize-set", TRUE, NULL);
964 gtk_tree_view_column_pack_start (column, renderer, TRUE);
965 gtk_tree_view_column_set_cell_data_func(column, renderer,
966 text_cell_data, treeview, NULL);
968 /* Set selection mode */
969 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
970 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
972 /* Set treeview appearance */
973 gtk_tree_view_column_set_spacing (column, 2);
974 gtk_tree_view_column_set_resizable (column, TRUE);
975 gtk_tree_view_column_set_fixed_width (column, TRUE);
976 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
977 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
980 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
984 modest_folder_view_init (ModestFolderView *obj)
986 ModestFolderViewPrivate *priv;
989 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
991 priv->timer_expander = 0;
992 priv->account_store = NULL;
994 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
995 priv->cur_folder_store = NULL;
996 priv->visible_account_id = NULL;
997 priv->folder_to_select = NULL;
999 priv->reexpand = TRUE;
1001 /* Initialize the local account name */
1002 conf = modest_runtime_get_conf();
1003 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1005 /* Init email clipboard */
1006 priv->clipboard = modest_runtime_get_email_clipboard ();
1007 priv->hidding_ids = NULL;
1008 priv->n_selected = 0;
1009 priv->reselect = FALSE;
1010 priv->show_non_move = TRUE;
1012 /* Build treeview */
1013 add_columns (GTK_WIDGET (obj));
1015 /* Setup drag and drop */
1016 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1018 /* Connect signals */
1019 g_signal_connect (G_OBJECT (obj),
1021 G_CALLBACK (on_key_pressed), NULL);
1023 priv->display_name_changed_signal =
1024 g_signal_connect (modest_runtime_get_account_mgr (),
1025 "display_name_changed",
1026 G_CALLBACK (on_display_name_changed),
1030 * Track changes in the local account name (in the device it
1031 * will be the device name)
1033 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1035 G_CALLBACK(on_configuration_key_changed),
1040 tny_account_store_view_init (gpointer g, gpointer iface_data)
1042 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1044 klass->set_account_store_func = modest_folder_view_set_account_store;
1050 modest_folder_view_finalize (GObject *obj)
1052 ModestFolderViewPrivate *priv;
1053 GtkTreeSelection *sel;
1055 g_return_if_fail (obj);
1057 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1059 if (priv->timer_expander != 0) {
1060 g_source_remove (priv->timer_expander);
1061 priv->timer_expander = 0;
1064 if (priv->account_store) {
1065 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1066 priv->account_inserted_signal);
1067 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1068 priv->account_removed_signal);
1069 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1070 priv->account_changed_signal);
1071 g_object_unref (G_OBJECT(priv->account_store));
1072 priv->account_store = NULL;
1076 g_object_unref (G_OBJECT (priv->query));
1080 /* modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1081 if (priv->folder_to_select) {
1082 g_object_unref (G_OBJECT(priv->folder_to_select));
1083 priv->folder_to_select = NULL;
1086 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1088 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1090 g_free (priv->local_account_name);
1091 g_free (priv->visible_account_id);
1093 if (priv->conf_key_signal) {
1094 g_signal_handler_disconnect (modest_runtime_get_conf (),
1095 priv->conf_key_signal);
1096 priv->conf_key_signal = 0;
1099 if (priv->cur_folder_store) {
1100 if (TNY_IS_FOLDER(priv->cur_folder_store))
1101 tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
1102 /* FALSE --> expunge the message */
1104 g_object_unref (priv->cur_folder_store);
1105 priv->cur_folder_store = NULL;
1108 /* Clear hidding array created by cut operation */
1109 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1111 G_OBJECT_CLASS(parent_class)->finalize (obj);
1116 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1118 ModestFolderViewPrivate *priv;
1121 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1122 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1124 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1125 device = tny_account_store_get_device (account_store);
1127 if (G_UNLIKELY (priv->account_store)) {
1129 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1130 priv->account_inserted_signal))
1131 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1132 priv->account_inserted_signal);
1133 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1134 priv->account_removed_signal))
1135 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1136 priv->account_removed_signal);
1137 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1138 priv->account_changed_signal))
1139 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1140 priv->account_changed_signal);
1141 g_object_unref (G_OBJECT (priv->account_store));
1144 priv->account_store = g_object_ref (G_OBJECT (account_store));
1146 priv->account_removed_signal =
1147 g_signal_connect (G_OBJECT(account_store), "account_removed",
1148 G_CALLBACK (on_account_removed), self);
1150 priv->account_inserted_signal =
1151 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1152 G_CALLBACK (on_account_inserted), self);
1154 priv->account_changed_signal =
1155 g_signal_connect (G_OBJECT(account_store), "account_changed",
1156 G_CALLBACK (on_account_changed), self);
1158 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1160 g_object_unref (G_OBJECT (device));
1164 on_connection_status_changed (TnyAccount *self,
1165 TnyConnectionStatus status,
1168 /* If the account becomes online then refresh it */
1169 if (status == TNY_CONNECTION_STATUS_CONNECTED) {
1170 const gchar *acc_name;
1171 GtkWidget *my_window;
1173 my_window = gtk_widget_get_ancestor (GTK_WIDGET (user_data), MODEST_TYPE_WINDOW);
1174 acc_name = modest_tny_account_get_parent_modest_account_name_for_server_account (self);
1175 modest_ui_actions_do_send_receive (acc_name, MODEST_WINDOW (my_window));
1180 on_account_inserted (TnyAccountStore *account_store,
1181 TnyAccount *account,
1184 ModestFolderViewPrivate *priv;
1185 GtkTreeModel *sort_model, *filter_model;
1187 /* Ignore transport account insertions, we're not showing them
1188 in the folder view */
1189 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1192 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1194 /* If we're adding a new account, and there is no previous
1195 one, we need to select the visible server account */
1196 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1197 !priv->visible_account_id)
1198 modest_widget_memory_restore (modest_runtime_get_conf(),
1199 G_OBJECT (user_data),
1200 MODEST_CONF_FOLDER_VIEW_KEY);
1202 /* Get the inner model */
1203 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1204 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1206 /* Insert the account in the model */
1207 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1208 G_OBJECT (account));
1211 /* When the store account gets online refresh it */
1212 g_signal_connect (account, "connection_status_changed",
1213 G_CALLBACK (on_connection_status_changed),
1214 MODEST_FOLDER_VIEW (user_data));
1216 /* Refilter the model */
1217 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1222 on_account_changed (TnyAccountStore *account_store,
1223 TnyAccount *tny_account,
1226 ModestFolderViewPrivate *priv;
1227 GtkTreeModel *sort_model, *filter_model;
1229 /* Ignore transport account insertions, we're not showing them
1230 in the folder view */
1231 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1234 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1236 /* Get the inner model */
1237 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1238 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1240 /* Remove the account from the model */
1241 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1242 G_OBJECT (tny_account));
1244 /* Insert the account in the model */
1245 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1246 G_OBJECT (tny_account));
1248 /* Refilter the model */
1249 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1254 * Selects the first inbox or the local account in an idle
1257 on_idle_select_first_inbox_or_local (gpointer user_data)
1259 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1261 modest_folder_view_select_first_inbox_or_local (self);
1268 on_account_removed (TnyAccountStore *account_store,
1269 TnyAccount *account,
1272 ModestFolderView *self = NULL;
1273 ModestFolderViewPrivate *priv;
1274 GtkTreeModel *sort_model, *filter_model;
1275 GtkTreeSelection *sel = NULL;
1276 gboolean same_account_selected = FALSE;
1278 /* Ignore transport account removals, we're not showing them
1279 in the folder view */
1280 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1283 self = MODEST_FOLDER_VIEW (user_data);
1284 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1286 /* Invalidate the cur_folder_store only if the selected folder
1287 belongs to the account that is being removed */
1288 if (priv->cur_folder_store) {
1289 TnyAccount *selected_folder_account = NULL;
1291 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1292 selected_folder_account =
1293 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1295 selected_folder_account =
1296 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1299 if (selected_folder_account == account) {
1300 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1301 gtk_tree_selection_unselect_all (sel);
1302 same_account_selected = TRUE;
1304 g_object_unref (selected_folder_account);
1307 /* Invalidate row to select only if the folder to select
1308 belongs to the account that is being removed*/
1309 if (priv->folder_to_select) {
1310 TnyAccount *folder_to_select_account = NULL;
1312 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1313 if (folder_to_select_account == account) {
1314 modest_folder_view_disable_next_folder_selection (self);
1315 g_object_unref (priv->folder_to_select);
1316 priv->folder_to_select = NULL;
1318 g_object_unref (folder_to_select_account);
1321 /* Remove the account from the model */
1322 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1323 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1324 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1325 G_OBJECT (account));
1327 /* If the removed account is the currently viewed one then
1328 clear the configuration value. The new visible account will be the default account */
1329 if (priv->visible_account_id &&
1330 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1332 /* Clear the current visible account_id */
1333 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1335 /* Call the restore method, this will set the new visible account */
1336 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1337 MODEST_CONF_FOLDER_VIEW_KEY);
1340 /* Refilter the model */
1341 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1343 /* Select the first INBOX if the currently selected folder
1344 belongs to the account that is being deleted */
1345 if (same_account_selected)
1346 g_idle_add (on_idle_select_first_inbox_or_local, self);
1350 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1352 GtkTreeViewColumn *col;
1354 g_return_if_fail (self);
1356 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1358 g_printerr ("modest: failed get column for title\n");
1362 gtk_tree_view_column_set_title (col, title);
1363 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1368 modest_folder_view_on_map (ModestFolderView *self,
1369 GdkEventExpose *event,
1372 ModestFolderViewPrivate *priv;
1374 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1376 /* This won't happen often */
1377 if (G_UNLIKELY (priv->reselect)) {
1378 /* Select the first inbox or the local account if not found */
1380 /* TODO: this could cause a lock at startup, so we
1381 comment it for the moment. We know that this will
1382 be a bug, because the INBOX is not selected, but we
1383 need to rewrite some parts of Modest to avoid the
1384 deathlock situation */
1385 /* TODO: check if this is still the case */
1386 priv->reselect = FALSE;
1387 modest_folder_view_select_first_inbox_or_local (self);
1388 /* Notify the display name observers */
1389 g_signal_emit (G_OBJECT(self),
1390 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1394 if (priv->reexpand) {
1395 expand_root_items (self);
1396 priv->reexpand = FALSE;
1403 modest_folder_view_new (TnyFolderStoreQuery *query)
1406 ModestFolderViewPrivate *priv;
1407 GtkTreeSelection *sel;
1409 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1410 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1413 priv->query = g_object_ref (query);
1415 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1416 priv->changed_signal = g_signal_connect (sel, "changed",
1417 G_CALLBACK (on_selection_changed), self);
1419 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1421 return GTK_WIDGET(self);
1424 /* this feels dirty; any other way to expand all the root items? */
1426 expand_root_items (ModestFolderView *self)
1429 GtkTreeModel *model;
1432 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1433 path = gtk_tree_path_new_first ();
1435 /* all folders should have child items, so.. */
1437 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1438 gtk_tree_path_next (path);
1439 } while (gtk_tree_model_get_iter (model, &iter, path));
1441 gtk_tree_path_free (path);
1445 * We use this function to implement the
1446 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1447 * account in this case, and the local folders.
1450 filter_row (GtkTreeModel *model,
1454 ModestFolderViewPrivate *priv;
1455 gboolean retval = TRUE;
1456 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1457 GObject *instance = NULL;
1458 const gchar *id = NULL;
1460 gboolean found = FALSE;
1461 gboolean cleared = FALSE;
1463 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1464 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1466 gtk_tree_model_get (model, iter,
1467 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1468 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1471 /* Do not show if there is no instance, this could indeed
1472 happen when the model is being modified while it's being
1473 drawn. This could occur for example when moving folders
1478 if (type == TNY_FOLDER_TYPE_ROOT) {
1479 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1480 account instead of a folder. */
1481 if (TNY_IS_ACCOUNT (instance)) {
1482 TnyAccount *acc = TNY_ACCOUNT (instance);
1483 const gchar *account_id = tny_account_get_id (acc);
1485 /* If it isn't a special folder,
1486 * don't show it unless it is the visible account: */
1487 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1488 !modest_tny_account_is_virtual_local_folders (acc) &&
1489 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1491 /* Show only the visible account id */
1492 if (priv->visible_account_id) {
1493 if (strcmp (account_id, priv->visible_account_id))
1500 /* Never show these to the user. They are merged into one folder
1501 * in the local-folders account instead: */
1502 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1507 /* Check hiding (if necessary) */
1508 cleared = modest_email_clipboard_cleared (priv->clipboard);
1509 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1510 id = tny_folder_get_id (TNY_FOLDER(instance));
1511 if (priv->hidding_ids != NULL)
1512 for (i=0; i < priv->n_selected && !found; i++)
1513 if (priv->hidding_ids[i] != NULL && id != NULL)
1514 found = (!strcmp (priv->hidding_ids[i], id));
1520 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1521 folder as no message can be move there according to UI specs */
1522 if (!priv->show_non_move) {
1524 case TNY_FOLDER_TYPE_OUTBOX:
1525 case TNY_FOLDER_TYPE_SENT:
1526 case TNY_FOLDER_TYPE_DRAFTS:
1529 case TNY_FOLDER_TYPE_UNKNOWN:
1530 case TNY_FOLDER_TYPE_NORMAL:
1531 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1532 if (type == TNY_FOLDER_TYPE_INVALID)
1533 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1535 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1536 type == TNY_FOLDER_TYPE_SENT
1537 || type == TNY_FOLDER_TYPE_DRAFTS)
1546 g_object_unref (instance);
1553 modest_folder_view_update_model (ModestFolderView *self,
1554 TnyAccountStore *account_store)
1556 ModestFolderViewPrivate *priv;
1557 GtkTreeModel *model /* , *old_model */;
1558 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1560 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1561 g_return_val_if_fail (account_store, FALSE);
1563 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1565 /* Notify that there is no folder selected */
1566 g_signal_emit (G_OBJECT(self),
1567 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1569 if (priv->cur_folder_store) {
1570 g_object_unref (priv->cur_folder_store);
1571 priv->cur_folder_store = NULL;
1574 /* FIXME: the local accounts are not shown when the query
1575 selects only the subscribed folders */
1576 model = tny_gtk_folder_store_tree_model_new (NULL);
1578 /* Get the accounts: */
1579 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1581 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1583 sortable = gtk_tree_model_sort_new_with_model (model);
1584 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1585 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1586 GTK_SORT_ASCENDING);
1587 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1588 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1589 cmp_rows, NULL, NULL);
1591 /* Create filter model */
1592 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1593 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1599 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1600 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1601 (GCallback) on_row_inserted_maybe_select_folder, self);
1604 g_object_unref (model);
1605 g_object_unref (filter_model);
1606 g_object_unref (sortable);
1608 /* Force a reselection of the INBOX next time the widget is shown */
1609 priv->reselect = TRUE;
1616 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1618 GtkTreeModel *model = NULL;
1619 TnyFolderStore *folder = NULL;
1621 ModestFolderView *tree_view = NULL;
1622 ModestFolderViewPrivate *priv = NULL;
1623 gboolean selected = FALSE;
1625 g_return_if_fail (sel);
1626 g_return_if_fail (user_data);
1628 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1630 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1632 /* Notify the display name observers */
1633 g_signal_emit (G_OBJECT(user_data),
1634 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1637 tree_view = MODEST_FOLDER_VIEW (user_data);
1640 gtk_tree_model_get (model, &iter,
1641 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1644 /* If the folder is the same do not notify */
1645 if (folder && priv->cur_folder_store == folder) {
1646 g_object_unref (folder);
1651 /* Current folder was unselected */
1652 if (priv->cur_folder_store) {
1653 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1654 priv->cur_folder_store, FALSE);
1656 if (TNY_IS_FOLDER(priv->cur_folder_store))
1657 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1658 FALSE, NULL, NULL, NULL);
1660 /* FALSE --> don't expunge the messages */
1662 g_object_unref (priv->cur_folder_store);
1663 priv->cur_folder_store = NULL;
1666 /* New current references */
1667 priv->cur_folder_store = folder;
1669 /* New folder has been selected. Do not notify if there is
1670 nothing new selected */
1672 g_signal_emit (G_OBJECT(tree_view),
1673 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1674 0, priv->cur_folder_store, TRUE);
1679 modest_folder_view_get_selected (ModestFolderView *self)
1681 ModestFolderViewPrivate *priv;
1683 g_return_val_if_fail (self, NULL);
1685 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1686 if (priv->cur_folder_store)
1687 g_object_ref (priv->cur_folder_store);
1689 return priv->cur_folder_store;
1693 get_cmp_rows_type_pos (GObject *folder)
1695 /* Remote accounts -> Local account -> MMC account .*/
1698 if (TNY_IS_ACCOUNT (folder) &&
1699 modest_tny_account_is_virtual_local_folders (
1700 TNY_ACCOUNT (folder))) {
1702 } else if (TNY_IS_ACCOUNT (folder)) {
1703 TnyAccount *account = TNY_ACCOUNT (folder);
1704 const gchar *account_id = tny_account_get_id (account);
1705 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1711 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1712 return -1; /* Should never happen */
1717 get_cmp_subfolder_type_pos (TnyFolderType t)
1719 /* Inbox, Outbox, Drafts, Sent, User */
1723 case TNY_FOLDER_TYPE_INBOX:
1726 case TNY_FOLDER_TYPE_OUTBOX:
1729 case TNY_FOLDER_TYPE_DRAFTS:
1732 case TNY_FOLDER_TYPE_SENT:
1741 * This function orders the mail accounts according to these rules:
1742 * 1st - remote accounts
1743 * 2nd - local account
1747 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1751 gchar *name1 = NULL;
1752 gchar *name2 = NULL;
1753 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1754 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1755 GObject *folder1 = NULL;
1756 GObject *folder2 = NULL;
1758 gtk_tree_model_get (tree_model, iter1,
1759 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1760 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1761 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1763 gtk_tree_model_get (tree_model, iter2,
1764 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1765 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1766 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1769 /* Return if we get no folder. This could happen when folder
1770 operations are happening. The model is updated after the
1771 folder copy/move actually occurs, so there could be
1772 situations where the model to be drawn is not correct */
1773 if (!folder1 || !folder2)
1776 if (type == TNY_FOLDER_TYPE_ROOT) {
1777 /* Compare the types, so that
1778 * Remote accounts -> Local account -> MMC account .*/
1779 const gint pos1 = get_cmp_rows_type_pos (folder1);
1780 const gint pos2 = get_cmp_rows_type_pos (folder2);
1781 /* printf ("DEBUG: %s:\n type1=%s, pos1=%d\n type2=%s, pos2=%d\n",
1782 __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1785 else if (pos1 > pos2)
1788 /* Compare items of the same type: */
1790 TnyAccount *account1 = NULL;
1791 if (TNY_IS_ACCOUNT (folder1))
1792 account1 = TNY_ACCOUNT (folder1);
1794 TnyAccount *account2 = NULL;
1795 if (TNY_IS_ACCOUNT (folder2))
1796 account2 = TNY_ACCOUNT (folder2);
1798 const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1799 const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1801 if (!account_id && !account_id2) {
1803 } else if (!account_id) {
1805 } else if (!account_id2) {
1807 } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1810 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1814 gint cmp1 = 0, cmp2 = 0;
1815 /* get the parent to know if it's a local folder */
1818 gboolean has_parent;
1819 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1821 GObject *parent_folder;
1822 TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1823 gtk_tree_model_get (tree_model, &parent,
1824 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1825 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1827 if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1828 TNY_IS_ACCOUNT (parent_folder) &&
1829 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1830 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1831 (TNY_FOLDER (folder1)));
1832 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1833 (TNY_FOLDER (folder2)));
1835 g_object_unref (parent_folder);
1838 /* if they are not local folders */
1840 cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1841 cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1845 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1847 cmp = (cmp1 - cmp2);
1852 g_object_unref(G_OBJECT(folder1));
1854 g_object_unref(G_OBJECT(folder2));
1862 /*****************************************************************************/
1863 /* DRAG and DROP stuff */
1864 /*****************************************************************************/
1866 * This function fills the #GtkSelectionData with the row and the
1867 * model that has been dragged. It's called when this widget is a
1868 * source for dnd after the event drop happened
1871 on_drag_data_get (GtkWidget *widget,
1872 GdkDragContext *context,
1873 GtkSelectionData *selection_data,
1878 GtkTreeSelection *selection;
1879 GtkTreeModel *model;
1881 GtkTreePath *source_row;
1883 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1884 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1886 source_row = gtk_tree_model_get_path (model, &iter);
1887 gtk_tree_set_row_drag_data (selection_data,
1891 gtk_tree_path_free (source_row);
1895 typedef struct _DndHelper {
1896 gboolean delete_source;
1897 GtkTreePath *source_row;
1898 GdkDragContext *context;
1903 dnd_helper_destroyer (DndHelper *helper)
1905 /* Free the helper */
1906 gtk_tree_path_free (helper->source_row);
1907 g_slice_free (DndHelper, helper);
1911 * This function is the callback of the
1912 * modest_mail_operation_xfer_msgs () and
1913 * modest_mail_operation_xfer_folder() calls. We check here if the
1914 * message/folder was correctly asynchronously transferred. The reason
1915 * to use the same callback is that the code is the same, it only has
1916 * to check that the operation went fine and then finalize the drag
1920 xfer_cb (ModestMailOperation *mail_op,
1926 helper = (DndHelper *) user_data;
1928 if (modest_mail_operation_get_status (mail_op) ==
1929 MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1935 /* Notify the drag source. Never call delete, the monitor will
1936 do the job if needed */
1937 gtk_drag_finish (helper->context, success, FALSE, helper->time);
1939 /* Free the helper */
1940 dnd_helper_destroyer (helper);
1943 /* get the folder for the row the treepath refers to. */
1944 /* folder must be unref'd */
1945 static TnyFolderStore *
1946 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1949 TnyFolderStore *folder = NULL;
1951 if (gtk_tree_model_get_iter (model,&iter, path))
1952 gtk_tree_model_get (model, &iter,
1953 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1959 * This function is used by drag_data_received_cb to manage drag and
1960 * drop of a header, i.e, and drag from the header view to the folder
1964 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1965 GtkTreeModel *dest_model,
1966 GtkTreePath *dest_row,
1967 GtkSelectionData *selection_data,
1970 TnyList *headers = NULL;
1971 TnyFolder *folder = NULL;
1972 TnyFolderType folder_type;
1973 ModestMailOperation *mail_op = NULL;
1974 GtkTreeIter source_iter, dest_iter;
1975 ModestWindowMgr *mgr = NULL;
1976 ModestWindow *main_win = NULL;
1977 gchar **uris, **tmp;
1980 /* Build the list of headers */
1981 mgr = modest_runtime_get_window_mgr ();
1982 headers = tny_simple_list_new ();
1983 uris = modest_dnd_selection_data_get_paths (selection_data);
1986 while (*tmp != NULL) {
1991 path = gtk_tree_path_new_from_string (*tmp);
1992 gtk_tree_model_get_iter (source_model, &source_iter, path);
1993 gtk_tree_model_get (source_model, &source_iter,
1994 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1997 /* Do not enable d&d of headers already opened */
1998 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
1999 tny_list_append (headers, G_OBJECT (header));
2001 /* Free and go on */
2002 gtk_tree_path_free (path);
2003 g_object_unref (header);
2008 /* Get the target folder */
2009 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2010 gtk_tree_model_get (dest_model, &dest_iter,
2011 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2014 if (!folder || !TNY_IS_FOLDER(folder)) {
2015 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2019 folder_type = modest_tny_folder_guess_folder_type (folder);
2020 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2021 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2022 goto cleanup; /* cannot move messages there */
2025 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2026 /* g_warning ("folder not writable"); */
2027 goto cleanup; /* verboten! */
2030 /* Ask for confirmation to move */
2031 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2033 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2037 response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder,
2039 if (response == GTK_RESPONSE_CANCEL)
2042 /* Transfer messages */
2043 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2044 modest_ui_actions_move_folder_error_handler,
2047 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2050 modest_mail_operation_xfer_msgs (mail_op,
2053 helper->delete_source,
2058 if (G_IS_OBJECT(mail_op))
2059 g_object_unref (G_OBJECT (mail_op));
2060 if (G_IS_OBJECT(folder))
2061 g_object_unref (G_OBJECT (folder));
2062 if (G_IS_OBJECT(headers))
2063 g_object_unref (headers);
2067 TnyFolderStore *src_folder;
2068 TnyFolderStore *dst_folder;
2073 dnd_folder_info_destroyer (DndFolderInfo *info)
2075 if (info->src_folder)
2076 g_object_unref (info->src_folder);
2077 if (info->dst_folder)
2078 g_object_unref (info->dst_folder);
2079 g_slice_free (DndFolderInfo, info);
2083 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2084 GtkWindow *parent_window,
2085 TnyAccount *account)
2087 time_t dnd_time = info->helper->time;
2088 GdkDragContext *context = info->helper->context;
2091 modest_ui_actions_on_account_connection_error (parent_window, account);
2093 /* Free the helper & info */
2094 dnd_helper_destroyer (info->helper);
2095 dnd_folder_info_destroyer (info);
2097 /* Notify the drag source. Never call delete, the monitor will
2098 do the job if needed */
2099 gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2104 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2106 GtkWindow *parent_window,
2107 TnyAccount *account,
2110 DndFolderInfo *info = NULL;
2111 ModestMailOperation *mail_op;
2113 info = (DndFolderInfo *) user_data;
2115 if (err || canceled) {
2116 dnd_on_connection_failed_destroyer (info, parent_window, account);
2120 /* Do the mail operation */
2121 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2122 modest_ui_actions_move_folder_error_handler,
2123 info->src_folder, NULL);
2125 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2128 /* Transfer the folder */
2129 modest_mail_operation_xfer_folder (mail_op,
2130 TNY_FOLDER (info->src_folder),
2132 info->helper->delete_source,
2136 g_object_unref (G_OBJECT (mail_op));
2141 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2143 GtkWindow *parent_window,
2144 TnyAccount *account,
2147 DndFolderInfo *info = NULL;
2149 info = (DndFolderInfo *) user_data;
2151 if (err || canceled) {
2152 dnd_on_connection_failed_destroyer (info, parent_window, account);
2156 /* Connect to source folder and perform the copy/move */
2157 modest_platform_connect_and_perform_if_network_folderstore (NULL,
2159 drag_and_drop_from_folder_view_src_folder_performer,
2164 * This function is used by drag_data_received_cb to manage drag and
2165 * drop of a folder, i.e, and drag from the folder view to the same
2169 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2170 GtkTreeModel *dest_model,
2171 GtkTreePath *dest_row,
2172 GtkSelectionData *selection_data,
2175 GtkTreeIter dest_iter, iter;
2176 TnyFolderStore *dest_folder = NULL;
2177 TnyFolderStore *folder = NULL;
2178 gboolean forbidden = FALSE;
2180 DndFolderInfo *info = NULL;
2182 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2184 g_warning ("%s: BUG: no main window", __FUNCTION__);
2189 /* check the folder rules for the destination */
2190 folder = tree_path_to_folder (dest_model, dest_row);
2191 if (TNY_IS_FOLDER(folder)) {
2192 ModestTnyFolderRules rules =
2193 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2194 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2195 } else if (TNY_IS_FOLDER_STORE(folder)) {
2196 /* enable local root as destination for folders */
2197 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
2198 && TNY_IS_ACCOUNT (folder))
2201 g_object_unref (folder);
2204 /* check the folder rules for the source */
2205 folder = tree_path_to_folder (source_model, helper->source_row);
2206 if (TNY_IS_FOLDER(folder)) {
2207 ModestTnyFolderRules rules =
2208 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2209 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2212 g_object_unref (folder);
2216 /* Check if the drag is possible */
2217 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2218 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2219 gtk_tree_path_free (helper->source_row);
2220 g_slice_free (DndHelper, helper);
2225 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2226 gtk_tree_model_get (dest_model, &dest_iter,
2227 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2229 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2230 gtk_tree_model_get (source_model, &iter,
2231 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2234 /* Create the info for the performer */
2235 info = g_slice_new (DndFolderInfo);
2236 info->src_folder = g_object_ref (folder);
2237 info->dst_folder = g_object_ref (dest_folder);
2238 info->helper = helper;
2240 /* Connect to the destination folder and perform the copy/move */
2241 modest_platform_connect_and_perform_if_network_folderstore (GTK_WINDOW (win),
2243 drag_and_drop_from_folder_view_dst_folder_performer,
2247 g_object_unref (dest_folder);
2248 g_object_unref (folder);
2252 * This function receives the data set by the "drag-data-get" signal
2253 * handler. This information comes within the #GtkSelectionData. This
2254 * function will manage both the drags of folders of the treeview and
2255 * drags of headers of the header view widget.
2258 on_drag_data_received (GtkWidget *widget,
2259 GdkDragContext *context,
2262 GtkSelectionData *selection_data,
2267 GtkWidget *source_widget;
2268 GtkTreeModel *dest_model, *source_model;
2269 GtkTreePath *source_row, *dest_row;
2270 GtkTreeViewDropPosition pos;
2271 gboolean success = FALSE, delete_source = FALSE;
2272 DndHelper *helper = NULL;
2274 /* Do not allow further process */
2275 g_signal_stop_emission_by_name (widget, "drag-data-received");
2276 source_widget = gtk_drag_get_source_widget (context);
2278 /* Get the action */
2279 if (context->action == GDK_ACTION_MOVE) {
2280 delete_source = TRUE;
2282 /* Notify that there is no folder selected. We need to
2283 do this in order to update the headers view (and
2284 its monitors, because when moving, the old folder
2285 won't longer exist. We can not wait for the end of
2286 the operation, because the operation won't start if
2287 the folder is in use */
2288 if (source_widget == widget) {
2289 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2290 gtk_tree_selection_unselect_all (sel);
2294 /* Check if the get_data failed */
2295 if (selection_data == NULL || selection_data->length < 0)
2296 gtk_drag_finish (context, success, FALSE, time);
2298 /* Select the destination model */
2299 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2301 /* Get the path to the destination row. Can not call
2302 gtk_tree_view_get_drag_dest_row() because the source row
2303 is not selected anymore */
2304 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2307 /* Only allow drops IN other rows */
2309 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2310 pos == GTK_TREE_VIEW_DROP_AFTER)
2311 gtk_drag_finish (context, success, FALSE, time);
2313 /* Create the helper */
2314 helper = g_slice_new0 (DndHelper);
2315 helper->delete_source = delete_source;
2316 helper->context = context;
2317 helper->time = time;
2319 /* Drags from the header view */
2320 if (source_widget != widget) {
2321 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2323 drag_and_drop_from_header_view (source_model,
2329 /* Get the source model and row */
2330 gtk_tree_get_row_drag_data (selection_data,
2333 helper->source_row = gtk_tree_path_copy (source_row);
2335 drag_and_drop_from_folder_view (source_model,
2341 gtk_tree_path_free (source_row);
2345 gtk_tree_path_free (dest_row);
2349 * We define a "drag-drop" signal handler because we do not want to
2350 * use the default one, because the default one always calls
2351 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2352 * signal handler, because there we have all the information available
2353 * to know if the dnd was a success or not.
2356 drag_drop_cb (GtkWidget *widget,
2357 GdkDragContext *context,
2365 if (!context->targets)
2368 /* Check if we're dragging a folder row */
2369 target = gtk_drag_dest_find_target (widget, context, NULL);
2371 /* Request the data from the source. */
2372 gtk_drag_get_data(widget, context, target, time);
2378 * This function expands a node of a tree view if it's not expanded
2379 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2380 * does that, so that's why they're here.
2383 expand_row_timeout (gpointer data)
2385 GtkTreeView *tree_view = data;
2386 GtkTreePath *dest_path = NULL;
2387 GtkTreeViewDropPosition pos;
2388 gboolean result = FALSE;
2390 gdk_threads_enter ();
2392 gtk_tree_view_get_drag_dest_row (tree_view,
2397 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2398 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2399 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2400 gtk_tree_path_free (dest_path);
2404 gtk_tree_path_free (dest_path);
2409 gdk_threads_leave ();
2415 * This function is called whenever the pointer is moved over a widget
2416 * while dragging some data. It installs a timeout that will expand a
2417 * node of the treeview if not expanded yet. This function also calls
2418 * gdk_drag_status in order to set the suggested action that will be
2419 * used by the "drag-data-received" signal handler to know if we
2420 * should do a move or just a copy of the data.
2423 on_drag_motion (GtkWidget *widget,
2424 GdkDragContext *context,
2430 GtkTreeViewDropPosition pos;
2431 GtkTreePath *dest_row;
2432 GtkTreeModel *dest_model;
2433 ModestFolderViewPrivate *priv;
2434 GdkDragAction suggested_action;
2435 gboolean valid_location = FALSE;
2436 TnyFolderStore *folder = NULL;
2438 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2440 if (priv->timer_expander != 0) {
2441 g_source_remove (priv->timer_expander);
2442 priv->timer_expander = 0;
2445 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2450 /* Do not allow drops between folders */
2452 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2453 pos == GTK_TREE_VIEW_DROP_AFTER) {
2454 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2455 gdk_drag_status(context, 0, time);
2456 valid_location = FALSE;
2459 valid_location = TRUE;
2462 /* Check that the destination folder is writable */
2463 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2464 folder = tree_path_to_folder (dest_model, dest_row);
2465 if (folder && TNY_IS_FOLDER (folder)) {
2466 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2468 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2469 valid_location = FALSE;
2474 /* Expand the selected row after 1/2 second */
2475 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2476 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2477 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2480 /* Select the desired action. By default we pick MOVE */
2481 suggested_action = GDK_ACTION_MOVE;
2483 if (context->actions == GDK_ACTION_COPY)
2484 gdk_drag_status(context, GDK_ACTION_COPY, time);
2485 else if (context->actions == GDK_ACTION_MOVE)
2486 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2487 else if (context->actions & suggested_action)
2488 gdk_drag_status(context, suggested_action, time);
2490 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2494 g_object_unref (folder);
2496 gtk_tree_path_free (dest_row);
2497 g_signal_stop_emission_by_name (widget, "drag-motion");
2499 return valid_location;
2503 * This function sets the treeview as a source and a target for dnd
2504 * events. It also connects all the requirede signals.
2507 setup_drag_and_drop (GtkTreeView *self)
2509 /* Set up the folder view as a dnd destination. Set only the
2510 highlight flag, otherwise gtk will have a different
2512 gtk_drag_dest_set (GTK_WIDGET (self),
2513 GTK_DEST_DEFAULT_HIGHLIGHT,
2514 folder_view_drag_types,
2515 G_N_ELEMENTS (folder_view_drag_types),
2516 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2518 g_signal_connect (G_OBJECT (self),
2519 "drag_data_received",
2520 G_CALLBACK (on_drag_data_received),
2524 /* Set up the treeview as a dnd source */
2525 gtk_drag_source_set (GTK_WIDGET (self),
2527 folder_view_drag_types,
2528 G_N_ELEMENTS (folder_view_drag_types),
2529 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2531 g_signal_connect (G_OBJECT (self),
2533 G_CALLBACK (on_drag_motion),
2536 g_signal_connect (G_OBJECT (self),
2538 G_CALLBACK (on_drag_data_get),
2541 g_signal_connect (G_OBJECT (self),
2543 G_CALLBACK (drag_drop_cb),
2548 * This function manages the navigation through the folders using the
2549 * keyboard or the hardware keys in the device
2552 on_key_pressed (GtkWidget *self,
2556 GtkTreeSelection *selection;
2558 GtkTreeModel *model;
2559 gboolean retval = FALSE;
2561 /* Up and Down are automatically managed by the treeview */
2562 if (event->keyval == GDK_Return) {
2563 /* Expand/Collapse the selected row */
2564 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2565 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2568 path = gtk_tree_model_get_path (model, &iter);
2570 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2571 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2573 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2574 gtk_tree_path_free (path);
2576 /* No further processing */
2584 * We listen to the changes in the local folder account name key,
2585 * because we want to show the right name in the view. The local
2586 * folder account name corresponds to the device name in the Maemo
2587 * version. We do this because we do not want to query gconf on each
2588 * tree view refresh. It's better to cache it and change whenever
2592 on_configuration_key_changed (ModestConf* conf,
2594 ModestConfEvent event,
2595 ModestConfNotificationId id,
2596 ModestFolderView *self)
2598 ModestFolderViewPrivate *priv;
2601 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2602 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2604 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2605 g_free (priv->local_account_name);
2607 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2608 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2610 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2611 MODEST_CONF_DEVICE_NAME, NULL);
2613 /* Force a redraw */
2614 #if GTK_CHECK_VERSION(2, 8, 0)
2615 GtkTreeViewColumn * tree_column;
2617 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2618 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2619 gtk_tree_view_column_queue_resize (tree_column);
2621 gtk_widget_queue_draw (GTK_WIDGET (self));
2627 modest_folder_view_set_style (ModestFolderView *self,
2628 ModestFolderViewStyle style)
2630 ModestFolderViewPrivate *priv;
2632 g_return_if_fail (self);
2634 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2636 priv->style = style;
2640 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2641 const gchar *account_id)
2643 ModestFolderViewPrivate *priv;
2644 GtkTreeModel *model;
2646 g_return_if_fail (self);
2648 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2650 /* This will be used by the filter_row callback,
2651 * to decided which rows to show: */
2652 if (priv->visible_account_id) {
2653 g_free (priv->visible_account_id);
2654 priv->visible_account_id = NULL;
2657 priv->visible_account_id = g_strdup (account_id);
2660 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2661 if (GTK_IS_TREE_MODEL_FILTER (model))
2662 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2664 /* Save settings to gconf */
2665 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2666 MODEST_CONF_FOLDER_VIEW_KEY);
2670 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2672 ModestFolderViewPrivate *priv;
2674 g_return_val_if_fail (self, NULL);
2676 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2678 return (const gchar *) priv->visible_account_id;
2682 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2686 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2688 gtk_tree_model_get (model, iter,
2689 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN,
2692 gboolean result = FALSE;
2693 if (type == TNY_FOLDER_TYPE_INBOX) {
2697 *inbox_iter = *iter;
2701 if (gtk_tree_model_iter_children (model, &child, iter)) {
2702 if (find_inbox_iter (model, &child, inbox_iter))
2706 } while (gtk_tree_model_iter_next (model, iter));
2715 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2717 GtkTreeModel *model;
2718 GtkTreeIter iter, inbox_iter;
2719 GtkTreeSelection *sel;
2720 GtkTreePath *path = NULL;
2722 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2726 expand_root_items (self);
2727 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2729 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2730 g_warning ("%s: model is empty", __FUNCTION__);
2734 if (find_inbox_iter (model, &iter, &inbox_iter))
2735 path = gtk_tree_model_get_path (model, &inbox_iter);
2737 path = gtk_tree_path_new_first ();
2739 /* Select the row and free */
2740 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2741 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2742 gtk_tree_path_free (path);
2745 gtk_widget_grab_focus (GTK_WIDGET(self));
2751 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2756 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2757 TnyFolder* a_folder;
2760 gtk_tree_model_get (model, iter,
2761 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2762 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2763 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2767 if (folder == a_folder) {
2768 g_object_unref (a_folder);
2769 *folder_iter = *iter;
2772 g_object_unref (a_folder);
2774 if (gtk_tree_model_iter_children (model, &child, iter)) {
2775 if (find_folder_iter (model, &child, folder_iter, folder))
2779 } while (gtk_tree_model_iter_next (model, iter));
2786 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter,
2787 ModestFolderView *self)
2789 ModestFolderViewPrivate *priv = NULL;
2790 GtkTreeSelection *sel;
2791 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2792 GObject *instance = NULL;
2794 if (!MODEST_IS_FOLDER_VIEW(self))
2797 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2799 priv->reexpand = TRUE;
2801 gtk_tree_model_get (tree_model, iter,
2802 TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2803 TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2805 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2806 priv->folder_to_select = g_object_ref (instance);
2808 g_object_unref (instance);
2811 if (priv->folder_to_select) {
2813 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2816 path = gtk_tree_model_get_path (tree_model, iter);
2817 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2819 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2821 gtk_tree_selection_select_iter (sel, iter);
2822 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2824 gtk_tree_path_free (path);
2829 modest_folder_view_disable_next_folder_selection (self);
2830 /* g_object_unref (priv->folder_to_select); */
2831 /* priv->folder_to_select = NULL; */
2837 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2839 ModestFolderViewPrivate *priv = NULL;
2841 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2842 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2844 if (priv->folder_to_select)
2845 g_object_unref(priv->folder_to_select);
2847 priv->folder_to_select = NULL;
2851 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2852 gboolean after_change)
2854 GtkTreeModel *model;
2855 GtkTreeIter iter, folder_iter;
2856 GtkTreeSelection *sel;
2857 ModestFolderViewPrivate *priv = NULL;
2859 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2860 g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);
2862 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2866 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2867 gtk_tree_selection_unselect_all (sel);
2869 if (priv->folder_to_select)
2870 g_object_unref(priv->folder_to_select);
2871 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2875 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2879 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2880 g_warning ("%s: model is empty", __FUNCTION__);
2884 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2887 path = gtk_tree_model_get_path (model, &folder_iter);
2888 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2890 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2891 gtk_tree_selection_select_iter (sel, &folder_iter);
2892 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2894 gtk_tree_path_free (path);
2902 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2904 /* Copy selection */
2905 _clipboard_set_selected_data (folder_view, FALSE);
2909 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2911 ModestFolderViewPrivate *priv = NULL;
2912 GtkTreeModel *model = NULL;
2913 const gchar **hidding = NULL;
2914 guint i, n_selected;
2916 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2917 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2919 /* Copy selection */
2920 if (!_clipboard_set_selected_data (folder_view, TRUE))
2923 /* Get hidding ids */
2924 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2926 /* Clear hidding array created by previous cut operation */
2927 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2929 /* Copy hidding array */
2930 priv->n_selected = n_selected;
2931 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2932 for (i=0; i < n_selected; i++)
2933 priv->hidding_ids[i] = g_strdup(hidding[i]);
2935 /* Hide cut folders */
2936 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2937 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2941 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2942 ModestFolderView *folder_view_dst)
2944 GtkTreeModel *filter_model = NULL;
2945 GtkTreeModel *model = NULL;
2946 GtkTreeModel *new_filter_model = NULL;
2948 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2949 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2952 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2953 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2955 /* Build new filter model */
2956 new_filter_model = gtk_tree_model_filter_new (model, NULL);
2957 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2961 /* Set copied model */
2962 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2963 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
2964 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
2967 g_object_unref (new_filter_model);
2971 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2974 GtkTreeModel *model = NULL;
2975 ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2976 priv->show_non_move = show;
2977 /* modest_folder_view_update_model(folder_view, */
2978 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2980 /* Hide special folders */
2981 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2982 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2983 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2987 /* Returns FALSE if it did not selected anything */
2989 _clipboard_set_selected_data (ModestFolderView *folder_view,
2992 ModestFolderViewPrivate *priv = NULL;
2993 TnyFolderStore *folder = NULL;
2994 gboolean retval = FALSE;
2996 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2997 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2999 /* Set selected data on clipboard */
3000 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3001 folder = modest_folder_view_get_selected (folder_view);
3003 /* Do not allow to select an account */
3004 if (TNY_IS_FOLDER (folder)) {
3005 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3010 g_object_unref (folder);
3016 _clear_hidding_filter (ModestFolderView *folder_view)
3018 ModestFolderViewPrivate *priv;
3021 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3022 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3024 if (priv->hidding_ids != NULL) {
3025 for (i=0; i < priv->n_selected; i++)
3026 g_free (priv->hidding_ids[i]);
3027 g_free(priv->hidding_ids);
3033 on_display_name_changed (ModestAccountMgr *mgr,
3034 const gchar *account,
3037 ModestFolderView *self;
3039 self = MODEST_FOLDER_VIEW (user_data);
3041 /* Force a redraw */
3042 #if GTK_CHECK_VERSION(2, 8, 0)
3043 GtkTreeViewColumn * tree_column;
3045 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3046 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3047 gtk_tree_view_column_queue_resize (tree_column);
3049 gtk_widget_queue_draw (GTK_WIDGET (self));