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 "modest-ui-constants.h"
66 #include "widgets/modest-window.h"
68 /* Folder view drag types */
69 const GtkTargetEntry folder_view_drag_types[] =
71 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
72 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
75 /* Default icon sizes for Fremantle style are different */
76 #ifdef MODEST_TOOLKIT_HILDON2
77 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
79 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
82 /* Column names depending on we use list store or tree store */
83 #ifdef MODEST_TOOLKIT_HILDON2
84 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
85 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
86 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
87 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
88 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
90 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
91 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
92 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
93 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
94 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
97 /* 'private'/'protected' functions */
98 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
99 static void modest_folder_view_init (ModestFolderView *obj);
100 static void modest_folder_view_finalize (GObject *obj);
102 static void tny_account_store_view_init (gpointer g,
103 gpointer iface_data);
105 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
106 TnyAccountStore *account_store);
108 static void on_selection_changed (GtkTreeSelection *sel,
111 static void on_row_activated (GtkTreeView *treeview,
113 GtkTreeViewColumn *column,
116 static void on_account_removed (TnyAccountStore *self,
120 static void on_account_inserted (TnyAccountStore *self,
124 static void on_account_changed (TnyAccountStore *self,
128 static gint cmp_rows (GtkTreeModel *tree_model,
133 static gboolean filter_row (GtkTreeModel *model,
137 static gboolean on_key_pressed (GtkWidget *self,
141 static void on_configuration_key_changed (ModestConf* conf,
143 ModestConfEvent event,
144 ModestConfNotificationId notification_id,
145 ModestFolderView *self);
148 static void on_drag_data_get (GtkWidget *widget,
149 GdkDragContext *context,
150 GtkSelectionData *selection_data,
155 static void on_drag_data_received (GtkWidget *widget,
156 GdkDragContext *context,
159 GtkSelectionData *selection_data,
164 static gboolean on_drag_motion (GtkWidget *widget,
165 GdkDragContext *context,
171 static void expand_root_items (ModestFolderView *self);
173 static gint expand_row_timeout (gpointer data);
175 static void setup_drag_and_drop (GtkTreeView *self);
177 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
180 static void _clear_hidding_filter (ModestFolderView *folder_view);
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
186 ModestFolderView *self);
189 static void on_display_name_changed (ModestAccountMgr *self,
190 const gchar *account,
192 static void update_style (ModestFolderView *self);
193 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
196 static gboolean get_inner_models (ModestFolderView *self,
197 GtkTreeModel **filter_model,
198 GtkTreeModel **sort_model,
199 GtkTreeModel **tny_model);
202 FOLDER_SELECTION_CHANGED_SIGNAL,
203 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
204 FOLDER_ACTIVATED_SIGNAL,
208 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
209 struct _ModestFolderViewPrivate {
210 TnyAccountStore *account_store;
211 TnyFolderStore *cur_folder_store;
213 TnyFolder *folder_to_select; /* folder to select after the next update */
215 gulong changed_signal;
216 gulong account_inserted_signal;
217 gulong account_removed_signal;
218 gulong account_changed_signal;
219 gulong conf_key_signal;
220 gulong display_name_changed_signal;
222 /* not unref this object, its a singlenton */
223 ModestEmailClipboard *clipboard;
225 /* Filter tree model */
228 ModestFolderViewFilter filter;
230 TnyFolderStoreQuery *query;
231 guint timer_expander;
233 gchar *local_account_name;
234 gchar *visible_account_id;
235 ModestFolderViewStyle style;
236 ModestFolderViewCellStyle cell_style;
238 gboolean reselect; /* we use this to force a reselection of the INBOX */
239 gboolean show_non_move;
240 gboolean reexpand; /* next time we expose, we'll expand all root folders */
242 GtkCellRenderer *messages_renderer;
244 gulong outbox_deleted_handler;
246 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
247 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
248 MODEST_TYPE_FOLDER_VIEW, \
249 ModestFolderViewPrivate))
251 static GObjectClass *parent_class = NULL;
253 static guint signals[LAST_SIGNAL] = {0};
256 modest_folder_view_get_type (void)
258 static GType my_type = 0;
260 static const GTypeInfo my_info = {
261 sizeof(ModestFolderViewClass),
262 NULL, /* base init */
263 NULL, /* base finalize */
264 (GClassInitFunc) modest_folder_view_class_init,
265 NULL, /* class finalize */
266 NULL, /* class data */
267 sizeof(ModestFolderView),
269 (GInstanceInitFunc) modest_folder_view_init,
273 static const GInterfaceInfo tny_account_store_view_info = {
274 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
275 NULL, /* interface_finalize */
276 NULL /* interface_data */
280 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
284 g_type_add_interface_static (my_type,
285 TNY_TYPE_ACCOUNT_STORE_VIEW,
286 &tny_account_store_view_info);
292 modest_folder_view_class_init (ModestFolderViewClass *klass)
294 GObjectClass *gobject_class;
295 GtkTreeViewClass *treeview_class;
296 gobject_class = (GObjectClass*) klass;
297 treeview_class = (GtkTreeViewClass*) klass;
299 parent_class = g_type_class_peek_parent (klass);
300 gobject_class->finalize = modest_folder_view_finalize;
302 g_type_class_add_private (gobject_class,
303 sizeof(ModestFolderViewPrivate));
305 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
306 g_signal_new ("folder_selection_changed",
307 G_TYPE_FROM_CLASS (gobject_class),
309 G_STRUCT_OFFSET (ModestFolderViewClass,
310 folder_selection_changed),
312 modest_marshal_VOID__POINTER_BOOLEAN,
313 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
316 * This signal is emitted whenever the currently selected
317 * folder display name is computed. Note that the name could
318 * be different to the folder name, because we could append
319 * the unread messages count to the folder name to build the
320 * folder display name
322 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
323 g_signal_new ("folder-display-name-changed",
324 G_TYPE_FROM_CLASS (gobject_class),
326 G_STRUCT_OFFSET (ModestFolderViewClass,
327 folder_display_name_changed),
329 g_cclosure_marshal_VOID__STRING,
330 G_TYPE_NONE, 1, G_TYPE_STRING);
332 signals[FOLDER_ACTIVATED_SIGNAL] =
333 g_signal_new ("folder_activated",
334 G_TYPE_FROM_CLASS (gobject_class),
336 G_STRUCT_OFFSET (ModestFolderViewClass,
339 g_cclosure_marshal_VOID__POINTER,
340 G_TYPE_NONE, 1, G_TYPE_POINTER);
342 treeview_class->select_cursor_parent = NULL;
344 #ifdef MODEST_TOOLKIT_HILDON2
345 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
351 /* Retrieves the filter, sort and tny models of the folder view. If
352 any of these does not exist then it returns FALSE */
354 get_inner_models (ModestFolderView *self,
355 GtkTreeModel **filter_model,
356 GtkTreeModel **sort_model,
357 GtkTreeModel **tny_model)
359 GtkTreeModel *s_model, *f_model, *t_model;
361 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
362 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
363 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
367 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
368 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
369 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
373 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
377 *filter_model = f_model;
379 *sort_model = s_model;
381 *tny_model = t_model;
386 /* Simplify checks for NULLs: */
388 strings_are_equal (const gchar *a, const gchar *b)
394 return (strcmp (a, b) == 0);
401 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
403 GObject *instance = NULL;
405 gtk_tree_model_get (model, iter,
406 INSTANCE_COLUMN, &instance,
410 return FALSE; /* keep walking */
412 if (!TNY_IS_ACCOUNT (instance)) {
413 g_object_unref (instance);
414 return FALSE; /* keep walking */
417 /* Check if this is the looked-for account: */
418 TnyAccount *this_account = TNY_ACCOUNT (instance);
419 TnyAccount *account = TNY_ACCOUNT (data);
421 const gchar *this_account_id = tny_account_get_id(this_account);
422 const gchar *account_id = tny_account_get_id(account);
423 g_object_unref (instance);
426 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
427 if (strings_are_equal(this_account_id, account_id)) {
428 /* Tell the model that the data has changed, so that
429 * it calls the cell_data_func callbacks again: */
430 /* TODO: This does not seem to actually cause the new string to be shown: */
431 gtk_tree_model_row_changed (model, path, iter);
433 return TRUE; /* stop walking */
436 return FALSE; /* keep walking */
441 ModestFolderView *self;
442 gchar *previous_name;
443 } GetMmcAccountNameData;
446 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
448 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
450 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
452 if (!strings_are_equal (
453 tny_account_get_name(TNY_ACCOUNT(account)),
454 data->previous_name)) {
456 /* Tell the model that the data has changed, so that
457 * it calls the cell_data_func callbacks again: */
458 ModestFolderView *self = data->self;
459 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
461 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
464 g_free (data->previous_name);
465 g_slice_free (GetMmcAccountNameData, data);
469 convert_parent_folders_to_dots (gchar **item_name)
473 gchar *last_separator;
475 if (item_name == NULL)
478 for (c = *item_name; *c != '\0'; c++) {
479 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
484 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
485 if (last_separator != NULL) {
486 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
493 buffer = g_string_new ("");
494 for (i = 0; i < n_parents; i++) {
495 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
497 buffer = g_string_append (buffer, last_separator);
499 *item_name = g_string_free (buffer, FALSE);
505 format_compact_style (gchar **item_name,
508 gboolean multiaccount,
509 gboolean *use_markup)
513 TnyFolderType folder_type;
515 if (!TNY_IS_FOLDER (instance))
518 folder = (TnyFolder *) instance;
520 folder_type = tny_folder_get_folder_type (folder);
521 is_special = (get_cmp_pos (folder_type, folder)!= 4);
523 if (!is_special || multiaccount) {
524 TnyAccount *account = tny_folder_get_account (folder);
525 const gchar *folder_name;
526 gboolean concat_folder_name = FALSE;
529 /* Should not happen */
533 /* convert parent folders to dots */
534 convert_parent_folders_to_dots (item_name);
536 folder_name = tny_folder_get_name (folder);
537 if (g_str_has_suffix (*item_name, folder_name)) {
538 gchar *offset = g_strrstr (*item_name, folder_name);
540 concat_folder_name = TRUE;
543 buffer = g_string_new ("");
545 buffer = g_string_append (buffer, *item_name);
546 if (concat_folder_name) {
547 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
548 buffer = g_string_append (buffer, folder_name);
549 if (bold) buffer = g_string_append (buffer, "</span>");
552 g_object_unref (account);
554 *item_name = g_string_free (buffer, FALSE);
562 text_cell_data (GtkTreeViewColumn *column,
563 GtkCellRenderer *renderer,
564 GtkTreeModel *tree_model,
568 ModestFolderViewPrivate *priv;
569 GObject *rendobj = (GObject *) renderer;
571 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
572 GObject *instance = NULL;
573 gboolean use_markup = FALSE;
575 gtk_tree_model_get (tree_model, iter,
578 INSTANCE_COLUMN, &instance,
580 if (!fname || !instance)
583 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
584 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
586 gchar *item_name = NULL;
587 gint item_weight = 400;
589 if (type != TNY_FOLDER_TYPE_ROOT) {
593 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
594 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
595 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
596 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
598 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
601 /* Sometimes an special folder is reported by the server as
602 NORMAL, like some versions of Dovecot */
603 if (type == TNY_FOLDER_TYPE_NORMAL ||
604 type == TNY_FOLDER_TYPE_UNKNOWN) {
605 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
609 if (type == TNY_FOLDER_TYPE_INBOX) {
611 fname = g_strdup (_("mcen_me_folder_inbox"));
614 /* note: we cannot reliably get the counts from the tree model, we need
615 * to use explicit calls on tny_folder for some reason.
617 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
618 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
619 (type == TNY_FOLDER_TYPE_OUTBOX) ||
620 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
621 number = tny_folder_get_all_count (TNY_FOLDER(instance));
624 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
628 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
629 item_name = g_strdup (fname);
636 /* Use bold font style if there are unread or unset messages */
638 item_name = g_strdup_printf ("%s (%d)", fname, number);
641 item_name = g_strdup (fname);
646 } else if (TNY_IS_ACCOUNT (instance)) {
647 /* If it's a server account */
648 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
649 item_name = g_strdup (priv->local_account_name);
651 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
652 /* fname is only correct when the items are first
653 * added to the model, not when the account is
654 * changed later, so get the name from the account
656 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
659 item_name = g_strdup (fname);
665 item_name = g_strdup ("unknown");
667 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
668 gboolean multiaccount;
670 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
671 /* Convert item_name to markup */
672 format_compact_style (&item_name, instance,
674 multiaccount, &use_markup);
677 if (item_name && item_weight) {
678 /* Set the name in the treeview cell: */
680 g_object_set (rendobj, "markup", item_name, NULL);
682 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
684 /* Notify display name observers */
685 /* TODO: What listens for this signal, and how can it use only the new name? */
686 if (((GObject *) priv->cur_folder_store) == instance) {
687 g_signal_emit (G_OBJECT(self),
688 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
695 /* If it is a Memory card account, make sure that we have the correct name.
696 * This function will be trigerred again when the name has been retrieved: */
697 if (TNY_IS_STORE_ACCOUNT (instance) &&
698 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
700 /* Get the account name asynchronously: */
701 GetMmcAccountNameData *callback_data =
702 g_slice_new0(GetMmcAccountNameData);
703 callback_data->self = self;
705 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
707 callback_data->previous_name = g_strdup (name);
709 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
710 on_get_mmc_account_name, callback_data);
714 g_object_unref (G_OBJECT (instance));
720 messages_cell_data (GtkTreeViewColumn *column,
721 GtkCellRenderer *renderer,
722 GtkTreeModel *tree_model,
726 ModestFolderView *self;
727 ModestFolderViewPrivate *priv;
728 GObject *rendobj = (GObject *) renderer;
729 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
730 GObject *instance = NULL;
731 gchar *item_name = NULL;
733 gtk_tree_model_get (tree_model, iter,
735 INSTANCE_COLUMN, &instance,
740 self = MODEST_FOLDER_VIEW (data);
741 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
744 if (type != TNY_FOLDER_TYPE_ROOT) {
748 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
749 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
750 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
752 /* Sometimes an special folder is reported by the server as
753 NORMAL, like some versions of Dovecot */
754 if (type == TNY_FOLDER_TYPE_NORMAL ||
755 type == TNY_FOLDER_TYPE_UNKNOWN) {
756 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
760 /* note: we cannot reliably get the counts from the tree model, we need
761 * to use explicit calls on tny_folder for some reason.
763 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
764 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
765 (type == TNY_FOLDER_TYPE_OUTBOX) ||
766 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
767 number = tny_folder_get_all_count (TNY_FOLDER(instance));
770 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
774 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
776 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
784 item_name = g_strdup ("");
787 /* Set the name in the treeview cell: */
788 g_object_set (rendobj,"text", item_name, NULL);
796 g_object_unref (G_OBJECT (instance));
802 GdkPixbuf *pixbuf_open;
803 GdkPixbuf *pixbuf_close;
807 static inline GdkPixbuf *
808 get_composite_pixbuf (const gchar *icon_name,
810 GdkPixbuf *base_pixbuf)
812 GdkPixbuf *emblem, *retval = NULL;
814 emblem = modest_platform_get_icon (icon_name, size);
816 retval = gdk_pixbuf_copy (base_pixbuf);
817 gdk_pixbuf_composite (emblem, retval, 0, 0,
818 MIN (gdk_pixbuf_get_width (emblem),
819 gdk_pixbuf_get_width (retval)),
820 MIN (gdk_pixbuf_get_height (emblem),
821 gdk_pixbuf_get_height (retval)),
822 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
823 g_object_unref (emblem);
828 static inline ThreePixbufs *
829 get_composite_icons (const gchar *icon_code,
831 GdkPixbuf **pixbuf_open,
832 GdkPixbuf **pixbuf_close)
834 ThreePixbufs *retval;
837 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
840 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
845 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
849 retval = g_slice_new0 (ThreePixbufs);
851 retval->pixbuf = g_object_ref (*pixbuf);
853 retval->pixbuf_open = g_object_ref (*pixbuf_open);
855 retval->pixbuf_close = g_object_ref (*pixbuf_close);
860 static inline ThreePixbufs*
861 get_folder_icons (TnyFolderType type, GObject *instance)
863 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
864 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
865 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
866 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
867 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
869 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
870 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
871 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
872 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
873 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
875 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
876 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
877 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
878 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
879 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
881 ThreePixbufs *retval = NULL;
883 /* Sometimes an special folder is reported by the server as
884 NORMAL, like some versions of Dovecot */
885 if (type == TNY_FOLDER_TYPE_NORMAL ||
886 type == TNY_FOLDER_TYPE_UNKNOWN) {
887 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
890 /* Remote folders should not be treated as special folders */
891 if (TNY_IS_FOLDER_STORE (instance) &&
892 !TNY_IS_ACCOUNT (instance) &&
893 type != TNY_FOLDER_TYPE_INBOX &&
894 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
895 #ifdef MODEST_TOOLKIT_HILDON2
896 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
899 &anorm_pixbuf_close);
901 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
904 &normal_pixbuf_close);
910 case TNY_FOLDER_TYPE_INVALID:
911 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
914 case TNY_FOLDER_TYPE_ROOT:
915 if (TNY_IS_ACCOUNT (instance)) {
917 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
918 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
921 &avirt_pixbuf_close);
923 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
925 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
926 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
931 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
934 &anorm_pixbuf_close);
939 case TNY_FOLDER_TYPE_INBOX:
940 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
943 &inbox_pixbuf_close);
945 case TNY_FOLDER_TYPE_OUTBOX:
946 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
949 &outbox_pixbuf_close);
951 case TNY_FOLDER_TYPE_JUNK:
952 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
957 case TNY_FOLDER_TYPE_SENT:
958 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
963 case TNY_FOLDER_TYPE_TRASH:
964 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
967 &trash_pixbuf_close);
969 case TNY_FOLDER_TYPE_DRAFTS:
970 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
973 &draft_pixbuf_close);
975 case TNY_FOLDER_TYPE_ARCHIVE:
976 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
981 case TNY_FOLDER_TYPE_NORMAL:
983 /* Memory card folders could have an special icon */
984 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
985 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
990 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
993 &normal_pixbuf_close);
1002 free_pixbufs (ThreePixbufs *pixbufs)
1004 if (pixbufs->pixbuf)
1005 g_object_unref (pixbufs->pixbuf);
1006 if (pixbufs->pixbuf_open)
1007 g_object_unref (pixbufs->pixbuf_open);
1008 if (pixbufs->pixbuf_close)
1009 g_object_unref (pixbufs->pixbuf_close);
1010 g_slice_free (ThreePixbufs, pixbufs);
1014 icon_cell_data (GtkTreeViewColumn *column,
1015 GtkCellRenderer *renderer,
1016 GtkTreeModel *tree_model,
1020 GObject *rendobj = NULL, *instance = NULL;
1021 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1022 gboolean has_children;
1023 ThreePixbufs *pixbufs;
1025 rendobj = (GObject *) renderer;
1027 gtk_tree_model_get (tree_model, iter,
1029 INSTANCE_COLUMN, &instance,
1035 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1036 pixbufs = get_folder_icons (type, instance);
1037 g_object_unref (instance);
1040 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1043 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1044 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1047 free_pixbufs (pixbufs);
1051 add_columns (GtkWidget *treeview)
1053 GtkTreeViewColumn *column;
1054 GtkCellRenderer *renderer;
1055 GtkTreeSelection *sel;
1056 ModestFolderViewPrivate *priv;
1058 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1061 column = gtk_tree_view_column_new ();
1063 /* Set icon and text render function */
1064 renderer = gtk_cell_renderer_pixbuf_new();
1065 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1066 gtk_tree_view_column_set_cell_data_func(column, renderer,
1067 icon_cell_data, treeview, NULL);
1069 renderer = gtk_cell_renderer_text_new();
1070 g_object_set (renderer,
1071 #ifdef MODEST_TOOLKIT_HILDON2
1072 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1074 "ellipsize", PANGO_ELLIPSIZE_END,
1076 "ellipsize-set", TRUE, NULL);
1077 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1078 gtk_tree_view_column_set_cell_data_func(column, renderer,
1079 text_cell_data, treeview, NULL);
1081 priv->messages_renderer = gtk_cell_renderer_text_new ();
1082 g_object_set (priv->messages_renderer,
1083 "scale", PANGO_SCALE_X_SMALL,
1085 "alignment", PANGO_ALIGN_RIGHT,
1089 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1090 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1091 messages_cell_data, treeview, NULL);
1093 /* Set selection mode */
1094 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1095 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1097 /* Set treeview appearance */
1098 gtk_tree_view_column_set_spacing (column, 2);
1099 gtk_tree_view_column_set_resizable (column, TRUE);
1100 gtk_tree_view_column_set_fixed_width (column, TRUE);
1101 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1102 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1105 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1109 modest_folder_view_init (ModestFolderView *obj)
1111 ModestFolderViewPrivate *priv;
1114 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1116 priv->timer_expander = 0;
1117 priv->account_store = NULL;
1119 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1120 priv->cur_folder_store = NULL;
1121 priv->visible_account_id = NULL;
1122 priv->folder_to_select = NULL;
1123 priv->outbox_deleted_handler = 0;
1124 priv->reexpand = TRUE;
1126 /* Initialize the local account name */
1127 conf = modest_runtime_get_conf();
1128 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1130 /* Init email clipboard */
1131 priv->clipboard = modest_runtime_get_email_clipboard ();
1132 priv->hidding_ids = NULL;
1133 priv->n_selected = 0;
1134 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1135 priv->reselect = FALSE;
1136 priv->show_non_move = TRUE;
1138 /* Build treeview */
1139 add_columns (GTK_WIDGET (obj));
1141 /* Setup drag and drop */
1142 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1144 /* Connect signals */
1145 g_signal_connect (G_OBJECT (obj),
1147 G_CALLBACK (on_key_pressed), NULL);
1149 priv->display_name_changed_signal =
1150 g_signal_connect (modest_runtime_get_account_mgr (),
1151 "display_name_changed",
1152 G_CALLBACK (on_display_name_changed),
1156 * Track changes in the local account name (in the device it
1157 * will be the device name)
1159 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1161 G_CALLBACK(on_configuration_key_changed),
1165 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1171 tny_account_store_view_init (gpointer g, gpointer iface_data)
1173 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1175 klass->set_account_store = modest_folder_view_set_account_store;
1179 modest_folder_view_finalize (GObject *obj)
1181 ModestFolderViewPrivate *priv;
1182 GtkTreeSelection *sel;
1183 TnyAccount *local_account;
1185 g_return_if_fail (obj);
1187 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1189 if (priv->timer_expander != 0) {
1190 g_source_remove (priv->timer_expander);
1191 priv->timer_expander = 0;
1194 local_account = (TnyAccount *)
1195 modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *)priv->account_store);
1196 if (local_account) {
1197 if (g_signal_handler_is_connected (local_account,
1198 priv->outbox_deleted_handler))
1199 g_signal_handler_disconnect (local_account,
1200 priv->outbox_deleted_handler);
1201 g_object_unref (local_account);
1204 if (priv->account_store) {
1205 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1206 priv->account_inserted_signal);
1207 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1208 priv->account_removed_signal);
1209 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1210 priv->account_changed_signal);
1211 g_object_unref (G_OBJECT(priv->account_store));
1212 priv->account_store = NULL;
1215 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1216 priv->display_name_changed_signal)) {
1217 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1218 priv->display_name_changed_signal);
1219 priv->display_name_changed_signal = 0;
1223 g_object_unref (G_OBJECT (priv->query));
1227 if (priv->folder_to_select) {
1228 g_object_unref (G_OBJECT(priv->folder_to_select));
1229 priv->folder_to_select = NULL;
1232 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1234 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1236 g_free (priv->local_account_name);
1237 g_free (priv->visible_account_id);
1239 if (priv->conf_key_signal) {
1240 g_signal_handler_disconnect (modest_runtime_get_conf (),
1241 priv->conf_key_signal);
1242 priv->conf_key_signal = 0;
1245 if (priv->cur_folder_store) {
1246 g_object_unref (priv->cur_folder_store);
1247 priv->cur_folder_store = NULL;
1250 /* Clear hidding array created by cut operation */
1251 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1253 G_OBJECT_CLASS(parent_class)->finalize (obj);
1258 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1260 ModestFolderViewPrivate *priv;
1263 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1264 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1266 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1267 device = tny_account_store_get_device (account_store);
1269 if (G_UNLIKELY (priv->account_store)) {
1271 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1272 priv->account_inserted_signal))
1273 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1274 priv->account_inserted_signal);
1275 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1276 priv->account_removed_signal))
1277 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1278 priv->account_removed_signal);
1279 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1280 priv->account_changed_signal))
1281 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1282 priv->account_changed_signal);
1283 g_object_unref (G_OBJECT (priv->account_store));
1286 priv->account_store = g_object_ref (G_OBJECT (account_store));
1288 priv->account_removed_signal =
1289 g_signal_connect (G_OBJECT(account_store), "account_removed",
1290 G_CALLBACK (on_account_removed), self);
1292 priv->account_inserted_signal =
1293 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1294 G_CALLBACK (on_account_inserted), self);
1296 priv->account_changed_signal =
1297 g_signal_connect (G_OBJECT(account_store), "account_changed",
1298 G_CALLBACK (on_account_changed), self);
1300 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1301 priv->reselect = FALSE;
1302 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1304 g_object_unref (G_OBJECT (device));
1308 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1311 ModestFolderView *self;
1312 GtkTreeModel *model, *filter_model;
1315 self = MODEST_FOLDER_VIEW (user_data);
1317 if (!get_inner_models (self, &filter_model, NULL, &model))
1320 /* Remove outbox from model */
1321 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1322 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1323 g_object_unref (outbox);
1326 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1330 on_account_inserted (TnyAccountStore *account_store,
1331 TnyAccount *account,
1334 ModestFolderViewPrivate *priv;
1335 GtkTreeModel *model, *filter_model;
1337 /* Ignore transport account insertions, we're not showing them
1338 in the folder view */
1339 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1342 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1345 /* If we're adding a new account, and there is no previous
1346 one, we need to select the visible server account */
1347 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1348 !priv->visible_account_id)
1349 modest_widget_memory_restore (modest_runtime_get_conf(),
1350 G_OBJECT (user_data),
1351 MODEST_CONF_FOLDER_VIEW_KEY);
1355 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1356 &filter_model, NULL, &model))
1359 /* Insert the account in the model */
1360 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1362 /* When the model is a list store (plain representation) the
1363 outbox is not a child of any account so we have to manually
1364 delete it because removing the local folders account won't
1365 delete it (because tny_folder_get_account() is not defined
1366 for a merge folder */
1367 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1368 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1370 priv->outbox_deleted_handler =
1371 g_signal_connect (account,
1373 G_CALLBACK (on_outbox_deleted_cb),
1377 /* Refilter the model */
1378 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1383 same_account_selected (ModestFolderView *self,
1384 TnyAccount *account)
1386 ModestFolderViewPrivate *priv;
1387 gboolean same_account = FALSE;
1389 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1391 if (priv->cur_folder_store) {
1392 TnyAccount *selected_folder_account = NULL;
1394 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1395 selected_folder_account =
1396 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1398 selected_folder_account =
1399 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1402 if (selected_folder_account == account)
1403 same_account = TRUE;
1405 g_object_unref (selected_folder_account);
1407 return same_account;
1412 * Selects the first inbox or the local account in an idle
1415 on_idle_select_first_inbox_or_local (gpointer user_data)
1417 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1419 gdk_threads_enter ();
1420 modest_folder_view_select_first_inbox_or_local (self);
1421 gdk_threads_leave ();
1427 on_account_changed (TnyAccountStore *account_store,
1428 TnyAccount *tny_account,
1431 ModestFolderView *self;
1432 ModestFolderViewPrivate *priv;
1433 GtkTreeModel *model, *filter_model;
1434 GtkTreeSelection *sel;
1435 gboolean same_account;
1437 /* Ignore transport account insertions, we're not showing them
1438 in the folder view */
1439 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1442 self = MODEST_FOLDER_VIEW (user_data);
1443 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1445 /* Get the inner model */
1446 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1447 &filter_model, NULL, &model))
1450 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1452 /* Invalidate the cur_folder_store only if the selected folder
1453 belongs to the account that is being removed */
1454 same_account = same_account_selected (self, tny_account);
1456 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1457 gtk_tree_selection_unselect_all (sel);
1460 /* Remove the account from the model */
1461 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1463 /* Insert the account in the model */
1464 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1466 /* Refilter the model */
1467 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1469 /* Select the first INBOX if the currently selected folder
1470 belongs to the account that is being deleted */
1471 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1472 g_idle_add (on_idle_select_first_inbox_or_local, self);
1476 on_account_removed (TnyAccountStore *account_store,
1477 TnyAccount *account,
1480 ModestFolderView *self = NULL;
1481 ModestFolderViewPrivate *priv;
1482 GtkTreeModel *model, *filter_model;
1483 GtkTreeSelection *sel = NULL;
1484 gboolean same_account = FALSE;
1486 /* Ignore transport account removals, we're not showing them
1487 in the folder view */
1488 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1491 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1492 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1496 self = MODEST_FOLDER_VIEW (user_data);
1497 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1499 /* Invalidate the cur_folder_store only if the selected folder
1500 belongs to the account that is being removed */
1501 same_account = same_account_selected (self, account);
1503 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1504 gtk_tree_selection_unselect_all (sel);
1507 /* Invalidate row to select only if the folder to select
1508 belongs to the account that is being removed*/
1509 if (priv->folder_to_select) {
1510 TnyAccount *folder_to_select_account = NULL;
1512 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1513 if (folder_to_select_account == account) {
1514 modest_folder_view_disable_next_folder_selection (self);
1515 g_object_unref (priv->folder_to_select);
1516 priv->folder_to_select = NULL;
1518 g_object_unref (folder_to_select_account);
1521 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1522 &filter_model, NULL, &model))
1525 /* Disconnect the signal handler */
1526 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1527 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1528 if (g_signal_handler_is_connected (account,
1529 priv->outbox_deleted_handler))
1530 g_signal_handler_disconnect (account,
1531 priv->outbox_deleted_handler);
1534 /* Remove the account from the model */
1535 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1537 /* If the removed account is the currently viewed one then
1538 clear the configuration value. The new visible account will be the default account */
1539 if (priv->visible_account_id &&
1540 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1542 /* Clear the current visible account_id */
1543 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1545 /* Call the restore method, this will set the new visible account */
1546 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1547 MODEST_CONF_FOLDER_VIEW_KEY);
1550 /* Refilter the model */
1551 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1553 /* Select the first INBOX if the currently selected folder
1554 belongs to the account that is being deleted */
1556 g_idle_add (on_idle_select_first_inbox_or_local, self);
1560 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1562 GtkTreeViewColumn *col;
1564 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1566 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1568 g_printerr ("modest: failed get column for title\n");
1572 gtk_tree_view_column_set_title (col, title);
1573 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1578 modest_folder_view_on_map (ModestFolderView *self,
1579 GdkEventExpose *event,
1582 ModestFolderViewPrivate *priv;
1584 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1586 /* This won't happen often */
1587 if (G_UNLIKELY (priv->reselect)) {
1588 /* Select the first inbox or the local account if not found */
1590 /* TODO: this could cause a lock at startup, so we
1591 comment it for the moment. We know that this will
1592 be a bug, because the INBOX is not selected, but we
1593 need to rewrite some parts of Modest to avoid the
1594 deathlock situation */
1595 /* TODO: check if this is still the case */
1596 priv->reselect = FALSE;
1597 modest_folder_view_select_first_inbox_or_local (self);
1598 /* Notify the display name observers */
1599 g_signal_emit (G_OBJECT(self),
1600 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1604 if (priv->reexpand) {
1605 expand_root_items (self);
1606 priv->reexpand = FALSE;
1613 modest_folder_view_new (TnyFolderStoreQuery *query)
1616 ModestFolderViewPrivate *priv;
1617 GtkTreeSelection *sel;
1619 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1620 #ifdef MODEST_TOOLKIT_HILDON2
1621 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1624 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1627 priv->query = g_object_ref (query);
1629 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1630 priv->changed_signal = g_signal_connect (sel, "changed",
1631 G_CALLBACK (on_selection_changed), self);
1633 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1635 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1637 return GTK_WIDGET(self);
1640 /* this feels dirty; any other way to expand all the root items? */
1642 expand_root_items (ModestFolderView *self)
1645 GtkTreeModel *model;
1648 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1649 path = gtk_tree_path_new_first ();
1651 /* all folders should have child items, so.. */
1653 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1654 gtk_tree_path_next (path);
1655 } while (gtk_tree_model_get_iter (model, &iter, path));
1657 gtk_tree_path_free (path);
1661 * We use this function to implement the
1662 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1663 * account in this case, and the local folders.
1666 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1668 ModestFolderViewPrivate *priv;
1669 gboolean retval = TRUE;
1670 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1671 GObject *instance = NULL;
1672 const gchar *id = NULL;
1674 gboolean found = FALSE;
1675 gboolean cleared = FALSE;
1676 ModestTnyFolderRules rules = 0;
1678 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1679 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1681 gtk_tree_model_get (model, iter,
1683 INSTANCE_COLUMN, &instance,
1686 /* Do not show if there is no instance, this could indeed
1687 happen when the model is being modified while it's being
1688 drawn. This could occur for example when moving folders
1693 if (TNY_IS_ACCOUNT (instance)) {
1694 #ifdef MODEST_TOOLKIT_HILDON2
1695 /* In hildon 2.2 we don't show the account rows */
1698 TnyAccount *acc = TNY_ACCOUNT (instance);
1699 const gchar *account_id = tny_account_get_id (acc);
1701 /* If it isn't a special folder,
1702 * don't show it unless it is the visible account: */
1703 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1704 !modest_tny_account_is_virtual_local_folders (acc) &&
1705 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1707 /* Show only the visible account id */
1708 if (priv->visible_account_id) {
1709 if (strcmp (account_id, priv->visible_account_id))
1716 /* Never show these to the user. They are merged into one folder
1717 * in the local-folders account instead: */
1718 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1722 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1723 /* Only show special folders for current account if needed */
1724 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1725 TnyAccount *account;
1727 account = tny_folder_get_account (TNY_FOLDER (instance));
1729 if (TNY_IS_ACCOUNT (account)) {
1730 const gchar *account_id = tny_account_get_id (account);
1732 if (!modest_tny_account_is_virtual_local_folders (account) &&
1733 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1734 /* Show only the visible account id */
1735 if (priv->visible_account_id) {
1736 if (strcmp (account_id, priv->visible_account_id))
1740 g_object_unref (account);
1747 /* Check hiding (if necessary) */
1748 cleared = modest_email_clipboard_cleared (priv->clipboard);
1749 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1750 id = tny_folder_get_id (TNY_FOLDER(instance));
1751 if (priv->hidding_ids != NULL)
1752 for (i=0; i < priv->n_selected && !found; i++)
1753 if (priv->hidding_ids[i] != NULL && id != NULL)
1754 found = (!strcmp (priv->hidding_ids[i], id));
1759 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1760 folder as no message can be move there according to UI specs */
1761 if (!priv->show_non_move) {
1763 case TNY_FOLDER_TYPE_OUTBOX:
1764 case TNY_FOLDER_TYPE_SENT:
1765 case TNY_FOLDER_TYPE_DRAFTS:
1768 case TNY_FOLDER_TYPE_UNKNOWN:
1769 case TNY_FOLDER_TYPE_NORMAL:
1770 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1771 if (type == TNY_FOLDER_TYPE_INVALID)
1772 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1774 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1775 type == TNY_FOLDER_TYPE_SENT
1776 || type == TNY_FOLDER_TYPE_DRAFTS)
1784 /* apply special filters */
1785 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1786 if (TNY_IS_FOLDER (instance)) {
1787 TnyFolderCaps capabilities;
1789 capabilities = tny_folder_get_caps (TNY_FOLDER (instance));
1790 retval = !(capabilities & TNY_FOLDER_CAPS_NOCHILDREN);
1793 retval = ((type != TNY_FOLDER_TYPE_DRAFTS) &&
1794 (type != TNY_FOLDER_TYPE_OUTBOX) &&
1795 (type != TNY_FOLDER_TYPE_SENT));
1797 } else if (TNY_IS_ACCOUNT (instance)) {
1802 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1803 if (TNY_IS_FOLDER (instance)) {
1804 TnyFolderType guess_type;
1806 if (TNY_FOLDER_TYPE_NORMAL) {
1807 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1813 case TNY_FOLDER_TYPE_OUTBOX:
1814 case TNY_FOLDER_TYPE_SENT:
1815 case TNY_FOLDER_TYPE_DRAFTS:
1816 case TNY_FOLDER_TYPE_ARCHIVE:
1817 case TNY_FOLDER_TYPE_INBOX:
1820 case TNY_FOLDER_TYPE_UNKNOWN:
1821 case TNY_FOLDER_TYPE_NORMAL:
1827 } else if (TNY_IS_ACCOUNT (instance)) {
1832 if (retval && TNY_IS_FOLDER (instance)) {
1833 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1836 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1837 if (TNY_IS_FOLDER (instance)) {
1838 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1839 } else if (TNY_IS_ACCOUNT (instance)) {
1844 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1845 if (TNY_IS_FOLDER (instance)) {
1846 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1847 } else if (TNY_IS_ACCOUNT (instance)) {
1852 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1853 if (TNY_IS_FOLDER (instance)) {
1854 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1855 } else if (TNY_IS_ACCOUNT (instance)) {
1861 g_object_unref (instance);
1868 modest_folder_view_update_model (ModestFolderView *self,
1869 TnyAccountStore *account_store)
1871 ModestFolderViewPrivate *priv;
1872 GtkTreeModel *model /* , *old_model */;
1873 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1875 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1876 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1879 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1881 /* Notify that there is no folder selected */
1882 g_signal_emit (G_OBJECT(self),
1883 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1885 if (priv->cur_folder_store) {
1886 g_object_unref (priv->cur_folder_store);
1887 priv->cur_folder_store = NULL;
1890 /* FIXME: the local accounts are not shown when the query
1891 selects only the subscribed folders */
1892 #ifdef MODEST_TOOLKIT_HILDON2
1893 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1894 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1895 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1896 MODEST_FOLDER_PATH_SEPARATOR);
1898 model = tny_gtk_folder_store_tree_model_new (NULL);
1901 /* When the model is a list store (plain representation) the
1902 outbox is not a child of any account so we have to manually
1903 delete it because removing the local folders account won't
1904 delete it (because tny_folder_get_account() is not defined
1905 for a merge folder */
1906 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1907 TnyAccount *account;
1908 ModestTnyAccountStore *acc_store;
1910 acc_store = MODEST_TNY_ACCOUNT_STORE (priv->account_store);
1911 account = modest_tny_account_store_get_local_folders_account (acc_store);
1913 if (g_signal_handler_is_connected (account,
1914 priv->outbox_deleted_handler))
1915 g_signal_handler_disconnect (account,
1916 priv->outbox_deleted_handler);
1918 priv->outbox_deleted_handler =
1919 g_signal_connect (account,
1921 G_CALLBACK (on_outbox_deleted_cb),
1923 g_object_unref (account);
1926 /* Get the accounts: */
1927 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1929 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1931 sortable = gtk_tree_model_sort_new_with_model (model);
1932 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1934 GTK_SORT_ASCENDING);
1935 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1937 cmp_rows, NULL, NULL);
1939 /* Create filter model */
1940 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1941 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1947 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1948 #ifndef MODEST_TOOLKIT_HILDON2
1949 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1950 (GCallback) on_row_inserted_maybe_select_folder, self);
1953 g_object_unref (model);
1954 g_object_unref (filter_model);
1955 g_object_unref (sortable);
1957 /* Force a reselection of the INBOX next time the widget is shown */
1958 priv->reselect = TRUE;
1965 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1967 GtkTreeModel *model = NULL;
1968 TnyFolderStore *folder = NULL;
1970 ModestFolderView *tree_view = NULL;
1971 ModestFolderViewPrivate *priv = NULL;
1972 gboolean selected = FALSE;
1974 g_return_if_fail (sel);
1975 g_return_if_fail (user_data);
1977 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1979 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1981 tree_view = MODEST_FOLDER_VIEW (user_data);
1984 gtk_tree_model_get (model, &iter,
1985 INSTANCE_COLUMN, &folder,
1988 /* If the folder is the same do not notify */
1989 if (folder && priv->cur_folder_store == folder) {
1990 g_object_unref (folder);
1995 /* Current folder was unselected */
1996 if (priv->cur_folder_store) {
1997 /* We must do this firstly because a libtinymail-camel
1998 implementation detail. If we issue the signal
1999 before doing the sync_async, then that signal could
2000 cause (and it actually does it) a free of the
2001 summary of the folder (because the main window will
2002 clear the headers view */
2003 if (TNY_IS_FOLDER(priv->cur_folder_store))
2004 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2005 FALSE, NULL, NULL, NULL);
2007 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2008 priv->cur_folder_store, FALSE);
2010 g_object_unref (priv->cur_folder_store);
2011 priv->cur_folder_store = NULL;
2014 /* New current references */
2015 priv->cur_folder_store = folder;
2017 /* New folder has been selected. Do not notify if there is
2018 nothing new selected */
2020 g_signal_emit (G_OBJECT(tree_view),
2021 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2022 0, priv->cur_folder_store, TRUE);
2027 on_row_activated (GtkTreeView *treeview,
2028 GtkTreePath *treepath,
2029 GtkTreeViewColumn *column,
2032 GtkTreeModel *model = NULL;
2033 TnyFolderStore *folder = NULL;
2035 ModestFolderView *self = NULL;
2036 ModestFolderViewPrivate *priv = NULL;
2038 g_return_if_fail (treeview);
2039 g_return_if_fail (user_data);
2041 self = MODEST_FOLDER_VIEW (user_data);
2042 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2044 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2046 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2049 gtk_tree_model_get (model, &iter,
2050 INSTANCE_COLUMN, &folder,
2053 g_signal_emit (G_OBJECT(self),
2054 signals[FOLDER_ACTIVATED_SIGNAL],
2057 #ifdef MODEST_TOOLKIT_HILDON2
2058 HildonUIMode ui_mode;
2059 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2060 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2061 if (priv->cur_folder_store)
2062 g_object_unref (priv->cur_folder_store);
2063 priv->cur_folder_store = g_object_ref (folder);
2067 g_object_unref (folder);
2071 modest_folder_view_get_selected (ModestFolderView *self)
2073 ModestFolderViewPrivate *priv;
2075 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2077 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2078 if (priv->cur_folder_store)
2079 g_object_ref (priv->cur_folder_store);
2081 return priv->cur_folder_store;
2085 get_cmp_rows_type_pos (GObject *folder)
2087 /* Remote accounts -> Local account -> MMC account .*/
2090 if (TNY_IS_ACCOUNT (folder) &&
2091 modest_tny_account_is_virtual_local_folders (
2092 TNY_ACCOUNT (folder))) {
2094 } else if (TNY_IS_ACCOUNT (folder)) {
2095 TnyAccount *account = TNY_ACCOUNT (folder);
2096 const gchar *account_id = tny_account_get_id (account);
2097 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2103 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2104 return -1; /* Should never happen */
2109 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2111 TnyAccount *account;
2112 gboolean is_special;
2113 /* Inbox, Outbox, Drafts, Sent, User */
2116 if (!TNY_IS_FOLDER (folder_store))
2119 case TNY_FOLDER_TYPE_INBOX:
2121 account = tny_folder_get_account (folder_store);
2122 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2123 g_object_unref (account);
2124 return is_special?0:4;
2127 case TNY_FOLDER_TYPE_OUTBOX:
2130 case TNY_FOLDER_TYPE_DRAFTS:
2132 account = tny_folder_get_account (folder_store);
2133 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2134 g_object_unref (account);
2135 return is_special?1:4;
2138 case TNY_FOLDER_TYPE_SENT:
2140 account = tny_folder_get_account (folder_store);
2141 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2142 g_object_unref (account);
2143 return is_special?3:4;
2152 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2154 const gchar *a1_name, *a2_name;
2156 a1_name = tny_account_get_name (a1);
2157 a2_name = tny_account_get_name (a2);
2159 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2163 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2165 TnyAccount *a1, *a2;
2168 if (TNY_IS_ACCOUNT (s1)) {
2169 a1 = TNY_ACCOUNT (g_object_ref (s1));
2171 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2174 if (TNY_IS_ACCOUNT (s2)) {
2175 a2 = TNY_ACCOUNT (g_object_ref (s2));
2177 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2184 /* First we sort with the type of account */
2185 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2189 cmp = compare_account_names (a1, a2);
2192 g_object_unref (a1);
2193 g_object_unref (a2);
2199 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2201 gint is_account1, is_account2;
2203 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2204 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2206 return is_account2 - is_account1;
2210 * This function orders the mail accounts according to these rules:
2211 * 1st - remote accounts
2212 * 2nd - local account
2216 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2220 gchar *name1 = NULL;
2221 gchar *name2 = NULL;
2222 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2223 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2224 GObject *folder1 = NULL;
2225 GObject *folder2 = NULL;
2227 gtk_tree_model_get (tree_model, iter1,
2228 NAME_COLUMN, &name1,
2230 INSTANCE_COLUMN, &folder1,
2232 gtk_tree_model_get (tree_model, iter2,
2233 NAME_COLUMN, &name2,
2234 TYPE_COLUMN, &type2,
2235 INSTANCE_COLUMN, &folder2,
2238 /* Return if we get no folder. This could happen when folder
2239 operations are happening. The model is updated after the
2240 folder copy/move actually occurs, so there could be
2241 situations where the model to be drawn is not correct */
2242 if (!folder1 || !folder2)
2245 /* Sort by type. First the special folders, then the archives */
2246 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2250 /* Now we sort using the account of each folder */
2251 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2255 /* Each group is preceeded by its account */
2256 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2260 /* Pure sort by name */
2261 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2264 g_object_unref(G_OBJECT(folder1));
2266 g_object_unref(G_OBJECT(folder2));
2274 /*****************************************************************************/
2275 /* DRAG and DROP stuff */
2276 /*****************************************************************************/
2278 * This function fills the #GtkSelectionData with the row and the
2279 * model that has been dragged. It's called when this widget is a
2280 * source for dnd after the event drop happened
2283 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2284 guint info, guint time, gpointer data)
2286 GtkTreeSelection *selection;
2287 GtkTreeModel *model;
2289 GtkTreePath *source_row;
2291 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2292 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2294 source_row = gtk_tree_model_get_path (model, &iter);
2295 gtk_tree_set_row_drag_data (selection_data,
2299 gtk_tree_path_free (source_row);
2303 typedef struct _DndHelper {
2304 ModestFolderView *folder_view;
2305 gboolean delete_source;
2306 GtkTreePath *source_row;
2310 dnd_helper_destroyer (DndHelper *helper)
2312 /* Free the helper */
2313 gtk_tree_path_free (helper->source_row);
2314 g_slice_free (DndHelper, helper);
2318 xfer_folder_cb (ModestMailOperation *mail_op,
2319 TnyFolder *new_folder,
2323 /* Select the folder */
2324 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2330 /* get the folder for the row the treepath refers to. */
2331 /* folder must be unref'd */
2332 static TnyFolderStore *
2333 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2336 TnyFolderStore *folder = NULL;
2338 if (gtk_tree_model_get_iter (model,&iter, path))
2339 gtk_tree_model_get (model, &iter,
2340 INSTANCE_COLUMN, &folder,
2347 * This function is used by drag_data_received_cb to manage drag and
2348 * drop of a header, i.e, and drag from the header view to the folder
2352 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2353 GtkTreeModel *dest_model,
2354 GtkTreePath *dest_row,
2355 GtkSelectionData *selection_data)
2357 TnyList *headers = NULL;
2358 TnyFolder *folder = NULL, *src_folder = NULL;
2359 TnyFolderType folder_type;
2360 GtkTreeIter source_iter, dest_iter;
2361 ModestWindowMgr *mgr = NULL;
2362 ModestWindow *main_win = NULL;
2363 gchar **uris, **tmp;
2365 /* Build the list of headers */
2366 mgr = modest_runtime_get_window_mgr ();
2367 headers = tny_simple_list_new ();
2368 uris = modest_dnd_selection_data_get_paths (selection_data);
2371 while (*tmp != NULL) {
2374 gboolean first = TRUE;
2377 path = gtk_tree_path_new_from_string (*tmp);
2378 gtk_tree_model_get_iter (source_model, &source_iter, path);
2379 gtk_tree_model_get (source_model, &source_iter,
2380 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2383 /* Do not enable d&d of headers already opened */
2384 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2385 tny_list_append (headers, G_OBJECT (header));
2387 if (G_UNLIKELY (first)) {
2388 src_folder = tny_header_get_folder (header);
2392 /* Free and go on */
2393 gtk_tree_path_free (path);
2394 g_object_unref (header);
2399 /* This could happen ig we perform a d&d very quickly over the
2400 same row that row could dissapear because message is
2402 if (!TNY_IS_FOLDER (src_folder))
2405 /* Get the target folder */
2406 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2407 gtk_tree_model_get (dest_model, &dest_iter,
2411 if (!folder || !TNY_IS_FOLDER(folder)) {
2412 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2416 folder_type = modest_tny_folder_guess_folder_type (folder);
2417 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2418 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2419 goto cleanup; /* cannot move messages there */
2422 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2423 /* g_warning ("folder not writable"); */
2424 goto cleanup; /* verboten! */
2427 /* Ask for confirmation to move */
2428 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2430 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2434 /* Transfer messages */
2435 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2440 if (G_IS_OBJECT (src_folder))
2441 g_object_unref (src_folder);
2442 if (G_IS_OBJECT(folder))
2443 g_object_unref (G_OBJECT (folder));
2444 if (G_IS_OBJECT(headers))
2445 g_object_unref (headers);
2449 TnyFolderStore *src_folder;
2450 TnyFolderStore *dst_folder;
2451 ModestFolderView *folder_view;
2456 dnd_folder_info_destroyer (DndFolderInfo *info)
2458 if (info->src_folder)
2459 g_object_unref (info->src_folder);
2460 if (info->dst_folder)
2461 g_object_unref (info->dst_folder);
2462 g_slice_free (DndFolderInfo, info);
2466 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2467 GtkWindow *parent_window,
2468 TnyAccount *account)
2471 modest_ui_actions_on_account_connection_error (parent_window, account);
2473 /* Free the helper & info */
2474 dnd_helper_destroyer (info->helper);
2475 dnd_folder_info_destroyer (info);
2479 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2481 GtkWindow *parent_window,
2482 TnyAccount *account,
2485 DndFolderInfo *info = NULL;
2486 ModestMailOperation *mail_op;
2488 info = (DndFolderInfo *) user_data;
2490 if (err || canceled) {
2491 dnd_on_connection_failed_destroyer (info, parent_window, account);
2495 /* Do the mail operation */
2496 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2497 modest_ui_actions_move_folder_error_handler,
2498 info->src_folder, NULL);
2500 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2503 /* Transfer the folder */
2504 modest_mail_operation_xfer_folder (mail_op,
2505 TNY_FOLDER (info->src_folder),
2507 info->helper->delete_source,
2509 info->helper->folder_view);
2512 g_object_unref (G_OBJECT (mail_op));
2513 dnd_helper_destroyer (info->helper);
2514 dnd_folder_info_destroyer (info);
2519 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2521 GtkWindow *parent_window,
2522 TnyAccount *account,
2525 DndFolderInfo *info = NULL;
2527 info = (DndFolderInfo *) user_data;
2529 if (err || canceled) {
2530 dnd_on_connection_failed_destroyer (info, parent_window, account);
2534 /* Connect to source folder and perform the copy/move */
2535 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2537 drag_and_drop_from_folder_view_src_folder_performer,
2542 * This function is used by drag_data_received_cb to manage drag and
2543 * drop of a folder, i.e, and drag from the folder view to the same
2547 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2548 GtkTreeModel *dest_model,
2549 GtkTreePath *dest_row,
2550 GtkSelectionData *selection_data,
2553 GtkTreeIter dest_iter, iter;
2554 TnyFolderStore *dest_folder = NULL;
2555 TnyFolderStore *folder = NULL;
2556 gboolean forbidden = FALSE;
2558 DndFolderInfo *info = NULL;
2560 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2562 g_warning ("%s: BUG: no main window", __FUNCTION__);
2563 dnd_helper_destroyer (helper);
2568 /* check the folder rules for the destination */
2569 folder = tree_path_to_folder (dest_model, dest_row);
2570 if (TNY_IS_FOLDER(folder)) {
2571 ModestTnyFolderRules rules =
2572 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2573 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2574 } else if (TNY_IS_FOLDER_STORE(folder)) {
2575 /* enable local root as destination for folders */
2576 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2577 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2580 g_object_unref (folder);
2583 /* check the folder rules for the source */
2584 folder = tree_path_to_folder (source_model, helper->source_row);
2585 if (TNY_IS_FOLDER(folder)) {
2586 ModestTnyFolderRules rules =
2587 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2588 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2591 g_object_unref (folder);
2595 /* Check if the drag is possible */
2596 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2598 modest_platform_run_information_dialog ((GtkWindow *) win,
2599 _("mail_in_ui_folder_move_target_error"),
2601 /* Restore the previous selection */
2602 folder = tree_path_to_folder (source_model, helper->source_row);
2604 if (TNY_IS_FOLDER (folder))
2605 modest_folder_view_select_folder (helper->folder_view,
2606 TNY_FOLDER (folder), FALSE);
2607 g_object_unref (folder);
2609 dnd_helper_destroyer (helper);
2614 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2615 gtk_tree_model_get (dest_model, &dest_iter,
2618 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2619 gtk_tree_model_get (source_model, &iter,
2623 /* Create the info for the performer */
2624 info = g_slice_new0 (DndFolderInfo);
2625 info->src_folder = g_object_ref (folder);
2626 info->dst_folder = g_object_ref (dest_folder);
2627 info->helper = helper;
2629 /* Connect to the destination folder and perform the copy/move */
2630 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2632 drag_and_drop_from_folder_view_dst_folder_performer,
2636 g_object_unref (dest_folder);
2637 g_object_unref (folder);
2641 * This function receives the data set by the "drag-data-get" signal
2642 * handler. This information comes within the #GtkSelectionData. This
2643 * function will manage both the drags of folders of the treeview and
2644 * drags of headers of the header view widget.
2647 on_drag_data_received (GtkWidget *widget,
2648 GdkDragContext *context,
2651 GtkSelectionData *selection_data,
2656 GtkWidget *source_widget;
2657 GtkTreeModel *dest_model, *source_model;
2658 GtkTreePath *source_row, *dest_row;
2659 GtkTreeViewDropPosition pos;
2660 gboolean delete_source = FALSE;
2661 gboolean success = FALSE;
2663 /* Do not allow further process */
2664 g_signal_stop_emission_by_name (widget, "drag-data-received");
2665 source_widget = gtk_drag_get_source_widget (context);
2667 /* Get the action */
2668 if (context->action == GDK_ACTION_MOVE) {
2669 delete_source = TRUE;
2671 /* Notify that there is no folder selected. We need to
2672 do this in order to update the headers view (and
2673 its monitors, because when moving, the old folder
2674 won't longer exist. We can not wait for the end of
2675 the operation, because the operation won't start if
2676 the folder is in use */
2677 if (source_widget == widget) {
2678 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2679 gtk_tree_selection_unselect_all (sel);
2683 /* Check if the get_data failed */
2684 if (selection_data == NULL || selection_data->length < 0)
2687 /* Select the destination model */
2688 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2690 /* Get the path to the destination row. Can not call
2691 gtk_tree_view_get_drag_dest_row() because the source row
2692 is not selected anymore */
2693 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2696 /* Only allow drops IN other rows */
2698 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2699 pos == GTK_TREE_VIEW_DROP_AFTER)
2703 /* Drags from the header view */
2704 if (source_widget != widget) {
2705 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2707 drag_and_drop_from_header_view (source_model,
2712 DndHelper *helper = NULL;
2714 /* Get the source model and row */
2715 gtk_tree_get_row_drag_data (selection_data,
2719 /* Create the helper */
2720 helper = g_slice_new0 (DndHelper);
2721 helper->delete_source = delete_source;
2722 helper->source_row = gtk_tree_path_copy (source_row);
2723 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2725 drag_and_drop_from_folder_view (source_model,
2731 gtk_tree_path_free (source_row);
2735 gtk_tree_path_free (dest_row);
2738 /* Finish the drag and drop */
2739 gtk_drag_finish (context, success, FALSE, time);
2743 * We define a "drag-drop" signal handler because we do not want to
2744 * use the default one, because the default one always calls
2745 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2746 * signal handler, because there we have all the information available
2747 * to know if the dnd was a success or not.
2750 drag_drop_cb (GtkWidget *widget,
2751 GdkDragContext *context,
2759 if (!context->targets)
2762 /* Check if we're dragging a folder row */
2763 target = gtk_drag_dest_find_target (widget, context, NULL);
2765 /* Request the data from the source. */
2766 gtk_drag_get_data(widget, context, target, time);
2772 * This function expands a node of a tree view if it's not expanded
2773 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2774 * does that, so that's why they're here.
2777 expand_row_timeout (gpointer data)
2779 GtkTreeView *tree_view = data;
2780 GtkTreePath *dest_path = NULL;
2781 GtkTreeViewDropPosition pos;
2782 gboolean result = FALSE;
2784 gdk_threads_enter ();
2786 gtk_tree_view_get_drag_dest_row (tree_view,
2791 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2792 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2793 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2794 gtk_tree_path_free (dest_path);
2798 gtk_tree_path_free (dest_path);
2803 gdk_threads_leave ();
2809 * This function is called whenever the pointer is moved over a widget
2810 * while dragging some data. It installs a timeout that will expand a
2811 * node of the treeview if not expanded yet. This function also calls
2812 * gdk_drag_status in order to set the suggested action that will be
2813 * used by the "drag-data-received" signal handler to know if we
2814 * should do a move or just a copy of the data.
2817 on_drag_motion (GtkWidget *widget,
2818 GdkDragContext *context,
2824 GtkTreeViewDropPosition pos;
2825 GtkTreePath *dest_row;
2826 GtkTreeModel *dest_model;
2827 ModestFolderViewPrivate *priv;
2828 GdkDragAction suggested_action;
2829 gboolean valid_location = FALSE;
2830 TnyFolderStore *folder = NULL;
2832 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2834 if (priv->timer_expander != 0) {
2835 g_source_remove (priv->timer_expander);
2836 priv->timer_expander = 0;
2839 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2844 /* Do not allow drops between folders */
2846 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2847 pos == GTK_TREE_VIEW_DROP_AFTER) {
2848 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2849 gdk_drag_status(context, 0, time);
2850 valid_location = FALSE;
2853 valid_location = TRUE;
2856 /* Check that the destination folder is writable */
2857 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2858 folder = tree_path_to_folder (dest_model, dest_row);
2859 if (folder && TNY_IS_FOLDER (folder)) {
2860 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2862 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2863 valid_location = FALSE;
2868 /* Expand the selected row after 1/2 second */
2869 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2870 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2872 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2874 /* Select the desired action. By default we pick MOVE */
2875 suggested_action = GDK_ACTION_MOVE;
2877 if (context->actions == GDK_ACTION_COPY)
2878 gdk_drag_status(context, GDK_ACTION_COPY, time);
2879 else if (context->actions == GDK_ACTION_MOVE)
2880 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2881 else if (context->actions & suggested_action)
2882 gdk_drag_status(context, suggested_action, time);
2884 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2888 g_object_unref (folder);
2890 gtk_tree_path_free (dest_row);
2892 g_signal_stop_emission_by_name (widget, "drag-motion");
2894 return valid_location;
2898 * This function sets the treeview as a source and a target for dnd
2899 * events. It also connects all the requirede signals.
2902 setup_drag_and_drop (GtkTreeView *self)
2904 /* Set up the folder view as a dnd destination. Set only the
2905 highlight flag, otherwise gtk will have a different
2907 #ifdef MODEST_TOOLKIT_HILDON2
2910 gtk_drag_dest_set (GTK_WIDGET (self),
2911 GTK_DEST_DEFAULT_HIGHLIGHT,
2912 folder_view_drag_types,
2913 G_N_ELEMENTS (folder_view_drag_types),
2914 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2916 g_signal_connect (G_OBJECT (self),
2917 "drag_data_received",
2918 G_CALLBACK (on_drag_data_received),
2922 /* Set up the treeview as a dnd source */
2923 gtk_drag_source_set (GTK_WIDGET (self),
2925 folder_view_drag_types,
2926 G_N_ELEMENTS (folder_view_drag_types),
2927 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2929 g_signal_connect (G_OBJECT (self),
2931 G_CALLBACK (on_drag_motion),
2934 g_signal_connect (G_OBJECT (self),
2936 G_CALLBACK (on_drag_data_get),
2939 g_signal_connect (G_OBJECT (self),
2941 G_CALLBACK (drag_drop_cb),
2946 * This function manages the navigation through the folders using the
2947 * keyboard or the hardware keys in the device
2950 on_key_pressed (GtkWidget *self,
2954 GtkTreeSelection *selection;
2956 GtkTreeModel *model;
2957 gboolean retval = FALSE;
2959 /* Up and Down are automatically managed by the treeview */
2960 if (event->keyval == GDK_Return) {
2961 /* Expand/Collapse the selected row */
2962 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2963 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2966 path = gtk_tree_model_get_path (model, &iter);
2968 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2969 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2971 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2972 gtk_tree_path_free (path);
2974 /* No further processing */
2982 * We listen to the changes in the local folder account name key,
2983 * because we want to show the right name in the view. The local
2984 * folder account name corresponds to the device name in the Maemo
2985 * version. We do this because we do not want to query gconf on each
2986 * tree view refresh. It's better to cache it and change whenever
2990 on_configuration_key_changed (ModestConf* conf,
2992 ModestConfEvent event,
2993 ModestConfNotificationId id,
2994 ModestFolderView *self)
2996 ModestFolderViewPrivate *priv;
2999 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3000 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3002 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3003 g_free (priv->local_account_name);
3005 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3006 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3008 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3009 MODEST_CONF_DEVICE_NAME, NULL);
3011 /* Force a redraw */
3012 #if GTK_CHECK_VERSION(2, 8, 0)
3013 GtkTreeViewColumn * tree_column;
3015 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3017 gtk_tree_view_column_queue_resize (tree_column);
3019 gtk_widget_queue_draw (GTK_WIDGET (self));
3025 modest_folder_view_set_style (ModestFolderView *self,
3026 ModestFolderViewStyle style)
3028 ModestFolderViewPrivate *priv;
3030 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3031 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3032 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3034 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3037 priv->style = style;
3041 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3042 const gchar *account_id)
3044 ModestFolderViewPrivate *priv;
3045 GtkTreeModel *model;
3047 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3049 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3051 /* This will be used by the filter_row callback,
3052 * to decided which rows to show: */
3053 if (priv->visible_account_id) {
3054 g_free (priv->visible_account_id);
3055 priv->visible_account_id = NULL;
3058 priv->visible_account_id = g_strdup (account_id);
3061 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3062 if (GTK_IS_TREE_MODEL_FILTER (model))
3063 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3065 /* Save settings to gconf */
3066 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3067 MODEST_CONF_FOLDER_VIEW_KEY);
3071 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3073 ModestFolderViewPrivate *priv;
3075 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3077 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3079 return (const gchar *) priv->visible_account_id;
3083 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3087 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3089 gtk_tree_model_get (model, iter,
3093 gboolean result = FALSE;
3094 if (type == TNY_FOLDER_TYPE_INBOX) {
3098 *inbox_iter = *iter;
3102 if (gtk_tree_model_iter_children (model, &child, iter)) {
3103 if (find_inbox_iter (model, &child, inbox_iter))
3107 } while (gtk_tree_model_iter_next (model, iter));
3116 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3118 GtkTreeModel *model;
3119 GtkTreeIter iter, inbox_iter;
3120 GtkTreeSelection *sel;
3121 GtkTreePath *path = NULL;
3123 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3125 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3129 expand_root_items (self);
3130 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3132 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3133 g_warning ("%s: model is empty", __FUNCTION__);
3137 if (find_inbox_iter (model, &iter, &inbox_iter))
3138 path = gtk_tree_model_get_path (model, &inbox_iter);
3140 path = gtk_tree_path_new_first ();
3142 /* Select the row and free */
3143 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3144 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3145 gtk_tree_path_free (path);
3148 gtk_widget_grab_focus (GTK_WIDGET(self));
3154 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3159 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3160 TnyFolder* a_folder;
3163 gtk_tree_model_get (model, iter,
3164 INSTANCE_COLUMN, &a_folder,
3170 if (folder == a_folder) {
3171 g_object_unref (a_folder);
3172 *folder_iter = *iter;
3175 g_object_unref (a_folder);
3177 if (gtk_tree_model_iter_children (model, &child, iter)) {
3178 if (find_folder_iter (model, &child, folder_iter, folder))
3182 } while (gtk_tree_model_iter_next (model, iter));
3187 #ifndef MODEST_TOOLKIT_HILDON2
3189 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3192 ModestFolderView *self)
3194 ModestFolderViewPrivate *priv = NULL;
3195 GtkTreeSelection *sel;
3196 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3197 GObject *instance = NULL;
3199 if (!MODEST_IS_FOLDER_VIEW(self))
3202 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3204 priv->reexpand = TRUE;
3206 gtk_tree_model_get (tree_model, iter,
3208 INSTANCE_COLUMN, &instance,
3214 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3215 priv->folder_to_select = g_object_ref (instance);
3217 g_object_unref (instance);
3219 if (priv->folder_to_select) {
3221 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3224 path = gtk_tree_model_get_path (tree_model, iter);
3225 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3227 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3229 gtk_tree_selection_select_iter (sel, iter);
3230 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3232 gtk_tree_path_free (path);
3236 modest_folder_view_disable_next_folder_selection (self);
3238 /* Refilter the model */
3239 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3245 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3247 ModestFolderViewPrivate *priv;
3249 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3251 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3253 if (priv->folder_to_select)
3254 g_object_unref(priv->folder_to_select);
3256 priv->folder_to_select = NULL;
3260 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3261 gboolean after_change)
3263 GtkTreeModel *model;
3264 GtkTreeIter iter, folder_iter;
3265 GtkTreeSelection *sel;
3266 ModestFolderViewPrivate *priv = NULL;
3268 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3269 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3271 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3274 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3275 gtk_tree_selection_unselect_all (sel);
3277 if (priv->folder_to_select)
3278 g_object_unref(priv->folder_to_select);
3279 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3283 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3288 /* Refilter the model, before selecting the folder */
3289 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3291 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3292 g_warning ("%s: model is empty", __FUNCTION__);
3296 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3299 path = gtk_tree_model_get_path (model, &folder_iter);
3300 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3302 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3303 gtk_tree_selection_select_iter (sel, &folder_iter);
3304 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3306 gtk_tree_path_free (path);
3314 modest_folder_view_copy_selection (ModestFolderView *self)
3316 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3318 /* Copy selection */
3319 _clipboard_set_selected_data (self, FALSE);
3323 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3325 ModestFolderViewPrivate *priv = NULL;
3326 GtkTreeModel *model = NULL;
3327 const gchar **hidding = NULL;
3328 guint i, n_selected;
3330 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3331 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3333 /* Copy selection */
3334 if (!_clipboard_set_selected_data (folder_view, TRUE))
3337 /* Get hidding ids */
3338 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3340 /* Clear hidding array created by previous cut operation */
3341 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3343 /* Copy hidding array */
3344 priv->n_selected = n_selected;
3345 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3346 for (i=0; i < n_selected; i++)
3347 priv->hidding_ids[i] = g_strdup(hidding[i]);
3349 /* Hide cut folders */
3350 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3351 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3355 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3356 ModestFolderView *folder_view_dst)
3358 GtkTreeModel *filter_model = NULL;
3359 GtkTreeModel *model = NULL;
3360 GtkTreeModel *new_filter_model = NULL;
3362 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3363 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3366 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3367 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3369 /* Build new filter model */
3370 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3371 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3375 /* Set copied model */
3376 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3377 #ifndef MODEST_TOOLKIT_HILDON2
3378 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3379 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3383 g_object_unref (new_filter_model);
3387 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3390 GtkTreeModel *model = NULL;
3391 ModestFolderViewPrivate* priv;
3393 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3395 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3396 priv->show_non_move = show;
3397 /* modest_folder_view_update_model(folder_view, */
3398 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3400 /* Hide special folders */
3401 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3402 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3403 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3407 /* Returns FALSE if it did not selected anything */
3409 _clipboard_set_selected_data (ModestFolderView *folder_view,
3412 ModestFolderViewPrivate *priv = NULL;
3413 TnyFolderStore *folder = NULL;
3414 gboolean retval = FALSE;
3416 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3417 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3419 /* Set selected data on clipboard */
3420 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3421 folder = modest_folder_view_get_selected (folder_view);
3423 /* Do not allow to select an account */
3424 if (TNY_IS_FOLDER (folder)) {
3425 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3430 g_object_unref (folder);
3436 _clear_hidding_filter (ModestFolderView *folder_view)
3438 ModestFolderViewPrivate *priv;
3441 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3442 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3444 if (priv->hidding_ids != NULL) {
3445 for (i=0; i < priv->n_selected; i++)
3446 g_free (priv->hidding_ids[i]);
3447 g_free(priv->hidding_ids);
3453 on_display_name_changed (ModestAccountMgr *mgr,
3454 const gchar *account,
3457 ModestFolderView *self;
3459 self = MODEST_FOLDER_VIEW (user_data);
3461 /* Force a redraw */
3462 #if GTK_CHECK_VERSION(2, 8, 0)
3463 GtkTreeViewColumn * tree_column;
3465 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3467 gtk_tree_view_column_queue_resize (tree_column);
3469 gtk_widget_queue_draw (GTK_WIDGET (self));
3474 modest_folder_view_set_cell_style (ModestFolderView *self,
3475 ModestFolderViewCellStyle cell_style)
3477 ModestFolderViewPrivate *priv = NULL;
3479 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3480 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3482 priv->cell_style = cell_style;
3484 g_object_set (G_OBJECT (priv->messages_renderer),
3485 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3488 gtk_widget_queue_draw (GTK_WIDGET (self));
3492 update_style (ModestFolderView *self)
3494 ModestFolderViewPrivate *priv;
3495 GdkColor style_color;
3497 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3498 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3500 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3501 gdk_color_parse ("grey", &style_color);
3504 g_object_set (G_OBJECT (priv->messages_renderer),
3505 "foreground-gdk", &style_color,
3506 "foreground-set", TRUE,
3511 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3513 if (strcmp ("style", spec->name) == 0) {
3514 update_style (MODEST_FOLDER_VIEW (obj));
3515 gtk_widget_queue_draw (GTK_WIDGET (obj));
3520 modest_folder_view_set_filter (ModestFolderView *self,
3521 ModestFolderViewFilter filter)
3523 ModestFolderViewPrivate *priv;
3524 GtkTreeModel *filter_model;
3526 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3527 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3529 priv->filter = filter;
3531 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3532 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3533 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));