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 /* Filter mailboxes */
1908 if (!g_str_has_prefix (fname, priv->mailbox)) {
1910 } else if (!strcmp (fname, priv->mailbox)) {
1911 /* Hide mailbox parent */
1917 g_object_unref (account);
1924 /* Check hiding (if necessary) */
1925 cleared = modest_email_clipboard_cleared (priv->clipboard);
1926 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1927 id = tny_folder_get_id (TNY_FOLDER(instance));
1928 if (priv->hidding_ids != NULL)
1929 for (i=0; i < priv->n_selected && !found; i++)
1930 if (priv->hidding_ids[i] != NULL && id != NULL)
1931 found = (!strcmp (priv->hidding_ids[i], id));
1936 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1937 folder as no message can be move there according to UI specs */
1938 if (retval && !priv->show_non_move) {
1939 if (priv->list_to_move &&
1940 tny_list_get_length (priv->list_to_move) > 0 &&
1941 TNY_IS_FOLDER (instance)) {
1942 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1944 if (retval && TNY_IS_FOLDER (instance) &&
1945 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1947 case TNY_FOLDER_TYPE_OUTBOX:
1948 case TNY_FOLDER_TYPE_SENT:
1949 case TNY_FOLDER_TYPE_DRAFTS:
1952 case TNY_FOLDER_TYPE_UNKNOWN:
1953 case TNY_FOLDER_TYPE_NORMAL:
1954 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1955 if (type == TNY_FOLDER_TYPE_INVALID)
1956 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1958 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1959 type == TNY_FOLDER_TYPE_SENT
1960 || type == TNY_FOLDER_TYPE_DRAFTS)
1969 /* apply special filters */
1970 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1971 if (TNY_IS_ACCOUNT (instance))
1975 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
1976 if (TNY_IS_FOLDER (instance))
1980 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
1981 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1986 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
1987 /* A mailbox is a fake folder with an @ in the middle of the name */
1988 if (!TNY_IS_FOLDER (instance) ||
1989 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
1992 const gchar *folder_name;
1993 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
1994 if (!folder_name || strchr (folder_name, '@') == NULL)
2000 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2001 if (TNY_IS_FOLDER (instance)) {
2002 /* Check folder rules */
2003 ModestTnyFolderRules rules;
2005 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2006 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2007 } else if (TNY_IS_ACCOUNT (instance)) {
2008 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2016 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2017 if (TNY_IS_FOLDER (instance)) {
2018 TnyFolderType guess_type;
2020 if (TNY_FOLDER_TYPE_NORMAL) {
2021 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2027 case TNY_FOLDER_TYPE_OUTBOX:
2028 case TNY_FOLDER_TYPE_SENT:
2029 case TNY_FOLDER_TYPE_DRAFTS:
2030 case TNY_FOLDER_TYPE_ARCHIVE:
2031 case TNY_FOLDER_TYPE_INBOX:
2034 case TNY_FOLDER_TYPE_UNKNOWN:
2035 case TNY_FOLDER_TYPE_NORMAL:
2041 } else if (TNY_IS_ACCOUNT (instance)) {
2046 if (retval && TNY_IS_FOLDER (instance)) {
2047 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2050 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2051 if (TNY_IS_FOLDER (instance)) {
2052 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2053 } else if (TNY_IS_ACCOUNT (instance)) {
2058 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2059 if (TNY_IS_FOLDER (instance)) {
2060 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2061 } else if (TNY_IS_ACCOUNT (instance)) {
2066 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2067 if (TNY_IS_FOLDER (instance)) {
2068 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2069 } else if (TNY_IS_ACCOUNT (instance)) {
2075 g_object_unref (instance);
2083 modest_folder_view_update_model (ModestFolderView *self,
2084 TnyAccountStore *account_store)
2086 ModestFolderViewPrivate *priv;
2087 GtkTreeModel *model /* , *old_model */;
2088 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2090 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2091 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2094 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2096 /* Notify that there is no folder selected */
2097 g_signal_emit (G_OBJECT(self),
2098 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2100 if (priv->cur_folder_store) {
2101 g_object_unref (priv->cur_folder_store);
2102 priv->cur_folder_store = NULL;
2105 /* FIXME: the local accounts are not shown when the query
2106 selects only the subscribed folders */
2107 #ifdef MODEST_TOOLKIT_HILDON2
2108 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2109 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2110 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2111 MODEST_FOLDER_PATH_SEPARATOR);
2113 model = tny_gtk_folder_store_tree_model_new (NULL);
2116 /* When the model is a list store (plain representation) the
2117 outbox is not a child of any account so we have to manually
2118 delete it because removing the local folders account won't
2119 delete it (because tny_folder_get_account() is not defined
2120 for a merge folder */
2121 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2122 TnyAccount *account;
2123 ModestTnyAccountStore *acc_store;
2125 acc_store = modest_runtime_get_account_store ();
2126 account = modest_tny_account_store_get_local_folders_account (acc_store);
2128 if (g_signal_handler_is_connected (account,
2129 priv->outbox_deleted_handler))
2130 g_signal_handler_disconnect (account,
2131 priv->outbox_deleted_handler);
2133 priv->outbox_deleted_handler =
2134 g_signal_connect (account,
2136 G_CALLBACK (on_outbox_deleted_cb),
2138 g_object_unref (account);
2141 /* Get the accounts: */
2142 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2144 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2146 sortable = gtk_tree_model_sort_new_with_model (model);
2147 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2149 GTK_SORT_ASCENDING);
2150 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2152 cmp_rows, NULL, NULL);
2154 /* Create filter model */
2155 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2156 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2162 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2163 #ifndef MODEST_TOOLKIT_HILDON2
2164 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2165 (GCallback) on_row_inserted_maybe_select_folder, self);
2168 g_object_unref (model);
2169 g_object_unref (filter_model);
2170 g_object_unref (sortable);
2172 /* Force a reselection of the INBOX next time the widget is shown */
2173 priv->reselect = TRUE;
2180 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2182 GtkTreeModel *model = NULL;
2183 TnyFolderStore *folder = NULL;
2185 ModestFolderView *tree_view = NULL;
2186 ModestFolderViewPrivate *priv = NULL;
2187 gboolean selected = FALSE;
2189 g_return_if_fail (sel);
2190 g_return_if_fail (user_data);
2192 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2194 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2196 tree_view = MODEST_FOLDER_VIEW (user_data);
2199 gtk_tree_model_get (model, &iter,
2200 INSTANCE_COLUMN, &folder,
2203 /* If the folder is the same do not notify */
2204 if (folder && priv->cur_folder_store == folder) {
2205 g_object_unref (folder);
2210 /* Current folder was unselected */
2211 if (priv->cur_folder_store) {
2212 /* We must do this firstly because a libtinymail-camel
2213 implementation detail. If we issue the signal
2214 before doing the sync_async, then that signal could
2215 cause (and it actually does it) a free of the
2216 summary of the folder (because the main window will
2217 clear the headers view */
2218 if (TNY_IS_FOLDER(priv->cur_folder_store))
2219 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2220 FALSE, NULL, NULL, NULL);
2222 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2223 priv->cur_folder_store, FALSE);
2225 g_object_unref (priv->cur_folder_store);
2226 priv->cur_folder_store = NULL;
2229 /* New current references */
2230 priv->cur_folder_store = folder;
2232 /* New folder has been selected. Do not notify if there is
2233 nothing new selected */
2235 g_signal_emit (G_OBJECT(tree_view),
2236 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2237 0, priv->cur_folder_store, TRUE);
2242 on_row_activated (GtkTreeView *treeview,
2243 GtkTreePath *treepath,
2244 GtkTreeViewColumn *column,
2247 GtkTreeModel *model = NULL;
2248 TnyFolderStore *folder = NULL;
2250 ModestFolderView *self = NULL;
2251 ModestFolderViewPrivate *priv = NULL;
2253 g_return_if_fail (treeview);
2254 g_return_if_fail (user_data);
2256 self = MODEST_FOLDER_VIEW (user_data);
2257 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2259 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2261 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2264 gtk_tree_model_get (model, &iter,
2265 INSTANCE_COLUMN, &folder,
2268 g_signal_emit (G_OBJECT(self),
2269 signals[FOLDER_ACTIVATED_SIGNAL],
2272 #ifdef MODEST_TOOLKIT_HILDON2
2273 HildonUIMode ui_mode;
2274 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2275 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2276 if (priv->cur_folder_store)
2277 g_object_unref (priv->cur_folder_store);
2278 priv->cur_folder_store = g_object_ref (folder);
2282 g_object_unref (folder);
2286 modest_folder_view_get_selected (ModestFolderView *self)
2288 ModestFolderViewPrivate *priv;
2290 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2292 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2293 if (priv->cur_folder_store)
2294 g_object_ref (priv->cur_folder_store);
2296 return priv->cur_folder_store;
2300 get_cmp_rows_type_pos (GObject *folder)
2302 /* Remote accounts -> Local account -> MMC account .*/
2305 if (TNY_IS_ACCOUNT (folder) &&
2306 modest_tny_account_is_virtual_local_folders (
2307 TNY_ACCOUNT (folder))) {
2309 } else if (TNY_IS_ACCOUNT (folder)) {
2310 TnyAccount *account = TNY_ACCOUNT (folder);
2311 const gchar *account_id = tny_account_get_id (account);
2312 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2318 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2319 return -1; /* Should never happen */
2324 inbox_is_special (TnyFolderStore *folder_store)
2326 gboolean is_special = TRUE;
2328 if (TNY_IS_FOLDER (folder_store)) {
2332 gchar *last_inbox_bar;
2334 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2335 downcase = g_utf8_strdown (id, -1);
2336 last_bar = g_strrstr (downcase, "/");
2338 last_inbox_bar = g_strrstr (downcase, "inbox/");
2339 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2350 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2352 TnyAccount *account;
2353 gboolean is_special;
2354 /* Inbox, Outbox, Drafts, Sent, User */
2357 if (!TNY_IS_FOLDER (folder_store))
2360 case TNY_FOLDER_TYPE_INBOX:
2362 account = tny_folder_get_account (folder_store);
2363 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2365 /* In inbox case we need to know if the inbox is really the top
2366 * inbox of the account, or if it's a submailbox inbox. To do
2367 * this we'll apply an heuristic rule: Find last "/" and check
2368 * if it's preceeded by another Inbox */
2369 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2370 g_object_unref (account);
2371 return is_special?0:4;
2374 case TNY_FOLDER_TYPE_OUTBOX:
2375 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2377 case TNY_FOLDER_TYPE_DRAFTS:
2379 account = tny_folder_get_account (folder_store);
2380 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2381 g_object_unref (account);
2382 return is_special?1:4;
2385 case TNY_FOLDER_TYPE_SENT:
2387 account = tny_folder_get_account (folder_store);
2388 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2389 g_object_unref (account);
2390 return is_special?3:4;
2399 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2401 const gchar *a1_name, *a2_name;
2403 a1_name = tny_account_get_name (a1);
2404 a2_name = tny_account_get_name (a2);
2406 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2410 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2412 TnyAccount *a1 = NULL, *a2 = NULL;
2415 if (TNY_IS_ACCOUNT (s1)) {
2416 a1 = TNY_ACCOUNT (g_object_ref (s1));
2417 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2418 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2421 if (TNY_IS_ACCOUNT (s2)) {
2422 a2 = TNY_ACCOUNT (g_object_ref (s2));
2423 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2424 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2441 /* First we sort with the type of account */
2442 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2446 cmp = compare_account_names (a1, a2);
2450 g_object_unref (a1);
2452 g_object_unref (a2);
2458 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2460 gint is_account1, is_account2;
2462 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2463 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2465 return is_account2 - is_account1;
2469 * This function orders the mail accounts according to these rules:
2470 * 1st - remote accounts
2471 * 2nd - local account
2475 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2479 gchar *name1 = NULL;
2480 gchar *name2 = NULL;
2481 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2482 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2483 GObject *folder1 = NULL;
2484 GObject *folder2 = NULL;
2486 gtk_tree_model_get (tree_model, iter1,
2487 NAME_COLUMN, &name1,
2489 INSTANCE_COLUMN, &folder1,
2491 gtk_tree_model_get (tree_model, iter2,
2492 NAME_COLUMN, &name2,
2493 TYPE_COLUMN, &type2,
2494 INSTANCE_COLUMN, &folder2,
2497 /* Return if we get no folder. This could happen when folder
2498 operations are happening. The model is updated after the
2499 folder copy/move actually occurs, so there could be
2500 situations where the model to be drawn is not correct */
2501 if (!folder1 || !folder2)
2504 /* Sort by type. First the special folders, then the archives */
2505 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2509 /* Now we sort using the account of each folder */
2510 if (TNY_IS_FOLDER_STORE (folder1) &&
2511 TNY_IS_FOLDER_STORE (folder2)) {
2512 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2516 /* Each group is preceeded by its account */
2517 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2522 /* Pure sort by name */
2523 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2526 g_object_unref(G_OBJECT(folder1));
2528 g_object_unref(G_OBJECT(folder2));
2536 /*****************************************************************************/
2537 /* DRAG and DROP stuff */
2538 /*****************************************************************************/
2540 * This function fills the #GtkSelectionData with the row and the
2541 * model that has been dragged. It's called when this widget is a
2542 * source for dnd after the event drop happened
2545 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2546 guint info, guint time, gpointer data)
2548 GtkTreeSelection *selection;
2549 GtkTreeModel *model;
2551 GtkTreePath *source_row;
2553 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2554 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2556 source_row = gtk_tree_model_get_path (model, &iter);
2557 gtk_tree_set_row_drag_data (selection_data,
2561 gtk_tree_path_free (source_row);
2565 typedef struct _DndHelper {
2566 ModestFolderView *folder_view;
2567 gboolean delete_source;
2568 GtkTreePath *source_row;
2572 dnd_helper_destroyer (DndHelper *helper)
2574 /* Free the helper */
2575 gtk_tree_path_free (helper->source_row);
2576 g_slice_free (DndHelper, helper);
2580 xfer_folder_cb (ModestMailOperation *mail_op,
2581 TnyFolder *new_folder,
2585 /* Select the folder */
2586 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2592 /* get the folder for the row the treepath refers to. */
2593 /* folder must be unref'd */
2594 static TnyFolderStore *
2595 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2598 TnyFolderStore *folder = NULL;
2600 if (gtk_tree_model_get_iter (model,&iter, path))
2601 gtk_tree_model_get (model, &iter,
2602 INSTANCE_COLUMN, &folder,
2609 * This function is used by drag_data_received_cb to manage drag and
2610 * drop of a header, i.e, and drag from the header view to the folder
2614 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2615 GtkTreeModel *dest_model,
2616 GtkTreePath *dest_row,
2617 GtkSelectionData *selection_data)
2619 TnyList *headers = NULL;
2620 TnyFolder *folder = NULL, *src_folder = NULL;
2621 TnyFolderType folder_type;
2622 GtkTreeIter source_iter, dest_iter;
2623 ModestWindowMgr *mgr = NULL;
2624 ModestWindow *main_win = NULL;
2625 gchar **uris, **tmp;
2627 /* Build the list of headers */
2628 mgr = modest_runtime_get_window_mgr ();
2629 headers = tny_simple_list_new ();
2630 uris = modest_dnd_selection_data_get_paths (selection_data);
2633 while (*tmp != NULL) {
2636 gboolean first = TRUE;
2639 path = gtk_tree_path_new_from_string (*tmp);
2640 gtk_tree_model_get_iter (source_model, &source_iter, path);
2641 gtk_tree_model_get (source_model, &source_iter,
2642 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2645 /* Do not enable d&d of headers already opened */
2646 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2647 tny_list_append (headers, G_OBJECT (header));
2649 if (G_UNLIKELY (first)) {
2650 src_folder = tny_header_get_folder (header);
2654 /* Free and go on */
2655 gtk_tree_path_free (path);
2656 g_object_unref (header);
2661 /* This could happen ig we perform a d&d very quickly over the
2662 same row that row could dissapear because message is
2664 if (!TNY_IS_FOLDER (src_folder))
2667 /* Get the target folder */
2668 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2669 gtk_tree_model_get (dest_model, &dest_iter,
2673 if (!folder || !TNY_IS_FOLDER(folder)) {
2674 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2678 folder_type = modest_tny_folder_guess_folder_type (folder);
2679 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2680 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2681 goto cleanup; /* cannot move messages there */
2684 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2685 /* g_warning ("folder not writable"); */
2686 goto cleanup; /* verboten! */
2689 /* Ask for confirmation to move */
2690 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2692 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2696 /* Transfer messages */
2697 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2702 if (G_IS_OBJECT (src_folder))
2703 g_object_unref (src_folder);
2704 if (G_IS_OBJECT(folder))
2705 g_object_unref (G_OBJECT (folder));
2706 if (G_IS_OBJECT(headers))
2707 g_object_unref (headers);
2711 TnyFolderStore *src_folder;
2712 TnyFolderStore *dst_folder;
2713 ModestFolderView *folder_view;
2718 dnd_folder_info_destroyer (DndFolderInfo *info)
2720 if (info->src_folder)
2721 g_object_unref (info->src_folder);
2722 if (info->dst_folder)
2723 g_object_unref (info->dst_folder);
2724 g_slice_free (DndFolderInfo, info);
2728 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2729 GtkWindow *parent_window,
2730 TnyAccount *account)
2733 modest_ui_actions_on_account_connection_error (parent_window, account);
2735 /* Free the helper & info */
2736 dnd_helper_destroyer (info->helper);
2737 dnd_folder_info_destroyer (info);
2741 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2743 GtkWindow *parent_window,
2744 TnyAccount *account,
2747 DndFolderInfo *info = NULL;
2748 ModestMailOperation *mail_op;
2750 info = (DndFolderInfo *) user_data;
2752 if (err || canceled) {
2753 dnd_on_connection_failed_destroyer (info, parent_window, account);
2757 /* Do the mail operation */
2758 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2759 modest_ui_actions_move_folder_error_handler,
2760 info->src_folder, NULL);
2762 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2765 /* Transfer the folder */
2766 modest_mail_operation_xfer_folder (mail_op,
2767 TNY_FOLDER (info->src_folder),
2769 info->helper->delete_source,
2771 info->helper->folder_view);
2774 g_object_unref (G_OBJECT (mail_op));
2775 dnd_helper_destroyer (info->helper);
2776 dnd_folder_info_destroyer (info);
2781 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2783 GtkWindow *parent_window,
2784 TnyAccount *account,
2787 DndFolderInfo *info = NULL;
2789 info = (DndFolderInfo *) user_data;
2791 if (err || canceled) {
2792 dnd_on_connection_failed_destroyer (info, parent_window, account);
2796 /* Connect to source folder and perform the copy/move */
2797 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2799 drag_and_drop_from_folder_view_src_folder_performer,
2804 * This function is used by drag_data_received_cb to manage drag and
2805 * drop of a folder, i.e, and drag from the folder view to the same
2809 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2810 GtkTreeModel *dest_model,
2811 GtkTreePath *dest_row,
2812 GtkSelectionData *selection_data,
2815 GtkTreeIter dest_iter, iter;
2816 TnyFolderStore *dest_folder = NULL;
2817 TnyFolderStore *folder = NULL;
2818 gboolean forbidden = FALSE;
2820 DndFolderInfo *info = NULL;
2822 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2824 g_warning ("%s: BUG: no main window", __FUNCTION__);
2825 dnd_helper_destroyer (helper);
2830 /* check the folder rules for the destination */
2831 folder = tree_path_to_folder (dest_model, dest_row);
2832 if (TNY_IS_FOLDER(folder)) {
2833 ModestTnyFolderRules rules =
2834 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2835 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2836 } else if (TNY_IS_FOLDER_STORE(folder)) {
2837 /* enable local root as destination for folders */
2838 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2839 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2842 g_object_unref (folder);
2845 /* check the folder rules for the source */
2846 folder = tree_path_to_folder (source_model, helper->source_row);
2847 if (TNY_IS_FOLDER(folder)) {
2848 ModestTnyFolderRules rules =
2849 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2850 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2853 g_object_unref (folder);
2857 /* Check if the drag is possible */
2858 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2860 modest_platform_run_information_dialog ((GtkWindow *) win,
2861 _("mail_in_ui_folder_move_target_error"),
2863 /* Restore the previous selection */
2864 folder = tree_path_to_folder (source_model, helper->source_row);
2866 if (TNY_IS_FOLDER (folder))
2867 modest_folder_view_select_folder (helper->folder_view,
2868 TNY_FOLDER (folder), FALSE);
2869 g_object_unref (folder);
2871 dnd_helper_destroyer (helper);
2876 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2877 gtk_tree_model_get (dest_model, &dest_iter,
2880 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2881 gtk_tree_model_get (source_model, &iter,
2885 /* Create the info for the performer */
2886 info = g_slice_new0 (DndFolderInfo);
2887 info->src_folder = g_object_ref (folder);
2888 info->dst_folder = g_object_ref (dest_folder);
2889 info->helper = helper;
2891 /* Connect to the destination folder and perform the copy/move */
2892 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2894 drag_and_drop_from_folder_view_dst_folder_performer,
2898 g_object_unref (dest_folder);
2899 g_object_unref (folder);
2903 * This function receives the data set by the "drag-data-get" signal
2904 * handler. This information comes within the #GtkSelectionData. This
2905 * function will manage both the drags of folders of the treeview and
2906 * drags of headers of the header view widget.
2909 on_drag_data_received (GtkWidget *widget,
2910 GdkDragContext *context,
2913 GtkSelectionData *selection_data,
2918 GtkWidget *source_widget;
2919 GtkTreeModel *dest_model, *source_model;
2920 GtkTreePath *source_row, *dest_row;
2921 GtkTreeViewDropPosition pos;
2922 gboolean delete_source = FALSE;
2923 gboolean success = FALSE;
2925 /* Do not allow further process */
2926 g_signal_stop_emission_by_name (widget, "drag-data-received");
2927 source_widget = gtk_drag_get_source_widget (context);
2929 /* Get the action */
2930 if (context->action == GDK_ACTION_MOVE) {
2931 delete_source = TRUE;
2933 /* Notify that there is no folder selected. We need to
2934 do this in order to update the headers view (and
2935 its monitors, because when moving, the old folder
2936 won't longer exist. We can not wait for the end of
2937 the operation, because the operation won't start if
2938 the folder is in use */
2939 if (source_widget == widget) {
2940 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2941 gtk_tree_selection_unselect_all (sel);
2945 /* Check if the get_data failed */
2946 if (selection_data == NULL || selection_data->length < 0)
2949 /* Select the destination model */
2950 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2952 /* Get the path to the destination row. Can not call
2953 gtk_tree_view_get_drag_dest_row() because the source row
2954 is not selected anymore */
2955 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2958 /* Only allow drops IN other rows */
2960 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2961 pos == GTK_TREE_VIEW_DROP_AFTER)
2965 /* Drags from the header view */
2966 if (source_widget != widget) {
2967 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2969 drag_and_drop_from_header_view (source_model,
2974 DndHelper *helper = NULL;
2976 /* Get the source model and row */
2977 gtk_tree_get_row_drag_data (selection_data,
2981 /* Create the helper */
2982 helper = g_slice_new0 (DndHelper);
2983 helper->delete_source = delete_source;
2984 helper->source_row = gtk_tree_path_copy (source_row);
2985 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2987 drag_and_drop_from_folder_view (source_model,
2993 gtk_tree_path_free (source_row);
2997 gtk_tree_path_free (dest_row);
3000 /* Finish the drag and drop */
3001 gtk_drag_finish (context, success, FALSE, time);
3005 * We define a "drag-drop" signal handler because we do not want to
3006 * use the default one, because the default one always calls
3007 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3008 * signal handler, because there we have all the information available
3009 * to know if the dnd was a success or not.
3012 drag_drop_cb (GtkWidget *widget,
3013 GdkDragContext *context,
3021 if (!context->targets)
3024 /* Check if we're dragging a folder row */
3025 target = gtk_drag_dest_find_target (widget, context, NULL);
3027 /* Request the data from the source. */
3028 gtk_drag_get_data(widget, context, target, time);
3034 * This function expands a node of a tree view if it's not expanded
3035 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3036 * does that, so that's why they're here.
3039 expand_row_timeout (gpointer data)
3041 GtkTreeView *tree_view = data;
3042 GtkTreePath *dest_path = NULL;
3043 GtkTreeViewDropPosition pos;
3044 gboolean result = FALSE;
3046 gdk_threads_enter ();
3048 gtk_tree_view_get_drag_dest_row (tree_view,
3053 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3054 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3055 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3056 gtk_tree_path_free (dest_path);
3060 gtk_tree_path_free (dest_path);
3065 gdk_threads_leave ();
3071 * This function is called whenever the pointer is moved over a widget
3072 * while dragging some data. It installs a timeout that will expand a
3073 * node of the treeview if not expanded yet. This function also calls
3074 * gdk_drag_status in order to set the suggested action that will be
3075 * used by the "drag-data-received" signal handler to know if we
3076 * should do a move or just a copy of the data.
3079 on_drag_motion (GtkWidget *widget,
3080 GdkDragContext *context,
3086 GtkTreeViewDropPosition pos;
3087 GtkTreePath *dest_row;
3088 GtkTreeModel *dest_model;
3089 ModestFolderViewPrivate *priv;
3090 GdkDragAction suggested_action;
3091 gboolean valid_location = FALSE;
3092 TnyFolderStore *folder = NULL;
3094 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3096 if (priv->timer_expander != 0) {
3097 g_source_remove (priv->timer_expander);
3098 priv->timer_expander = 0;
3101 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3106 /* Do not allow drops between folders */
3108 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3109 pos == GTK_TREE_VIEW_DROP_AFTER) {
3110 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3111 gdk_drag_status(context, 0, time);
3112 valid_location = FALSE;
3115 valid_location = TRUE;
3118 /* Check that the destination folder is writable */
3119 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3120 folder = tree_path_to_folder (dest_model, dest_row);
3121 if (folder && TNY_IS_FOLDER (folder)) {
3122 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3124 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3125 valid_location = FALSE;
3130 /* Expand the selected row after 1/2 second */
3131 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3132 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3134 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3136 /* Select the desired action. By default we pick MOVE */
3137 suggested_action = GDK_ACTION_MOVE;
3139 if (context->actions == GDK_ACTION_COPY)
3140 gdk_drag_status(context, GDK_ACTION_COPY, time);
3141 else if (context->actions == GDK_ACTION_MOVE)
3142 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3143 else if (context->actions & suggested_action)
3144 gdk_drag_status(context, suggested_action, time);
3146 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3150 g_object_unref (folder);
3152 gtk_tree_path_free (dest_row);
3154 g_signal_stop_emission_by_name (widget, "drag-motion");
3156 return valid_location;
3160 * This function sets the treeview as a source and a target for dnd
3161 * events. It also connects all the requirede signals.
3164 setup_drag_and_drop (GtkTreeView *self)
3166 /* Set up the folder view as a dnd destination. Set only the
3167 highlight flag, otherwise gtk will have a different
3169 #ifdef MODEST_TOOLKIT_HILDON2
3172 gtk_drag_dest_set (GTK_WIDGET (self),
3173 GTK_DEST_DEFAULT_HIGHLIGHT,
3174 folder_view_drag_types,
3175 G_N_ELEMENTS (folder_view_drag_types),
3176 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3178 g_signal_connect (G_OBJECT (self),
3179 "drag_data_received",
3180 G_CALLBACK (on_drag_data_received),
3184 /* Set up the treeview as a dnd source */
3185 gtk_drag_source_set (GTK_WIDGET (self),
3187 folder_view_drag_types,
3188 G_N_ELEMENTS (folder_view_drag_types),
3189 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3191 g_signal_connect (G_OBJECT (self),
3193 G_CALLBACK (on_drag_motion),
3196 g_signal_connect (G_OBJECT (self),
3198 G_CALLBACK (on_drag_data_get),
3201 g_signal_connect (G_OBJECT (self),
3203 G_CALLBACK (drag_drop_cb),
3208 * This function manages the navigation through the folders using the
3209 * keyboard or the hardware keys in the device
3212 on_key_pressed (GtkWidget *self,
3216 GtkTreeSelection *selection;
3218 GtkTreeModel *model;
3219 gboolean retval = FALSE;
3221 /* Up and Down are automatically managed by the treeview */
3222 if (event->keyval == GDK_Return) {
3223 /* Expand/Collapse the selected row */
3224 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3225 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3228 path = gtk_tree_model_get_path (model, &iter);
3230 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3231 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3233 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3234 gtk_tree_path_free (path);
3236 /* No further processing */
3244 * We listen to the changes in the local folder account name key,
3245 * because we want to show the right name in the view. The local
3246 * folder account name corresponds to the device name in the Maemo
3247 * version. We do this because we do not want to query gconf on each
3248 * tree view refresh. It's better to cache it and change whenever
3252 on_configuration_key_changed (ModestConf* conf,
3254 ModestConfEvent event,
3255 ModestConfNotificationId id,
3256 ModestFolderView *self)
3258 ModestFolderViewPrivate *priv;
3261 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3262 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3264 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3265 g_free (priv->local_account_name);
3267 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3268 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3270 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3271 MODEST_CONF_DEVICE_NAME, NULL);
3273 /* Force a redraw */
3274 #if GTK_CHECK_VERSION(2, 8, 0)
3275 GtkTreeViewColumn * tree_column;
3277 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3279 gtk_tree_view_column_queue_resize (tree_column);
3281 gtk_widget_queue_draw (GTK_WIDGET (self));
3287 modest_folder_view_set_style (ModestFolderView *self,
3288 ModestFolderViewStyle style)
3290 ModestFolderViewPrivate *priv;
3292 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3293 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3294 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3296 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3299 priv->style = style;
3303 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3304 const gchar *account_id)
3306 ModestFolderViewPrivate *priv;
3307 GtkTreeModel *model;
3309 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3311 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3313 /* This will be used by the filter_row callback,
3314 * to decided which rows to show: */
3315 if (priv->visible_account_id) {
3316 g_free (priv->visible_account_id);
3317 priv->visible_account_id = NULL;
3320 priv->visible_account_id = g_strdup (account_id);
3323 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3324 if (GTK_IS_TREE_MODEL_FILTER (model))
3325 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3327 /* Save settings to gconf */
3328 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3329 MODEST_CONF_FOLDER_VIEW_KEY);
3331 /* Notify observers */
3332 g_signal_emit (G_OBJECT(self),
3333 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3338 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3340 ModestFolderViewPrivate *priv;
3342 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3344 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3346 return (const gchar *) priv->visible_account_id;
3350 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3354 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3356 gtk_tree_model_get (model, iter,
3360 gboolean result = FALSE;
3361 if (type == TNY_FOLDER_TYPE_INBOX) {
3365 *inbox_iter = *iter;
3369 if (gtk_tree_model_iter_children (model, &child, iter)) {
3370 if (find_inbox_iter (model, &child, inbox_iter))
3374 } while (gtk_tree_model_iter_next (model, iter));
3383 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3385 GtkTreeModel *model;
3386 GtkTreeIter iter, inbox_iter;
3387 GtkTreeSelection *sel;
3388 GtkTreePath *path = NULL;
3390 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3392 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3396 expand_root_items (self);
3397 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3399 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3400 g_warning ("%s: model is empty", __FUNCTION__);
3404 if (find_inbox_iter (model, &iter, &inbox_iter))
3405 path = gtk_tree_model_get_path (model, &inbox_iter);
3407 path = gtk_tree_path_new_first ();
3409 /* Select the row and free */
3410 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3411 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3412 gtk_tree_path_free (path);
3415 gtk_widget_grab_focus (GTK_WIDGET(self));
3421 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3426 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3427 TnyFolder* a_folder;
3430 gtk_tree_model_get (model, iter,
3431 INSTANCE_COLUMN, &a_folder,
3437 if (folder == a_folder) {
3438 g_object_unref (a_folder);
3439 *folder_iter = *iter;
3442 g_object_unref (a_folder);
3444 if (gtk_tree_model_iter_children (model, &child, iter)) {
3445 if (find_folder_iter (model, &child, folder_iter, folder))
3449 } while (gtk_tree_model_iter_next (model, iter));
3454 #ifndef MODEST_TOOLKIT_HILDON2
3456 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3459 ModestFolderView *self)
3461 ModestFolderViewPrivate *priv = NULL;
3462 GtkTreeSelection *sel;
3463 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3464 GObject *instance = NULL;
3466 if (!MODEST_IS_FOLDER_VIEW(self))
3469 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3471 priv->reexpand = TRUE;
3473 gtk_tree_model_get (tree_model, iter,
3475 INSTANCE_COLUMN, &instance,
3481 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3482 priv->folder_to_select = g_object_ref (instance);
3484 g_object_unref (instance);
3486 if (priv->folder_to_select) {
3488 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3491 path = gtk_tree_model_get_path (tree_model, iter);
3492 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3494 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3496 gtk_tree_selection_select_iter (sel, iter);
3497 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3499 gtk_tree_path_free (path);
3503 modest_folder_view_disable_next_folder_selection (self);
3505 /* Refilter the model */
3506 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3512 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3514 ModestFolderViewPrivate *priv;
3516 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3518 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3520 if (priv->folder_to_select)
3521 g_object_unref(priv->folder_to_select);
3523 priv->folder_to_select = NULL;
3527 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3528 gboolean after_change)
3530 GtkTreeModel *model;
3531 GtkTreeIter iter, folder_iter;
3532 GtkTreeSelection *sel;
3533 ModestFolderViewPrivate *priv = NULL;
3535 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3536 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3538 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3541 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3542 gtk_tree_selection_unselect_all (sel);
3544 if (priv->folder_to_select)
3545 g_object_unref(priv->folder_to_select);
3546 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3550 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3555 /* Refilter the model, before selecting the folder */
3556 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3558 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3559 g_warning ("%s: model is empty", __FUNCTION__);
3563 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3566 path = gtk_tree_model_get_path (model, &folder_iter);
3567 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3569 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3570 gtk_tree_selection_select_iter (sel, &folder_iter);
3571 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3573 gtk_tree_path_free (path);
3581 modest_folder_view_copy_selection (ModestFolderView *self)
3583 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3585 /* Copy selection */
3586 _clipboard_set_selected_data (self, FALSE);
3590 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3592 ModestFolderViewPrivate *priv = NULL;
3593 GtkTreeModel *model = NULL;
3594 const gchar **hidding = NULL;
3595 guint i, n_selected;
3597 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3598 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3600 /* Copy selection */
3601 if (!_clipboard_set_selected_data (folder_view, TRUE))
3604 /* Get hidding ids */
3605 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3607 /* Clear hidding array created by previous cut operation */
3608 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3610 /* Copy hidding array */
3611 priv->n_selected = n_selected;
3612 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3613 for (i=0; i < n_selected; i++)
3614 priv->hidding_ids[i] = g_strdup(hidding[i]);
3616 /* Hide cut folders */
3617 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3618 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3622 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3623 ModestFolderView *folder_view_dst)
3625 GtkTreeModel *filter_model = NULL;
3626 GtkTreeModel *model = NULL;
3627 GtkTreeModel *new_filter_model = NULL;
3629 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3630 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3633 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3634 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3636 /* Build new filter model */
3637 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3638 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3642 /* Set copied model */
3643 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3644 #ifndef MODEST_TOOLKIT_HILDON2
3645 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3646 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3650 g_object_unref (new_filter_model);
3654 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3657 GtkTreeModel *model = NULL;
3658 ModestFolderViewPrivate* priv;
3660 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3662 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3663 priv->show_non_move = show;
3664 /* modest_folder_view_update_model(folder_view, */
3665 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3667 /* Hide special folders */
3668 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3669 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3670 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3674 /* Returns FALSE if it did not selected anything */
3676 _clipboard_set_selected_data (ModestFolderView *folder_view,
3679 ModestFolderViewPrivate *priv = NULL;
3680 TnyFolderStore *folder = NULL;
3681 gboolean retval = FALSE;
3683 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3684 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3686 /* Set selected data on clipboard */
3687 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3688 folder = modest_folder_view_get_selected (folder_view);
3690 /* Do not allow to select an account */
3691 if (TNY_IS_FOLDER (folder)) {
3692 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3697 g_object_unref (folder);
3703 _clear_hidding_filter (ModestFolderView *folder_view)
3705 ModestFolderViewPrivate *priv;
3708 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3709 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3711 if (priv->hidding_ids != NULL) {
3712 for (i=0; i < priv->n_selected; i++)
3713 g_free (priv->hidding_ids[i]);
3714 g_free(priv->hidding_ids);
3720 on_display_name_changed (ModestAccountMgr *mgr,
3721 const gchar *account,
3724 ModestFolderView *self;
3726 self = MODEST_FOLDER_VIEW (user_data);
3728 /* Force a redraw */
3729 #if GTK_CHECK_VERSION(2, 8, 0)
3730 GtkTreeViewColumn * tree_column;
3732 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3734 gtk_tree_view_column_queue_resize (tree_column);
3736 gtk_widget_queue_draw (GTK_WIDGET (self));
3741 modest_folder_view_set_cell_style (ModestFolderView *self,
3742 ModestFolderViewCellStyle cell_style)
3744 ModestFolderViewPrivate *priv = NULL;
3746 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3747 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3749 priv->cell_style = cell_style;
3751 g_object_set (G_OBJECT (priv->messages_renderer),
3752 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3755 gtk_widget_queue_draw (GTK_WIDGET (self));
3759 update_style (ModestFolderView *self)
3761 ModestFolderViewPrivate *priv;
3762 GdkColor style_color;
3763 PangoAttrList *attr_list;
3765 PangoAttribute *attr;
3767 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3768 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3772 attr_list = pango_attr_list_new ();
3773 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3774 gdk_color_parse ("grey", &style_color);
3776 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3777 pango_attr_list_insert (attr_list, attr);
3780 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3782 "SmallSystemFont", NULL,
3785 attr = pango_attr_font_desc_new (pango_font_description_copy
3786 (style->font_desc));
3787 pango_attr_list_insert (attr_list, attr);
3789 g_object_set (G_OBJECT (priv->messages_renderer),
3790 "foreground-gdk", &style_color,
3791 "foreground-set", TRUE,
3792 "attributes", attr_list,
3794 pango_attr_list_unref (attr_list);
3799 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3801 if (strcmp ("style", spec->name) == 0) {
3802 update_style (MODEST_FOLDER_VIEW (obj));
3803 gtk_widget_queue_draw (GTK_WIDGET (obj));
3808 modest_folder_view_set_filter (ModestFolderView *self,
3809 ModestFolderViewFilter filter)
3811 ModestFolderViewPrivate *priv;
3812 GtkTreeModel *filter_model;
3814 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3815 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3817 priv->filter |= filter;
3819 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3820 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3821 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3826 modest_folder_view_unset_filter (ModestFolderView *self,
3827 ModestFolderViewFilter filter)
3829 ModestFolderViewPrivate *priv;
3830 GtkTreeModel *filter_model;
3832 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3833 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3835 priv->filter &= ~filter;
3837 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3838 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3839 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3844 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3845 ModestTnyFolderRules rules)
3847 GtkTreeModel *filter_model;
3849 gboolean fulfil = FALSE;
3851 if (!get_inner_models (self, &filter_model, NULL, NULL))
3854 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3858 TnyFolderStore *folder;
3860 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3862 if (TNY_IS_FOLDER (folder)) {
3863 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3864 /* Folder rules are negative: non_writable, non_deletable... */
3865 if (!(folder_rules & rules))
3868 g_object_unref (folder);
3871 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3877 modest_folder_view_set_list_to_move (ModestFolderView *self,
3880 ModestFolderViewPrivate *priv;
3882 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3883 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3885 if (priv->list_to_move)
3886 g_object_unref (priv->list_to_move);
3889 g_object_ref (list);
3891 priv->list_to_move = list;
3895 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3897 ModestFolderViewPrivate *priv;
3899 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3900 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3903 g_free (priv->mailbox);
3905 priv->mailbox = g_strdup (mailbox);
3907 /* Notify observers */
3908 g_signal_emit (G_OBJECT(self),
3909 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3910 priv->visible_account_id);
3914 modest_folder_view_get_mailbox (ModestFolderView *self)
3916 ModestFolderViewPrivate *priv;
3918 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3919 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3921 return (const gchar *) priv->mailbox;