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 #ifdef MODEST_TOOLKIT_HILDON2
36 #include <tny-gtk-folder-list-store.h>
38 #include <tny-gtk-folder-store-tree-model.h>
40 #include <tny-gtk-header-list-model.h>
41 #include <tny-merge-folder.h>
42 #include <tny-folder.h>
43 #include <tny-folder-store-observer.h>
44 #include <tny-account-store.h>
45 #include <tny-account.h>
46 #include <tny-folder.h>
47 #include <tny-camel-folder.h>
48 #include <tny-simple-list.h>
49 #include <tny-camel-account.h>
50 #include <modest-tny-account.h>
51 #include <modest-tny-folder.h>
52 #include <modest-tny-local-folders-account.h>
53 #include <modest-tny-outbox-account.h>
54 #include <modest-marshal.h>
55 #include <modest-icon-names.h>
56 #include <modest-tny-account-store.h>
57 #include <modest-tny-local-folders-account.h>
58 #include <modest-text-utils.h>
59 #include <modest-runtime.h>
60 #include "modest-folder-view.h"
61 #include <modest-platform.h>
62 #include <modest-widget-memory.h>
63 #include <modest-ui-actions.h>
64 #include "modest-dnd.h"
65 #include "widgets/modest-window.h"
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
98 static void modest_folder_view_init (ModestFolderView *obj);
99 static void modest_folder_view_finalize (GObject *obj);
101 static void tny_account_store_view_init (gpointer g,
102 gpointer iface_data);
104 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
105 TnyAccountStore *account_store);
107 static void on_selection_changed (GtkTreeSelection *sel,
110 static void on_row_activated (GtkTreeView *treeview,
112 GtkTreeViewColumn *column,
115 static void on_account_removed (TnyAccountStore *self,
119 static void on_account_inserted (TnyAccountStore *self,
123 static void on_account_changed (TnyAccountStore *self,
127 static gint cmp_rows (GtkTreeModel *tree_model,
132 static gboolean filter_row (GtkTreeModel *model,
136 static gboolean on_key_pressed (GtkWidget *self,
140 static void on_configuration_key_changed (ModestConf* conf,
142 ModestConfEvent event,
143 ModestConfNotificationId notification_id,
144 ModestFolderView *self);
147 static void on_drag_data_get (GtkWidget *widget,
148 GdkDragContext *context,
149 GtkSelectionData *selection_data,
154 static void on_drag_data_received (GtkWidget *widget,
155 GdkDragContext *context,
158 GtkSelectionData *selection_data,
163 static gboolean on_drag_motion (GtkWidget *widget,
164 GdkDragContext *context,
170 static void expand_root_items (ModestFolderView *self);
172 static gint expand_row_timeout (gpointer data);
174 static void setup_drag_and_drop (GtkTreeView *self);
176 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
179 static void _clear_hidding_filter (ModestFolderView *folder_view);
181 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
184 ModestFolderView *self);
186 static void on_display_name_changed (ModestAccountMgr *self,
187 const gchar *account,
189 static void update_style (ModestFolderView *self);
190 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
193 FOLDER_SELECTION_CHANGED_SIGNAL,
194 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
195 FOLDER_ACTIVATED_SIGNAL,
199 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
200 struct _ModestFolderViewPrivate {
201 TnyAccountStore *account_store;
202 TnyFolderStore *cur_folder_store;
204 TnyFolder *folder_to_select; /* folder to select after the next update */
206 gulong changed_signal;
207 gulong account_inserted_signal;
208 gulong account_removed_signal;
209 gulong account_changed_signal;
210 gulong conf_key_signal;
211 gulong display_name_changed_signal;
213 /* not unref this object, its a singlenton */
214 ModestEmailClipboard *clipboard;
216 /* Filter tree model */
220 TnyFolderStoreQuery *query;
221 guint timer_expander;
223 gchar *local_account_name;
224 gchar *visible_account_id;
225 ModestFolderViewStyle style;
226 ModestFolderViewCellStyle cell_style;
228 gboolean reselect; /* we use this to force a reselection of the INBOX */
229 gboolean show_non_move;
230 gboolean reexpand; /* next time we expose, we'll expand all root folders */
232 GtkCellRenderer *messages_renderer;
234 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
235 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
236 MODEST_TYPE_FOLDER_VIEW, \
237 ModestFolderViewPrivate))
239 static GObjectClass *parent_class = NULL;
241 static guint signals[LAST_SIGNAL] = {0};
244 modest_folder_view_get_type (void)
246 static GType my_type = 0;
248 static const GTypeInfo my_info = {
249 sizeof(ModestFolderViewClass),
250 NULL, /* base init */
251 NULL, /* base finalize */
252 (GClassInitFunc) modest_folder_view_class_init,
253 NULL, /* class finalize */
254 NULL, /* class data */
255 sizeof(ModestFolderView),
257 (GInstanceInitFunc) modest_folder_view_init,
261 static const GInterfaceInfo tny_account_store_view_info = {
262 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
263 NULL, /* interface_finalize */
264 NULL /* interface_data */
268 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
272 g_type_add_interface_static (my_type,
273 TNY_TYPE_ACCOUNT_STORE_VIEW,
274 &tny_account_store_view_info);
280 modest_folder_view_class_init (ModestFolderViewClass *klass)
282 GObjectClass *gobject_class;
283 GtkTreeViewClass *treeview_class;
284 gobject_class = (GObjectClass*) klass;
285 treeview_class = (GtkTreeViewClass*) klass;
287 parent_class = g_type_class_peek_parent (klass);
288 gobject_class->finalize = modest_folder_view_finalize;
290 g_type_class_add_private (gobject_class,
291 sizeof(ModestFolderViewPrivate));
293 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
294 g_signal_new ("folder_selection_changed",
295 G_TYPE_FROM_CLASS (gobject_class),
297 G_STRUCT_OFFSET (ModestFolderViewClass,
298 folder_selection_changed),
300 modest_marshal_VOID__POINTER_BOOLEAN,
301 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
304 * This signal is emitted whenever the currently selected
305 * folder display name is computed. Note that the name could
306 * be different to the folder name, because we could append
307 * the unread messages count to the folder name to build the
308 * folder display name
310 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
311 g_signal_new ("folder-display-name-changed",
312 G_TYPE_FROM_CLASS (gobject_class),
314 G_STRUCT_OFFSET (ModestFolderViewClass,
315 folder_display_name_changed),
317 g_cclosure_marshal_VOID__STRING,
318 G_TYPE_NONE, 1, G_TYPE_STRING);
320 signals[FOLDER_ACTIVATED_SIGNAL] =
321 g_signal_new ("folder_activated",
322 G_TYPE_FROM_CLASS (gobject_class),
324 G_STRUCT_OFFSET (ModestFolderViewClass,
327 g_cclosure_marshal_VOID__POINTER,
328 G_TYPE_NONE, 1, G_TYPE_POINTER);
330 treeview_class->select_cursor_parent = NULL;
332 #ifdef MODEST_TOOLKIT_HILDON2
333 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
339 /* Simplify checks for NULLs: */
341 strings_are_equal (const gchar *a, const gchar *b)
347 return (strcmp (a, b) == 0);
354 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
356 GObject *instance = NULL;
358 gtk_tree_model_get (model, iter,
359 INSTANCE_COLUMN, &instance,
363 return FALSE; /* keep walking */
365 if (!TNY_IS_ACCOUNT (instance)) {
366 g_object_unref (instance);
367 return FALSE; /* keep walking */
370 /* Check if this is the looked-for account: */
371 TnyAccount *this_account = TNY_ACCOUNT (instance);
372 TnyAccount *account = TNY_ACCOUNT (data);
374 const gchar *this_account_id = tny_account_get_id(this_account);
375 const gchar *account_id = tny_account_get_id(account);
376 g_object_unref (instance);
379 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
380 if (strings_are_equal(this_account_id, account_id)) {
381 /* Tell the model that the data has changed, so that
382 * it calls the cell_data_func callbacks again: */
383 /* TODO: This does not seem to actually cause the new string to be shown: */
384 gtk_tree_model_row_changed (model, path, iter);
386 return TRUE; /* stop walking */
389 return FALSE; /* keep walking */
394 ModestFolderView *self;
395 gchar *previous_name;
396 } GetMmcAccountNameData;
399 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
401 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
403 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
405 if (!strings_are_equal (
406 tny_account_get_name(TNY_ACCOUNT(account)),
407 data->previous_name)) {
409 /* Tell the model that the data has changed, so that
410 * it calls the cell_data_func callbacks again: */
411 ModestFolderView *self = data->self;
412 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
414 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
417 g_free (data->previous_name);
418 g_slice_free (GetMmcAccountNameData, data);
422 text_cell_data (GtkTreeViewColumn *column,
423 GtkCellRenderer *renderer,
424 GtkTreeModel *tree_model,
428 ModestFolderViewPrivate *priv;
429 GObject *rendobj = (GObject *) renderer;
431 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
432 GObject *instance = NULL;
434 gtk_tree_model_get (tree_model, iter,
437 INSTANCE_COLUMN, &instance,
439 if (!fname || !instance)
442 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
443 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
445 gchar *item_name = NULL;
446 gint item_weight = 400;
448 if (type != TNY_FOLDER_TYPE_ROOT) {
452 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
453 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
454 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
455 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
457 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
460 /* Sometimes an special folder is reported by the server as
461 NORMAL, like some versions of Dovecot */
462 if (type == TNY_FOLDER_TYPE_NORMAL ||
463 type == TNY_FOLDER_TYPE_UNKNOWN) {
464 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
468 if (type == TNY_FOLDER_TYPE_INBOX) {
470 fname = g_strdup (_("mcen_me_folder_inbox"));
473 /* note: we cannot reliably get the counts from the tree model, we need
474 * to use explicit calls on tny_folder for some reason.
476 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
477 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
478 (type == TNY_FOLDER_TYPE_OUTBOX) ||
479 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
480 number = tny_folder_get_all_count (TNY_FOLDER(instance));
483 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
487 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
488 item_name = g_strdup (fname);
495 /* Use bold font style if there are unread or unset messages */
497 item_name = g_strdup_printf ("%s (%d)", fname, number);
500 item_name = g_strdup (fname);
505 } else if (TNY_IS_ACCOUNT (instance)) {
506 /* If it's a server account */
507 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
508 item_name = g_strdup (priv->local_account_name);
510 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
511 /* fname is only correct when the items are first
512 * added to the model, not when the account is
513 * changed later, so get the name from the account
515 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
518 item_name = g_strdup (fname);
524 item_name = g_strdup ("unknown");
526 if (item_name && item_weight) {
527 /* Set the name in the treeview cell: */
528 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
530 /* Notify display name observers */
531 /* TODO: What listens for this signal, and how can it use only the new name? */
532 if (((GObject *) priv->cur_folder_store) == instance) {
533 g_signal_emit (G_OBJECT(self),
534 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
541 /* If it is a Memory card account, make sure that we have the correct name.
542 * This function will be trigerred again when the name has been retrieved: */
543 if (TNY_IS_STORE_ACCOUNT (instance) &&
544 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
546 /* Get the account name asynchronously: */
547 GetMmcAccountNameData *callback_data =
548 g_slice_new0(GetMmcAccountNameData);
549 callback_data->self = self;
551 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
553 callback_data->previous_name = g_strdup (name);
555 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
556 on_get_mmc_account_name, callback_data);
560 g_object_unref (G_OBJECT (instance));
566 messages_cell_data (GtkTreeViewColumn *column,
567 GtkCellRenderer *renderer,
568 GtkTreeModel *tree_model,
572 ModestFolderView *self;
573 ModestFolderViewPrivate *priv;
574 GObject *rendobj = (GObject *) renderer;
575 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
576 GObject *instance = NULL;
577 gchar *item_name = NULL;
579 gtk_tree_model_get (tree_model, iter,
581 INSTANCE_COLUMN, &instance,
586 self = MODEST_FOLDER_VIEW (data);
587 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
590 if (type != TNY_FOLDER_TYPE_ROOT) {
594 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
595 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
596 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
598 /* Sometimes an special folder is reported by the server as
599 NORMAL, like some versions of Dovecot */
600 if (type == TNY_FOLDER_TYPE_NORMAL ||
601 type == TNY_FOLDER_TYPE_UNKNOWN) {
602 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
606 /* note: we cannot reliably get the counts from the tree model, we need
607 * to use explicit calls on tny_folder for some reason.
609 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
610 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
611 (type == TNY_FOLDER_TYPE_OUTBOX) ||
612 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
613 number = tny_folder_get_all_count (TNY_FOLDER(instance));
616 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
620 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
622 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
630 item_name = g_strdup ("");
633 /* Set the name in the treeview cell: */
634 g_object_set (rendobj,"text", item_name, NULL);
642 g_object_unref (G_OBJECT (instance));
648 GdkPixbuf *pixbuf_open;
649 GdkPixbuf *pixbuf_close;
653 static inline GdkPixbuf *
654 get_composite_pixbuf (const gchar *icon_name,
656 GdkPixbuf *base_pixbuf)
658 GdkPixbuf *emblem, *retval = NULL;
660 emblem = modest_platform_get_icon (icon_name, size);
662 retval = gdk_pixbuf_copy (base_pixbuf);
663 gdk_pixbuf_composite (emblem, retval, 0, 0,
664 MIN (gdk_pixbuf_get_width (emblem),
665 gdk_pixbuf_get_width (retval)),
666 MIN (gdk_pixbuf_get_height (emblem),
667 gdk_pixbuf_get_height (retval)),
668 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
669 g_object_unref (emblem);
674 static inline ThreePixbufs *
675 get_composite_icons (const gchar *icon_code,
677 GdkPixbuf **pixbuf_open,
678 GdkPixbuf **pixbuf_close)
680 ThreePixbufs *retval;
683 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
686 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
691 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
695 retval = g_slice_new0 (ThreePixbufs);
697 retval->pixbuf = g_object_ref (*pixbuf);
699 retval->pixbuf_open = g_object_ref (*pixbuf_open);
701 retval->pixbuf_close = g_object_ref (*pixbuf_close);
707 get_folder_icons (TnyFolderType type, GObject *instance)
709 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
710 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
711 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
712 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
713 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
715 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
716 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
717 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
718 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
719 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
721 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
722 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
723 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
724 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
725 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
727 ThreePixbufs *retval = NULL;
729 /* Sometimes an special folder is reported by the server as
730 NORMAL, like some versions of Dovecot */
731 if (type == TNY_FOLDER_TYPE_NORMAL ||
732 type == TNY_FOLDER_TYPE_UNKNOWN) {
733 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
737 case TNY_FOLDER_TYPE_INVALID:
738 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
741 case TNY_FOLDER_TYPE_ROOT:
742 if (TNY_IS_ACCOUNT (instance)) {
744 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
745 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
748 &avirt_pixbuf_close);
750 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
752 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
753 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
758 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
761 &anorm_pixbuf_close);
766 case TNY_FOLDER_TYPE_INBOX:
767 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
770 &inbox_pixbuf_close);
772 case TNY_FOLDER_TYPE_OUTBOX:
773 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
776 &outbox_pixbuf_close);
778 case TNY_FOLDER_TYPE_JUNK:
779 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
784 case TNY_FOLDER_TYPE_SENT:
785 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
790 case TNY_FOLDER_TYPE_TRASH:
791 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
794 &trash_pixbuf_close);
796 case TNY_FOLDER_TYPE_DRAFTS:
797 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
800 &draft_pixbuf_close);
802 case TNY_FOLDER_TYPE_NORMAL:
804 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
807 &normal_pixbuf_close);
815 free_pixbufs (ThreePixbufs *pixbufs)
818 g_object_unref (pixbufs->pixbuf);
819 if (pixbufs->pixbuf_open)
820 g_object_unref (pixbufs->pixbuf_open);
821 if (pixbufs->pixbuf_close)
822 g_object_unref (pixbufs->pixbuf_close);
823 g_slice_free (ThreePixbufs, pixbufs);
827 icon_cell_data (GtkTreeViewColumn *column,
828 GtkCellRenderer *renderer,
829 GtkTreeModel *tree_model,
833 GObject *rendobj = NULL, *instance = NULL;
834 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
835 gboolean has_children;
836 ThreePixbufs *pixbufs;
838 rendobj = (GObject *) renderer;
840 gtk_tree_model_get (tree_model, iter,
842 INSTANCE_COLUMN, &instance,
848 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
849 pixbufs = get_folder_icons (type, instance);
850 g_object_unref (instance);
853 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
856 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
857 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
860 free_pixbufs (pixbufs);
864 add_columns (GtkWidget *treeview)
866 GtkTreeViewColumn *column;
867 GtkCellRenderer *renderer;
868 GtkTreeSelection *sel;
869 ModestFolderViewPrivate *priv;
871 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
874 column = gtk_tree_view_column_new ();
876 /* Set icon and text render function */
877 renderer = gtk_cell_renderer_pixbuf_new();
878 gtk_tree_view_column_pack_start (column, renderer, FALSE);
879 gtk_tree_view_column_set_cell_data_func(column, renderer,
880 icon_cell_data, treeview, NULL);
882 renderer = gtk_cell_renderer_text_new();
883 g_object_set (renderer,
884 #ifdef MODEST_TOOLKIT_HILDON2
885 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
887 "ellipsize", PANGO_ELLIPSIZE_END,
889 "ellipsize-set", TRUE, NULL);
890 gtk_tree_view_column_pack_start (column, renderer, TRUE);
891 gtk_tree_view_column_set_cell_data_func(column, renderer,
892 text_cell_data, treeview, NULL);
894 priv->messages_renderer = gtk_cell_renderer_text_new ();
895 g_object_set (priv->messages_renderer,
896 "scale", PANGO_SCALE_X_SMALL,
898 "alignment", PANGO_ALIGN_RIGHT,
902 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
903 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
904 messages_cell_data, treeview, NULL);
906 /* Set selection mode */
907 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
908 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
910 /* Set treeview appearance */
911 gtk_tree_view_column_set_spacing (column, 2);
912 gtk_tree_view_column_set_resizable (column, TRUE);
913 gtk_tree_view_column_set_fixed_width (column, TRUE);
914 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
915 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
918 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
922 modest_folder_view_init (ModestFolderView *obj)
924 ModestFolderViewPrivate *priv;
927 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
929 priv->timer_expander = 0;
930 priv->account_store = NULL;
932 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
933 priv->cur_folder_store = NULL;
934 priv->visible_account_id = NULL;
935 priv->folder_to_select = NULL;
937 priv->reexpand = TRUE;
939 /* Initialize the local account name */
940 conf = modest_runtime_get_conf();
941 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
943 /* Init email clipboard */
944 priv->clipboard = modest_runtime_get_email_clipboard ();
945 priv->hidding_ids = NULL;
946 priv->n_selected = 0;
947 priv->reselect = FALSE;
948 priv->show_non_move = TRUE;
951 add_columns (GTK_WIDGET (obj));
953 /* Setup drag and drop */
954 setup_drag_and_drop (GTK_TREE_VIEW(obj));
956 /* Connect signals */
957 g_signal_connect (G_OBJECT (obj),
959 G_CALLBACK (on_key_pressed), NULL);
961 priv->display_name_changed_signal =
962 g_signal_connect (modest_runtime_get_account_mgr (),
963 "display_name_changed",
964 G_CALLBACK (on_display_name_changed),
968 * Track changes in the local account name (in the device it
969 * will be the device name)
971 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
973 G_CALLBACK(on_configuration_key_changed),
977 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
983 tny_account_store_view_init (gpointer g, gpointer iface_data)
985 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
987 klass->set_account_store = modest_folder_view_set_account_store;
991 modest_folder_view_finalize (GObject *obj)
993 ModestFolderViewPrivate *priv;
994 GtkTreeSelection *sel;
996 g_return_if_fail (obj);
998 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1000 if (priv->timer_expander != 0) {
1001 g_source_remove (priv->timer_expander);
1002 priv->timer_expander = 0;
1005 if (priv->account_store) {
1006 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1007 priv->account_inserted_signal);
1008 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1009 priv->account_removed_signal);
1010 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1011 priv->account_changed_signal);
1012 g_object_unref (G_OBJECT(priv->account_store));
1013 priv->account_store = NULL;
1016 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1017 priv->display_name_changed_signal)) {
1018 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1019 priv->display_name_changed_signal);
1020 priv->display_name_changed_signal = 0;
1024 g_object_unref (G_OBJECT (priv->query));
1028 if (priv->folder_to_select) {
1029 g_object_unref (G_OBJECT(priv->folder_to_select));
1030 priv->folder_to_select = NULL;
1033 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1035 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1037 g_free (priv->local_account_name);
1038 g_free (priv->visible_account_id);
1040 if (priv->conf_key_signal) {
1041 g_signal_handler_disconnect (modest_runtime_get_conf (),
1042 priv->conf_key_signal);
1043 priv->conf_key_signal = 0;
1046 if (priv->cur_folder_store) {
1047 g_object_unref (priv->cur_folder_store);
1048 priv->cur_folder_store = NULL;
1051 /* Clear hidding array created by cut operation */
1052 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1054 G_OBJECT_CLASS(parent_class)->finalize (obj);
1059 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1061 ModestFolderViewPrivate *priv;
1064 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1065 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1067 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1068 device = tny_account_store_get_device (account_store);
1070 if (G_UNLIKELY (priv->account_store)) {
1072 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1073 priv->account_inserted_signal))
1074 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1075 priv->account_inserted_signal);
1076 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1077 priv->account_removed_signal))
1078 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1079 priv->account_removed_signal);
1080 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1081 priv->account_changed_signal))
1082 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1083 priv->account_changed_signal);
1084 g_object_unref (G_OBJECT (priv->account_store));
1087 priv->account_store = g_object_ref (G_OBJECT (account_store));
1089 priv->account_removed_signal =
1090 g_signal_connect (G_OBJECT(account_store), "account_removed",
1091 G_CALLBACK (on_account_removed), self);
1093 priv->account_inserted_signal =
1094 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1095 G_CALLBACK (on_account_inserted), self);
1097 priv->account_changed_signal =
1098 g_signal_connect (G_OBJECT(account_store), "account_changed",
1099 G_CALLBACK (on_account_changed), self);
1101 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1102 priv->reselect = FALSE;
1103 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1105 g_object_unref (G_OBJECT (device));
1109 on_account_inserted (TnyAccountStore *account_store,
1110 TnyAccount *account,
1113 ModestFolderViewPrivate *priv;
1114 GtkTreeModel *sort_model, *filter_model;
1116 /* Ignore transport account insertions, we're not showing them
1117 in the folder view */
1118 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1121 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1124 /* If we're adding a new account, and there is no previous
1125 one, we need to select the visible server account */
1126 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1127 !priv->visible_account_id)
1128 modest_widget_memory_restore (modest_runtime_get_conf(),
1129 G_OBJECT (user_data),
1130 MODEST_CONF_FOLDER_VIEW_KEY);
1132 if (!GTK_IS_TREE_VIEW(user_data)) {
1133 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1137 /* Get the inner model */
1138 /* check, is some rare cases, we did not get the right thing here,
1140 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1141 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1142 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1146 /* check, is some rare cases, we did not get the right thing here,
1148 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1149 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1150 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1154 /* Insert the account in the model */
1155 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1156 G_OBJECT (account));
1158 /* Refilter the model */
1159 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1164 same_account_selected (ModestFolderView *self,
1165 TnyAccount *account)
1167 ModestFolderViewPrivate *priv;
1168 gboolean same_account = FALSE;
1170 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1172 if (priv->cur_folder_store) {
1173 TnyAccount *selected_folder_account = NULL;
1175 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1176 selected_folder_account =
1177 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1179 selected_folder_account =
1180 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1183 if (selected_folder_account == account)
1184 same_account = TRUE;
1186 g_object_unref (selected_folder_account);
1188 return same_account;
1193 * Selects the first inbox or the local account in an idle
1196 on_idle_select_first_inbox_or_local (gpointer user_data)
1198 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1200 gdk_threads_enter ();
1201 modest_folder_view_select_first_inbox_or_local (self);
1202 gdk_threads_leave ();
1208 on_account_changed (TnyAccountStore *account_store,
1209 TnyAccount *tny_account,
1212 ModestFolderView *self;
1213 ModestFolderViewPrivate *priv;
1214 GtkTreeModel *sort_model, *filter_model;
1215 GtkTreeSelection *sel;
1216 gboolean same_account;
1218 /* Ignore transport account insertions, we're not showing them
1219 in the folder view */
1220 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1223 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1224 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1228 self = MODEST_FOLDER_VIEW (user_data);
1229 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1231 /* Get the inner model */
1232 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1233 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1234 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1238 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1239 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1240 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1244 /* Invalidate the cur_folder_store only if the selected folder
1245 belongs to the account that is being removed */
1246 same_account = same_account_selected (self, tny_account);
1248 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1249 gtk_tree_selection_unselect_all (sel);
1252 /* Remove the account from the model */
1253 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1254 G_OBJECT (tny_account));
1256 /* Insert the account in the model */
1257 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1258 G_OBJECT (tny_account));
1260 /* Refilter the model */
1261 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1263 /* Select the first INBOX if the currently selected folder
1264 belongs to the account that is being deleted */
1265 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1266 g_idle_add (on_idle_select_first_inbox_or_local, self);
1270 on_account_removed (TnyAccountStore *account_store,
1271 TnyAccount *account,
1274 ModestFolderView *self = NULL;
1275 ModestFolderViewPrivate *priv;
1276 GtkTreeModel *sort_model, *filter_model;
1277 GtkTreeSelection *sel = NULL;
1278 gboolean same_account = FALSE;
1280 /* Ignore transport account removals, we're not showing them
1281 in the folder view */
1282 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1285 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1286 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1290 self = MODEST_FOLDER_VIEW (user_data);
1291 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1293 /* Invalidate the cur_folder_store only if the selected folder
1294 belongs to the account that is being removed */
1295 same_account = same_account_selected (self, account);
1297 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1298 gtk_tree_selection_unselect_all (sel);
1301 /* Invalidate row to select only if the folder to select
1302 belongs to the account that is being removed*/
1303 if (priv->folder_to_select) {
1304 TnyAccount *folder_to_select_account = NULL;
1306 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1307 if (folder_to_select_account == account) {
1308 modest_folder_view_disable_next_folder_selection (self);
1309 g_object_unref (priv->folder_to_select);
1310 priv->folder_to_select = NULL;
1312 g_object_unref (folder_to_select_account);
1315 /* Remove the account from the model */
1316 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1317 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1318 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1322 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1323 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1324 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1328 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1329 G_OBJECT (account));
1331 /* If the removed account is the currently viewed one then
1332 clear the configuration value. The new visible account will be the default account */
1333 if (priv->visible_account_id &&
1334 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1336 /* Clear the current visible account_id */
1337 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1339 /* Call the restore method, this will set the new visible account */
1340 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1341 MODEST_CONF_FOLDER_VIEW_KEY);
1344 /* Refilter the model */
1345 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1347 /* Select the first INBOX if the currently selected folder
1348 belongs to the account that is being deleted */
1350 g_idle_add (on_idle_select_first_inbox_or_local, self);
1354 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1356 GtkTreeViewColumn *col;
1358 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1360 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1362 g_printerr ("modest: failed get column for title\n");
1366 gtk_tree_view_column_set_title (col, title);
1367 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1372 modest_folder_view_on_map (ModestFolderView *self,
1373 GdkEventExpose *event,
1376 ModestFolderViewPrivate *priv;
1378 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1380 /* This won't happen often */
1381 if (G_UNLIKELY (priv->reselect)) {
1382 /* Select the first inbox or the local account if not found */
1384 /* TODO: this could cause a lock at startup, so we
1385 comment it for the moment. We know that this will
1386 be a bug, because the INBOX is not selected, but we
1387 need to rewrite some parts of Modest to avoid the
1388 deathlock situation */
1389 /* TODO: check if this is still the case */
1390 priv->reselect = FALSE;
1391 modest_folder_view_select_first_inbox_or_local (self);
1392 /* Notify the display name observers */
1393 g_signal_emit (G_OBJECT(self),
1394 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1398 if (priv->reexpand) {
1399 expand_root_items (self);
1400 priv->reexpand = FALSE;
1407 modest_folder_view_new (TnyFolderStoreQuery *query)
1410 ModestFolderViewPrivate *priv;
1411 GtkTreeSelection *sel;
1413 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1414 #ifdef MODEST_TOOLKIT_HILDON2
1415 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1418 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1421 priv->query = g_object_ref (query);
1423 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1424 priv->changed_signal = g_signal_connect (sel, "changed",
1425 G_CALLBACK (on_selection_changed), self);
1427 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1429 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1431 return GTK_WIDGET(self);
1434 /* this feels dirty; any other way to expand all the root items? */
1436 expand_root_items (ModestFolderView *self)
1439 GtkTreeModel *model;
1442 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1443 path = gtk_tree_path_new_first ();
1445 /* all folders should have child items, so.. */
1447 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1448 gtk_tree_path_next (path);
1449 } while (gtk_tree_model_get_iter (model, &iter, path));
1451 gtk_tree_path_free (path);
1455 * We use this function to implement the
1456 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1457 * account in this case, and the local folders.
1460 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1462 ModestFolderViewPrivate *priv;
1463 gboolean retval = TRUE;
1464 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1465 GObject *instance = NULL;
1466 const gchar *id = NULL;
1468 gboolean found = FALSE;
1469 gboolean cleared = FALSE;
1471 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1472 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1474 gtk_tree_model_get (model, iter,
1476 INSTANCE_COLUMN, &instance,
1479 /* Do not show if there is no instance, this could indeed
1480 happen when the model is being modified while it's being
1481 drawn. This could occur for example when moving folders
1486 if (TNY_IS_ACCOUNT (instance)) {
1487 TnyAccount *acc = TNY_ACCOUNT (instance);
1488 const gchar *account_id = tny_account_get_id (acc);
1490 /* If it isn't a special folder,
1491 * don't show it unless it is the visible account: */
1492 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1493 !modest_tny_account_is_virtual_local_folders (acc) &&
1494 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1496 /* Show only the visible account id */
1497 if (priv->visible_account_id) {
1498 if (strcmp (account_id, priv->visible_account_id))
1505 /* Never show these to the user. They are merged into one folder
1506 * in the local-folders account instead: */
1507 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1512 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1513 !gtk_tree_model_iter_parent (model, iter, &parent)) {
1514 /* Only show special folders for current account if needed */
1515 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1516 TnyAccount *account;
1518 account = tny_folder_get_account (TNY_FOLDER (instance));
1520 if (TNY_IS_ACCOUNT (account)) {
1521 const gchar *account_id = tny_account_get_id (account);
1523 if (!modest_tny_account_is_virtual_local_folders (account) &&
1524 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1525 /* Show only the visible account id */
1526 if (priv->visible_account_id) {
1527 if (strcmp (account_id, priv->visible_account_id))
1531 g_object_unref (account);
1538 /* Check hiding (if necessary) */
1539 cleared = modest_email_clipboard_cleared (priv->clipboard);
1540 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1541 id = tny_folder_get_id (TNY_FOLDER(instance));
1542 if (priv->hidding_ids != NULL)
1543 for (i=0; i < priv->n_selected && !found; i++)
1544 if (priv->hidding_ids[i] != NULL && id != NULL)
1545 found = (!strcmp (priv->hidding_ids[i], id));
1551 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1552 folder as no message can be move there according to UI specs */
1553 if (!priv->show_non_move) {
1555 case TNY_FOLDER_TYPE_OUTBOX:
1556 case TNY_FOLDER_TYPE_SENT:
1557 case TNY_FOLDER_TYPE_DRAFTS:
1560 case TNY_FOLDER_TYPE_UNKNOWN:
1561 case TNY_FOLDER_TYPE_NORMAL:
1562 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1563 if (type == TNY_FOLDER_TYPE_INVALID)
1564 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1566 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1567 type == TNY_FOLDER_TYPE_SENT
1568 || type == TNY_FOLDER_TYPE_DRAFTS)
1577 g_object_unref (instance);
1584 modest_folder_view_update_model (ModestFolderView *self,
1585 TnyAccountStore *account_store)
1587 ModestFolderViewPrivate *priv;
1588 GtkTreeModel *model /* , *old_model */;
1589 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1591 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1592 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1595 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1597 /* Notify that there is no folder selected */
1598 g_signal_emit (G_OBJECT(self),
1599 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1601 if (priv->cur_folder_store) {
1602 g_object_unref (priv->cur_folder_store);
1603 priv->cur_folder_store = NULL;
1606 /* FIXME: the local accounts are not shown when the query
1607 selects only the subscribed folders */
1608 #ifdef MODEST_TOOLKIT_HILDON2
1609 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1610 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1612 model = tny_gtk_folder_store_tree_model_new (NULL);
1615 /* Get the accounts: */
1616 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1618 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1620 sortable = gtk_tree_model_sort_new_with_model (model);
1621 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1623 GTK_SORT_ASCENDING);
1624 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1626 cmp_rows, NULL, NULL);
1628 /* Create filter model */
1629 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1630 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1636 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1637 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1638 (GCallback) on_row_inserted_maybe_select_folder, self);
1640 g_object_unref (model);
1641 g_object_unref (filter_model);
1642 g_object_unref (sortable);
1644 /* Force a reselection of the INBOX next time the widget is shown */
1645 priv->reselect = TRUE;
1652 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1654 GtkTreeModel *model = NULL;
1655 TnyFolderStore *folder = NULL;
1657 ModestFolderView *tree_view = NULL;
1658 ModestFolderViewPrivate *priv = NULL;
1659 gboolean selected = FALSE;
1661 g_return_if_fail (sel);
1662 g_return_if_fail (user_data);
1664 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1666 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1668 tree_view = MODEST_FOLDER_VIEW (user_data);
1671 gtk_tree_model_get (model, &iter,
1672 INSTANCE_COLUMN, &folder,
1675 /* If the folder is the same do not notify */
1676 if (folder && priv->cur_folder_store == folder) {
1677 g_object_unref (folder);
1682 /* Current folder was unselected */
1683 if (priv->cur_folder_store) {
1684 /* We must do this firstly because a libtinymail-camel
1685 implementation detail. If we issue the signal
1686 before doing the sync_async, then that signal could
1687 cause (and it actually does it) a free of the
1688 summary of the folder (because the main window will
1689 clear the headers view */
1690 if (TNY_IS_FOLDER(priv->cur_folder_store))
1691 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1692 FALSE, NULL, NULL, NULL);
1694 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1695 priv->cur_folder_store, FALSE);
1697 g_object_unref (priv->cur_folder_store);
1698 priv->cur_folder_store = NULL;
1701 /* New current references */
1702 priv->cur_folder_store = folder;
1704 /* New folder has been selected. Do not notify if there is
1705 nothing new selected */
1707 g_signal_emit (G_OBJECT(tree_view),
1708 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1709 0, priv->cur_folder_store, TRUE);
1714 on_row_activated (GtkTreeView *treeview,
1715 GtkTreePath *treepath,
1716 GtkTreeViewColumn *column,
1719 GtkTreeModel *model = NULL;
1720 TnyFolderStore *folder = NULL;
1722 ModestFolderView *self = NULL;
1723 ModestFolderViewPrivate *priv = NULL;
1725 g_return_if_fail (treeview);
1726 g_return_if_fail (user_data);
1728 self = MODEST_FOLDER_VIEW (user_data);
1729 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1731 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1733 if (!gtk_tree_model_get_iter (model, &iter, treepath))
1736 gtk_tree_model_get (model, &iter,
1737 INSTANCE_COLUMN, &folder,
1740 g_signal_emit (G_OBJECT(self),
1741 signals[FOLDER_ACTIVATED_SIGNAL],
1744 g_object_unref (folder);
1748 modest_folder_view_get_selected (ModestFolderView *self)
1750 ModestFolderViewPrivate *priv;
1752 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1754 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1755 if (priv->cur_folder_store)
1756 g_object_ref (priv->cur_folder_store);
1758 return priv->cur_folder_store;
1762 get_cmp_rows_type_pos (GObject *folder)
1764 /* Remote accounts -> Local account -> MMC account .*/
1767 if (TNY_IS_ACCOUNT (folder) &&
1768 modest_tny_account_is_virtual_local_folders (
1769 TNY_ACCOUNT (folder))) {
1771 } else if (TNY_IS_ACCOUNT (folder)) {
1772 TnyAccount *account = TNY_ACCOUNT (folder);
1773 const gchar *account_id = tny_account_get_id (account);
1774 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1780 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1781 return -1; /* Should never happen */
1786 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
1788 TnyAccount *account;
1789 gboolean is_special;
1790 /* Inbox, Outbox, Drafts, Sent, User */
1793 if (!TNY_IS_FOLDER (folder_store))
1796 case TNY_FOLDER_TYPE_INBOX:
1798 account = tny_folder_get_account (folder_store);
1799 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
1800 g_object_unref (account);
1801 return is_special?0:4;
1804 case TNY_FOLDER_TYPE_OUTBOX:
1806 account = tny_folder_get_account (folder_store);
1807 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1808 g_object_unref (account);
1809 return is_special?1:4;
1812 case TNY_FOLDER_TYPE_DRAFTS:
1814 account = tny_folder_get_account (folder_store);
1815 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1816 g_object_unref (account);
1817 return is_special?2:4;
1820 case TNY_FOLDER_TYPE_SENT:
1822 account = tny_folder_get_account (folder_store);
1823 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1824 g_object_unref (account);
1825 return is_special?3:4;
1834 compare_account_names (TnyAccount *a1, TnyAccount *a2)
1836 const gchar *a1_name, *a2_name;
1838 a1_name = tny_account_get_name (a1);
1839 a2_name = tny_account_get_name (a2);
1841 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
1845 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
1847 TnyAccount *a1, *a2;
1850 if (TNY_IS_ACCOUNT (s1)) {
1851 a1 = TNY_ACCOUNT (g_object_ref (s1));
1853 a1 = tny_folder_get_account (TNY_FOLDER (s1));
1856 if (TNY_IS_ACCOUNT (s2)) {
1857 a2 = TNY_ACCOUNT (g_object_ref (s2));
1859 a2 = tny_folder_get_account (TNY_FOLDER (s2));
1866 /* First we sort with the type of account */
1867 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
1871 cmp = compare_account_names (a1, a2);
1874 g_object_unref (a1);
1875 g_object_unref (a2);
1881 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
1883 gint is_account1, is_account2;
1885 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
1886 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
1888 return is_account2 - is_account1;
1892 * This function orders the mail accounts according to these rules:
1893 * 1st - remote accounts
1894 * 2nd - local account
1898 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1902 gchar *name1 = NULL;
1903 gchar *name2 = NULL;
1904 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1905 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1906 GObject *folder1 = NULL;
1907 GObject *folder2 = NULL;
1909 gtk_tree_model_get (tree_model, iter1,
1910 NAME_COLUMN, &name1,
1912 INSTANCE_COLUMN, &folder1,
1914 gtk_tree_model_get (tree_model, iter2,
1915 NAME_COLUMN, &name2,
1916 TYPE_COLUMN, &type2,
1917 INSTANCE_COLUMN, &folder2,
1920 /* Return if we get no folder. This could happen when folder
1921 operations are happening. The model is updated after the
1922 folder copy/move actually occurs, so there could be
1923 situations where the model to be drawn is not correct */
1924 if (!folder1 || !folder2)
1927 /* Sort by type. First the special folders, then the archives */
1928 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
1932 /* Now we sort using the account of each folder */
1933 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
1937 /* Each group is preceeded by its account */
1938 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
1942 /* Pure sort by name */
1943 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1946 g_object_unref(G_OBJECT(folder1));
1948 g_object_unref(G_OBJECT(folder2));
1956 /*****************************************************************************/
1957 /* DRAG and DROP stuff */
1958 /*****************************************************************************/
1960 * This function fills the #GtkSelectionData with the row and the
1961 * model that has been dragged. It's called when this widget is a
1962 * source for dnd after the event drop happened
1965 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1966 guint info, guint time, gpointer data)
1968 GtkTreeSelection *selection;
1969 GtkTreeModel *model;
1971 GtkTreePath *source_row;
1973 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1974 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1976 source_row = gtk_tree_model_get_path (model, &iter);
1977 gtk_tree_set_row_drag_data (selection_data,
1981 gtk_tree_path_free (source_row);
1985 typedef struct _DndHelper {
1986 ModestFolderView *folder_view;
1987 gboolean delete_source;
1988 GtkTreePath *source_row;
1992 dnd_helper_destroyer (DndHelper *helper)
1994 /* Free the helper */
1995 gtk_tree_path_free (helper->source_row);
1996 g_slice_free (DndHelper, helper);
2000 xfer_folder_cb (ModestMailOperation *mail_op,
2001 TnyFolder *new_folder,
2005 /* Select the folder */
2006 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2012 /* get the folder for the row the treepath refers to. */
2013 /* folder must be unref'd */
2014 static TnyFolderStore *
2015 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2018 TnyFolderStore *folder = NULL;
2020 if (gtk_tree_model_get_iter (model,&iter, path))
2021 gtk_tree_model_get (model, &iter,
2022 INSTANCE_COLUMN, &folder,
2029 * This function is used by drag_data_received_cb to manage drag and
2030 * drop of a header, i.e, and drag from the header view to the folder
2034 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2035 GtkTreeModel *dest_model,
2036 GtkTreePath *dest_row,
2037 GtkSelectionData *selection_data)
2039 TnyList *headers = NULL;
2040 TnyFolder *folder = NULL, *src_folder = NULL;
2041 TnyFolderType folder_type;
2042 GtkTreeIter source_iter, dest_iter;
2043 ModestWindowMgr *mgr = NULL;
2044 ModestWindow *main_win = NULL;
2045 gchar **uris, **tmp;
2047 /* Build the list of headers */
2048 mgr = modest_runtime_get_window_mgr ();
2049 headers = tny_simple_list_new ();
2050 uris = modest_dnd_selection_data_get_paths (selection_data);
2053 while (*tmp != NULL) {
2056 gboolean first = TRUE;
2059 path = gtk_tree_path_new_from_string (*tmp);
2060 gtk_tree_model_get_iter (source_model, &source_iter, path);
2061 gtk_tree_model_get (source_model, &source_iter,
2062 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2065 /* Do not enable d&d of headers already opened */
2066 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2067 tny_list_append (headers, G_OBJECT (header));
2069 if (G_UNLIKELY (first)) {
2070 src_folder = tny_header_get_folder (header);
2074 /* Free and go on */
2075 gtk_tree_path_free (path);
2076 g_object_unref (header);
2081 /* This could happen ig we perform a d&d very quickly over the
2082 same row that row could dissapear because message is
2084 if (!TNY_IS_FOLDER (src_folder))
2087 /* Get the target folder */
2088 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2089 gtk_tree_model_get (dest_model, &dest_iter,
2093 if (!folder || !TNY_IS_FOLDER(folder)) {
2094 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2098 folder_type = modest_tny_folder_guess_folder_type (folder);
2099 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2100 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2101 goto cleanup; /* cannot move messages there */
2104 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2105 /* g_warning ("folder not writable"); */
2106 goto cleanup; /* verboten! */
2109 /* Ask for confirmation to move */
2110 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2112 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2116 /* Transfer messages */
2117 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2122 if (G_IS_OBJECT (src_folder))
2123 g_object_unref (src_folder);
2124 if (G_IS_OBJECT(folder))
2125 g_object_unref (G_OBJECT (folder));
2126 if (G_IS_OBJECT(headers))
2127 g_object_unref (headers);
2131 TnyFolderStore *src_folder;
2132 TnyFolderStore *dst_folder;
2133 ModestFolderView *folder_view;
2138 dnd_folder_info_destroyer (DndFolderInfo *info)
2140 if (info->src_folder)
2141 g_object_unref (info->src_folder);
2142 if (info->dst_folder)
2143 g_object_unref (info->dst_folder);
2144 g_slice_free (DndFolderInfo, info);
2148 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2149 GtkWindow *parent_window,
2150 TnyAccount *account)
2153 modest_ui_actions_on_account_connection_error (parent_window, account);
2155 /* Free the helper & info */
2156 dnd_helper_destroyer (info->helper);
2157 dnd_folder_info_destroyer (info);
2161 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2163 GtkWindow *parent_window,
2164 TnyAccount *account,
2167 DndFolderInfo *info = NULL;
2168 ModestMailOperation *mail_op;
2170 info = (DndFolderInfo *) user_data;
2172 if (err || canceled) {
2173 dnd_on_connection_failed_destroyer (info, parent_window, account);
2177 /* Do the mail operation */
2178 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2179 modest_ui_actions_move_folder_error_handler,
2180 info->src_folder, NULL);
2182 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2185 /* Transfer the folder */
2186 modest_mail_operation_xfer_folder (mail_op,
2187 TNY_FOLDER (info->src_folder),
2189 info->helper->delete_source,
2191 info->helper->folder_view);
2194 g_object_unref (G_OBJECT (mail_op));
2195 dnd_helper_destroyer (info->helper);
2196 dnd_folder_info_destroyer (info);
2201 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2203 GtkWindow *parent_window,
2204 TnyAccount *account,
2207 DndFolderInfo *info = NULL;
2209 info = (DndFolderInfo *) user_data;
2211 if (err || canceled) {
2212 dnd_on_connection_failed_destroyer (info, parent_window, account);
2216 /* Connect to source folder and perform the copy/move */
2217 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2219 drag_and_drop_from_folder_view_src_folder_performer,
2224 * This function is used by drag_data_received_cb to manage drag and
2225 * drop of a folder, i.e, and drag from the folder view to the same
2229 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2230 GtkTreeModel *dest_model,
2231 GtkTreePath *dest_row,
2232 GtkSelectionData *selection_data,
2235 GtkTreeIter dest_iter, iter;
2236 TnyFolderStore *dest_folder = NULL;
2237 TnyFolderStore *folder = NULL;
2238 gboolean forbidden = FALSE;
2240 DndFolderInfo *info = NULL;
2242 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2244 g_warning ("%s: BUG: no main window", __FUNCTION__);
2245 dnd_helper_destroyer (helper);
2250 /* check the folder rules for the destination */
2251 folder = tree_path_to_folder (dest_model, dest_row);
2252 if (TNY_IS_FOLDER(folder)) {
2253 ModestTnyFolderRules rules =
2254 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2255 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2256 } else if (TNY_IS_FOLDER_STORE(folder)) {
2257 /* enable local root as destination for folders */
2258 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2259 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2262 g_object_unref (folder);
2265 /* check the folder rules for the source */
2266 folder = tree_path_to_folder (source_model, helper->source_row);
2267 if (TNY_IS_FOLDER(folder)) {
2268 ModestTnyFolderRules rules =
2269 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2270 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2273 g_object_unref (folder);
2277 /* Check if the drag is possible */
2278 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2280 modest_platform_run_information_dialog ((GtkWindow *) win,
2281 _("mail_in_ui_folder_move_target_error"),
2283 /* Restore the previous selection */
2284 folder = tree_path_to_folder (source_model, helper->source_row);
2286 if (TNY_IS_FOLDER (folder))
2287 modest_folder_view_select_folder (helper->folder_view,
2288 TNY_FOLDER (folder), FALSE);
2289 g_object_unref (folder);
2291 dnd_helper_destroyer (helper);
2296 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2297 gtk_tree_model_get (dest_model, &dest_iter,
2300 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2301 gtk_tree_model_get (source_model, &iter,
2305 /* Create the info for the performer */
2306 info = g_slice_new0 (DndFolderInfo);
2307 info->src_folder = g_object_ref (folder);
2308 info->dst_folder = g_object_ref (dest_folder);
2309 info->helper = helper;
2311 /* Connect to the destination folder and perform the copy/move */
2312 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2314 drag_and_drop_from_folder_view_dst_folder_performer,
2318 g_object_unref (dest_folder);
2319 g_object_unref (folder);
2323 * This function receives the data set by the "drag-data-get" signal
2324 * handler. This information comes within the #GtkSelectionData. This
2325 * function will manage both the drags of folders of the treeview and
2326 * drags of headers of the header view widget.
2329 on_drag_data_received (GtkWidget *widget,
2330 GdkDragContext *context,
2333 GtkSelectionData *selection_data,
2338 GtkWidget *source_widget;
2339 GtkTreeModel *dest_model, *source_model;
2340 GtkTreePath *source_row, *dest_row;
2341 GtkTreeViewDropPosition pos;
2342 gboolean delete_source = FALSE;
2343 gboolean success = FALSE;
2345 /* Do not allow further process */
2346 g_signal_stop_emission_by_name (widget, "drag-data-received");
2347 source_widget = gtk_drag_get_source_widget (context);
2349 /* Get the action */
2350 if (context->action == GDK_ACTION_MOVE) {
2351 delete_source = TRUE;
2353 /* Notify that there is no folder selected. We need to
2354 do this in order to update the headers view (and
2355 its monitors, because when moving, the old folder
2356 won't longer exist. We can not wait for the end of
2357 the operation, because the operation won't start if
2358 the folder is in use */
2359 if (source_widget == widget) {
2360 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2361 gtk_tree_selection_unselect_all (sel);
2365 /* Check if the get_data failed */
2366 if (selection_data == NULL || selection_data->length < 0)
2369 /* Select the destination model */
2370 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2372 /* Get the path to the destination row. Can not call
2373 gtk_tree_view_get_drag_dest_row() because the source row
2374 is not selected anymore */
2375 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2378 /* Only allow drops IN other rows */
2380 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2381 pos == GTK_TREE_VIEW_DROP_AFTER)
2385 /* Drags from the header view */
2386 if (source_widget != widget) {
2387 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2389 drag_and_drop_from_header_view (source_model,
2394 DndHelper *helper = NULL;
2396 /* Get the source model and row */
2397 gtk_tree_get_row_drag_data (selection_data,
2401 /* Create the helper */
2402 helper = g_slice_new0 (DndHelper);
2403 helper->delete_source = delete_source;
2404 helper->source_row = gtk_tree_path_copy (source_row);
2405 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2407 drag_and_drop_from_folder_view (source_model,
2413 gtk_tree_path_free (source_row);
2417 gtk_tree_path_free (dest_row);
2420 /* Finish the drag and drop */
2421 gtk_drag_finish (context, success, FALSE, time);
2425 * We define a "drag-drop" signal handler because we do not want to
2426 * use the default one, because the default one always calls
2427 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2428 * signal handler, because there we have all the information available
2429 * to know if the dnd was a success or not.
2432 drag_drop_cb (GtkWidget *widget,
2433 GdkDragContext *context,
2441 if (!context->targets)
2444 /* Check if we're dragging a folder row */
2445 target = gtk_drag_dest_find_target (widget, context, NULL);
2447 /* Request the data from the source. */
2448 gtk_drag_get_data(widget, context, target, time);
2454 * This function expands a node of a tree view if it's not expanded
2455 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2456 * does that, so that's why they're here.
2459 expand_row_timeout (gpointer data)
2461 GtkTreeView *tree_view = data;
2462 GtkTreePath *dest_path = NULL;
2463 GtkTreeViewDropPosition pos;
2464 gboolean result = FALSE;
2466 gdk_threads_enter ();
2468 gtk_tree_view_get_drag_dest_row (tree_view,
2473 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2474 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2475 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2476 gtk_tree_path_free (dest_path);
2480 gtk_tree_path_free (dest_path);
2485 gdk_threads_leave ();
2491 * This function is called whenever the pointer is moved over a widget
2492 * while dragging some data. It installs a timeout that will expand a
2493 * node of the treeview if not expanded yet. This function also calls
2494 * gdk_drag_status in order to set the suggested action that will be
2495 * used by the "drag-data-received" signal handler to know if we
2496 * should do a move or just a copy of the data.
2499 on_drag_motion (GtkWidget *widget,
2500 GdkDragContext *context,
2506 GtkTreeViewDropPosition pos;
2507 GtkTreePath *dest_row;
2508 GtkTreeModel *dest_model;
2509 ModestFolderViewPrivate *priv;
2510 GdkDragAction suggested_action;
2511 gboolean valid_location = FALSE;
2512 TnyFolderStore *folder = NULL;
2514 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2516 if (priv->timer_expander != 0) {
2517 g_source_remove (priv->timer_expander);
2518 priv->timer_expander = 0;
2521 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2526 /* Do not allow drops between folders */
2528 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2529 pos == GTK_TREE_VIEW_DROP_AFTER) {
2530 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2531 gdk_drag_status(context, 0, time);
2532 valid_location = FALSE;
2535 valid_location = TRUE;
2538 /* Check that the destination folder is writable */
2539 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2540 folder = tree_path_to_folder (dest_model, dest_row);
2541 if (folder && TNY_IS_FOLDER (folder)) {
2542 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2544 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2545 valid_location = FALSE;
2550 /* Expand the selected row after 1/2 second */
2551 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2552 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2554 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2556 /* Select the desired action. By default we pick MOVE */
2557 suggested_action = GDK_ACTION_MOVE;
2559 if (context->actions == GDK_ACTION_COPY)
2560 gdk_drag_status(context, GDK_ACTION_COPY, time);
2561 else if (context->actions == GDK_ACTION_MOVE)
2562 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2563 else if (context->actions & suggested_action)
2564 gdk_drag_status(context, suggested_action, time);
2566 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2570 g_object_unref (folder);
2572 gtk_tree_path_free (dest_row);
2574 g_signal_stop_emission_by_name (widget, "drag-motion");
2576 return valid_location;
2580 * This function sets the treeview as a source and a target for dnd
2581 * events. It also connects all the requirede signals.
2584 setup_drag_and_drop (GtkTreeView *self)
2586 /* Set up the folder view as a dnd destination. Set only the
2587 highlight flag, otherwise gtk will have a different
2589 #ifdef MODEST_TOOLKIT_HILDON2
2592 gtk_drag_dest_set (GTK_WIDGET (self),
2593 GTK_DEST_DEFAULT_HIGHLIGHT,
2594 folder_view_drag_types,
2595 G_N_ELEMENTS (folder_view_drag_types),
2596 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2598 g_signal_connect (G_OBJECT (self),
2599 "drag_data_received",
2600 G_CALLBACK (on_drag_data_received),
2604 /* Set up the treeview as a dnd source */
2605 gtk_drag_source_set (GTK_WIDGET (self),
2607 folder_view_drag_types,
2608 G_N_ELEMENTS (folder_view_drag_types),
2609 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2611 g_signal_connect (G_OBJECT (self),
2613 G_CALLBACK (on_drag_motion),
2616 g_signal_connect (G_OBJECT (self),
2618 G_CALLBACK (on_drag_data_get),
2621 g_signal_connect (G_OBJECT (self),
2623 G_CALLBACK (drag_drop_cb),
2628 * This function manages the navigation through the folders using the
2629 * keyboard or the hardware keys in the device
2632 on_key_pressed (GtkWidget *self,
2636 GtkTreeSelection *selection;
2638 GtkTreeModel *model;
2639 gboolean retval = FALSE;
2641 /* Up and Down are automatically managed by the treeview */
2642 if (event->keyval == GDK_Return) {
2643 /* Expand/Collapse the selected row */
2644 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2645 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2648 path = gtk_tree_model_get_path (model, &iter);
2650 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2651 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2653 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2654 gtk_tree_path_free (path);
2656 /* No further processing */
2664 * We listen to the changes in the local folder account name key,
2665 * because we want to show the right name in the view. The local
2666 * folder account name corresponds to the device name in the Maemo
2667 * version. We do this because we do not want to query gconf on each
2668 * tree view refresh. It's better to cache it and change whenever
2672 on_configuration_key_changed (ModestConf* conf,
2674 ModestConfEvent event,
2675 ModestConfNotificationId id,
2676 ModestFolderView *self)
2678 ModestFolderViewPrivate *priv;
2681 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2682 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2684 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2685 g_free (priv->local_account_name);
2687 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2688 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2690 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2691 MODEST_CONF_DEVICE_NAME, NULL);
2693 /* Force a redraw */
2694 #if GTK_CHECK_VERSION(2, 8, 0)
2695 GtkTreeViewColumn * tree_column;
2697 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2699 gtk_tree_view_column_queue_resize (tree_column);
2701 gtk_widget_queue_draw (GTK_WIDGET (self));
2707 modest_folder_view_set_style (ModestFolderView *self,
2708 ModestFolderViewStyle style)
2710 ModestFolderViewPrivate *priv;
2712 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2713 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2714 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2716 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2719 priv->style = style;
2723 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2724 const gchar *account_id)
2726 ModestFolderViewPrivate *priv;
2727 GtkTreeModel *model;
2729 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2731 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2733 /* This will be used by the filter_row callback,
2734 * to decided which rows to show: */
2735 if (priv->visible_account_id) {
2736 g_free (priv->visible_account_id);
2737 priv->visible_account_id = NULL;
2740 priv->visible_account_id = g_strdup (account_id);
2743 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2744 if (GTK_IS_TREE_MODEL_FILTER (model))
2745 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2747 /* Save settings to gconf */
2748 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2749 MODEST_CONF_FOLDER_VIEW_KEY);
2753 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2755 ModestFolderViewPrivate *priv;
2757 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2759 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2761 return (const gchar *) priv->visible_account_id;
2765 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2769 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2771 gtk_tree_model_get (model, iter,
2775 gboolean result = FALSE;
2776 if (type == TNY_FOLDER_TYPE_INBOX) {
2780 *inbox_iter = *iter;
2784 if (gtk_tree_model_iter_children (model, &child, iter)) {
2785 if (find_inbox_iter (model, &child, inbox_iter))
2789 } while (gtk_tree_model_iter_next (model, iter));
2798 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2800 GtkTreeModel *model;
2801 GtkTreeIter iter, inbox_iter;
2802 GtkTreeSelection *sel;
2803 GtkTreePath *path = NULL;
2805 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2807 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2811 expand_root_items (self);
2812 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2814 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2815 g_warning ("%s: model is empty", __FUNCTION__);
2819 if (find_inbox_iter (model, &iter, &inbox_iter))
2820 path = gtk_tree_model_get_path (model, &inbox_iter);
2822 path = gtk_tree_path_new_first ();
2824 /* Select the row and free */
2825 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2826 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2827 gtk_tree_path_free (path);
2830 gtk_widget_grab_focus (GTK_WIDGET(self));
2836 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2841 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2842 TnyFolder* a_folder;
2845 gtk_tree_model_get (model, iter,
2846 INSTANCE_COLUMN, &a_folder,
2852 if (folder == a_folder) {
2853 g_object_unref (a_folder);
2854 *folder_iter = *iter;
2857 g_object_unref (a_folder);
2859 if (gtk_tree_model_iter_children (model, &child, iter)) {
2860 if (find_folder_iter (model, &child, folder_iter, folder))
2864 } while (gtk_tree_model_iter_next (model, iter));
2871 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2874 ModestFolderView *self)
2876 ModestFolderViewPrivate *priv = NULL;
2877 GtkTreeSelection *sel;
2878 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2879 GObject *instance = NULL;
2881 if (!MODEST_IS_FOLDER_VIEW(self))
2884 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2886 priv->reexpand = TRUE;
2888 gtk_tree_model_get (tree_model, iter,
2890 INSTANCE_COLUMN, &instance,
2892 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2893 priv->folder_to_select = g_object_ref (instance);
2895 g_object_unref (instance);
2897 if (priv->folder_to_select) {
2899 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2902 path = gtk_tree_model_get_path (tree_model, iter);
2903 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2905 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2907 gtk_tree_selection_select_iter (sel, iter);
2908 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2910 gtk_tree_path_free (path);
2914 modest_folder_view_disable_next_folder_selection (self);
2916 /* Refilter the model */
2917 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2923 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2925 ModestFolderViewPrivate *priv;
2927 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2929 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2931 if (priv->folder_to_select)
2932 g_object_unref(priv->folder_to_select);
2934 priv->folder_to_select = NULL;
2938 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2939 gboolean after_change)
2941 GtkTreeModel *model;
2942 GtkTreeIter iter, folder_iter;
2943 GtkTreeSelection *sel;
2944 ModestFolderViewPrivate *priv = NULL;
2946 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2947 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2949 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2952 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2953 gtk_tree_selection_unselect_all (sel);
2955 if (priv->folder_to_select)
2956 g_object_unref(priv->folder_to_select);
2957 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2961 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2966 /* Refilter the model, before selecting the folder */
2967 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2969 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2970 g_warning ("%s: model is empty", __FUNCTION__);
2974 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2977 path = gtk_tree_model_get_path (model, &folder_iter);
2978 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2980 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2981 gtk_tree_selection_select_iter (sel, &folder_iter);
2982 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2984 gtk_tree_path_free (path);
2992 modest_folder_view_copy_selection (ModestFolderView *self)
2994 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2996 /* Copy selection */
2997 _clipboard_set_selected_data (self, FALSE);
3001 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3003 ModestFolderViewPrivate *priv = NULL;
3004 GtkTreeModel *model = NULL;
3005 const gchar **hidding = NULL;
3006 guint i, n_selected;
3008 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3009 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3011 /* Copy selection */
3012 if (!_clipboard_set_selected_data (folder_view, TRUE))
3015 /* Get hidding ids */
3016 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3018 /* Clear hidding array created by previous cut operation */
3019 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3021 /* Copy hidding array */
3022 priv->n_selected = n_selected;
3023 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3024 for (i=0; i < n_selected; i++)
3025 priv->hidding_ids[i] = g_strdup(hidding[i]);
3027 /* Hide cut folders */
3028 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3029 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3033 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3034 ModestFolderView *folder_view_dst)
3036 GtkTreeModel *filter_model = NULL;
3037 GtkTreeModel *model = NULL;
3038 GtkTreeModel *new_filter_model = NULL;
3040 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3041 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3044 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3045 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3047 /* Build new filter model */
3048 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3049 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3053 /* Set copied model */
3054 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3055 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3056 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3059 g_object_unref (new_filter_model);
3063 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3066 GtkTreeModel *model = NULL;
3067 ModestFolderViewPrivate* priv;
3069 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3071 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3072 priv->show_non_move = show;
3073 /* modest_folder_view_update_model(folder_view, */
3074 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3076 /* Hide special folders */
3077 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3078 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3079 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3083 /* Returns FALSE if it did not selected anything */
3085 _clipboard_set_selected_data (ModestFolderView *folder_view,
3088 ModestFolderViewPrivate *priv = NULL;
3089 TnyFolderStore *folder = NULL;
3090 gboolean retval = FALSE;
3092 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3093 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3095 /* Set selected data on clipboard */
3096 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3097 folder = modest_folder_view_get_selected (folder_view);
3099 /* Do not allow to select an account */
3100 if (TNY_IS_FOLDER (folder)) {
3101 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3106 g_object_unref (folder);
3112 _clear_hidding_filter (ModestFolderView *folder_view)
3114 ModestFolderViewPrivate *priv;
3117 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3118 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3120 if (priv->hidding_ids != NULL) {
3121 for (i=0; i < priv->n_selected; i++)
3122 g_free (priv->hidding_ids[i]);
3123 g_free(priv->hidding_ids);
3129 on_display_name_changed (ModestAccountMgr *mgr,
3130 const gchar *account,
3133 ModestFolderView *self;
3135 self = MODEST_FOLDER_VIEW (user_data);
3137 /* Force a redraw */
3138 #if GTK_CHECK_VERSION(2, 8, 0)
3139 GtkTreeViewColumn * tree_column;
3141 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3143 gtk_tree_view_column_queue_resize (tree_column);
3145 gtk_widget_queue_draw (GTK_WIDGET (self));
3150 modest_folder_view_set_cell_style (ModestFolderView *self,
3151 ModestFolderViewCellStyle cell_style)
3153 ModestFolderViewPrivate *priv = NULL;
3155 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3156 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3158 priv->cell_style = cell_style;
3160 g_object_set (G_OBJECT (priv->messages_renderer),
3161 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3164 gtk_widget_queue_draw (GTK_WIDGET (self));
3168 update_style (ModestFolderView *self)
3170 ModestFolderViewPrivate *priv;
3171 GdkColor style_color;
3173 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3174 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3176 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3177 gdk_color_parse ("grey", &style_color);
3180 g_object_set (G_OBJECT (priv->messages_renderer),
3181 "foreground-gdk", &style_color,
3182 "foreground-set", TRUE,
3187 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3189 if (strcmp ("style", spec->name) == 0) {
3190 update_style (MODEST_FOLDER_VIEW (obj));
3191 gtk_widget_queue_draw (GTK_WIDGET (obj));