1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
66 /* Folder view drag types */
67 const GtkTargetEntry folder_view_drag_types[] =
69 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
70 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
73 /* Default icon sizes for Fremantle style are different */
74 #ifdef MODEST_TOOLKIT_HILDON2
75 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
77 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
80 /* Column names depending on we use list store or tree store */
81 #ifdef MODEST_TOOLKIT_HILDON2
82 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
83 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
84 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
85 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
86 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
88 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
89 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
90 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
91 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
92 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
95 /* 'private'/'protected' functions */
96 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
97 static void modest_folder_view_init (ModestFolderView *obj);
98 static void modest_folder_view_finalize (GObject *obj);
100 static void tny_account_store_view_init (gpointer g,
101 gpointer iface_data);
103 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
104 TnyAccountStore *account_store);
106 static void on_selection_changed (GtkTreeSelection *sel,
109 static void on_row_activated (GtkTreeView *treeview,
111 GtkTreeViewColumn *column,
114 static void on_account_removed (TnyAccountStore *self,
118 static void on_account_inserted (TnyAccountStore *self,
122 static void on_account_changed (TnyAccountStore *self,
126 static gint cmp_rows (GtkTreeModel *tree_model,
131 static gboolean filter_row (GtkTreeModel *model,
135 static gboolean on_key_pressed (GtkWidget *self,
139 static void on_configuration_key_changed (ModestConf* conf,
141 ModestConfEvent event,
142 ModestConfNotificationId notification_id,
143 ModestFolderView *self);
146 static void on_drag_data_get (GtkWidget *widget,
147 GdkDragContext *context,
148 GtkSelectionData *selection_data,
153 static void on_drag_data_received (GtkWidget *widget,
154 GdkDragContext *context,
157 GtkSelectionData *selection_data,
162 static gboolean on_drag_motion (GtkWidget *widget,
163 GdkDragContext *context,
169 static void expand_root_items (ModestFolderView *self);
171 static gint expand_row_timeout (gpointer data);
173 static void setup_drag_and_drop (GtkTreeView *self);
175 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
178 static void _clear_hidding_filter (ModestFolderView *folder_view);
180 #ifndef MODEST_TOOLKIT_HILDON2
181 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
184 ModestFolderView *self);
187 static void on_display_name_changed (ModestAccountMgr *self,
188 const gchar *account,
190 static void update_style (ModestFolderView *self);
191 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
192 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
193 static gboolean inbox_is_special (TnyFolderStore *folder_store);
195 static gboolean get_inner_models (ModestFolderView *self,
196 GtkTreeModel **filter_model,
197 GtkTreeModel **sort_model,
198 GtkTreeModel **tny_model);
201 FOLDER_SELECTION_CHANGED_SIGNAL,
202 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
203 FOLDER_ACTIVATED_SIGNAL,
204 VISIBLE_ACCOUNT_CHANGED_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;
236 ModestFolderViewStyle style;
237 ModestFolderViewCellStyle cell_style;
239 gboolean reselect; /* we use this to force a reselection of the INBOX */
240 gboolean show_non_move;
241 TnyList *list_to_move;
242 gboolean reexpand; /* next time we expose, we'll expand all root folders */
244 GtkCellRenderer *messages_renderer;
246 gulong outbox_deleted_handler;
248 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
249 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
250 MODEST_TYPE_FOLDER_VIEW, \
251 ModestFolderViewPrivate))
253 static GObjectClass *parent_class = NULL;
255 static guint signals[LAST_SIGNAL] = {0};
258 modest_folder_view_get_type (void)
260 static GType my_type = 0;
262 static const GTypeInfo my_info = {
263 sizeof(ModestFolderViewClass),
264 NULL, /* base init */
265 NULL, /* base finalize */
266 (GClassInitFunc) modest_folder_view_class_init,
267 NULL, /* class finalize */
268 NULL, /* class data */
269 sizeof(ModestFolderView),
271 (GInstanceInitFunc) modest_folder_view_init,
275 static const GInterfaceInfo tny_account_store_view_info = {
276 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
277 NULL, /* interface_finalize */
278 NULL /* interface_data */
282 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
286 g_type_add_interface_static (my_type,
287 TNY_TYPE_ACCOUNT_STORE_VIEW,
288 &tny_account_store_view_info);
294 modest_folder_view_class_init (ModestFolderViewClass *klass)
296 GObjectClass *gobject_class;
297 GtkTreeViewClass *treeview_class;
298 gobject_class = (GObjectClass*) klass;
299 treeview_class = (GtkTreeViewClass*) klass;
301 parent_class = g_type_class_peek_parent (klass);
302 gobject_class->finalize = modest_folder_view_finalize;
304 g_type_class_add_private (gobject_class,
305 sizeof(ModestFolderViewPrivate));
307 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
308 g_signal_new ("folder_selection_changed",
309 G_TYPE_FROM_CLASS (gobject_class),
311 G_STRUCT_OFFSET (ModestFolderViewClass,
312 folder_selection_changed),
314 modest_marshal_VOID__POINTER_BOOLEAN,
315 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
318 * This signal is emitted whenever the currently selected
319 * folder display name is computed. Note that the name could
320 * be different to the folder name, because we could append
321 * the unread messages count to the folder name to build the
322 * folder display name
324 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
325 g_signal_new ("folder-display-name-changed",
326 G_TYPE_FROM_CLASS (gobject_class),
328 G_STRUCT_OFFSET (ModestFolderViewClass,
329 folder_display_name_changed),
331 g_cclosure_marshal_VOID__STRING,
332 G_TYPE_NONE, 1, G_TYPE_STRING);
334 signals[FOLDER_ACTIVATED_SIGNAL] =
335 g_signal_new ("folder_activated",
336 G_TYPE_FROM_CLASS (gobject_class),
338 G_STRUCT_OFFSET (ModestFolderViewClass,
341 g_cclosure_marshal_VOID__POINTER,
342 G_TYPE_NONE, 1, G_TYPE_POINTER);
345 * Emitted whenever the visible account changes
347 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
348 g_signal_new ("visible-account-changed",
349 G_TYPE_FROM_CLASS (gobject_class),
351 G_STRUCT_OFFSET (ModestFolderViewClass,
352 visible_account_changed),
354 g_cclosure_marshal_VOID__STRING,
355 G_TYPE_NONE, 1, G_TYPE_STRING);
357 treeview_class->select_cursor_parent = NULL;
359 #ifdef MODEST_TOOLKIT_HILDON2
360 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
366 /* Retrieves the filter, sort and tny models of the folder view. If
367 any of these does not exist then it returns FALSE */
369 get_inner_models (ModestFolderView *self,
370 GtkTreeModel **filter_model,
371 GtkTreeModel **sort_model,
372 GtkTreeModel **tny_model)
374 GtkTreeModel *s_model, *f_model, *t_model;
376 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
377 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
378 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
382 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
383 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
384 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
388 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
392 *filter_model = f_model;
394 *sort_model = s_model;
396 *tny_model = t_model;
401 /* Simplify checks for NULLs: */
403 strings_are_equal (const gchar *a, const gchar *b)
409 return (strcmp (a, b) == 0);
416 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
418 GObject *instance = NULL;
420 gtk_tree_model_get (model, iter,
421 INSTANCE_COLUMN, &instance,
425 return FALSE; /* keep walking */
427 if (!TNY_IS_ACCOUNT (instance)) {
428 g_object_unref (instance);
429 return FALSE; /* keep walking */
432 /* Check if this is the looked-for account: */
433 TnyAccount *this_account = TNY_ACCOUNT (instance);
434 TnyAccount *account = TNY_ACCOUNT (data);
436 const gchar *this_account_id = tny_account_get_id(this_account);
437 const gchar *account_id = tny_account_get_id(account);
438 g_object_unref (instance);
441 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
442 if (strings_are_equal(this_account_id, account_id)) {
443 /* Tell the model that the data has changed, so that
444 * it calls the cell_data_func callbacks again: */
445 /* TODO: This does not seem to actually cause the new string to be shown: */
446 gtk_tree_model_row_changed (model, path, iter);
448 return TRUE; /* stop walking */
451 return FALSE; /* keep walking */
456 ModestFolderView *self;
457 gchar *previous_name;
458 } GetMmcAccountNameData;
461 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
463 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
465 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
467 if (!strings_are_equal (
468 tny_account_get_name(TNY_ACCOUNT(account)),
469 data->previous_name)) {
471 /* Tell the model that the data has changed, so that
472 * it calls the cell_data_func callbacks again: */
473 ModestFolderView *self = data->self;
474 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
476 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
479 g_free (data->previous_name);
480 g_slice_free (GetMmcAccountNameData, data);
484 convert_parent_folders_to_dots (gchar **item_name)
488 gchar *last_separator;
490 if (item_name == NULL)
493 for (c = *item_name; *c != '\0'; c++) {
494 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
499 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
500 if (last_separator != NULL) {
501 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
508 buffer = g_string_new ("");
509 for (i = 0; i < n_parents; i++) {
510 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
512 buffer = g_string_append (buffer, last_separator);
514 *item_name = g_string_free (buffer, FALSE);
520 format_compact_style (gchar **item_name,
523 gboolean multiaccount,
524 gboolean *use_markup)
528 TnyFolderType folder_type;
530 if (!TNY_IS_FOLDER (instance))
533 folder = (TnyFolder *) instance;
535 folder_type = tny_folder_get_folder_type (folder);
536 is_special = (get_cmp_pos (folder_type, folder)!= 4);
538 if (!is_special || multiaccount) {
539 TnyAccount *account = tny_folder_get_account (folder);
540 const gchar *folder_name;
541 gboolean concat_folder_name = FALSE;
544 /* Should not happen */
548 /* convert parent folders to dots */
549 convert_parent_folders_to_dots (item_name);
551 folder_name = tny_folder_get_name (folder);
552 if (g_str_has_suffix (*item_name, folder_name)) {
553 gchar *offset = g_strrstr (*item_name, folder_name);
555 concat_folder_name = TRUE;
558 buffer = g_string_new ("");
560 buffer = g_string_append (buffer, *item_name);
561 if (concat_folder_name) {
562 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
563 buffer = g_string_append (buffer, folder_name);
564 if (bold) buffer = g_string_append (buffer, "</span>");
567 g_object_unref (account);
569 *item_name = g_string_free (buffer, FALSE);
577 text_cell_data (GtkTreeViewColumn *column,
578 GtkCellRenderer *renderer,
579 GtkTreeModel *tree_model,
583 ModestFolderViewPrivate *priv;
584 GObject *rendobj = (GObject *) renderer;
586 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
587 GObject *instance = NULL;
588 gboolean use_markup = FALSE;
590 gtk_tree_model_get (tree_model, iter,
593 INSTANCE_COLUMN, &instance,
595 if (!fname || !instance)
598 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
599 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
601 gchar *item_name = NULL;
602 gint item_weight = 400;
604 if (type != TNY_FOLDER_TYPE_ROOT) {
608 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
609 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
610 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
611 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
613 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
616 /* Sometimes an special folder is reported by the server as
617 NORMAL, like some versions of Dovecot */
618 if (type == TNY_FOLDER_TYPE_NORMAL ||
619 type == TNY_FOLDER_TYPE_UNKNOWN) {
620 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
624 /* note: we cannot reliably get the counts from the
625 * tree model, we need to use explicit calls on
626 * tny_folder for some reason. Select the number to
627 * show: the unread or unsent messages. in case of
628 * outbox/drafts, show all */
629 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
630 (type == TNY_FOLDER_TYPE_OUTBOX) ||
631 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
632 number = tny_folder_get_all_count (TNY_FOLDER(instance));
635 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
639 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
640 item_name = g_strdup (fname);
647 /* Use bold font style if there are unread or unset messages */
649 item_name = g_strdup_printf ("%s (%d)", fname, number);
652 item_name = g_strdup (fname);
657 } else if (TNY_IS_ACCOUNT (instance)) {
658 /* If it's a server account */
659 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
660 item_name = g_strdup (priv->local_account_name);
662 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
663 /* fname is only correct when the items are first
664 * added to the model, not when the account is
665 * changed later, so get the name from the account
667 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
670 item_name = g_strdup (fname);
676 item_name = g_strdup ("unknown");
678 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
679 gboolean multiaccount;
681 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
682 /* Convert item_name to markup */
683 format_compact_style (&item_name, instance,
685 multiaccount, &use_markup);
688 if (item_name && item_weight) {
689 /* Set the name in the treeview cell: */
691 g_object_set (rendobj, "markup", item_name, NULL);
693 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
695 /* Notify display name observers */
696 /* TODO: What listens for this signal, and how can it use only the new name? */
697 if (((GObject *) priv->cur_folder_store) == instance) {
698 g_signal_emit (G_OBJECT(self),
699 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
706 /* If it is a Memory card account, make sure that we have the correct name.
707 * This function will be trigerred again when the name has been retrieved: */
708 if (TNY_IS_STORE_ACCOUNT (instance) &&
709 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
711 /* Get the account name asynchronously: */
712 GetMmcAccountNameData *callback_data =
713 g_slice_new0(GetMmcAccountNameData);
714 callback_data->self = self;
716 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
718 callback_data->previous_name = g_strdup (name);
720 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
721 on_get_mmc_account_name, callback_data);
725 g_object_unref (G_OBJECT (instance));
731 messages_cell_data (GtkTreeViewColumn *column,
732 GtkCellRenderer *renderer,
733 GtkTreeModel *tree_model,
737 ModestFolderView *self;
738 ModestFolderViewPrivate *priv;
739 GObject *rendobj = (GObject *) renderer;
740 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
741 GObject *instance = NULL;
742 gchar *item_name = NULL;
744 gtk_tree_model_get (tree_model, iter,
746 INSTANCE_COLUMN, &instance,
751 self = MODEST_FOLDER_VIEW (data);
752 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
755 if (type != TNY_FOLDER_TYPE_ROOT) {
759 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
760 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
761 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
763 /* Sometimes an special folder is reported by the server as
764 NORMAL, like some versions of Dovecot */
765 if (type == TNY_FOLDER_TYPE_NORMAL ||
766 type == TNY_FOLDER_TYPE_UNKNOWN) {
767 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
771 /* note: we cannot reliably get the counts from the tree model, we need
772 * to use explicit calls on tny_folder for some reason.
774 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
775 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
776 (type == TNY_FOLDER_TYPE_OUTBOX) ||
777 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
778 number = tny_folder_get_all_count (TNY_FOLDER(instance));
781 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
785 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
787 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
795 item_name = g_strdup ("");
798 /* Set the name in the treeview cell: */
799 g_object_set (rendobj,"text", item_name, NULL);
807 g_object_unref (G_OBJECT (instance));
813 GdkPixbuf *pixbuf_open;
814 GdkPixbuf *pixbuf_close;
818 static inline GdkPixbuf *
819 get_composite_pixbuf (const gchar *icon_name,
821 GdkPixbuf *base_pixbuf)
823 GdkPixbuf *emblem, *retval = NULL;
825 emblem = modest_platform_get_icon (icon_name, size);
827 retval = gdk_pixbuf_copy (base_pixbuf);
828 gdk_pixbuf_composite (emblem, retval, 0, 0,
829 MIN (gdk_pixbuf_get_width (emblem),
830 gdk_pixbuf_get_width (retval)),
831 MIN (gdk_pixbuf_get_height (emblem),
832 gdk_pixbuf_get_height (retval)),
833 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
834 g_object_unref (emblem);
839 static inline ThreePixbufs *
840 get_composite_icons (const gchar *icon_code,
842 GdkPixbuf **pixbuf_open,
843 GdkPixbuf **pixbuf_close)
845 ThreePixbufs *retval;
848 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
851 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
856 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
860 retval = g_slice_new0 (ThreePixbufs);
862 retval->pixbuf = g_object_ref (*pixbuf);
864 retval->pixbuf_open = g_object_ref (*pixbuf_open);
866 retval->pixbuf_close = g_object_ref (*pixbuf_close);
871 static inline ThreePixbufs*
872 get_folder_icons (TnyFolderType type, GObject *instance)
874 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
875 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
876 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
877 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
878 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
880 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
881 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
882 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
883 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
884 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
886 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
887 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
888 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
889 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
890 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
892 ThreePixbufs *retval = NULL;
894 /* Sometimes an special folder is reported by the server as
895 NORMAL, like some versions of Dovecot */
896 if (type == TNY_FOLDER_TYPE_NORMAL ||
897 type == TNY_FOLDER_TYPE_UNKNOWN) {
898 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
901 /* It's not enough with check the folder type. We need to
902 ensure that we're not giving a special folder icon to a
903 normal folder with the same name than a special folder */
904 if (TNY_IS_FOLDER (instance) &&
905 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
906 type = TNY_FOLDER_TYPE_NORMAL;
908 /* Remote folders should not be treated as special folders */
909 if (TNY_IS_FOLDER_STORE (instance) &&
910 !TNY_IS_ACCOUNT (instance) &&
911 type != TNY_FOLDER_TYPE_INBOX &&
912 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
913 #ifdef MODEST_TOOLKIT_HILDON2
914 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
917 &anorm_pixbuf_close);
919 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
922 &normal_pixbuf_close);
928 case TNY_FOLDER_TYPE_INVALID:
929 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
932 case TNY_FOLDER_TYPE_ROOT:
933 if (TNY_IS_ACCOUNT (instance)) {
935 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
936 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
939 &avirt_pixbuf_close);
941 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
943 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
944 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
949 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
952 &anorm_pixbuf_close);
957 case TNY_FOLDER_TYPE_INBOX:
958 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
961 &inbox_pixbuf_close);
963 case TNY_FOLDER_TYPE_OUTBOX:
964 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
967 &outbox_pixbuf_close);
969 case TNY_FOLDER_TYPE_JUNK:
970 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
975 case TNY_FOLDER_TYPE_SENT:
976 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
981 case TNY_FOLDER_TYPE_TRASH:
982 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
985 &trash_pixbuf_close);
987 case TNY_FOLDER_TYPE_DRAFTS:
988 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
991 &draft_pixbuf_close);
993 case TNY_FOLDER_TYPE_ARCHIVE:
994 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
999 case TNY_FOLDER_TYPE_NORMAL:
1001 /* Memory card folders could have an special icon */
1002 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1003 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1008 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1010 &normal_pixbuf_open,
1011 &normal_pixbuf_close);
1020 free_pixbufs (ThreePixbufs *pixbufs)
1022 if (pixbufs->pixbuf)
1023 g_object_unref (pixbufs->pixbuf);
1024 if (pixbufs->pixbuf_open)
1025 g_object_unref (pixbufs->pixbuf_open);
1026 if (pixbufs->pixbuf_close)
1027 g_object_unref (pixbufs->pixbuf_close);
1028 g_slice_free (ThreePixbufs, pixbufs);
1032 icon_cell_data (GtkTreeViewColumn *column,
1033 GtkCellRenderer *renderer,
1034 GtkTreeModel *tree_model,
1038 GObject *rendobj = NULL, *instance = NULL;
1039 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1040 gboolean has_children;
1041 ThreePixbufs *pixbufs;
1043 rendobj = (GObject *) renderer;
1045 gtk_tree_model_get (tree_model, iter,
1047 INSTANCE_COLUMN, &instance,
1053 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1054 pixbufs = get_folder_icons (type, instance);
1055 g_object_unref (instance);
1058 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1061 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1062 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1065 free_pixbufs (pixbufs);
1069 add_columns (GtkWidget *treeview)
1071 GtkTreeViewColumn *column;
1072 GtkCellRenderer *renderer;
1073 GtkTreeSelection *sel;
1074 ModestFolderViewPrivate *priv;
1076 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1079 column = gtk_tree_view_column_new ();
1081 /* Set icon and text render function */
1082 renderer = gtk_cell_renderer_pixbuf_new();
1083 #ifdef MODEST_TOOLKIT_HILDON2
1084 g_object_set (renderer,
1085 "xpad", MODEST_MARGIN_DEFAULT,
1086 "ypad", MODEST_MARGIN_DEFAULT,
1089 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1090 gtk_tree_view_column_set_cell_data_func(column, renderer,
1091 icon_cell_data, treeview, NULL);
1093 renderer = gtk_cell_renderer_text_new();
1094 g_object_set (renderer,
1095 #ifdef MODEST_TOOLKIT_HILDON2
1096 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1097 "ypad", MODEST_MARGIN_DEFAULT,
1099 "ellipsize", PANGO_ELLIPSIZE_END,
1101 "ellipsize-set", TRUE, NULL);
1102 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1103 gtk_tree_view_column_set_cell_data_func(column, renderer,
1104 text_cell_data, treeview, NULL);
1106 priv->messages_renderer = gtk_cell_renderer_text_new ();
1107 g_object_set (priv->messages_renderer,
1108 #ifdef MODEST_TOOLKIT_HILDON2
1110 "ypad", MODEST_MARGIN_DEFAULT,
1111 "xpad", MODEST_MARGIN_DOUBLE,
1113 "scale", PANGO_SCALE_X_SMALL,
1116 "alignment", PANGO_ALIGN_RIGHT,
1120 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1121 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1122 messages_cell_data, treeview, NULL);
1124 /* Set selection mode */
1125 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1126 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1128 /* Set treeview appearance */
1129 gtk_tree_view_column_set_spacing (column, 2);
1130 gtk_tree_view_column_set_resizable (column, TRUE);
1131 gtk_tree_view_column_set_fixed_width (column, TRUE);
1132 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1133 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1136 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1140 modest_folder_view_init (ModestFolderView *obj)
1142 ModestFolderViewPrivate *priv;
1145 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1147 priv->timer_expander = 0;
1148 priv->account_store = NULL;
1150 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1151 priv->cur_folder_store = NULL;
1152 priv->visible_account_id = NULL;
1153 priv->mailbox = NULL;
1154 priv->folder_to_select = NULL;
1155 priv->outbox_deleted_handler = 0;
1156 priv->reexpand = TRUE;
1158 /* Initialize the local account name */
1159 conf = modest_runtime_get_conf();
1160 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1162 /* Init email clipboard */
1163 priv->clipboard = modest_runtime_get_email_clipboard ();
1164 priv->hidding_ids = NULL;
1165 priv->n_selected = 0;
1166 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1167 priv->reselect = FALSE;
1168 priv->show_non_move = TRUE;
1169 priv->list_to_move = NULL;
1171 /* Build treeview */
1172 add_columns (GTK_WIDGET (obj));
1174 /* Setup drag and drop */
1175 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1177 /* Connect signals */
1178 g_signal_connect (G_OBJECT (obj),
1180 G_CALLBACK (on_key_pressed), NULL);
1182 priv->display_name_changed_signal =
1183 g_signal_connect (modest_runtime_get_account_mgr (),
1184 "display_name_changed",
1185 G_CALLBACK (on_display_name_changed),
1189 * Track changes in the local account name (in the device it
1190 * will be the device name)
1192 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1194 G_CALLBACK(on_configuration_key_changed),
1198 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1204 tny_account_store_view_init (gpointer g, gpointer iface_data)
1206 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1208 klass->set_account_store = modest_folder_view_set_account_store;
1212 modest_folder_view_finalize (GObject *obj)
1214 ModestFolderViewPrivate *priv;
1215 GtkTreeSelection *sel;
1216 TnyAccount *local_account;
1218 g_return_if_fail (obj);
1220 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1222 if (priv->timer_expander != 0) {
1223 g_source_remove (priv->timer_expander);
1224 priv->timer_expander = 0;
1227 local_account = (TnyAccount *)
1228 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1229 if (local_account) {
1230 if (g_signal_handler_is_connected (local_account,
1231 priv->outbox_deleted_handler))
1232 g_signal_handler_disconnect (local_account,
1233 priv->outbox_deleted_handler);
1234 g_object_unref (local_account);
1237 if (priv->account_store) {
1238 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1239 priv->account_inserted_signal);
1240 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1241 priv->account_removed_signal);
1242 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1243 priv->account_changed_signal);
1244 g_object_unref (G_OBJECT(priv->account_store));
1245 priv->account_store = NULL;
1248 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1249 priv->display_name_changed_signal)) {
1250 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1251 priv->display_name_changed_signal);
1252 priv->display_name_changed_signal = 0;
1256 g_object_unref (G_OBJECT (priv->query));
1260 if (priv->folder_to_select) {
1261 g_object_unref (G_OBJECT(priv->folder_to_select));
1262 priv->folder_to_select = NULL;
1265 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1267 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1269 g_free (priv->local_account_name);
1270 g_free (priv->visible_account_id);
1271 g_free (priv->mailbox);
1273 if (priv->conf_key_signal) {
1274 g_signal_handler_disconnect (modest_runtime_get_conf (),
1275 priv->conf_key_signal);
1276 priv->conf_key_signal = 0;
1279 if (priv->cur_folder_store) {
1280 g_object_unref (priv->cur_folder_store);
1281 priv->cur_folder_store = NULL;
1284 if (priv->list_to_move) {
1285 g_object_unref (priv->list_to_move);
1286 priv->list_to_move = NULL;
1289 /* Clear hidding array created by cut operation */
1290 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1292 G_OBJECT_CLASS(parent_class)->finalize (obj);
1297 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1299 ModestFolderViewPrivate *priv;
1302 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1303 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1305 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1306 device = tny_account_store_get_device (account_store);
1308 if (G_UNLIKELY (priv->account_store)) {
1310 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1311 priv->account_inserted_signal))
1312 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1313 priv->account_inserted_signal);
1314 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1315 priv->account_removed_signal))
1316 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1317 priv->account_removed_signal);
1318 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1319 priv->account_changed_signal))
1320 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1321 priv->account_changed_signal);
1322 g_object_unref (G_OBJECT (priv->account_store));
1325 priv->account_store = g_object_ref (G_OBJECT (account_store));
1327 priv->account_removed_signal =
1328 g_signal_connect (G_OBJECT(account_store), "account_removed",
1329 G_CALLBACK (on_account_removed), self);
1331 priv->account_inserted_signal =
1332 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1333 G_CALLBACK (on_account_inserted), self);
1335 priv->account_changed_signal =
1336 g_signal_connect (G_OBJECT(account_store), "account_changed",
1337 G_CALLBACK (on_account_changed), self);
1339 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1340 priv->reselect = FALSE;
1341 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1343 g_object_unref (G_OBJECT (device));
1347 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1350 ModestFolderView *self;
1351 GtkTreeModel *model, *filter_model;
1354 self = MODEST_FOLDER_VIEW (user_data);
1356 if (!get_inner_models (self, &filter_model, NULL, &model))
1359 /* Remove outbox from model */
1360 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1361 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1362 g_object_unref (outbox);
1365 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1369 on_account_inserted (TnyAccountStore *account_store,
1370 TnyAccount *account,
1373 ModestFolderViewPrivate *priv;
1374 GtkTreeModel *model, *filter_model;
1376 /* Ignore transport account insertions, we're not showing them
1377 in the folder view */
1378 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1381 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1384 /* If we're adding a new account, and there is no previous
1385 one, we need to select the visible server account */
1386 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1387 !priv->visible_account_id)
1388 modest_widget_memory_restore (modest_runtime_get_conf(),
1389 G_OBJECT (user_data),
1390 MODEST_CONF_FOLDER_VIEW_KEY);
1394 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1395 &filter_model, NULL, &model))
1398 /* Insert the account in the model */
1399 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1401 /* When the model is a list store (plain representation) the
1402 outbox is not a child of any account so we have to manually
1403 delete it because removing the local folders account won't
1404 delete it (because tny_folder_get_account() is not defined
1405 for a merge folder */
1406 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1407 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1409 priv->outbox_deleted_handler =
1410 g_signal_connect (account,
1412 G_CALLBACK (on_outbox_deleted_cb),
1416 /* Refilter the model */
1417 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1422 same_account_selected (ModestFolderView *self,
1423 TnyAccount *account)
1425 ModestFolderViewPrivate *priv;
1426 gboolean same_account = FALSE;
1428 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1430 if (priv->cur_folder_store) {
1431 TnyAccount *selected_folder_account = NULL;
1433 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1434 selected_folder_account =
1435 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1437 selected_folder_account =
1438 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1441 if (selected_folder_account == account)
1442 same_account = TRUE;
1444 g_object_unref (selected_folder_account);
1446 return same_account;
1451 * Selects the first inbox or the local account in an idle
1454 on_idle_select_first_inbox_or_local (gpointer user_data)
1456 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1458 gdk_threads_enter ();
1459 modest_folder_view_select_first_inbox_or_local (self);
1460 gdk_threads_leave ();
1466 on_account_changed (TnyAccountStore *account_store,
1467 TnyAccount *tny_account,
1470 ModestFolderView *self;
1471 ModestFolderViewPrivate *priv;
1472 GtkTreeModel *model, *filter_model;
1473 GtkTreeSelection *sel;
1474 gboolean same_account;
1476 /* Ignore transport account insertions, we're not showing them
1477 in the folder view */
1478 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1481 self = MODEST_FOLDER_VIEW (user_data);
1482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1484 /* Get the inner model */
1485 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1486 &filter_model, NULL, &model))
1489 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1491 /* Invalidate the cur_folder_store only if the selected folder
1492 belongs to the account that is being removed */
1493 same_account = same_account_selected (self, tny_account);
1495 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1496 gtk_tree_selection_unselect_all (sel);
1499 /* Remove the account from the model */
1500 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1502 /* Insert the account in the model */
1503 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1505 /* Refilter the model */
1506 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1508 /* Select the first INBOX if the currently selected folder
1509 belongs to the account that is being deleted */
1510 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1511 g_idle_add (on_idle_select_first_inbox_or_local, self);
1515 on_account_removed (TnyAccountStore *account_store,
1516 TnyAccount *account,
1519 ModestFolderView *self = NULL;
1520 ModestFolderViewPrivate *priv;
1521 GtkTreeModel *model, *filter_model;
1522 GtkTreeSelection *sel = NULL;
1523 gboolean same_account = FALSE;
1525 /* Ignore transport account removals, we're not showing them
1526 in the folder view */
1527 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1530 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1531 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1535 self = MODEST_FOLDER_VIEW (user_data);
1536 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1538 /* Invalidate the cur_folder_store only if the selected folder
1539 belongs to the account that is being removed */
1540 same_account = same_account_selected (self, account);
1542 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1543 gtk_tree_selection_unselect_all (sel);
1546 /* Invalidate row to select only if the folder to select
1547 belongs to the account that is being removed*/
1548 if (priv->folder_to_select) {
1549 TnyAccount *folder_to_select_account = NULL;
1551 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1552 if (folder_to_select_account == account) {
1553 modest_folder_view_disable_next_folder_selection (self);
1554 g_object_unref (priv->folder_to_select);
1555 priv->folder_to_select = NULL;
1557 g_object_unref (folder_to_select_account);
1560 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1561 &filter_model, NULL, &model))
1564 /* Disconnect the signal handler */
1565 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1566 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1567 if (g_signal_handler_is_connected (account,
1568 priv->outbox_deleted_handler))
1569 g_signal_handler_disconnect (account,
1570 priv->outbox_deleted_handler);
1573 /* Remove the account from the model */
1574 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1576 /* If the removed account is the currently viewed one then
1577 clear the configuration value. The new visible account will be the default account */
1578 if (priv->visible_account_id &&
1579 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1581 /* Clear the current visible account_id */
1582 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1583 modest_folder_view_set_mailbox (self, NULL);
1585 /* Call the restore method, this will set the new visible account */
1586 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1587 MODEST_CONF_FOLDER_VIEW_KEY);
1590 /* Refilter the model */
1591 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1593 /* Select the first INBOX if the currently selected folder
1594 belongs to the account that is being deleted */
1596 g_idle_add (on_idle_select_first_inbox_or_local, self);
1600 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1602 GtkTreeViewColumn *col;
1604 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1606 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1608 g_printerr ("modest: failed get column for title\n");
1612 gtk_tree_view_column_set_title (col, title);
1613 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1618 modest_folder_view_on_map (ModestFolderView *self,
1619 GdkEventExpose *event,
1622 ModestFolderViewPrivate *priv;
1624 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1626 /* This won't happen often */
1627 if (G_UNLIKELY (priv->reselect)) {
1628 /* Select the first inbox or the local account if not found */
1630 /* TODO: this could cause a lock at startup, so we
1631 comment it for the moment. We know that this will
1632 be a bug, because the INBOX is not selected, but we
1633 need to rewrite some parts of Modest to avoid the
1634 deathlock situation */
1635 /* TODO: check if this is still the case */
1636 priv->reselect = FALSE;
1637 modest_folder_view_select_first_inbox_or_local (self);
1638 /* Notify the display name observers */
1639 g_signal_emit (G_OBJECT(self),
1640 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1644 if (priv->reexpand) {
1645 expand_root_items (self);
1646 priv->reexpand = FALSE;
1653 modest_folder_view_new (TnyFolderStoreQuery *query)
1656 ModestFolderViewPrivate *priv;
1657 GtkTreeSelection *sel;
1659 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1660 #ifdef MODEST_TOOLKIT_HILDON2
1661 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1664 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1667 priv->query = g_object_ref (query);
1669 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1670 priv->changed_signal = g_signal_connect (sel, "changed",
1671 G_CALLBACK (on_selection_changed), self);
1673 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1675 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1677 return GTK_WIDGET(self);
1680 /* this feels dirty; any other way to expand all the root items? */
1682 expand_root_items (ModestFolderView *self)
1685 GtkTreeModel *model;
1688 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1689 path = gtk_tree_path_new_first ();
1691 /* all folders should have child items, so.. */
1693 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1694 gtk_tree_path_next (path);
1695 } while (gtk_tree_model_get_iter (model, &iter, path));
1697 gtk_tree_path_free (path);
1701 is_parent_of (TnyFolder *a, TnyFolder *b)
1704 gboolean retval = FALSE;
1706 a_id = tny_folder_get_id (a);
1708 gchar *string_to_match;
1711 string_to_match = g_strconcat (a_id, "/", NULL);
1712 b_id = tny_folder_get_id (b);
1713 retval = g_str_has_prefix (b_id, string_to_match);
1714 g_free (string_to_match);
1720 typedef struct _ForeachFolderInfo {
1723 } ForeachFolderInfo;
1726 foreach_folder_with_id (GtkTreeModel *model,
1731 ForeachFolderInfo *info;
1734 info = (ForeachFolderInfo *) data;
1735 gtk_tree_model_get (model, iter,
1736 INSTANCE_COLUMN, &instance,
1739 if (TNY_IS_FOLDER (instance)) {
1742 id = tny_folder_get_id (TNY_FOLDER (instance));
1744 collate = g_utf8_collate_key (id, -1);
1745 info->found = !strcmp (info->needle, collate);
1756 has_folder_with_id (ModestFolderView *self, const gchar *id)
1758 GtkTreeModel *model;
1759 ForeachFolderInfo info = {NULL, FALSE};
1761 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1762 info.needle = g_utf8_collate_key (id, -1);
1764 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1765 g_free (info.needle);
1771 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1774 gboolean retval = FALSE;
1776 a_id = tny_folder_get_id (a);
1779 b_id = tny_folder_get_id (b);
1782 const gchar *last_bar;
1783 gchar *string_to_match;
1784 last_bar = g_strrstr (b_id, "/");
1789 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1790 retval = has_folder_with_id (self, string_to_match);
1791 g_free (string_to_match);
1799 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1801 ModestFolderViewPrivate *priv;
1802 TnyIterator *iterator;
1803 gboolean retval = TRUE;
1805 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1806 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1808 for (iterator = tny_list_create_iterator (priv->list_to_move);
1809 retval && !tny_iterator_is_done (iterator);
1810 tny_iterator_next (iterator)) {
1812 instance = tny_iterator_get_current (iterator);
1813 if (instance == (GObject *) folder) {
1815 } else if (TNY_IS_FOLDER (instance)) {
1816 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1818 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1821 g_object_unref (instance);
1823 g_object_unref (iterator);
1830 * We use this function to implement the
1831 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1832 * account in this case, and the local folders.
1835 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1837 ModestFolderViewPrivate *priv;
1838 gboolean retval = TRUE;
1839 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1840 GObject *instance = NULL;
1841 const gchar *id = NULL;
1843 gboolean found = FALSE;
1844 gboolean cleared = FALSE;
1845 ModestTnyFolderRules rules = 0;
1848 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1849 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1851 gtk_tree_model_get (model, iter,
1852 NAME_COLUMN, &fname,
1854 INSTANCE_COLUMN, &instance,
1857 /* Do not show if there is no instance, this could indeed
1858 happen when the model is being modified while it's being
1859 drawn. This could occur for example when moving folders
1866 if (TNY_IS_ACCOUNT (instance)) {
1867 TnyAccount *acc = TNY_ACCOUNT (instance);
1868 const gchar *account_id = tny_account_get_id (acc);
1870 /* If it isn't a special folder,
1871 * don't show it unless it is the visible account: */
1872 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1873 !modest_tny_account_is_virtual_local_folders (acc) &&
1874 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1876 /* Show only the visible account id */
1877 if (priv->visible_account_id) {
1878 if (strcmp (account_id, priv->visible_account_id))
1885 /* Never show these to the user. They are merged into one folder
1886 * in the local-folders account instead: */
1887 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1890 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1891 /* Only show special folders for current account if needed */
1892 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1893 TnyAccount *account;
1895 account = tny_folder_get_account (TNY_FOLDER (instance));
1897 if (TNY_IS_ACCOUNT (account)) {
1898 const gchar *account_id = tny_account_get_id (account);
1900 if (!modest_tny_account_is_virtual_local_folders (account) &&
1901 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1902 /* Show only the visible account id */
1903 if (priv->visible_account_id) {
1904 if (strcmp (account_id, priv->visible_account_id)) {
1906 } else if (priv->mailbox) {
1907 if (!g_str_has_prefix (fname, priv->mailbox)) {
1913 g_object_unref (account);
1920 /* Check hiding (if necessary) */
1921 cleared = modest_email_clipboard_cleared (priv->clipboard);
1922 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1923 id = tny_folder_get_id (TNY_FOLDER(instance));
1924 if (priv->hidding_ids != NULL)
1925 for (i=0; i < priv->n_selected && !found; i++)
1926 if (priv->hidding_ids[i] != NULL && id != NULL)
1927 found = (!strcmp (priv->hidding_ids[i], id));
1932 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1933 folder as no message can be move there according to UI specs */
1934 if (retval && !priv->show_non_move) {
1935 if (priv->list_to_move &&
1936 tny_list_get_length (priv->list_to_move) > 0 &&
1937 TNY_IS_FOLDER (instance)) {
1938 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1940 if (retval && TNY_IS_FOLDER (instance) &&
1941 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1943 case TNY_FOLDER_TYPE_OUTBOX:
1944 case TNY_FOLDER_TYPE_SENT:
1945 case TNY_FOLDER_TYPE_DRAFTS:
1948 case TNY_FOLDER_TYPE_UNKNOWN:
1949 case TNY_FOLDER_TYPE_NORMAL:
1950 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1951 if (type == TNY_FOLDER_TYPE_INVALID)
1952 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1954 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1955 type == TNY_FOLDER_TYPE_SENT
1956 || type == TNY_FOLDER_TYPE_DRAFTS)
1965 /* apply special filters */
1966 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1967 if (TNY_IS_ACCOUNT (instance))
1971 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
1972 if (TNY_IS_FOLDER (instance))
1976 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
1977 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1982 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
1983 /* A mailbox is a fake folder with an @ in the middle of the name */
1984 if (!TNY_IS_FOLDER (instance) ||
1985 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
1988 const gchar *folder_name;
1989 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
1990 if (!folder_name || strchr (folder_name, '@') == NULL)
1996 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1997 if (TNY_IS_FOLDER (instance)) {
1998 /* Check folder rules */
1999 ModestTnyFolderRules rules;
2001 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2002 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2003 } else if (TNY_IS_ACCOUNT (instance)) {
2004 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2012 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2013 if (TNY_IS_FOLDER (instance)) {
2014 TnyFolderType guess_type;
2016 if (TNY_FOLDER_TYPE_NORMAL) {
2017 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2023 case TNY_FOLDER_TYPE_OUTBOX:
2024 case TNY_FOLDER_TYPE_SENT:
2025 case TNY_FOLDER_TYPE_DRAFTS:
2026 case TNY_FOLDER_TYPE_ARCHIVE:
2027 case TNY_FOLDER_TYPE_INBOX:
2030 case TNY_FOLDER_TYPE_UNKNOWN:
2031 case TNY_FOLDER_TYPE_NORMAL:
2037 } else if (TNY_IS_ACCOUNT (instance)) {
2042 if (retval && TNY_IS_FOLDER (instance)) {
2043 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2046 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2047 if (TNY_IS_FOLDER (instance)) {
2048 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2049 } else if (TNY_IS_ACCOUNT (instance)) {
2054 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2055 if (TNY_IS_FOLDER (instance)) {
2056 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2057 } else if (TNY_IS_ACCOUNT (instance)) {
2062 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2063 if (TNY_IS_FOLDER (instance)) {
2064 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2065 } else if (TNY_IS_ACCOUNT (instance)) {
2071 g_object_unref (instance);
2079 modest_folder_view_update_model (ModestFolderView *self,
2080 TnyAccountStore *account_store)
2082 ModestFolderViewPrivate *priv;
2083 GtkTreeModel *model /* , *old_model */;
2084 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2086 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2087 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2090 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2092 /* Notify that there is no folder selected */
2093 g_signal_emit (G_OBJECT(self),
2094 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2096 if (priv->cur_folder_store) {
2097 g_object_unref (priv->cur_folder_store);
2098 priv->cur_folder_store = NULL;
2101 /* FIXME: the local accounts are not shown when the query
2102 selects only the subscribed folders */
2103 #ifdef MODEST_TOOLKIT_HILDON2
2104 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2105 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2106 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2107 MODEST_FOLDER_PATH_SEPARATOR);
2109 model = tny_gtk_folder_store_tree_model_new (NULL);
2112 /* When the model is a list store (plain representation) the
2113 outbox is not a child of any account so we have to manually
2114 delete it because removing the local folders account won't
2115 delete it (because tny_folder_get_account() is not defined
2116 for a merge folder */
2117 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2118 TnyAccount *account;
2119 ModestTnyAccountStore *acc_store;
2121 acc_store = modest_runtime_get_account_store ();
2122 account = modest_tny_account_store_get_local_folders_account (acc_store);
2124 if (g_signal_handler_is_connected (account,
2125 priv->outbox_deleted_handler))
2126 g_signal_handler_disconnect (account,
2127 priv->outbox_deleted_handler);
2129 priv->outbox_deleted_handler =
2130 g_signal_connect (account,
2132 G_CALLBACK (on_outbox_deleted_cb),
2134 g_object_unref (account);
2137 /* Get the accounts: */
2138 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2140 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2142 sortable = gtk_tree_model_sort_new_with_model (model);
2143 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2145 GTK_SORT_ASCENDING);
2146 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2148 cmp_rows, NULL, NULL);
2150 /* Create filter model */
2151 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2152 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2158 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2159 #ifndef MODEST_TOOLKIT_HILDON2
2160 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2161 (GCallback) on_row_inserted_maybe_select_folder, self);
2164 g_object_unref (model);
2165 g_object_unref (filter_model);
2166 g_object_unref (sortable);
2168 /* Force a reselection of the INBOX next time the widget is shown */
2169 priv->reselect = TRUE;
2176 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2178 GtkTreeModel *model = NULL;
2179 TnyFolderStore *folder = NULL;
2181 ModestFolderView *tree_view = NULL;
2182 ModestFolderViewPrivate *priv = NULL;
2183 gboolean selected = FALSE;
2185 g_return_if_fail (sel);
2186 g_return_if_fail (user_data);
2188 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2190 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2192 tree_view = MODEST_FOLDER_VIEW (user_data);
2195 gtk_tree_model_get (model, &iter,
2196 INSTANCE_COLUMN, &folder,
2199 /* If the folder is the same do not notify */
2200 if (folder && priv->cur_folder_store == folder) {
2201 g_object_unref (folder);
2206 /* Current folder was unselected */
2207 if (priv->cur_folder_store) {
2208 /* We must do this firstly because a libtinymail-camel
2209 implementation detail. If we issue the signal
2210 before doing the sync_async, then that signal could
2211 cause (and it actually does it) a free of the
2212 summary of the folder (because the main window will
2213 clear the headers view */
2214 if (TNY_IS_FOLDER(priv->cur_folder_store))
2215 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2216 FALSE, NULL, NULL, NULL);
2218 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2219 priv->cur_folder_store, FALSE);
2221 g_object_unref (priv->cur_folder_store);
2222 priv->cur_folder_store = NULL;
2225 /* New current references */
2226 priv->cur_folder_store = folder;
2228 /* New folder has been selected. Do not notify if there is
2229 nothing new selected */
2231 g_signal_emit (G_OBJECT(tree_view),
2232 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2233 0, priv->cur_folder_store, TRUE);
2238 on_row_activated (GtkTreeView *treeview,
2239 GtkTreePath *treepath,
2240 GtkTreeViewColumn *column,
2243 GtkTreeModel *model = NULL;
2244 TnyFolderStore *folder = NULL;
2246 ModestFolderView *self = NULL;
2247 ModestFolderViewPrivate *priv = NULL;
2249 g_return_if_fail (treeview);
2250 g_return_if_fail (user_data);
2252 self = MODEST_FOLDER_VIEW (user_data);
2253 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2255 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2257 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2260 gtk_tree_model_get (model, &iter,
2261 INSTANCE_COLUMN, &folder,
2264 g_signal_emit (G_OBJECT(self),
2265 signals[FOLDER_ACTIVATED_SIGNAL],
2268 #ifdef MODEST_TOOLKIT_HILDON2
2269 HildonUIMode ui_mode;
2270 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2271 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2272 if (priv->cur_folder_store)
2273 g_object_unref (priv->cur_folder_store);
2274 priv->cur_folder_store = g_object_ref (folder);
2278 g_object_unref (folder);
2282 modest_folder_view_get_selected (ModestFolderView *self)
2284 ModestFolderViewPrivate *priv;
2286 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2288 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2289 if (priv->cur_folder_store)
2290 g_object_ref (priv->cur_folder_store);
2292 return priv->cur_folder_store;
2296 get_cmp_rows_type_pos (GObject *folder)
2298 /* Remote accounts -> Local account -> MMC account .*/
2301 if (TNY_IS_ACCOUNT (folder) &&
2302 modest_tny_account_is_virtual_local_folders (
2303 TNY_ACCOUNT (folder))) {
2305 } else if (TNY_IS_ACCOUNT (folder)) {
2306 TnyAccount *account = TNY_ACCOUNT (folder);
2307 const gchar *account_id = tny_account_get_id (account);
2308 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2314 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2315 return -1; /* Should never happen */
2320 inbox_is_special (TnyFolderStore *folder_store)
2322 gboolean is_special = TRUE;
2324 if (TNY_IS_FOLDER (folder_store)) {
2328 gchar *last_inbox_bar;
2330 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2331 downcase = g_utf8_strdown (id, -1);
2332 last_bar = g_strrstr (downcase, "/");
2334 last_inbox_bar = g_strrstr (downcase, "inbox/");
2335 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2346 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2348 TnyAccount *account;
2349 gboolean is_special;
2350 /* Inbox, Outbox, Drafts, Sent, User */
2353 if (!TNY_IS_FOLDER (folder_store))
2356 case TNY_FOLDER_TYPE_INBOX:
2358 account = tny_folder_get_account (folder_store);
2359 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2361 /* In inbox case we need to know if the inbox is really the top
2362 * inbox of the account, or if it's a submailbox inbox. To do
2363 * this we'll apply an heuristic rule: Find last "/" and check
2364 * if it's preceeded by another Inbox */
2365 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2366 g_object_unref (account);
2367 return is_special?0:4;
2370 case TNY_FOLDER_TYPE_OUTBOX:
2371 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2373 case TNY_FOLDER_TYPE_DRAFTS:
2375 account = tny_folder_get_account (folder_store);
2376 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2377 g_object_unref (account);
2378 return is_special?1:4;
2381 case TNY_FOLDER_TYPE_SENT:
2383 account = tny_folder_get_account (folder_store);
2384 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2385 g_object_unref (account);
2386 return is_special?3:4;
2395 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2397 const gchar *a1_name, *a2_name;
2399 a1_name = tny_account_get_name (a1);
2400 a2_name = tny_account_get_name (a2);
2402 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2406 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2408 TnyAccount *a1 = NULL, *a2 = NULL;
2411 if (TNY_IS_ACCOUNT (s1)) {
2412 a1 = TNY_ACCOUNT (g_object_ref (s1));
2413 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2414 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2417 if (TNY_IS_ACCOUNT (s2)) {
2418 a2 = TNY_ACCOUNT (g_object_ref (s2));
2419 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2420 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2437 /* First we sort with the type of account */
2438 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2442 cmp = compare_account_names (a1, a2);
2446 g_object_unref (a1);
2448 g_object_unref (a2);
2454 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2456 gint is_account1, is_account2;
2458 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2459 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2461 return is_account2 - is_account1;
2465 * This function orders the mail accounts according to these rules:
2466 * 1st - remote accounts
2467 * 2nd - local account
2471 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2475 gchar *name1 = NULL;
2476 gchar *name2 = NULL;
2477 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2478 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2479 GObject *folder1 = NULL;
2480 GObject *folder2 = NULL;
2482 gtk_tree_model_get (tree_model, iter1,
2483 NAME_COLUMN, &name1,
2485 INSTANCE_COLUMN, &folder1,
2487 gtk_tree_model_get (tree_model, iter2,
2488 NAME_COLUMN, &name2,
2489 TYPE_COLUMN, &type2,
2490 INSTANCE_COLUMN, &folder2,
2493 /* Return if we get no folder. This could happen when folder
2494 operations are happening. The model is updated after the
2495 folder copy/move actually occurs, so there could be
2496 situations where the model to be drawn is not correct */
2497 if (!folder1 || !folder2)
2500 /* Sort by type. First the special folders, then the archives */
2501 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2505 /* Now we sort using the account of each folder */
2506 if (TNY_IS_FOLDER_STORE (folder1) &&
2507 TNY_IS_FOLDER_STORE (folder2)) {
2508 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2512 /* Each group is preceeded by its account */
2513 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2518 /* Pure sort by name */
2519 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2522 g_object_unref(G_OBJECT(folder1));
2524 g_object_unref(G_OBJECT(folder2));
2532 /*****************************************************************************/
2533 /* DRAG and DROP stuff */
2534 /*****************************************************************************/
2536 * This function fills the #GtkSelectionData with the row and the
2537 * model that has been dragged. It's called when this widget is a
2538 * source for dnd after the event drop happened
2541 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2542 guint info, guint time, gpointer data)
2544 GtkTreeSelection *selection;
2545 GtkTreeModel *model;
2547 GtkTreePath *source_row;
2549 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2550 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2552 source_row = gtk_tree_model_get_path (model, &iter);
2553 gtk_tree_set_row_drag_data (selection_data,
2557 gtk_tree_path_free (source_row);
2561 typedef struct _DndHelper {
2562 ModestFolderView *folder_view;
2563 gboolean delete_source;
2564 GtkTreePath *source_row;
2568 dnd_helper_destroyer (DndHelper *helper)
2570 /* Free the helper */
2571 gtk_tree_path_free (helper->source_row);
2572 g_slice_free (DndHelper, helper);
2576 xfer_folder_cb (ModestMailOperation *mail_op,
2577 TnyFolder *new_folder,
2581 /* Select the folder */
2582 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2588 /* get the folder for the row the treepath refers to. */
2589 /* folder must be unref'd */
2590 static TnyFolderStore *
2591 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2594 TnyFolderStore *folder = NULL;
2596 if (gtk_tree_model_get_iter (model,&iter, path))
2597 gtk_tree_model_get (model, &iter,
2598 INSTANCE_COLUMN, &folder,
2605 * This function is used by drag_data_received_cb to manage drag and
2606 * drop of a header, i.e, and drag from the header view to the folder
2610 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2611 GtkTreeModel *dest_model,
2612 GtkTreePath *dest_row,
2613 GtkSelectionData *selection_data)
2615 TnyList *headers = NULL;
2616 TnyFolder *folder = NULL, *src_folder = NULL;
2617 TnyFolderType folder_type;
2618 GtkTreeIter source_iter, dest_iter;
2619 ModestWindowMgr *mgr = NULL;
2620 ModestWindow *main_win = NULL;
2621 gchar **uris, **tmp;
2623 /* Build the list of headers */
2624 mgr = modest_runtime_get_window_mgr ();
2625 headers = tny_simple_list_new ();
2626 uris = modest_dnd_selection_data_get_paths (selection_data);
2629 while (*tmp != NULL) {
2632 gboolean first = TRUE;
2635 path = gtk_tree_path_new_from_string (*tmp);
2636 gtk_tree_model_get_iter (source_model, &source_iter, path);
2637 gtk_tree_model_get (source_model, &source_iter,
2638 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2641 /* Do not enable d&d of headers already opened */
2642 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2643 tny_list_append (headers, G_OBJECT (header));
2645 if (G_UNLIKELY (first)) {
2646 src_folder = tny_header_get_folder (header);
2650 /* Free and go on */
2651 gtk_tree_path_free (path);
2652 g_object_unref (header);
2657 /* This could happen ig we perform a d&d very quickly over the
2658 same row that row could dissapear because message is
2660 if (!TNY_IS_FOLDER (src_folder))
2663 /* Get the target folder */
2664 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2665 gtk_tree_model_get (dest_model, &dest_iter,
2669 if (!folder || !TNY_IS_FOLDER(folder)) {
2670 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2674 folder_type = modest_tny_folder_guess_folder_type (folder);
2675 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2676 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2677 goto cleanup; /* cannot move messages there */
2680 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2681 /* g_warning ("folder not writable"); */
2682 goto cleanup; /* verboten! */
2685 /* Ask for confirmation to move */
2686 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2688 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2692 /* Transfer messages */
2693 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2698 if (G_IS_OBJECT (src_folder))
2699 g_object_unref (src_folder);
2700 if (G_IS_OBJECT(folder))
2701 g_object_unref (G_OBJECT (folder));
2702 if (G_IS_OBJECT(headers))
2703 g_object_unref (headers);
2707 TnyFolderStore *src_folder;
2708 TnyFolderStore *dst_folder;
2709 ModestFolderView *folder_view;
2714 dnd_folder_info_destroyer (DndFolderInfo *info)
2716 if (info->src_folder)
2717 g_object_unref (info->src_folder);
2718 if (info->dst_folder)
2719 g_object_unref (info->dst_folder);
2720 g_slice_free (DndFolderInfo, info);
2724 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2725 GtkWindow *parent_window,
2726 TnyAccount *account)
2729 modest_ui_actions_on_account_connection_error (parent_window, account);
2731 /* Free the helper & info */
2732 dnd_helper_destroyer (info->helper);
2733 dnd_folder_info_destroyer (info);
2737 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2739 GtkWindow *parent_window,
2740 TnyAccount *account,
2743 DndFolderInfo *info = NULL;
2744 ModestMailOperation *mail_op;
2746 info = (DndFolderInfo *) user_data;
2748 if (err || canceled) {
2749 dnd_on_connection_failed_destroyer (info, parent_window, account);
2753 /* Do the mail operation */
2754 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2755 modest_ui_actions_move_folder_error_handler,
2756 info->src_folder, NULL);
2758 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2761 /* Transfer the folder */
2762 modest_mail_operation_xfer_folder (mail_op,
2763 TNY_FOLDER (info->src_folder),
2765 info->helper->delete_source,
2767 info->helper->folder_view);
2770 g_object_unref (G_OBJECT (mail_op));
2771 dnd_helper_destroyer (info->helper);
2772 dnd_folder_info_destroyer (info);
2777 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2779 GtkWindow *parent_window,
2780 TnyAccount *account,
2783 DndFolderInfo *info = NULL;
2785 info = (DndFolderInfo *) user_data;
2787 if (err || canceled) {
2788 dnd_on_connection_failed_destroyer (info, parent_window, account);
2792 /* Connect to source folder and perform the copy/move */
2793 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2795 drag_and_drop_from_folder_view_src_folder_performer,
2800 * This function is used by drag_data_received_cb to manage drag and
2801 * drop of a folder, i.e, and drag from the folder view to the same
2805 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2806 GtkTreeModel *dest_model,
2807 GtkTreePath *dest_row,
2808 GtkSelectionData *selection_data,
2811 GtkTreeIter dest_iter, iter;
2812 TnyFolderStore *dest_folder = NULL;
2813 TnyFolderStore *folder = NULL;
2814 gboolean forbidden = FALSE;
2816 DndFolderInfo *info = NULL;
2818 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2820 g_warning ("%s: BUG: no main window", __FUNCTION__);
2821 dnd_helper_destroyer (helper);
2826 /* check the folder rules for the destination */
2827 folder = tree_path_to_folder (dest_model, dest_row);
2828 if (TNY_IS_FOLDER(folder)) {
2829 ModestTnyFolderRules rules =
2830 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2831 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2832 } else if (TNY_IS_FOLDER_STORE(folder)) {
2833 /* enable local root as destination for folders */
2834 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2835 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2838 g_object_unref (folder);
2841 /* check the folder rules for the source */
2842 folder = tree_path_to_folder (source_model, helper->source_row);
2843 if (TNY_IS_FOLDER(folder)) {
2844 ModestTnyFolderRules rules =
2845 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2846 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2849 g_object_unref (folder);
2853 /* Check if the drag is possible */
2854 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2856 modest_platform_run_information_dialog ((GtkWindow *) win,
2857 _("mail_in_ui_folder_move_target_error"),
2859 /* Restore the previous selection */
2860 folder = tree_path_to_folder (source_model, helper->source_row);
2862 if (TNY_IS_FOLDER (folder))
2863 modest_folder_view_select_folder (helper->folder_view,
2864 TNY_FOLDER (folder), FALSE);
2865 g_object_unref (folder);
2867 dnd_helper_destroyer (helper);
2872 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2873 gtk_tree_model_get (dest_model, &dest_iter,
2876 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2877 gtk_tree_model_get (source_model, &iter,
2881 /* Create the info for the performer */
2882 info = g_slice_new0 (DndFolderInfo);
2883 info->src_folder = g_object_ref (folder);
2884 info->dst_folder = g_object_ref (dest_folder);
2885 info->helper = helper;
2887 /* Connect to the destination folder and perform the copy/move */
2888 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2890 drag_and_drop_from_folder_view_dst_folder_performer,
2894 g_object_unref (dest_folder);
2895 g_object_unref (folder);
2899 * This function receives the data set by the "drag-data-get" signal
2900 * handler. This information comes within the #GtkSelectionData. This
2901 * function will manage both the drags of folders of the treeview and
2902 * drags of headers of the header view widget.
2905 on_drag_data_received (GtkWidget *widget,
2906 GdkDragContext *context,
2909 GtkSelectionData *selection_data,
2914 GtkWidget *source_widget;
2915 GtkTreeModel *dest_model, *source_model;
2916 GtkTreePath *source_row, *dest_row;
2917 GtkTreeViewDropPosition pos;
2918 gboolean delete_source = FALSE;
2919 gboolean success = FALSE;
2921 /* Do not allow further process */
2922 g_signal_stop_emission_by_name (widget, "drag-data-received");
2923 source_widget = gtk_drag_get_source_widget (context);
2925 /* Get the action */
2926 if (context->action == GDK_ACTION_MOVE) {
2927 delete_source = TRUE;
2929 /* Notify that there is no folder selected. We need to
2930 do this in order to update the headers view (and
2931 its monitors, because when moving, the old folder
2932 won't longer exist. We can not wait for the end of
2933 the operation, because the operation won't start if
2934 the folder is in use */
2935 if (source_widget == widget) {
2936 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2937 gtk_tree_selection_unselect_all (sel);
2941 /* Check if the get_data failed */
2942 if (selection_data == NULL || selection_data->length < 0)
2945 /* Select the destination model */
2946 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2948 /* Get the path to the destination row. Can not call
2949 gtk_tree_view_get_drag_dest_row() because the source row
2950 is not selected anymore */
2951 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2954 /* Only allow drops IN other rows */
2956 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2957 pos == GTK_TREE_VIEW_DROP_AFTER)
2961 /* Drags from the header view */
2962 if (source_widget != widget) {
2963 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2965 drag_and_drop_from_header_view (source_model,
2970 DndHelper *helper = NULL;
2972 /* Get the source model and row */
2973 gtk_tree_get_row_drag_data (selection_data,
2977 /* Create the helper */
2978 helper = g_slice_new0 (DndHelper);
2979 helper->delete_source = delete_source;
2980 helper->source_row = gtk_tree_path_copy (source_row);
2981 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2983 drag_and_drop_from_folder_view (source_model,
2989 gtk_tree_path_free (source_row);
2993 gtk_tree_path_free (dest_row);
2996 /* Finish the drag and drop */
2997 gtk_drag_finish (context, success, FALSE, time);
3001 * We define a "drag-drop" signal handler because we do not want to
3002 * use the default one, because the default one always calls
3003 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3004 * signal handler, because there we have all the information available
3005 * to know if the dnd was a success or not.
3008 drag_drop_cb (GtkWidget *widget,
3009 GdkDragContext *context,
3017 if (!context->targets)
3020 /* Check if we're dragging a folder row */
3021 target = gtk_drag_dest_find_target (widget, context, NULL);
3023 /* Request the data from the source. */
3024 gtk_drag_get_data(widget, context, target, time);
3030 * This function expands a node of a tree view if it's not expanded
3031 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3032 * does that, so that's why they're here.
3035 expand_row_timeout (gpointer data)
3037 GtkTreeView *tree_view = data;
3038 GtkTreePath *dest_path = NULL;
3039 GtkTreeViewDropPosition pos;
3040 gboolean result = FALSE;
3042 gdk_threads_enter ();
3044 gtk_tree_view_get_drag_dest_row (tree_view,
3049 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3050 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3051 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3052 gtk_tree_path_free (dest_path);
3056 gtk_tree_path_free (dest_path);
3061 gdk_threads_leave ();
3067 * This function is called whenever the pointer is moved over a widget
3068 * while dragging some data. It installs a timeout that will expand a
3069 * node of the treeview if not expanded yet. This function also calls
3070 * gdk_drag_status in order to set the suggested action that will be
3071 * used by the "drag-data-received" signal handler to know if we
3072 * should do a move or just a copy of the data.
3075 on_drag_motion (GtkWidget *widget,
3076 GdkDragContext *context,
3082 GtkTreeViewDropPosition pos;
3083 GtkTreePath *dest_row;
3084 GtkTreeModel *dest_model;
3085 ModestFolderViewPrivate *priv;
3086 GdkDragAction suggested_action;
3087 gboolean valid_location = FALSE;
3088 TnyFolderStore *folder = NULL;
3090 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3092 if (priv->timer_expander != 0) {
3093 g_source_remove (priv->timer_expander);
3094 priv->timer_expander = 0;
3097 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3102 /* Do not allow drops between folders */
3104 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3105 pos == GTK_TREE_VIEW_DROP_AFTER) {
3106 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3107 gdk_drag_status(context, 0, time);
3108 valid_location = FALSE;
3111 valid_location = TRUE;
3114 /* Check that the destination folder is writable */
3115 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3116 folder = tree_path_to_folder (dest_model, dest_row);
3117 if (folder && TNY_IS_FOLDER (folder)) {
3118 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3120 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3121 valid_location = FALSE;
3126 /* Expand the selected row after 1/2 second */
3127 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3128 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3130 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3132 /* Select the desired action. By default we pick MOVE */
3133 suggested_action = GDK_ACTION_MOVE;
3135 if (context->actions == GDK_ACTION_COPY)
3136 gdk_drag_status(context, GDK_ACTION_COPY, time);
3137 else if (context->actions == GDK_ACTION_MOVE)
3138 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3139 else if (context->actions & suggested_action)
3140 gdk_drag_status(context, suggested_action, time);
3142 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3146 g_object_unref (folder);
3148 gtk_tree_path_free (dest_row);
3150 g_signal_stop_emission_by_name (widget, "drag-motion");
3152 return valid_location;
3156 * This function sets the treeview as a source and a target for dnd
3157 * events. It also connects all the requirede signals.
3160 setup_drag_and_drop (GtkTreeView *self)
3162 /* Set up the folder view as a dnd destination. Set only the
3163 highlight flag, otherwise gtk will have a different
3165 #ifdef MODEST_TOOLKIT_HILDON2
3168 gtk_drag_dest_set (GTK_WIDGET (self),
3169 GTK_DEST_DEFAULT_HIGHLIGHT,
3170 folder_view_drag_types,
3171 G_N_ELEMENTS (folder_view_drag_types),
3172 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3174 g_signal_connect (G_OBJECT (self),
3175 "drag_data_received",
3176 G_CALLBACK (on_drag_data_received),
3180 /* Set up the treeview as a dnd source */
3181 gtk_drag_source_set (GTK_WIDGET (self),
3183 folder_view_drag_types,
3184 G_N_ELEMENTS (folder_view_drag_types),
3185 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3187 g_signal_connect (G_OBJECT (self),
3189 G_CALLBACK (on_drag_motion),
3192 g_signal_connect (G_OBJECT (self),
3194 G_CALLBACK (on_drag_data_get),
3197 g_signal_connect (G_OBJECT (self),
3199 G_CALLBACK (drag_drop_cb),
3204 * This function manages the navigation through the folders using the
3205 * keyboard or the hardware keys in the device
3208 on_key_pressed (GtkWidget *self,
3212 GtkTreeSelection *selection;
3214 GtkTreeModel *model;
3215 gboolean retval = FALSE;
3217 /* Up and Down are automatically managed by the treeview */
3218 if (event->keyval == GDK_Return) {
3219 /* Expand/Collapse the selected row */
3220 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3221 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3224 path = gtk_tree_model_get_path (model, &iter);
3226 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3227 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3229 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3230 gtk_tree_path_free (path);
3232 /* No further processing */
3240 * We listen to the changes in the local folder account name key,
3241 * because we want to show the right name in the view. The local
3242 * folder account name corresponds to the device name in the Maemo
3243 * version. We do this because we do not want to query gconf on each
3244 * tree view refresh. It's better to cache it and change whenever
3248 on_configuration_key_changed (ModestConf* conf,
3250 ModestConfEvent event,
3251 ModestConfNotificationId id,
3252 ModestFolderView *self)
3254 ModestFolderViewPrivate *priv;
3257 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3258 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3260 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3261 g_free (priv->local_account_name);
3263 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3264 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3266 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3267 MODEST_CONF_DEVICE_NAME, NULL);
3269 /* Force a redraw */
3270 #if GTK_CHECK_VERSION(2, 8, 0)
3271 GtkTreeViewColumn * tree_column;
3273 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3275 gtk_tree_view_column_queue_resize (tree_column);
3277 gtk_widget_queue_draw (GTK_WIDGET (self));
3283 modest_folder_view_set_style (ModestFolderView *self,
3284 ModestFolderViewStyle style)
3286 ModestFolderViewPrivate *priv;
3288 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3289 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3290 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3292 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3295 priv->style = style;
3299 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3300 const gchar *account_id)
3302 ModestFolderViewPrivate *priv;
3303 GtkTreeModel *model;
3305 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3307 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3309 /* This will be used by the filter_row callback,
3310 * to decided which rows to show: */
3311 if (priv->visible_account_id) {
3312 g_free (priv->visible_account_id);
3313 priv->visible_account_id = NULL;
3316 priv->visible_account_id = g_strdup (account_id);
3319 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3320 if (GTK_IS_TREE_MODEL_FILTER (model))
3321 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3323 /* Save settings to gconf */
3324 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3325 MODEST_CONF_FOLDER_VIEW_KEY);
3327 /* Notify observers */
3328 g_signal_emit (G_OBJECT(self),
3329 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3334 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3336 ModestFolderViewPrivate *priv;
3338 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3340 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3342 return (const gchar *) priv->visible_account_id;
3346 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3350 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3352 gtk_tree_model_get (model, iter,
3356 gboolean result = FALSE;
3357 if (type == TNY_FOLDER_TYPE_INBOX) {
3361 *inbox_iter = *iter;
3365 if (gtk_tree_model_iter_children (model, &child, iter)) {
3366 if (find_inbox_iter (model, &child, inbox_iter))
3370 } while (gtk_tree_model_iter_next (model, iter));
3379 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3381 GtkTreeModel *model;
3382 GtkTreeIter iter, inbox_iter;
3383 GtkTreeSelection *sel;
3384 GtkTreePath *path = NULL;
3386 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3388 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3392 expand_root_items (self);
3393 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3395 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3396 g_warning ("%s: model is empty", __FUNCTION__);
3400 if (find_inbox_iter (model, &iter, &inbox_iter))
3401 path = gtk_tree_model_get_path (model, &inbox_iter);
3403 path = gtk_tree_path_new_first ();
3405 /* Select the row and free */
3406 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3407 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3408 gtk_tree_path_free (path);
3411 gtk_widget_grab_focus (GTK_WIDGET(self));
3417 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3422 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3423 TnyFolder* a_folder;
3426 gtk_tree_model_get (model, iter,
3427 INSTANCE_COLUMN, &a_folder,
3433 if (folder == a_folder) {
3434 g_object_unref (a_folder);
3435 *folder_iter = *iter;
3438 g_object_unref (a_folder);
3440 if (gtk_tree_model_iter_children (model, &child, iter)) {
3441 if (find_folder_iter (model, &child, folder_iter, folder))
3445 } while (gtk_tree_model_iter_next (model, iter));
3450 #ifndef MODEST_TOOLKIT_HILDON2
3452 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3455 ModestFolderView *self)
3457 ModestFolderViewPrivate *priv = NULL;
3458 GtkTreeSelection *sel;
3459 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3460 GObject *instance = NULL;
3462 if (!MODEST_IS_FOLDER_VIEW(self))
3465 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3467 priv->reexpand = TRUE;
3469 gtk_tree_model_get (tree_model, iter,
3471 INSTANCE_COLUMN, &instance,
3477 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3478 priv->folder_to_select = g_object_ref (instance);
3480 g_object_unref (instance);
3482 if (priv->folder_to_select) {
3484 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3487 path = gtk_tree_model_get_path (tree_model, iter);
3488 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3490 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3492 gtk_tree_selection_select_iter (sel, iter);
3493 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3495 gtk_tree_path_free (path);
3499 modest_folder_view_disable_next_folder_selection (self);
3501 /* Refilter the model */
3502 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3508 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3510 ModestFolderViewPrivate *priv;
3512 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3514 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3516 if (priv->folder_to_select)
3517 g_object_unref(priv->folder_to_select);
3519 priv->folder_to_select = NULL;
3523 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3524 gboolean after_change)
3526 GtkTreeModel *model;
3527 GtkTreeIter iter, folder_iter;
3528 GtkTreeSelection *sel;
3529 ModestFolderViewPrivate *priv = NULL;
3531 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3532 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3534 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3537 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3538 gtk_tree_selection_unselect_all (sel);
3540 if (priv->folder_to_select)
3541 g_object_unref(priv->folder_to_select);
3542 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3546 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3551 /* Refilter the model, before selecting the folder */
3552 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3554 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3555 g_warning ("%s: model is empty", __FUNCTION__);
3559 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3562 path = gtk_tree_model_get_path (model, &folder_iter);
3563 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3565 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3566 gtk_tree_selection_select_iter (sel, &folder_iter);
3567 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3569 gtk_tree_path_free (path);
3577 modest_folder_view_copy_selection (ModestFolderView *self)
3579 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3581 /* Copy selection */
3582 _clipboard_set_selected_data (self, FALSE);
3586 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3588 ModestFolderViewPrivate *priv = NULL;
3589 GtkTreeModel *model = NULL;
3590 const gchar **hidding = NULL;
3591 guint i, n_selected;
3593 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3594 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3596 /* Copy selection */
3597 if (!_clipboard_set_selected_data (folder_view, TRUE))
3600 /* Get hidding ids */
3601 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3603 /* Clear hidding array created by previous cut operation */
3604 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3606 /* Copy hidding array */
3607 priv->n_selected = n_selected;
3608 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3609 for (i=0; i < n_selected; i++)
3610 priv->hidding_ids[i] = g_strdup(hidding[i]);
3612 /* Hide cut folders */
3613 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3614 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3618 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3619 ModestFolderView *folder_view_dst)
3621 GtkTreeModel *filter_model = NULL;
3622 GtkTreeModel *model = NULL;
3623 GtkTreeModel *new_filter_model = NULL;
3625 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3626 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3629 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3630 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3632 /* Build new filter model */
3633 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3634 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3638 /* Set copied model */
3639 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3640 #ifndef MODEST_TOOLKIT_HILDON2
3641 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3642 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3646 g_object_unref (new_filter_model);
3650 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3653 GtkTreeModel *model = NULL;
3654 ModestFolderViewPrivate* priv;
3656 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3658 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3659 priv->show_non_move = show;
3660 /* modest_folder_view_update_model(folder_view, */
3661 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3663 /* Hide special folders */
3664 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3665 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3666 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3670 /* Returns FALSE if it did not selected anything */
3672 _clipboard_set_selected_data (ModestFolderView *folder_view,
3675 ModestFolderViewPrivate *priv = NULL;
3676 TnyFolderStore *folder = NULL;
3677 gboolean retval = FALSE;
3679 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3680 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3682 /* Set selected data on clipboard */
3683 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3684 folder = modest_folder_view_get_selected (folder_view);
3686 /* Do not allow to select an account */
3687 if (TNY_IS_FOLDER (folder)) {
3688 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3693 g_object_unref (folder);
3699 _clear_hidding_filter (ModestFolderView *folder_view)
3701 ModestFolderViewPrivate *priv;
3704 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3705 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3707 if (priv->hidding_ids != NULL) {
3708 for (i=0; i < priv->n_selected; i++)
3709 g_free (priv->hidding_ids[i]);
3710 g_free(priv->hidding_ids);
3716 on_display_name_changed (ModestAccountMgr *mgr,
3717 const gchar *account,
3720 ModestFolderView *self;
3722 self = MODEST_FOLDER_VIEW (user_data);
3724 /* Force a redraw */
3725 #if GTK_CHECK_VERSION(2, 8, 0)
3726 GtkTreeViewColumn * tree_column;
3728 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3730 gtk_tree_view_column_queue_resize (tree_column);
3732 gtk_widget_queue_draw (GTK_WIDGET (self));
3737 modest_folder_view_set_cell_style (ModestFolderView *self,
3738 ModestFolderViewCellStyle cell_style)
3740 ModestFolderViewPrivate *priv = NULL;
3742 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3743 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3745 priv->cell_style = cell_style;
3747 g_object_set (G_OBJECT (priv->messages_renderer),
3748 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3751 gtk_widget_queue_draw (GTK_WIDGET (self));
3755 update_style (ModestFolderView *self)
3757 ModestFolderViewPrivate *priv;
3758 GdkColor style_color;
3759 PangoAttrList *attr_list;
3761 PangoAttribute *attr;
3763 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3764 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3768 attr_list = pango_attr_list_new ();
3769 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3770 gdk_color_parse ("grey", &style_color);
3772 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3773 pango_attr_list_insert (attr_list, attr);
3776 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3778 "SmallSystemFont", NULL,
3781 attr = pango_attr_font_desc_new (pango_font_description_copy
3782 (style->font_desc));
3783 pango_attr_list_insert (attr_list, attr);
3785 g_object_set (G_OBJECT (priv->messages_renderer),
3786 "foreground-gdk", &style_color,
3787 "foreground-set", TRUE,
3788 "attributes", attr_list,
3790 pango_attr_list_unref (attr_list);
3795 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3797 if (strcmp ("style", spec->name) == 0) {
3798 update_style (MODEST_FOLDER_VIEW (obj));
3799 gtk_widget_queue_draw (GTK_WIDGET (obj));
3804 modest_folder_view_set_filter (ModestFolderView *self,
3805 ModestFolderViewFilter filter)
3807 ModestFolderViewPrivate *priv;
3808 GtkTreeModel *filter_model;
3810 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3811 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3813 priv->filter |= filter;
3815 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3816 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3817 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3822 modest_folder_view_unset_filter (ModestFolderView *self,
3823 ModestFolderViewFilter filter)
3825 ModestFolderViewPrivate *priv;
3826 GtkTreeModel *filter_model;
3828 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3829 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3831 priv->filter &= ~filter;
3833 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3834 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3835 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3840 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3841 ModestTnyFolderRules rules)
3843 GtkTreeModel *filter_model;
3845 gboolean fulfil = FALSE;
3847 if (!get_inner_models (self, &filter_model, NULL, NULL))
3850 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3854 TnyFolderStore *folder;
3856 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3858 if (TNY_IS_FOLDER (folder)) {
3859 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3860 /* Folder rules are negative: non_writable, non_deletable... */
3861 if (!(folder_rules & rules))
3864 g_object_unref (folder);
3867 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3873 modest_folder_view_set_list_to_move (ModestFolderView *self,
3876 ModestFolderViewPrivate *priv;
3878 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3879 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3881 if (priv->list_to_move)
3882 g_object_unref (priv->list_to_move);
3885 g_object_ref (list);
3887 priv->list_to_move = list;
3891 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3893 ModestFolderViewPrivate *priv;
3895 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3896 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3899 g_free (priv->mailbox);
3901 priv->mailbox = g_strdup (mailbox);