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... */
485 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
486 item_name = g_strdup (fname);
493 /* Use bold font style if there are unread or unset messages */
495 item_name = g_strdup_printf ("%s (%d)", fname, number);
498 item_name = g_strdup (fname);
503 } else if (TNY_IS_ACCOUNT (instance)) {
504 /* If it's a server account */
505 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
506 item_name = g_strdup (priv->local_account_name);
508 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
509 /* fname is only correct when the items are first
510 * added to the model, not when the account is
511 * changed later, so get the name from the account
513 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
516 item_name = g_strdup (fname);
522 item_name = g_strdup ("unknown");
524 if (item_name && item_weight) {
525 /* Set the name in the treeview cell: */
526 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
528 /* Notify display name observers */
529 /* TODO: What listens for this signal, and how can it use only the new name? */
530 if (((GObject *) priv->cur_folder_store) == instance) {
531 g_signal_emit (G_OBJECT(self),
532 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
539 /* If it is a Memory card account, make sure that we have the correct name.
540 * This function will be trigerred again when the name has been retrieved: */
541 if (TNY_IS_STORE_ACCOUNT (instance) &&
542 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
544 /* Get the account name asynchronously: */
545 GetMmcAccountNameData *callback_data =
546 g_slice_new0(GetMmcAccountNameData);
547 callback_data->self = self;
549 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
551 callback_data->previous_name = g_strdup (name);
553 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
554 on_get_mmc_account_name, callback_data);
558 g_object_unref (G_OBJECT (instance));
564 messages_cell_data (GtkTreeViewColumn *column,
565 GtkCellRenderer *renderer,
566 GtkTreeModel *tree_model,
570 ModestFolderView *self;
571 ModestFolderViewPrivate *priv;
572 GObject *rendobj = (GObject *) renderer;
573 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
574 GObject *instance = NULL;
575 gchar *item_name = NULL;
577 gtk_tree_model_get (tree_model, iter,
579 INSTANCE_COLUMN, &instance,
584 self = MODEST_FOLDER_VIEW (data);
585 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
588 if (type != TNY_FOLDER_TYPE_ROOT) {
592 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
593 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
594 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
596 /* Sometimes an special folder is reported by the server as
597 NORMAL, like some versions of Dovecot */
598 if (type == TNY_FOLDER_TYPE_NORMAL ||
599 type == TNY_FOLDER_TYPE_UNKNOWN) {
600 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
604 /* note: we cannot reliably get the counts from the tree model, we need
605 * to use explicit calls on tny_folder for some reason.
607 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
608 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
609 (type == TNY_FOLDER_TYPE_OUTBOX) ||
610 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
611 number = tny_folder_get_all_count (TNY_FOLDER(instance));
614 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
618 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
620 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
628 item_name = g_strdup ("");
631 /* Set the name in the treeview cell: */
632 g_object_set (rendobj,"text", item_name, NULL);
640 g_object_unref (G_OBJECT (instance));
646 GdkPixbuf *pixbuf_open;
647 GdkPixbuf *pixbuf_close;
651 static inline GdkPixbuf *
652 get_composite_pixbuf (const gchar *icon_name,
654 GdkPixbuf *base_pixbuf)
656 GdkPixbuf *emblem, *retval = NULL;
658 emblem = modest_platform_get_icon (icon_name, size);
660 retval = gdk_pixbuf_copy (base_pixbuf);
661 gdk_pixbuf_composite (emblem, retval, 0, 0,
662 MIN (gdk_pixbuf_get_width (emblem),
663 gdk_pixbuf_get_width (retval)),
664 MIN (gdk_pixbuf_get_height (emblem),
665 gdk_pixbuf_get_height (retval)),
666 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
667 g_object_unref (emblem);
672 static inline ThreePixbufs *
673 get_composite_icons (const gchar *icon_code,
675 GdkPixbuf **pixbuf_open,
676 GdkPixbuf **pixbuf_close)
678 ThreePixbufs *retval;
681 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
684 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
689 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
693 retval = g_slice_new0 (ThreePixbufs);
695 retval->pixbuf = g_object_ref (*pixbuf);
697 retval->pixbuf_open = g_object_ref (*pixbuf_open);
699 retval->pixbuf_close = g_object_ref (*pixbuf_close);
705 get_folder_icons (TnyFolderType type, GObject *instance)
707 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
708 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
709 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
710 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
711 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
713 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
714 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
715 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
716 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
717 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
719 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
720 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
721 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
722 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
723 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
725 ThreePixbufs *retval = NULL;
727 /* Sometimes an special folder is reported by the server as
728 NORMAL, like some versions of Dovecot */
729 if (type == TNY_FOLDER_TYPE_NORMAL ||
730 type == TNY_FOLDER_TYPE_UNKNOWN) {
731 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
735 case TNY_FOLDER_TYPE_INVALID:
736 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
739 case TNY_FOLDER_TYPE_ROOT:
740 if (TNY_IS_ACCOUNT (instance)) {
742 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
743 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
746 &avirt_pixbuf_close);
748 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
750 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
751 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
756 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
759 &anorm_pixbuf_close);
764 case TNY_FOLDER_TYPE_INBOX:
765 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
768 &inbox_pixbuf_close);
770 case TNY_FOLDER_TYPE_OUTBOX:
771 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
774 &outbox_pixbuf_close);
776 case TNY_FOLDER_TYPE_JUNK:
777 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
782 case TNY_FOLDER_TYPE_SENT:
783 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
788 case TNY_FOLDER_TYPE_TRASH:
789 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
792 &trash_pixbuf_close);
794 case TNY_FOLDER_TYPE_DRAFTS:
795 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
798 &draft_pixbuf_close);
800 case TNY_FOLDER_TYPE_NORMAL:
802 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
805 &normal_pixbuf_close);
813 free_pixbufs (ThreePixbufs *pixbufs)
816 g_object_unref (pixbufs->pixbuf);
817 if (pixbufs->pixbuf_open)
818 g_object_unref (pixbufs->pixbuf_open);
819 if (pixbufs->pixbuf_close)
820 g_object_unref (pixbufs->pixbuf_close);
821 g_slice_free (ThreePixbufs, pixbufs);
825 icon_cell_data (GtkTreeViewColumn *column,
826 GtkCellRenderer *renderer,
827 GtkTreeModel *tree_model,
831 GObject *rendobj = NULL, *instance = NULL;
832 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
833 gboolean has_children;
834 ThreePixbufs *pixbufs;
836 rendobj = (GObject *) renderer;
838 gtk_tree_model_get (tree_model, iter,
840 INSTANCE_COLUMN, &instance,
846 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
847 pixbufs = get_folder_icons (type, instance);
848 g_object_unref (instance);
851 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
854 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
855 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
858 free_pixbufs (pixbufs);
862 add_columns (GtkWidget *treeview)
864 GtkTreeViewColumn *column;
865 GtkCellRenderer *renderer;
866 GtkTreeSelection *sel;
867 ModestFolderViewPrivate *priv;
869 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
872 column = gtk_tree_view_column_new ();
874 /* Set icon and text render function */
875 renderer = gtk_cell_renderer_pixbuf_new();
876 gtk_tree_view_column_pack_start (column, renderer, FALSE);
877 gtk_tree_view_column_set_cell_data_func(column, renderer,
878 icon_cell_data, treeview, NULL);
880 renderer = gtk_cell_renderer_text_new();
881 g_object_set (renderer,
882 #ifdef MODEST_TOOLKIT_HILDON2
883 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
885 "ellipsize", PANGO_ELLIPSIZE_END,
887 "ellipsize-set", TRUE, NULL);
888 gtk_tree_view_column_pack_start (column, renderer, TRUE);
889 gtk_tree_view_column_set_cell_data_func(column, renderer,
890 text_cell_data, treeview, NULL);
892 priv->messages_renderer = gtk_cell_renderer_text_new ();
893 g_object_set (priv->messages_renderer,
894 "scale", PANGO_SCALE_X_SMALL,
897 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
898 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
899 messages_cell_data, treeview, NULL);
901 /* Set selection mode */
902 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
903 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
905 /* Set treeview appearance */
906 gtk_tree_view_column_set_spacing (column, 2);
907 gtk_tree_view_column_set_resizable (column, TRUE);
908 gtk_tree_view_column_set_fixed_width (column, TRUE);
909 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
910 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
913 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
917 modest_folder_view_init (ModestFolderView *obj)
919 ModestFolderViewPrivate *priv;
922 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
924 priv->timer_expander = 0;
925 priv->account_store = NULL;
927 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
928 priv->cur_folder_store = NULL;
929 priv->visible_account_id = NULL;
930 priv->folder_to_select = NULL;
932 priv->reexpand = TRUE;
934 /* Initialize the local account name */
935 conf = modest_runtime_get_conf();
936 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
938 /* Init email clipboard */
939 priv->clipboard = modest_runtime_get_email_clipboard ();
940 priv->hidding_ids = NULL;
941 priv->n_selected = 0;
942 priv->reselect = FALSE;
943 priv->show_non_move = TRUE;
946 add_columns (GTK_WIDGET (obj));
948 /* Setup drag and drop */
949 setup_drag_and_drop (GTK_TREE_VIEW(obj));
951 /* Connect signals */
952 g_signal_connect (G_OBJECT (obj),
954 G_CALLBACK (on_key_pressed), NULL);
956 priv->display_name_changed_signal =
957 g_signal_connect (modest_runtime_get_account_mgr (),
958 "display_name_changed",
959 G_CALLBACK (on_display_name_changed),
963 * Track changes in the local account name (in the device it
964 * will be the device name)
966 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
968 G_CALLBACK(on_configuration_key_changed),
972 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
978 tny_account_store_view_init (gpointer g, gpointer iface_data)
980 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
982 klass->set_account_store = modest_folder_view_set_account_store;
986 modest_folder_view_finalize (GObject *obj)
988 ModestFolderViewPrivate *priv;
989 GtkTreeSelection *sel;
991 g_return_if_fail (obj);
993 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
995 if (priv->timer_expander != 0) {
996 g_source_remove (priv->timer_expander);
997 priv->timer_expander = 0;
1000 if (priv->account_store) {
1001 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1002 priv->account_inserted_signal);
1003 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1004 priv->account_removed_signal);
1005 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1006 priv->account_changed_signal);
1007 g_object_unref (G_OBJECT(priv->account_store));
1008 priv->account_store = NULL;
1011 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1012 priv->display_name_changed_signal)) {
1013 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1014 priv->display_name_changed_signal);
1015 priv->display_name_changed_signal = 0;
1019 g_object_unref (G_OBJECT (priv->query));
1023 if (priv->folder_to_select) {
1024 g_object_unref (G_OBJECT(priv->folder_to_select));
1025 priv->folder_to_select = NULL;
1028 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1030 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1032 g_free (priv->local_account_name);
1033 g_free (priv->visible_account_id);
1035 if (priv->conf_key_signal) {
1036 g_signal_handler_disconnect (modest_runtime_get_conf (),
1037 priv->conf_key_signal);
1038 priv->conf_key_signal = 0;
1041 if (priv->cur_folder_store) {
1042 g_object_unref (priv->cur_folder_store);
1043 priv->cur_folder_store = NULL;
1046 /* Clear hidding array created by cut operation */
1047 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1049 G_OBJECT_CLASS(parent_class)->finalize (obj);
1054 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1056 ModestFolderViewPrivate *priv;
1059 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1060 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1062 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1063 device = tny_account_store_get_device (account_store);
1065 if (G_UNLIKELY (priv->account_store)) {
1067 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1068 priv->account_inserted_signal))
1069 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1070 priv->account_inserted_signal);
1071 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1072 priv->account_removed_signal))
1073 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1074 priv->account_removed_signal);
1075 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1076 priv->account_changed_signal))
1077 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1078 priv->account_changed_signal);
1079 g_object_unref (G_OBJECT (priv->account_store));
1082 priv->account_store = g_object_ref (G_OBJECT (account_store));
1084 priv->account_removed_signal =
1085 g_signal_connect (G_OBJECT(account_store), "account_removed",
1086 G_CALLBACK (on_account_removed), self);
1088 priv->account_inserted_signal =
1089 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1090 G_CALLBACK (on_account_inserted), self);
1092 priv->account_changed_signal =
1093 g_signal_connect (G_OBJECT(account_store), "account_changed",
1094 G_CALLBACK (on_account_changed), self);
1096 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1097 priv->reselect = FALSE;
1098 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1100 g_object_unref (G_OBJECT (device));
1104 on_account_inserted (TnyAccountStore *account_store,
1105 TnyAccount *account,
1108 ModestFolderViewPrivate *priv;
1109 GtkTreeModel *sort_model, *filter_model;
1111 /* Ignore transport account insertions, we're not showing them
1112 in the folder view */
1113 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1116 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1119 /* If we're adding a new account, and there is no previous
1120 one, we need to select the visible server account */
1121 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1122 !priv->visible_account_id)
1123 modest_widget_memory_restore (modest_runtime_get_conf(),
1124 G_OBJECT (user_data),
1125 MODEST_CONF_FOLDER_VIEW_KEY);
1127 if (!GTK_IS_TREE_VIEW(user_data)) {
1128 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1132 /* Get the inner model */
1133 /* check, is some rare cases, we did not get the right thing here,
1135 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1136 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1137 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1141 /* check, is some rare cases, we did not get the right thing here,
1143 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1144 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1145 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1149 /* Insert the account in the model */
1150 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1151 G_OBJECT (account));
1153 /* Refilter the model */
1154 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1159 same_account_selected (ModestFolderView *self,
1160 TnyAccount *account)
1162 ModestFolderViewPrivate *priv;
1163 gboolean same_account = FALSE;
1165 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1167 if (priv->cur_folder_store) {
1168 TnyAccount *selected_folder_account = NULL;
1170 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1171 selected_folder_account =
1172 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1174 selected_folder_account =
1175 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1178 if (selected_folder_account == account)
1179 same_account = TRUE;
1181 g_object_unref (selected_folder_account);
1183 return same_account;
1188 * Selects the first inbox or the local account in an idle
1191 on_idle_select_first_inbox_or_local (gpointer user_data)
1193 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1195 gdk_threads_enter ();
1196 modest_folder_view_select_first_inbox_or_local (self);
1197 gdk_threads_leave ();
1203 on_account_changed (TnyAccountStore *account_store,
1204 TnyAccount *tny_account,
1207 ModestFolderView *self;
1208 ModestFolderViewPrivate *priv;
1209 GtkTreeModel *sort_model, *filter_model;
1210 GtkTreeSelection *sel;
1211 gboolean same_account;
1213 /* Ignore transport account insertions, we're not showing them
1214 in the folder view */
1215 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1218 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1219 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1223 self = MODEST_FOLDER_VIEW (user_data);
1224 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1226 /* Get the inner model */
1227 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1228 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1229 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1233 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1234 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1235 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1239 /* Invalidate the cur_folder_store only if the selected folder
1240 belongs to the account that is being removed */
1241 same_account = same_account_selected (self, tny_account);
1243 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1244 gtk_tree_selection_unselect_all (sel);
1247 /* Remove the account from the model */
1248 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1249 G_OBJECT (tny_account));
1251 /* Insert the account in the model */
1252 tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1253 G_OBJECT (tny_account));
1255 /* Refilter the model */
1256 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1258 /* Select the first INBOX if the currently selected folder
1259 belongs to the account that is being deleted */
1260 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1261 g_idle_add (on_idle_select_first_inbox_or_local, self);
1265 on_account_removed (TnyAccountStore *account_store,
1266 TnyAccount *account,
1269 ModestFolderView *self = NULL;
1270 ModestFolderViewPrivate *priv;
1271 GtkTreeModel *sort_model, *filter_model;
1272 GtkTreeSelection *sel = NULL;
1273 gboolean same_account = FALSE;
1275 /* Ignore transport account removals, we're not showing them
1276 in the folder view */
1277 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1280 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1281 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1285 self = MODEST_FOLDER_VIEW (user_data);
1286 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1288 /* Invalidate the cur_folder_store only if the selected folder
1289 belongs to the account that is being removed */
1290 same_account = same_account_selected (self, account);
1292 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1293 gtk_tree_selection_unselect_all (sel);
1296 /* Invalidate row to select only if the folder to select
1297 belongs to the account that is being removed*/
1298 if (priv->folder_to_select) {
1299 TnyAccount *folder_to_select_account = NULL;
1301 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1302 if (folder_to_select_account == account) {
1303 modest_folder_view_disable_next_folder_selection (self);
1304 g_object_unref (priv->folder_to_select);
1305 priv->folder_to_select = NULL;
1307 g_object_unref (folder_to_select_account);
1310 /* Remove the account from the model */
1311 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1312 if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1313 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1317 sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1318 if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1319 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1323 tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1324 G_OBJECT (account));
1326 /* If the removed account is the currently viewed one then
1327 clear the configuration value. The new visible account will be the default account */
1328 if (priv->visible_account_id &&
1329 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1331 /* Clear the current visible account_id */
1332 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1334 /* Call the restore method, this will set the new visible account */
1335 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1336 MODEST_CONF_FOLDER_VIEW_KEY);
1339 /* Refilter the model */
1340 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1342 /* Select the first INBOX if the currently selected folder
1343 belongs to the account that is being deleted */
1345 g_idle_add (on_idle_select_first_inbox_or_local, self);
1349 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1351 GtkTreeViewColumn *col;
1353 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1355 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1357 g_printerr ("modest: failed get column for title\n");
1361 gtk_tree_view_column_set_title (col, title);
1362 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1367 modest_folder_view_on_map (ModestFolderView *self,
1368 GdkEventExpose *event,
1371 ModestFolderViewPrivate *priv;
1373 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1375 /* This won't happen often */
1376 if (G_UNLIKELY (priv->reselect)) {
1377 /* Select the first inbox or the local account if not found */
1379 /* TODO: this could cause a lock at startup, so we
1380 comment it for the moment. We know that this will
1381 be a bug, because the INBOX is not selected, but we
1382 need to rewrite some parts of Modest to avoid the
1383 deathlock situation */
1384 /* TODO: check if this is still the case */
1385 priv->reselect = FALSE;
1386 modest_folder_view_select_first_inbox_or_local (self);
1387 /* Notify the display name observers */
1388 g_signal_emit (G_OBJECT(self),
1389 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1393 if (priv->reexpand) {
1394 expand_root_items (self);
1395 priv->reexpand = FALSE;
1402 modest_folder_view_new (TnyFolderStoreQuery *query)
1405 ModestFolderViewPrivate *priv;
1406 GtkTreeSelection *sel;
1408 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1409 #ifdef MODEST_TOOLKIT_HILDON2
1410 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1413 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1416 priv->query = g_object_ref (query);
1418 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1419 priv->changed_signal = g_signal_connect (sel, "changed",
1420 G_CALLBACK (on_selection_changed), self);
1422 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1424 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1426 return GTK_WIDGET(self);
1429 /* this feels dirty; any other way to expand all the root items? */
1431 expand_root_items (ModestFolderView *self)
1434 GtkTreeModel *model;
1437 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1438 path = gtk_tree_path_new_first ();
1440 /* all folders should have child items, so.. */
1442 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1443 gtk_tree_path_next (path);
1444 } while (gtk_tree_model_get_iter (model, &iter, path));
1446 gtk_tree_path_free (path);
1450 * We use this function to implement the
1451 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1452 * account in this case, and the local folders.
1455 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1457 ModestFolderViewPrivate *priv;
1458 gboolean retval = TRUE;
1459 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1460 GObject *instance = NULL;
1461 const gchar *id = NULL;
1463 gboolean found = FALSE;
1464 gboolean cleared = FALSE;
1466 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1467 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1469 gtk_tree_model_get (model, iter,
1471 INSTANCE_COLUMN, &instance,
1474 /* Do not show if there is no instance, this could indeed
1475 happen when the model is being modified while it's being
1476 drawn. This could occur for example when moving folders
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 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1508 !gtk_tree_model_iter_parent (model, iter, &parent)) {
1509 /* Only show special folders for current account if needed */
1510 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1511 TnyAccount *account;
1513 account = tny_folder_get_account (TNY_FOLDER (instance));
1515 if (TNY_IS_ACCOUNT (account)) {
1516 const gchar *account_id = tny_account_get_id (account);
1518 if (!modest_tny_account_is_virtual_local_folders (account) &&
1519 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1520 /* Show only the visible account id */
1521 if (priv->visible_account_id) {
1522 if (strcmp (account_id, priv->visible_account_id))
1526 g_object_unref (account);
1533 /* Check hiding (if necessary) */
1534 cleared = modest_email_clipboard_cleared (priv->clipboard);
1535 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1536 id = tny_folder_get_id (TNY_FOLDER(instance));
1537 if (priv->hidding_ids != NULL)
1538 for (i=0; i < priv->n_selected && !found; i++)
1539 if (priv->hidding_ids[i] != NULL && id != NULL)
1540 found = (!strcmp (priv->hidding_ids[i], id));
1546 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1547 folder as no message can be move there according to UI specs */
1548 if (!priv->show_non_move) {
1550 case TNY_FOLDER_TYPE_OUTBOX:
1551 case TNY_FOLDER_TYPE_SENT:
1552 case TNY_FOLDER_TYPE_DRAFTS:
1555 case TNY_FOLDER_TYPE_UNKNOWN:
1556 case TNY_FOLDER_TYPE_NORMAL:
1557 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1558 if (type == TNY_FOLDER_TYPE_INVALID)
1559 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1561 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1562 type == TNY_FOLDER_TYPE_SENT
1563 || type == TNY_FOLDER_TYPE_DRAFTS)
1572 g_object_unref (instance);
1579 modest_folder_view_update_model (ModestFolderView *self,
1580 TnyAccountStore *account_store)
1582 ModestFolderViewPrivate *priv;
1583 GtkTreeModel *model /* , *old_model */;
1584 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1586 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1587 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1590 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1592 /* Notify that there is no folder selected */
1593 g_signal_emit (G_OBJECT(self),
1594 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1596 if (priv->cur_folder_store) {
1597 g_object_unref (priv->cur_folder_store);
1598 priv->cur_folder_store = NULL;
1601 /* FIXME: the local accounts are not shown when the query
1602 selects only the subscribed folders */
1603 #ifdef MODEST_TOOLKIT_HILDON2
1604 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1605 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1607 model = tny_gtk_folder_store_tree_model_new (NULL);
1610 /* Get the accounts: */
1611 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1613 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1615 sortable = gtk_tree_model_sort_new_with_model (model);
1616 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1618 GTK_SORT_ASCENDING);
1619 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1621 cmp_rows, NULL, NULL);
1623 /* Create filter model */
1624 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1625 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1631 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1632 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1633 (GCallback) on_row_inserted_maybe_select_folder, self);
1635 g_object_unref (model);
1636 g_object_unref (filter_model);
1637 g_object_unref (sortable);
1639 /* Force a reselection of the INBOX next time the widget is shown */
1640 priv->reselect = TRUE;
1647 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1649 GtkTreeModel *model = NULL;
1650 TnyFolderStore *folder = NULL;
1652 ModestFolderView *tree_view = NULL;
1653 ModestFolderViewPrivate *priv = NULL;
1654 gboolean selected = FALSE;
1656 g_return_if_fail (sel);
1657 g_return_if_fail (user_data);
1659 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1661 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1663 tree_view = MODEST_FOLDER_VIEW (user_data);
1666 gtk_tree_model_get (model, &iter,
1667 INSTANCE_COLUMN, &folder,
1670 /* If the folder is the same do not notify */
1671 if (folder && priv->cur_folder_store == folder) {
1672 g_object_unref (folder);
1677 /* Current folder was unselected */
1678 if (priv->cur_folder_store) {
1679 /* We must do this firstly because a libtinymail-camel
1680 implementation detail. If we issue the signal
1681 before doing the sync_async, then that signal could
1682 cause (and it actually does it) a free of the
1683 summary of the folder (because the main window will
1684 clear the headers view */
1685 if (TNY_IS_FOLDER(priv->cur_folder_store))
1686 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1687 FALSE, NULL, NULL, NULL);
1689 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1690 priv->cur_folder_store, FALSE);
1692 g_object_unref (priv->cur_folder_store);
1693 priv->cur_folder_store = NULL;
1696 /* New current references */
1697 priv->cur_folder_store = folder;
1699 /* New folder has been selected. Do not notify if there is
1700 nothing new selected */
1702 g_signal_emit (G_OBJECT(tree_view),
1703 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1704 0, priv->cur_folder_store, TRUE);
1709 on_row_activated (GtkTreeView *treeview,
1710 GtkTreePath *treepath,
1711 GtkTreeViewColumn *column,
1714 GtkTreeModel *model = NULL;
1715 TnyFolderStore *folder = NULL;
1717 ModestFolderView *self = NULL;
1718 ModestFolderViewPrivate *priv = NULL;
1720 g_return_if_fail (treeview);
1721 g_return_if_fail (user_data);
1723 self = MODEST_FOLDER_VIEW (user_data);
1724 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1726 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1728 if (!gtk_tree_model_get_iter (model, &iter, treepath))
1731 gtk_tree_model_get (model, &iter,
1732 INSTANCE_COLUMN, &folder,
1735 g_signal_emit (G_OBJECT(self),
1736 signals[FOLDER_ACTIVATED_SIGNAL],
1739 g_object_unref (folder);
1743 modest_folder_view_get_selected (ModestFolderView *self)
1745 ModestFolderViewPrivate *priv;
1747 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1749 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1750 if (priv->cur_folder_store)
1751 g_object_ref (priv->cur_folder_store);
1753 return priv->cur_folder_store;
1757 get_cmp_rows_type_pos (GObject *folder)
1759 /* Remote accounts -> Local account -> MMC account .*/
1762 if (TNY_IS_ACCOUNT (folder) &&
1763 modest_tny_account_is_virtual_local_folders (
1764 TNY_ACCOUNT (folder))) {
1766 } else if (TNY_IS_ACCOUNT (folder)) {
1767 TnyAccount *account = TNY_ACCOUNT (folder);
1768 const gchar *account_id = tny_account_get_id (account);
1769 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1775 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1776 return -1; /* Should never happen */
1781 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
1783 TnyAccount *account;
1784 gboolean is_special;
1785 /* Inbox, Outbox, Drafts, Sent, User */
1788 if (!TNY_IS_FOLDER (folder_store))
1791 case TNY_FOLDER_TYPE_INBOX:
1793 account = tny_folder_get_account (folder_store);
1794 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
1795 g_object_unref (account);
1796 return is_special?0:4;
1799 case TNY_FOLDER_TYPE_OUTBOX:
1801 account = tny_folder_get_account (folder_store);
1802 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1803 g_object_unref (account);
1804 return is_special?1:4;
1807 case TNY_FOLDER_TYPE_DRAFTS:
1809 account = tny_folder_get_account (folder_store);
1810 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1811 g_object_unref (account);
1812 return is_special?2:4;
1815 case TNY_FOLDER_TYPE_SENT:
1817 account = tny_folder_get_account (folder_store);
1818 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1819 g_object_unref (account);
1820 return is_special?3:4;
1829 compare_account_names (TnyAccount *a1, TnyAccount *a2)
1831 const gchar *a1_name, *a2_name;
1833 a1_name = tny_account_get_name (a1);
1834 a2_name = tny_account_get_name (a2);
1836 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
1840 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
1842 TnyAccount *a1, *a2;
1845 if (TNY_IS_ACCOUNT (s1)) {
1846 a1 = TNY_ACCOUNT (g_object_ref (s1));
1848 a1 = tny_folder_get_account (TNY_FOLDER (s1));
1851 if (TNY_IS_ACCOUNT (s2)) {
1852 a2 = TNY_ACCOUNT (g_object_ref (s2));
1854 a2 = tny_folder_get_account (TNY_FOLDER (s2));
1861 /* First we sort with the type of account */
1862 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
1866 cmp = compare_account_names (a1, a2);
1869 g_object_unref (a1);
1870 g_object_unref (a2);
1876 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
1878 gint is_account1, is_account2;
1880 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
1881 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
1883 return is_account2 - is_account1;
1887 * This function orders the mail accounts according to these rules:
1888 * 1st - remote accounts
1889 * 2nd - local account
1893 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1897 gchar *name1 = NULL;
1898 gchar *name2 = NULL;
1899 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1900 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1901 GObject *folder1 = NULL;
1902 GObject *folder2 = NULL;
1904 gtk_tree_model_get (tree_model, iter1,
1905 NAME_COLUMN, &name1,
1907 INSTANCE_COLUMN, &folder1,
1909 gtk_tree_model_get (tree_model, iter2,
1910 NAME_COLUMN, &name2,
1911 TYPE_COLUMN, &type2,
1912 INSTANCE_COLUMN, &folder2,
1915 /* Return if we get no folder. This could happen when folder
1916 operations are happening. The model is updated after the
1917 folder copy/move actually occurs, so there could be
1918 situations where the model to be drawn is not correct */
1919 if (!folder1 || !folder2)
1922 /* Sort by type. First the special folders, then the archives */
1923 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
1927 /* Now we sort using the account of each folder */
1928 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
1932 /* Each group is preceeded by its account */
1933 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
1937 /* Pure sort by name */
1938 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1941 g_object_unref(G_OBJECT(folder1));
1943 g_object_unref(G_OBJECT(folder2));
1951 /*****************************************************************************/
1952 /* DRAG and DROP stuff */
1953 /*****************************************************************************/
1955 * This function fills the #GtkSelectionData with the row and the
1956 * model that has been dragged. It's called when this widget is a
1957 * source for dnd after the event drop happened
1960 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
1961 guint info, guint time, gpointer data)
1963 GtkTreeSelection *selection;
1964 GtkTreeModel *model;
1966 GtkTreePath *source_row;
1968 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1969 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1971 source_row = gtk_tree_model_get_path (model, &iter);
1972 gtk_tree_set_row_drag_data (selection_data,
1976 gtk_tree_path_free (source_row);
1980 typedef struct _DndHelper {
1981 ModestFolderView *folder_view;
1982 gboolean delete_source;
1983 GtkTreePath *source_row;
1987 dnd_helper_destroyer (DndHelper *helper)
1989 /* Free the helper */
1990 gtk_tree_path_free (helper->source_row);
1991 g_slice_free (DndHelper, helper);
1995 xfer_folder_cb (ModestMailOperation *mail_op,
1996 TnyFolder *new_folder,
2000 /* Select the folder */
2001 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2007 /* get the folder for the row the treepath refers to. */
2008 /* folder must be unref'd */
2009 static TnyFolderStore *
2010 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2013 TnyFolderStore *folder = NULL;
2015 if (gtk_tree_model_get_iter (model,&iter, path))
2016 gtk_tree_model_get (model, &iter,
2017 INSTANCE_COLUMN, &folder,
2024 * This function is used by drag_data_received_cb to manage drag and
2025 * drop of a header, i.e, and drag from the header view to the folder
2029 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2030 GtkTreeModel *dest_model,
2031 GtkTreePath *dest_row,
2032 GtkSelectionData *selection_data)
2034 TnyList *headers = NULL;
2035 TnyFolder *folder = NULL, *src_folder = NULL;
2036 TnyFolderType folder_type;
2037 GtkTreeIter source_iter, dest_iter;
2038 ModestWindowMgr *mgr = NULL;
2039 ModestWindow *main_win = NULL;
2040 gchar **uris, **tmp;
2042 /* Build the list of headers */
2043 mgr = modest_runtime_get_window_mgr ();
2044 headers = tny_simple_list_new ();
2045 uris = modest_dnd_selection_data_get_paths (selection_data);
2048 while (*tmp != NULL) {
2051 gboolean first = TRUE;
2054 path = gtk_tree_path_new_from_string (*tmp);
2055 gtk_tree_model_get_iter (source_model, &source_iter, path);
2056 gtk_tree_model_get (source_model, &source_iter,
2057 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2060 /* Do not enable d&d of headers already opened */
2061 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2062 tny_list_append (headers, G_OBJECT (header));
2064 if (G_UNLIKELY (first)) {
2065 src_folder = tny_header_get_folder (header);
2069 /* Free and go on */
2070 gtk_tree_path_free (path);
2071 g_object_unref (header);
2076 /* This could happen ig we perform a d&d very quickly over the
2077 same row that row could dissapear because message is
2079 if (!TNY_IS_FOLDER (src_folder))
2082 /* Get the target folder */
2083 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2084 gtk_tree_model_get (dest_model, &dest_iter,
2088 if (!folder || !TNY_IS_FOLDER(folder)) {
2089 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2093 folder_type = modest_tny_folder_guess_folder_type (folder);
2094 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2095 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2096 goto cleanup; /* cannot move messages there */
2099 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2100 /* g_warning ("folder not writable"); */
2101 goto cleanup; /* verboten! */
2104 /* Ask for confirmation to move */
2105 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2107 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2111 /* Transfer messages */
2112 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2117 if (G_IS_OBJECT (src_folder))
2118 g_object_unref (src_folder);
2119 if (G_IS_OBJECT(folder))
2120 g_object_unref (G_OBJECT (folder));
2121 if (G_IS_OBJECT(headers))
2122 g_object_unref (headers);
2126 TnyFolderStore *src_folder;
2127 TnyFolderStore *dst_folder;
2128 ModestFolderView *folder_view;
2133 dnd_folder_info_destroyer (DndFolderInfo *info)
2135 if (info->src_folder)
2136 g_object_unref (info->src_folder);
2137 if (info->dst_folder)
2138 g_object_unref (info->dst_folder);
2139 g_slice_free (DndFolderInfo, info);
2143 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2144 GtkWindow *parent_window,
2145 TnyAccount *account)
2148 modest_ui_actions_on_account_connection_error (parent_window, account);
2150 /* Free the helper & info */
2151 dnd_helper_destroyer (info->helper);
2152 dnd_folder_info_destroyer (info);
2156 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2158 GtkWindow *parent_window,
2159 TnyAccount *account,
2162 DndFolderInfo *info = NULL;
2163 ModestMailOperation *mail_op;
2165 info = (DndFolderInfo *) user_data;
2167 if (err || canceled) {
2168 dnd_on_connection_failed_destroyer (info, parent_window, account);
2172 /* Do the mail operation */
2173 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2174 modest_ui_actions_move_folder_error_handler,
2175 info->src_folder, NULL);
2177 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2180 /* Transfer the folder */
2181 modest_mail_operation_xfer_folder (mail_op,
2182 TNY_FOLDER (info->src_folder),
2184 info->helper->delete_source,
2186 info->helper->folder_view);
2189 g_object_unref (G_OBJECT (mail_op));
2190 dnd_helper_destroyer (info->helper);
2191 dnd_folder_info_destroyer (info);
2196 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2198 GtkWindow *parent_window,
2199 TnyAccount *account,
2202 DndFolderInfo *info = NULL;
2204 info = (DndFolderInfo *) user_data;
2206 if (err || canceled) {
2207 dnd_on_connection_failed_destroyer (info, parent_window, account);
2211 /* Connect to source folder and perform the copy/move */
2212 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2214 drag_and_drop_from_folder_view_src_folder_performer,
2219 * This function is used by drag_data_received_cb to manage drag and
2220 * drop of a folder, i.e, and drag from the folder view to the same
2224 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2225 GtkTreeModel *dest_model,
2226 GtkTreePath *dest_row,
2227 GtkSelectionData *selection_data,
2230 GtkTreeIter dest_iter, iter;
2231 TnyFolderStore *dest_folder = NULL;
2232 TnyFolderStore *folder = NULL;
2233 gboolean forbidden = FALSE;
2235 DndFolderInfo *info = NULL;
2237 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2239 g_warning ("%s: BUG: no main window", __FUNCTION__);
2240 dnd_helper_destroyer (helper);
2245 /* check the folder rules for the destination */
2246 folder = tree_path_to_folder (dest_model, dest_row);
2247 if (TNY_IS_FOLDER(folder)) {
2248 ModestTnyFolderRules rules =
2249 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2250 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2251 } else if (TNY_IS_FOLDER_STORE(folder)) {
2252 /* enable local root as destination for folders */
2253 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2254 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2257 g_object_unref (folder);
2260 /* check the folder rules for the source */
2261 folder = tree_path_to_folder (source_model, helper->source_row);
2262 if (TNY_IS_FOLDER(folder)) {
2263 ModestTnyFolderRules rules =
2264 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2265 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2268 g_object_unref (folder);
2272 /* Check if the drag is possible */
2273 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2275 modest_platform_run_information_dialog ((GtkWindow *) win,
2276 _("mail_in_ui_folder_move_target_error"),
2278 /* Restore the previous selection */
2279 folder = tree_path_to_folder (source_model, helper->source_row);
2281 if (TNY_IS_FOLDER (folder))
2282 modest_folder_view_select_folder (helper->folder_view,
2283 TNY_FOLDER (folder), FALSE);
2284 g_object_unref (folder);
2286 dnd_helper_destroyer (helper);
2291 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2292 gtk_tree_model_get (dest_model, &dest_iter,
2295 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2296 gtk_tree_model_get (source_model, &iter,
2300 /* Create the info for the performer */
2301 info = g_slice_new0 (DndFolderInfo);
2302 info->src_folder = g_object_ref (folder);
2303 info->dst_folder = g_object_ref (dest_folder);
2304 info->helper = helper;
2306 /* Connect to the destination folder and perform the copy/move */
2307 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2309 drag_and_drop_from_folder_view_dst_folder_performer,
2313 g_object_unref (dest_folder);
2314 g_object_unref (folder);
2318 * This function receives the data set by the "drag-data-get" signal
2319 * handler. This information comes within the #GtkSelectionData. This
2320 * function will manage both the drags of folders of the treeview and
2321 * drags of headers of the header view widget.
2324 on_drag_data_received (GtkWidget *widget,
2325 GdkDragContext *context,
2328 GtkSelectionData *selection_data,
2333 GtkWidget *source_widget;
2334 GtkTreeModel *dest_model, *source_model;
2335 GtkTreePath *source_row, *dest_row;
2336 GtkTreeViewDropPosition pos;
2337 gboolean delete_source = FALSE;
2338 gboolean success = FALSE;
2340 /* Do not allow further process */
2341 g_signal_stop_emission_by_name (widget, "drag-data-received");
2342 source_widget = gtk_drag_get_source_widget (context);
2344 /* Get the action */
2345 if (context->action == GDK_ACTION_MOVE) {
2346 delete_source = TRUE;
2348 /* Notify that there is no folder selected. We need to
2349 do this in order to update the headers view (and
2350 its monitors, because when moving, the old folder
2351 won't longer exist. We can not wait for the end of
2352 the operation, because the operation won't start if
2353 the folder is in use */
2354 if (source_widget == widget) {
2355 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2356 gtk_tree_selection_unselect_all (sel);
2360 /* Check if the get_data failed */
2361 if (selection_data == NULL || selection_data->length < 0)
2364 /* Select the destination model */
2365 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2367 /* Get the path to the destination row. Can not call
2368 gtk_tree_view_get_drag_dest_row() because the source row
2369 is not selected anymore */
2370 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2373 /* Only allow drops IN other rows */
2375 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2376 pos == GTK_TREE_VIEW_DROP_AFTER)
2380 /* Drags from the header view */
2381 if (source_widget != widget) {
2382 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2384 drag_and_drop_from_header_view (source_model,
2389 DndHelper *helper = NULL;
2391 /* Get the source model and row */
2392 gtk_tree_get_row_drag_data (selection_data,
2396 /* Create the helper */
2397 helper = g_slice_new0 (DndHelper);
2398 helper->delete_source = delete_source;
2399 helper->source_row = gtk_tree_path_copy (source_row);
2400 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2402 drag_and_drop_from_folder_view (source_model,
2408 gtk_tree_path_free (source_row);
2412 gtk_tree_path_free (dest_row);
2415 /* Finish the drag and drop */
2416 gtk_drag_finish (context, success, FALSE, time);
2420 * We define a "drag-drop" signal handler because we do not want to
2421 * use the default one, because the default one always calls
2422 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2423 * signal handler, because there we have all the information available
2424 * to know if the dnd was a success or not.
2427 drag_drop_cb (GtkWidget *widget,
2428 GdkDragContext *context,
2436 if (!context->targets)
2439 /* Check if we're dragging a folder row */
2440 target = gtk_drag_dest_find_target (widget, context, NULL);
2442 /* Request the data from the source. */
2443 gtk_drag_get_data(widget, context, target, time);
2449 * This function expands a node of a tree view if it's not expanded
2450 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2451 * does that, so that's why they're here.
2454 expand_row_timeout (gpointer data)
2456 GtkTreeView *tree_view = data;
2457 GtkTreePath *dest_path = NULL;
2458 GtkTreeViewDropPosition pos;
2459 gboolean result = FALSE;
2461 gdk_threads_enter ();
2463 gtk_tree_view_get_drag_dest_row (tree_view,
2468 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2469 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2470 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2471 gtk_tree_path_free (dest_path);
2475 gtk_tree_path_free (dest_path);
2480 gdk_threads_leave ();
2486 * This function is called whenever the pointer is moved over a widget
2487 * while dragging some data. It installs a timeout that will expand a
2488 * node of the treeview if not expanded yet. This function also calls
2489 * gdk_drag_status in order to set the suggested action that will be
2490 * used by the "drag-data-received" signal handler to know if we
2491 * should do a move or just a copy of the data.
2494 on_drag_motion (GtkWidget *widget,
2495 GdkDragContext *context,
2501 GtkTreeViewDropPosition pos;
2502 GtkTreePath *dest_row;
2503 GtkTreeModel *dest_model;
2504 ModestFolderViewPrivate *priv;
2505 GdkDragAction suggested_action;
2506 gboolean valid_location = FALSE;
2507 TnyFolderStore *folder = NULL;
2509 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2511 if (priv->timer_expander != 0) {
2512 g_source_remove (priv->timer_expander);
2513 priv->timer_expander = 0;
2516 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2521 /* Do not allow drops between folders */
2523 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2524 pos == GTK_TREE_VIEW_DROP_AFTER) {
2525 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2526 gdk_drag_status(context, 0, time);
2527 valid_location = FALSE;
2530 valid_location = TRUE;
2533 /* Check that the destination folder is writable */
2534 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2535 folder = tree_path_to_folder (dest_model, dest_row);
2536 if (folder && TNY_IS_FOLDER (folder)) {
2537 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2539 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2540 valid_location = FALSE;
2545 /* Expand the selected row after 1/2 second */
2546 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2547 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2549 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2551 /* Select the desired action. By default we pick MOVE */
2552 suggested_action = GDK_ACTION_MOVE;
2554 if (context->actions == GDK_ACTION_COPY)
2555 gdk_drag_status(context, GDK_ACTION_COPY, time);
2556 else if (context->actions == GDK_ACTION_MOVE)
2557 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2558 else if (context->actions & suggested_action)
2559 gdk_drag_status(context, suggested_action, time);
2561 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2565 g_object_unref (folder);
2567 gtk_tree_path_free (dest_row);
2569 g_signal_stop_emission_by_name (widget, "drag-motion");
2571 return valid_location;
2575 * This function sets the treeview as a source and a target for dnd
2576 * events. It also connects all the requirede signals.
2579 setup_drag_and_drop (GtkTreeView *self)
2581 /* Set up the folder view as a dnd destination. Set only the
2582 highlight flag, otherwise gtk will have a different
2584 #ifdef MODEST_TOOLKIT_HILDON2
2587 gtk_drag_dest_set (GTK_WIDGET (self),
2588 GTK_DEST_DEFAULT_HIGHLIGHT,
2589 folder_view_drag_types,
2590 G_N_ELEMENTS (folder_view_drag_types),
2591 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2593 g_signal_connect (G_OBJECT (self),
2594 "drag_data_received",
2595 G_CALLBACK (on_drag_data_received),
2599 /* Set up the treeview as a dnd source */
2600 gtk_drag_source_set (GTK_WIDGET (self),
2602 folder_view_drag_types,
2603 G_N_ELEMENTS (folder_view_drag_types),
2604 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2606 g_signal_connect (G_OBJECT (self),
2608 G_CALLBACK (on_drag_motion),
2611 g_signal_connect (G_OBJECT (self),
2613 G_CALLBACK (on_drag_data_get),
2616 g_signal_connect (G_OBJECT (self),
2618 G_CALLBACK (drag_drop_cb),
2623 * This function manages the navigation through the folders using the
2624 * keyboard or the hardware keys in the device
2627 on_key_pressed (GtkWidget *self,
2631 GtkTreeSelection *selection;
2633 GtkTreeModel *model;
2634 gboolean retval = FALSE;
2636 /* Up and Down are automatically managed by the treeview */
2637 if (event->keyval == GDK_Return) {
2638 /* Expand/Collapse the selected row */
2639 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2640 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2643 path = gtk_tree_model_get_path (model, &iter);
2645 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2646 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2648 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2649 gtk_tree_path_free (path);
2651 /* No further processing */
2659 * We listen to the changes in the local folder account name key,
2660 * because we want to show the right name in the view. The local
2661 * folder account name corresponds to the device name in the Maemo
2662 * version. We do this because we do not want to query gconf on each
2663 * tree view refresh. It's better to cache it and change whenever
2667 on_configuration_key_changed (ModestConf* conf,
2669 ModestConfEvent event,
2670 ModestConfNotificationId id,
2671 ModestFolderView *self)
2673 ModestFolderViewPrivate *priv;
2676 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2677 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2679 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2680 g_free (priv->local_account_name);
2682 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2683 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2685 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2686 MODEST_CONF_DEVICE_NAME, NULL);
2688 /* Force a redraw */
2689 #if GTK_CHECK_VERSION(2, 8, 0)
2690 GtkTreeViewColumn * tree_column;
2692 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2694 gtk_tree_view_column_queue_resize (tree_column);
2696 gtk_widget_queue_draw (GTK_WIDGET (self));
2702 modest_folder_view_set_style (ModestFolderView *self,
2703 ModestFolderViewStyle style)
2705 ModestFolderViewPrivate *priv;
2707 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2708 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2709 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2711 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2714 priv->style = style;
2718 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2719 const gchar *account_id)
2721 ModestFolderViewPrivate *priv;
2722 GtkTreeModel *model;
2724 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2726 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2728 /* This will be used by the filter_row callback,
2729 * to decided which rows to show: */
2730 if (priv->visible_account_id) {
2731 g_free (priv->visible_account_id);
2732 priv->visible_account_id = NULL;
2735 priv->visible_account_id = g_strdup (account_id);
2738 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2739 if (GTK_IS_TREE_MODEL_FILTER (model))
2740 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2742 /* Save settings to gconf */
2743 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2744 MODEST_CONF_FOLDER_VIEW_KEY);
2748 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2750 ModestFolderViewPrivate *priv;
2752 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2754 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2756 return (const gchar *) priv->visible_account_id;
2760 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2764 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2766 gtk_tree_model_get (model, iter,
2770 gboolean result = FALSE;
2771 if (type == TNY_FOLDER_TYPE_INBOX) {
2775 *inbox_iter = *iter;
2779 if (gtk_tree_model_iter_children (model, &child, iter)) {
2780 if (find_inbox_iter (model, &child, inbox_iter))
2784 } while (gtk_tree_model_iter_next (model, iter));
2793 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2795 GtkTreeModel *model;
2796 GtkTreeIter iter, inbox_iter;
2797 GtkTreeSelection *sel;
2798 GtkTreePath *path = NULL;
2800 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2802 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2806 expand_root_items (self);
2807 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2809 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2810 g_warning ("%s: model is empty", __FUNCTION__);
2814 if (find_inbox_iter (model, &iter, &inbox_iter))
2815 path = gtk_tree_model_get_path (model, &inbox_iter);
2817 path = gtk_tree_path_new_first ();
2819 /* Select the row and free */
2820 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2821 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2822 gtk_tree_path_free (path);
2825 gtk_widget_grab_focus (GTK_WIDGET(self));
2831 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2836 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2837 TnyFolder* a_folder;
2840 gtk_tree_model_get (model, iter,
2841 INSTANCE_COLUMN, &a_folder,
2847 if (folder == a_folder) {
2848 g_object_unref (a_folder);
2849 *folder_iter = *iter;
2852 g_object_unref (a_folder);
2854 if (gtk_tree_model_iter_children (model, &child, iter)) {
2855 if (find_folder_iter (model, &child, folder_iter, folder))
2859 } while (gtk_tree_model_iter_next (model, iter));
2866 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
2869 ModestFolderView *self)
2871 ModestFolderViewPrivate *priv = NULL;
2872 GtkTreeSelection *sel;
2873 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2874 GObject *instance = NULL;
2876 if (!MODEST_IS_FOLDER_VIEW(self))
2879 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2881 priv->reexpand = TRUE;
2883 gtk_tree_model_get (tree_model, iter,
2885 INSTANCE_COLUMN, &instance,
2887 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2888 priv->folder_to_select = g_object_ref (instance);
2890 g_object_unref (instance);
2892 if (priv->folder_to_select) {
2894 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2897 path = gtk_tree_model_get_path (tree_model, iter);
2898 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2900 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2902 gtk_tree_selection_select_iter (sel, iter);
2903 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2905 gtk_tree_path_free (path);
2909 modest_folder_view_disable_next_folder_selection (self);
2911 /* Refilter the model */
2912 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2918 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2920 ModestFolderViewPrivate *priv;
2922 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2924 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2926 if (priv->folder_to_select)
2927 g_object_unref(priv->folder_to_select);
2929 priv->folder_to_select = NULL;
2933 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2934 gboolean after_change)
2936 GtkTreeModel *model;
2937 GtkTreeIter iter, folder_iter;
2938 GtkTreeSelection *sel;
2939 ModestFolderViewPrivate *priv = NULL;
2941 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2942 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2944 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2947 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2948 gtk_tree_selection_unselect_all (sel);
2950 if (priv->folder_to_select)
2951 g_object_unref(priv->folder_to_select);
2952 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2956 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2961 /* Refilter the model, before selecting the folder */
2962 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2964 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2965 g_warning ("%s: model is empty", __FUNCTION__);
2969 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2972 path = gtk_tree_model_get_path (model, &folder_iter);
2973 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2975 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2976 gtk_tree_selection_select_iter (sel, &folder_iter);
2977 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2979 gtk_tree_path_free (path);
2987 modest_folder_view_copy_selection (ModestFolderView *self)
2989 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2991 /* Copy selection */
2992 _clipboard_set_selected_data (self, FALSE);
2996 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2998 ModestFolderViewPrivate *priv = NULL;
2999 GtkTreeModel *model = NULL;
3000 const gchar **hidding = NULL;
3001 guint i, n_selected;
3003 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3004 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3006 /* Copy selection */
3007 if (!_clipboard_set_selected_data (folder_view, TRUE))
3010 /* Get hidding ids */
3011 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3013 /* Clear hidding array created by previous cut operation */
3014 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3016 /* Copy hidding array */
3017 priv->n_selected = n_selected;
3018 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3019 for (i=0; i < n_selected; i++)
3020 priv->hidding_ids[i] = g_strdup(hidding[i]);
3022 /* Hide cut folders */
3023 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3024 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3028 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3029 ModestFolderView *folder_view_dst)
3031 GtkTreeModel *filter_model = NULL;
3032 GtkTreeModel *model = NULL;
3033 GtkTreeModel *new_filter_model = NULL;
3035 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3036 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3039 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3040 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3042 /* Build new filter model */
3043 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3044 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3048 /* Set copied model */
3049 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3050 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3051 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3054 g_object_unref (new_filter_model);
3058 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3061 GtkTreeModel *model = NULL;
3062 ModestFolderViewPrivate* priv;
3064 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3066 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3067 priv->show_non_move = show;
3068 /* modest_folder_view_update_model(folder_view, */
3069 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3071 /* Hide special folders */
3072 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3073 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3074 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3078 /* Returns FALSE if it did not selected anything */
3080 _clipboard_set_selected_data (ModestFolderView *folder_view,
3083 ModestFolderViewPrivate *priv = NULL;
3084 TnyFolderStore *folder = NULL;
3085 gboolean retval = FALSE;
3087 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3088 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3090 /* Set selected data on clipboard */
3091 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3092 folder = modest_folder_view_get_selected (folder_view);
3094 /* Do not allow to select an account */
3095 if (TNY_IS_FOLDER (folder)) {
3096 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3101 g_object_unref (folder);
3107 _clear_hidding_filter (ModestFolderView *folder_view)
3109 ModestFolderViewPrivate *priv;
3112 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3113 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3115 if (priv->hidding_ids != NULL) {
3116 for (i=0; i < priv->n_selected; i++)
3117 g_free (priv->hidding_ids[i]);
3118 g_free(priv->hidding_ids);
3124 on_display_name_changed (ModestAccountMgr *mgr,
3125 const gchar *account,
3128 ModestFolderView *self;
3130 self = MODEST_FOLDER_VIEW (user_data);
3132 /* Force a redraw */
3133 #if GTK_CHECK_VERSION(2, 8, 0)
3134 GtkTreeViewColumn * tree_column;
3136 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3138 gtk_tree_view_column_queue_resize (tree_column);
3140 gtk_widget_queue_draw (GTK_WIDGET (self));
3145 modest_folder_view_set_cell_style (ModestFolderView *self,
3146 ModestFolderViewCellStyle cell_style)
3148 ModestFolderViewPrivate *priv = NULL;
3150 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3151 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3153 priv->cell_style = cell_style;
3155 g_object_set (G_OBJECT (priv->messages_renderer),
3156 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3159 gtk_widget_queue_draw (GTK_WIDGET (self));
3163 update_style (ModestFolderView *self)
3165 ModestFolderViewPrivate *priv;
3166 GdkColor style_color;
3168 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3169 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3171 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3172 gdk_color_parse ("grey", &style_color);
3175 g_object_set (G_OBJECT (priv->messages_renderer),
3176 "foreground-gdk", &style_color,
3177 "foreground-set", TRUE,
3182 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3184 if (strcmp ("style", spec->name) == 0) {
3185 update_style (MODEST_FOLDER_VIEW (obj));
3186 gtk_widget_queue_draw (GTK_WIDGET (obj));