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,
522 const gchar *mailbox,
524 gboolean multiaccount,
525 gboolean *use_markup)
529 TnyFolderType folder_type;
531 if (!TNY_IS_FOLDER (instance))
534 folder = (TnyFolder *) instance;
536 folder_type = tny_folder_get_folder_type (folder);
537 is_special = (get_cmp_pos (folder_type, folder)!= 4);
540 /* Remove mailbox prefix if any */
541 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
542 if (g_str_has_prefix (*item_name, prefix)) {
543 gchar *new_item_name;
545 new_item_name = g_strdup (*item_name + strlen (prefix));
546 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
547 g_free (new_item_name);
548 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
551 *item_name = new_item_name;
555 if (!is_special || multiaccount) {
556 TnyAccount *account = tny_folder_get_account (folder);
557 const gchar *folder_name;
558 gboolean concat_folder_name = FALSE;
561 /* Should not happen */
565 /* convert parent folders to dots */
566 convert_parent_folders_to_dots (item_name);
568 folder_name = tny_folder_get_name (folder);
569 if (g_str_has_suffix (*item_name, folder_name)) {
570 gchar *offset = g_strrstr (*item_name, folder_name);
572 concat_folder_name = TRUE;
575 buffer = g_string_new ("");
577 buffer = g_string_append (buffer, *item_name);
578 if (concat_folder_name) {
579 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
580 buffer = g_string_append (buffer, folder_name);
581 if (bold) buffer = g_string_append (buffer, "</span>");
584 g_object_unref (account);
586 *item_name = g_string_free (buffer, FALSE);
594 text_cell_data (GtkTreeViewColumn *column,
595 GtkCellRenderer *renderer,
596 GtkTreeModel *tree_model,
600 ModestFolderViewPrivate *priv;
601 GObject *rendobj = (GObject *) renderer;
603 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
604 GObject *instance = NULL;
605 gboolean use_markup = FALSE;
607 gtk_tree_model_get (tree_model, iter,
610 INSTANCE_COLUMN, &instance,
612 if (!fname || !instance)
615 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
616 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
618 gchar *item_name = NULL;
619 gint item_weight = 400;
621 if (type != TNY_FOLDER_TYPE_ROOT) {
625 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
626 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
627 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
628 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
630 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
633 /* Sometimes an special folder is reported by the server as
634 NORMAL, like some versions of Dovecot */
635 if (type == TNY_FOLDER_TYPE_NORMAL ||
636 type == TNY_FOLDER_TYPE_UNKNOWN) {
637 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
641 /* note: we cannot reliably get the counts from the
642 * tree model, we need to use explicit calls on
643 * tny_folder for some reason. Select the number to
644 * show: the unread or unsent messages. in case of
645 * outbox/drafts, show all */
646 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
647 (type == TNY_FOLDER_TYPE_OUTBOX) ||
648 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
649 number = tny_folder_get_all_count (TNY_FOLDER(instance));
652 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
656 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
657 item_name = g_strdup (fname);
664 /* Use bold font style if there are unread or unset messages */
666 item_name = g_strdup_printf ("%s (%d)", fname, number);
669 item_name = g_strdup (fname);
674 } else if (TNY_IS_ACCOUNT (instance)) {
675 /* If it's a server account */
676 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
677 item_name = g_strdup (priv->local_account_name);
679 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
680 /* fname is only correct when the items are first
681 * added to the model, not when the account is
682 * changed later, so get the name from the account
684 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
687 item_name = g_strdup (fname);
693 item_name = g_strdup ("unknown");
695 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
696 gboolean multiaccount;
698 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
699 /* Convert item_name to markup */
700 format_compact_style (&item_name, instance, priv->mailbox,
702 multiaccount, &use_markup);
705 if (item_name && item_weight) {
706 /* Set the name in the treeview cell: */
708 g_object_set (rendobj, "markup", item_name, NULL);
710 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
712 /* Notify display name observers */
713 /* TODO: What listens for this signal, and how can it use only the new name? */
714 if (((GObject *) priv->cur_folder_store) == instance) {
715 g_signal_emit (G_OBJECT(self),
716 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
723 /* If it is a Memory card account, make sure that we have the correct name.
724 * This function will be trigerred again when the name has been retrieved: */
725 if (TNY_IS_STORE_ACCOUNT (instance) &&
726 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
728 /* Get the account name asynchronously: */
729 GetMmcAccountNameData *callback_data =
730 g_slice_new0(GetMmcAccountNameData);
731 callback_data->self = self;
733 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
735 callback_data->previous_name = g_strdup (name);
737 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
738 on_get_mmc_account_name, callback_data);
742 g_object_unref (G_OBJECT (instance));
748 messages_cell_data (GtkTreeViewColumn *column,
749 GtkCellRenderer *renderer,
750 GtkTreeModel *tree_model,
754 ModestFolderView *self;
755 ModestFolderViewPrivate *priv;
756 GObject *rendobj = (GObject *) renderer;
757 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
758 GObject *instance = NULL;
759 gchar *item_name = NULL;
761 gtk_tree_model_get (tree_model, iter,
763 INSTANCE_COLUMN, &instance,
768 self = MODEST_FOLDER_VIEW (data);
769 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
772 if (type != TNY_FOLDER_TYPE_ROOT) {
776 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
777 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
778 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
780 /* Sometimes an special folder is reported by the server as
781 NORMAL, like some versions of Dovecot */
782 if (type == TNY_FOLDER_TYPE_NORMAL ||
783 type == TNY_FOLDER_TYPE_UNKNOWN) {
784 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
788 /* note: we cannot reliably get the counts from the tree model, we need
789 * to use explicit calls on tny_folder for some reason.
791 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
792 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
793 (type == TNY_FOLDER_TYPE_OUTBOX) ||
794 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
795 number = tny_folder_get_all_count (TNY_FOLDER(instance));
798 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
802 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
804 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
812 item_name = g_strdup ("");
815 /* Set the name in the treeview cell: */
816 g_object_set (rendobj,"text", item_name, NULL);
824 g_object_unref (G_OBJECT (instance));
830 GdkPixbuf *pixbuf_open;
831 GdkPixbuf *pixbuf_close;
835 static inline GdkPixbuf *
836 get_composite_pixbuf (const gchar *icon_name,
838 GdkPixbuf *base_pixbuf)
840 GdkPixbuf *emblem, *retval = NULL;
842 emblem = modest_platform_get_icon (icon_name, size);
844 retval = gdk_pixbuf_copy (base_pixbuf);
845 gdk_pixbuf_composite (emblem, retval, 0, 0,
846 MIN (gdk_pixbuf_get_width (emblem),
847 gdk_pixbuf_get_width (retval)),
848 MIN (gdk_pixbuf_get_height (emblem),
849 gdk_pixbuf_get_height (retval)),
850 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
851 g_object_unref (emblem);
856 static inline ThreePixbufs *
857 get_composite_icons (const gchar *icon_code,
859 GdkPixbuf **pixbuf_open,
860 GdkPixbuf **pixbuf_close)
862 ThreePixbufs *retval;
865 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
868 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
873 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
877 retval = g_slice_new0 (ThreePixbufs);
879 retval->pixbuf = g_object_ref (*pixbuf);
881 retval->pixbuf_open = g_object_ref (*pixbuf_open);
883 retval->pixbuf_close = g_object_ref (*pixbuf_close);
888 static inline ThreePixbufs*
889 get_folder_icons (TnyFolderType type, GObject *instance)
891 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
892 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
893 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
894 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
895 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
897 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
898 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
899 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
900 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
901 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
903 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
904 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
905 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
906 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
907 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
909 ThreePixbufs *retval = NULL;
911 /* Sometimes an special folder is reported by the server as
912 NORMAL, like some versions of Dovecot */
913 if (type == TNY_FOLDER_TYPE_NORMAL ||
914 type == TNY_FOLDER_TYPE_UNKNOWN) {
915 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
918 /* It's not enough with check the folder type. We need to
919 ensure that we're not giving a special folder icon to a
920 normal folder with the same name than a special folder */
921 if (TNY_IS_FOLDER (instance) &&
922 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
923 type = TNY_FOLDER_TYPE_NORMAL;
925 /* Remote folders should not be treated as special folders */
926 if (TNY_IS_FOLDER_STORE (instance) &&
927 !TNY_IS_ACCOUNT (instance) &&
928 type != TNY_FOLDER_TYPE_INBOX &&
929 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
930 #ifdef MODEST_TOOLKIT_HILDON2
931 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
934 &anorm_pixbuf_close);
936 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
939 &normal_pixbuf_close);
945 case TNY_FOLDER_TYPE_INVALID:
946 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
949 case TNY_FOLDER_TYPE_ROOT:
950 if (TNY_IS_ACCOUNT (instance)) {
952 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
953 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
956 &avirt_pixbuf_close);
958 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
960 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
961 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
966 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
969 &anorm_pixbuf_close);
974 case TNY_FOLDER_TYPE_INBOX:
975 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
978 &inbox_pixbuf_close);
980 case TNY_FOLDER_TYPE_OUTBOX:
981 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
984 &outbox_pixbuf_close);
986 case TNY_FOLDER_TYPE_JUNK:
987 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
992 case TNY_FOLDER_TYPE_SENT:
993 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
998 case TNY_FOLDER_TYPE_TRASH:
999 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1002 &trash_pixbuf_close);
1004 case TNY_FOLDER_TYPE_DRAFTS:
1005 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1008 &draft_pixbuf_close);
1010 case TNY_FOLDER_TYPE_ARCHIVE:
1011 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1016 case TNY_FOLDER_TYPE_NORMAL:
1018 /* Memory card folders could have an special icon */
1019 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1020 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1025 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1027 &normal_pixbuf_open,
1028 &normal_pixbuf_close);
1037 free_pixbufs (ThreePixbufs *pixbufs)
1039 if (pixbufs->pixbuf)
1040 g_object_unref (pixbufs->pixbuf);
1041 if (pixbufs->pixbuf_open)
1042 g_object_unref (pixbufs->pixbuf_open);
1043 if (pixbufs->pixbuf_close)
1044 g_object_unref (pixbufs->pixbuf_close);
1045 g_slice_free (ThreePixbufs, pixbufs);
1049 icon_cell_data (GtkTreeViewColumn *column,
1050 GtkCellRenderer *renderer,
1051 GtkTreeModel *tree_model,
1055 GObject *rendobj = NULL, *instance = NULL;
1056 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1057 gboolean has_children;
1058 ThreePixbufs *pixbufs;
1060 rendobj = (GObject *) renderer;
1062 gtk_tree_model_get (tree_model, iter,
1064 INSTANCE_COLUMN, &instance,
1070 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1071 pixbufs = get_folder_icons (type, instance);
1072 g_object_unref (instance);
1075 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1078 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1079 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1082 free_pixbufs (pixbufs);
1086 add_columns (GtkWidget *treeview)
1088 GtkTreeViewColumn *column;
1089 GtkCellRenderer *renderer;
1090 GtkTreeSelection *sel;
1091 ModestFolderViewPrivate *priv;
1093 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1096 column = gtk_tree_view_column_new ();
1098 /* Set icon and text render function */
1099 renderer = gtk_cell_renderer_pixbuf_new();
1100 #ifdef MODEST_TOOLKIT_HILDON2
1101 g_object_set (renderer,
1102 "xpad", MODEST_MARGIN_DEFAULT,
1103 "ypad", MODEST_MARGIN_DEFAULT,
1106 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1107 gtk_tree_view_column_set_cell_data_func(column, renderer,
1108 icon_cell_data, treeview, NULL);
1110 renderer = gtk_cell_renderer_text_new();
1111 g_object_set (renderer,
1112 #ifdef MODEST_TOOLKIT_HILDON2
1113 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1114 "ypad", MODEST_MARGIN_DEFAULT,
1116 "ellipsize", PANGO_ELLIPSIZE_END,
1118 "ellipsize-set", TRUE, NULL);
1119 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1120 gtk_tree_view_column_set_cell_data_func(column, renderer,
1121 text_cell_data, treeview, NULL);
1123 priv->messages_renderer = gtk_cell_renderer_text_new ();
1124 g_object_set (priv->messages_renderer,
1125 #ifdef MODEST_TOOLKIT_HILDON2
1127 "ypad", MODEST_MARGIN_DEFAULT,
1128 "xpad", MODEST_MARGIN_DOUBLE,
1130 "scale", PANGO_SCALE_X_SMALL,
1133 "alignment", PANGO_ALIGN_RIGHT,
1137 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1138 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1139 messages_cell_data, treeview, NULL);
1141 /* Set selection mode */
1142 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1143 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1145 /* Set treeview appearance */
1146 gtk_tree_view_column_set_spacing (column, 2);
1147 gtk_tree_view_column_set_resizable (column, TRUE);
1148 gtk_tree_view_column_set_fixed_width (column, TRUE);
1149 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1150 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1153 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1157 modest_folder_view_init (ModestFolderView *obj)
1159 ModestFolderViewPrivate *priv;
1162 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1164 priv->timer_expander = 0;
1165 priv->account_store = NULL;
1167 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1168 priv->cur_folder_store = NULL;
1169 priv->visible_account_id = NULL;
1170 priv->mailbox = NULL;
1171 priv->folder_to_select = NULL;
1172 priv->outbox_deleted_handler = 0;
1173 priv->reexpand = TRUE;
1175 /* Initialize the local account name */
1176 conf = modest_runtime_get_conf();
1177 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1179 /* Init email clipboard */
1180 priv->clipboard = modest_runtime_get_email_clipboard ();
1181 priv->hidding_ids = NULL;
1182 priv->n_selected = 0;
1183 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1184 priv->reselect = FALSE;
1185 priv->show_non_move = TRUE;
1186 priv->list_to_move = NULL;
1188 /* Build treeview */
1189 add_columns (GTK_WIDGET (obj));
1191 /* Setup drag and drop */
1192 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1194 /* Connect signals */
1195 g_signal_connect (G_OBJECT (obj),
1197 G_CALLBACK (on_key_pressed), NULL);
1199 priv->display_name_changed_signal =
1200 g_signal_connect (modest_runtime_get_account_mgr (),
1201 "display_name_changed",
1202 G_CALLBACK (on_display_name_changed),
1206 * Track changes in the local account name (in the device it
1207 * will be the device name)
1209 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1211 G_CALLBACK(on_configuration_key_changed),
1215 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1221 tny_account_store_view_init (gpointer g, gpointer iface_data)
1223 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1225 klass->set_account_store = modest_folder_view_set_account_store;
1229 modest_folder_view_finalize (GObject *obj)
1231 ModestFolderViewPrivate *priv;
1232 GtkTreeSelection *sel;
1233 TnyAccount *local_account;
1235 g_return_if_fail (obj);
1237 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1239 if (priv->timer_expander != 0) {
1240 g_source_remove (priv->timer_expander);
1241 priv->timer_expander = 0;
1244 local_account = (TnyAccount *)
1245 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1246 if (local_account) {
1247 if (g_signal_handler_is_connected (local_account,
1248 priv->outbox_deleted_handler))
1249 g_signal_handler_disconnect (local_account,
1250 priv->outbox_deleted_handler);
1251 g_object_unref (local_account);
1254 if (priv->account_store) {
1255 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1256 priv->account_inserted_signal);
1257 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1258 priv->account_removed_signal);
1259 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1260 priv->account_changed_signal);
1261 g_object_unref (G_OBJECT(priv->account_store));
1262 priv->account_store = NULL;
1265 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1266 priv->display_name_changed_signal)) {
1267 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1268 priv->display_name_changed_signal);
1269 priv->display_name_changed_signal = 0;
1273 g_object_unref (G_OBJECT (priv->query));
1277 if (priv->folder_to_select) {
1278 g_object_unref (G_OBJECT(priv->folder_to_select));
1279 priv->folder_to_select = NULL;
1282 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1284 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1286 g_free (priv->local_account_name);
1287 g_free (priv->visible_account_id);
1288 g_free (priv->mailbox);
1290 if (priv->conf_key_signal) {
1291 g_signal_handler_disconnect (modest_runtime_get_conf (),
1292 priv->conf_key_signal);
1293 priv->conf_key_signal = 0;
1296 if (priv->cur_folder_store) {
1297 g_object_unref (priv->cur_folder_store);
1298 priv->cur_folder_store = NULL;
1301 if (priv->list_to_move) {
1302 g_object_unref (priv->list_to_move);
1303 priv->list_to_move = NULL;
1306 /* Clear hidding array created by cut operation */
1307 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1309 G_OBJECT_CLASS(parent_class)->finalize (obj);
1314 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1316 ModestFolderViewPrivate *priv;
1319 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1320 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1322 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1323 device = tny_account_store_get_device (account_store);
1325 if (G_UNLIKELY (priv->account_store)) {
1327 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1328 priv->account_inserted_signal))
1329 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1330 priv->account_inserted_signal);
1331 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1332 priv->account_removed_signal))
1333 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1334 priv->account_removed_signal);
1335 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1336 priv->account_changed_signal))
1337 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1338 priv->account_changed_signal);
1339 g_object_unref (G_OBJECT (priv->account_store));
1342 priv->account_store = g_object_ref (G_OBJECT (account_store));
1344 priv->account_removed_signal =
1345 g_signal_connect (G_OBJECT(account_store), "account_removed",
1346 G_CALLBACK (on_account_removed), self);
1348 priv->account_inserted_signal =
1349 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1350 G_CALLBACK (on_account_inserted), self);
1352 priv->account_changed_signal =
1353 g_signal_connect (G_OBJECT(account_store), "account_changed",
1354 G_CALLBACK (on_account_changed), self);
1356 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1357 priv->reselect = FALSE;
1358 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1360 g_object_unref (G_OBJECT (device));
1364 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1367 ModestFolderView *self;
1368 GtkTreeModel *model, *filter_model;
1371 self = MODEST_FOLDER_VIEW (user_data);
1373 if (!get_inner_models (self, &filter_model, NULL, &model))
1376 /* Remove outbox from model */
1377 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1378 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1379 g_object_unref (outbox);
1382 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1386 on_account_inserted (TnyAccountStore *account_store,
1387 TnyAccount *account,
1390 ModestFolderViewPrivate *priv;
1391 GtkTreeModel *model, *filter_model;
1393 /* Ignore transport account insertions, we're not showing them
1394 in the folder view */
1395 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1398 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1401 /* If we're adding a new account, and there is no previous
1402 one, we need to select the visible server account */
1403 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1404 !priv->visible_account_id)
1405 modest_widget_memory_restore (modest_runtime_get_conf(),
1406 G_OBJECT (user_data),
1407 MODEST_CONF_FOLDER_VIEW_KEY);
1411 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1412 &filter_model, NULL, &model))
1415 /* Insert the account in the model */
1416 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1418 /* When the model is a list store (plain representation) the
1419 outbox is not a child of any account so we have to manually
1420 delete it because removing the local folders account won't
1421 delete it (because tny_folder_get_account() is not defined
1422 for a merge folder */
1423 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1424 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1426 priv->outbox_deleted_handler =
1427 g_signal_connect (account,
1429 G_CALLBACK (on_outbox_deleted_cb),
1433 /* Refilter the model */
1434 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1439 same_account_selected (ModestFolderView *self,
1440 TnyAccount *account)
1442 ModestFolderViewPrivate *priv;
1443 gboolean same_account = FALSE;
1445 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1447 if (priv->cur_folder_store) {
1448 TnyAccount *selected_folder_account = NULL;
1450 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1451 selected_folder_account =
1452 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1454 selected_folder_account =
1455 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1458 if (selected_folder_account == account)
1459 same_account = TRUE;
1461 g_object_unref (selected_folder_account);
1463 return same_account;
1468 * Selects the first inbox or the local account in an idle
1471 on_idle_select_first_inbox_or_local (gpointer user_data)
1473 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1475 gdk_threads_enter ();
1476 modest_folder_view_select_first_inbox_or_local (self);
1477 gdk_threads_leave ();
1483 on_account_changed (TnyAccountStore *account_store,
1484 TnyAccount *tny_account,
1487 ModestFolderView *self;
1488 ModestFolderViewPrivate *priv;
1489 GtkTreeModel *model, *filter_model;
1490 GtkTreeSelection *sel;
1491 gboolean same_account;
1493 /* Ignore transport account insertions, we're not showing them
1494 in the folder view */
1495 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1498 self = MODEST_FOLDER_VIEW (user_data);
1499 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1501 /* Get the inner model */
1502 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1503 &filter_model, NULL, &model))
1506 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1508 /* Invalidate the cur_folder_store only if the selected folder
1509 belongs to the account that is being removed */
1510 same_account = same_account_selected (self, tny_account);
1512 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1513 gtk_tree_selection_unselect_all (sel);
1516 /* Remove the account from the model */
1517 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1519 /* Insert the account in the model */
1520 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1522 /* Refilter the model */
1523 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1525 /* Select the first INBOX if the currently selected folder
1526 belongs to the account that is being deleted */
1527 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1528 g_idle_add (on_idle_select_first_inbox_or_local, self);
1532 on_account_removed (TnyAccountStore *account_store,
1533 TnyAccount *account,
1536 ModestFolderView *self = NULL;
1537 ModestFolderViewPrivate *priv;
1538 GtkTreeModel *model, *filter_model;
1539 GtkTreeSelection *sel = NULL;
1540 gboolean same_account = FALSE;
1542 /* Ignore transport account removals, we're not showing them
1543 in the folder view */
1544 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1547 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1548 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1552 self = MODEST_FOLDER_VIEW (user_data);
1553 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1555 /* Invalidate the cur_folder_store only if the selected folder
1556 belongs to the account that is being removed */
1557 same_account = same_account_selected (self, account);
1559 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1560 gtk_tree_selection_unselect_all (sel);
1563 /* Invalidate row to select only if the folder to select
1564 belongs to the account that is being removed*/
1565 if (priv->folder_to_select) {
1566 TnyAccount *folder_to_select_account = NULL;
1568 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1569 if (folder_to_select_account == account) {
1570 modest_folder_view_disable_next_folder_selection (self);
1571 g_object_unref (priv->folder_to_select);
1572 priv->folder_to_select = NULL;
1574 g_object_unref (folder_to_select_account);
1577 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1578 &filter_model, NULL, &model))
1581 /* Disconnect the signal handler */
1582 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1583 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1584 if (g_signal_handler_is_connected (account,
1585 priv->outbox_deleted_handler))
1586 g_signal_handler_disconnect (account,
1587 priv->outbox_deleted_handler);
1590 /* Remove the account from the model */
1591 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1593 /* If the removed account is the currently viewed one then
1594 clear the configuration value. The new visible account will be the default account */
1595 if (priv->visible_account_id &&
1596 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1598 /* Clear the current visible account_id */
1599 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1600 modest_folder_view_set_mailbox (self, NULL);
1602 /* Call the restore method, this will set the new visible account */
1603 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1604 MODEST_CONF_FOLDER_VIEW_KEY);
1607 /* Refilter the model */
1608 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1610 /* Select the first INBOX if the currently selected folder
1611 belongs to the account that is being deleted */
1613 g_idle_add (on_idle_select_first_inbox_or_local, self);
1617 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1619 GtkTreeViewColumn *col;
1621 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1623 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1625 g_printerr ("modest: failed get column for title\n");
1629 gtk_tree_view_column_set_title (col, title);
1630 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1635 modest_folder_view_on_map (ModestFolderView *self,
1636 GdkEventExpose *event,
1639 ModestFolderViewPrivate *priv;
1641 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1643 /* This won't happen often */
1644 if (G_UNLIKELY (priv->reselect)) {
1645 /* Select the first inbox or the local account if not found */
1647 /* TODO: this could cause a lock at startup, so we
1648 comment it for the moment. We know that this will
1649 be a bug, because the INBOX is not selected, but we
1650 need to rewrite some parts of Modest to avoid the
1651 deathlock situation */
1652 /* TODO: check if this is still the case */
1653 priv->reselect = FALSE;
1654 modest_folder_view_select_first_inbox_or_local (self);
1655 /* Notify the display name observers */
1656 g_signal_emit (G_OBJECT(self),
1657 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1661 if (priv->reexpand) {
1662 expand_root_items (self);
1663 priv->reexpand = FALSE;
1670 modest_folder_view_new (TnyFolderStoreQuery *query)
1673 ModestFolderViewPrivate *priv;
1674 GtkTreeSelection *sel;
1676 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1677 #ifdef MODEST_TOOLKIT_HILDON2
1678 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1681 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1684 priv->query = g_object_ref (query);
1686 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1687 priv->changed_signal = g_signal_connect (sel, "changed",
1688 G_CALLBACK (on_selection_changed), self);
1690 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1692 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1694 return GTK_WIDGET(self);
1697 /* this feels dirty; any other way to expand all the root items? */
1699 expand_root_items (ModestFolderView *self)
1702 GtkTreeModel *model;
1705 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1706 path = gtk_tree_path_new_first ();
1708 /* all folders should have child items, so.. */
1710 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1711 gtk_tree_path_next (path);
1712 } while (gtk_tree_model_get_iter (model, &iter, path));
1714 gtk_tree_path_free (path);
1718 is_parent_of (TnyFolder *a, TnyFolder *b)
1721 gboolean retval = FALSE;
1723 a_id = tny_folder_get_id (a);
1725 gchar *string_to_match;
1728 string_to_match = g_strconcat (a_id, "/", NULL);
1729 b_id = tny_folder_get_id (b);
1730 retval = g_str_has_prefix (b_id, string_to_match);
1731 g_free (string_to_match);
1737 typedef struct _ForeachFolderInfo {
1740 } ForeachFolderInfo;
1743 foreach_folder_with_id (GtkTreeModel *model,
1748 ForeachFolderInfo *info;
1751 info = (ForeachFolderInfo *) data;
1752 gtk_tree_model_get (model, iter,
1753 INSTANCE_COLUMN, &instance,
1756 if (TNY_IS_FOLDER (instance)) {
1759 id = tny_folder_get_id (TNY_FOLDER (instance));
1761 collate = g_utf8_collate_key (id, -1);
1762 info->found = !strcmp (info->needle, collate);
1773 has_folder_with_id (ModestFolderView *self, const gchar *id)
1775 GtkTreeModel *model;
1776 ForeachFolderInfo info = {NULL, FALSE};
1778 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1779 info.needle = g_utf8_collate_key (id, -1);
1781 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1782 g_free (info.needle);
1788 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1791 gboolean retval = FALSE;
1793 a_id = tny_folder_get_id (a);
1796 b_id = tny_folder_get_id (b);
1799 const gchar *last_bar;
1800 gchar *string_to_match;
1801 last_bar = g_strrstr (b_id, "/");
1806 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1807 retval = has_folder_with_id (self, string_to_match);
1808 g_free (string_to_match);
1816 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1818 ModestFolderViewPrivate *priv;
1819 TnyIterator *iterator;
1820 gboolean retval = TRUE;
1822 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1823 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1825 for (iterator = tny_list_create_iterator (priv->list_to_move);
1826 retval && !tny_iterator_is_done (iterator);
1827 tny_iterator_next (iterator)) {
1829 instance = tny_iterator_get_current (iterator);
1830 if (instance == (GObject *) folder) {
1832 } else if (TNY_IS_FOLDER (instance)) {
1833 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1835 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1838 g_object_unref (instance);
1840 g_object_unref (iterator);
1847 * We use this function to implement the
1848 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1849 * account in this case, and the local folders.
1852 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1854 ModestFolderViewPrivate *priv;
1855 gboolean retval = TRUE;
1856 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1857 GObject *instance = NULL;
1858 const gchar *id = NULL;
1860 gboolean found = FALSE;
1861 gboolean cleared = FALSE;
1862 ModestTnyFolderRules rules = 0;
1865 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1866 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1868 gtk_tree_model_get (model, iter,
1869 NAME_COLUMN, &fname,
1871 INSTANCE_COLUMN, &instance,
1874 /* Do not show if there is no instance, this could indeed
1875 happen when the model is being modified while it's being
1876 drawn. This could occur for example when moving folders
1883 if (TNY_IS_ACCOUNT (instance)) {
1884 TnyAccount *acc = TNY_ACCOUNT (instance);
1885 const gchar *account_id = tny_account_get_id (acc);
1887 /* If it isn't a special folder,
1888 * don't show it unless it is the visible account: */
1889 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1890 !modest_tny_account_is_virtual_local_folders (acc) &&
1891 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1893 /* Show only the visible account id */
1894 if (priv->visible_account_id) {
1895 if (strcmp (account_id, priv->visible_account_id))
1902 /* Never show these to the user. They are merged into one folder
1903 * in the local-folders account instead: */
1904 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1907 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1908 /* Only show special folders for current account if needed */
1909 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1910 TnyAccount *account;
1912 account = tny_folder_get_account (TNY_FOLDER (instance));
1914 if (TNY_IS_ACCOUNT (account)) {
1915 const gchar *account_id = tny_account_get_id (account);
1917 if (!modest_tny_account_is_virtual_local_folders (account) &&
1918 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1919 /* Show only the visible account id */
1920 if (priv->visible_account_id) {
1921 if (strcmp (account_id, priv->visible_account_id)) {
1923 } else if (priv->mailbox) {
1924 /* Filter mailboxes */
1925 if (!g_str_has_prefix (fname, priv->mailbox)) {
1927 } else if (!strcmp (fname, priv->mailbox)) {
1928 /* Hide mailbox parent */
1934 g_object_unref (account);
1941 /* Check hiding (if necessary) */
1942 cleared = modest_email_clipboard_cleared (priv->clipboard);
1943 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1944 id = tny_folder_get_id (TNY_FOLDER(instance));
1945 if (priv->hidding_ids != NULL)
1946 for (i=0; i < priv->n_selected && !found; i++)
1947 if (priv->hidding_ids[i] != NULL && id != NULL)
1948 found = (!strcmp (priv->hidding_ids[i], id));
1953 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1954 folder as no message can be move there according to UI specs */
1955 if (retval && !priv->show_non_move) {
1956 if (priv->list_to_move &&
1957 tny_list_get_length (priv->list_to_move) > 0 &&
1958 TNY_IS_FOLDER (instance)) {
1959 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1961 if (retval && TNY_IS_FOLDER (instance) &&
1962 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1964 case TNY_FOLDER_TYPE_OUTBOX:
1965 case TNY_FOLDER_TYPE_SENT:
1966 case TNY_FOLDER_TYPE_DRAFTS:
1969 case TNY_FOLDER_TYPE_UNKNOWN:
1970 case TNY_FOLDER_TYPE_NORMAL:
1971 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1972 if (type == TNY_FOLDER_TYPE_INVALID)
1973 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1975 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1976 type == TNY_FOLDER_TYPE_SENT
1977 || type == TNY_FOLDER_TYPE_DRAFTS)
1986 /* apply special filters */
1987 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1988 if (TNY_IS_ACCOUNT (instance))
1992 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
1993 if (TNY_IS_FOLDER (instance))
1997 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
1998 if (TNY_IS_ACCOUNT (instance)) {
1999 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2001 } else if (TNY_IS_FOLDER (instance)) {
2002 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2007 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2008 if (TNY_IS_ACCOUNT (instance)) {
2009 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2011 } else if (TNY_IS_FOLDER (instance)) {
2012 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2017 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2018 /* A mailbox is a fake folder with an @ in the middle of the name */
2019 if (!TNY_IS_FOLDER (instance) ||
2020 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2023 const gchar *folder_name;
2024 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2025 if (!folder_name || strchr (folder_name, '@') == NULL)
2031 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2032 if (TNY_IS_FOLDER (instance)) {
2033 /* Check folder rules */
2034 ModestTnyFolderRules rules;
2036 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2037 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2038 } else if (TNY_IS_ACCOUNT (instance)) {
2039 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2047 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2048 if (TNY_IS_FOLDER (instance)) {
2049 TnyFolderType guess_type;
2051 if (TNY_FOLDER_TYPE_NORMAL) {
2052 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2058 case TNY_FOLDER_TYPE_OUTBOX:
2059 case TNY_FOLDER_TYPE_SENT:
2060 case TNY_FOLDER_TYPE_DRAFTS:
2061 case TNY_FOLDER_TYPE_ARCHIVE:
2062 case TNY_FOLDER_TYPE_INBOX:
2065 case TNY_FOLDER_TYPE_UNKNOWN:
2066 case TNY_FOLDER_TYPE_NORMAL:
2072 } else if (TNY_IS_ACCOUNT (instance)) {
2077 if (retval && TNY_IS_FOLDER (instance)) {
2078 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2081 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2082 if (TNY_IS_FOLDER (instance)) {
2083 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2084 } else if (TNY_IS_ACCOUNT (instance)) {
2089 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2090 if (TNY_IS_FOLDER (instance)) {
2091 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2092 } else if (TNY_IS_ACCOUNT (instance)) {
2097 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2098 if (TNY_IS_FOLDER (instance)) {
2099 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2100 } else if (TNY_IS_ACCOUNT (instance)) {
2106 g_object_unref (instance);
2114 modest_folder_view_update_model (ModestFolderView *self,
2115 TnyAccountStore *account_store)
2117 ModestFolderViewPrivate *priv;
2118 GtkTreeModel *model /* , *old_model */;
2119 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2121 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2122 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2125 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2127 /* Notify that there is no folder selected */
2128 g_signal_emit (G_OBJECT(self),
2129 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2131 if (priv->cur_folder_store) {
2132 g_object_unref (priv->cur_folder_store);
2133 priv->cur_folder_store = NULL;
2136 /* FIXME: the local accounts are not shown when the query
2137 selects only the subscribed folders */
2138 #ifdef MODEST_TOOLKIT_HILDON2
2139 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2140 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2141 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2142 MODEST_FOLDER_PATH_SEPARATOR);
2144 model = tny_gtk_folder_store_tree_model_new (NULL);
2147 /* When the model is a list store (plain representation) the
2148 outbox is not a child of any account so we have to manually
2149 delete it because removing the local folders account won't
2150 delete it (because tny_folder_get_account() is not defined
2151 for a merge folder */
2152 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2153 TnyAccount *account;
2154 ModestTnyAccountStore *acc_store;
2156 acc_store = modest_runtime_get_account_store ();
2157 account = modest_tny_account_store_get_local_folders_account (acc_store);
2159 if (g_signal_handler_is_connected (account,
2160 priv->outbox_deleted_handler))
2161 g_signal_handler_disconnect (account,
2162 priv->outbox_deleted_handler);
2164 priv->outbox_deleted_handler =
2165 g_signal_connect (account,
2167 G_CALLBACK (on_outbox_deleted_cb),
2169 g_object_unref (account);
2172 /* Get the accounts: */
2173 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2175 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2177 sortable = gtk_tree_model_sort_new_with_model (model);
2178 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2180 GTK_SORT_ASCENDING);
2181 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2183 cmp_rows, NULL, NULL);
2185 /* Create filter model */
2186 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2187 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2193 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2194 #ifndef MODEST_TOOLKIT_HILDON2
2195 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2196 (GCallback) on_row_inserted_maybe_select_folder, self);
2199 g_object_unref (model);
2200 g_object_unref (filter_model);
2201 g_object_unref (sortable);
2203 /* Force a reselection of the INBOX next time the widget is shown */
2204 priv->reselect = TRUE;
2211 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2213 GtkTreeModel *model = NULL;
2214 TnyFolderStore *folder = NULL;
2216 ModestFolderView *tree_view = NULL;
2217 ModestFolderViewPrivate *priv = NULL;
2218 gboolean selected = FALSE;
2220 g_return_if_fail (sel);
2221 g_return_if_fail (user_data);
2223 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2225 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2227 tree_view = MODEST_FOLDER_VIEW (user_data);
2230 gtk_tree_model_get (model, &iter,
2231 INSTANCE_COLUMN, &folder,
2234 /* If the folder is the same do not notify */
2235 if (folder && priv->cur_folder_store == folder) {
2236 g_object_unref (folder);
2241 /* Current folder was unselected */
2242 if (priv->cur_folder_store) {
2243 /* We must do this firstly because a libtinymail-camel
2244 implementation detail. If we issue the signal
2245 before doing the sync_async, then that signal could
2246 cause (and it actually does it) a free of the
2247 summary of the folder (because the main window will
2248 clear the headers view */
2249 if (TNY_IS_FOLDER(priv->cur_folder_store))
2250 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2251 FALSE, NULL, NULL, NULL);
2253 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2254 priv->cur_folder_store, FALSE);
2256 g_object_unref (priv->cur_folder_store);
2257 priv->cur_folder_store = NULL;
2260 /* New current references */
2261 priv->cur_folder_store = folder;
2263 /* New folder has been selected. Do not notify if there is
2264 nothing new selected */
2266 g_signal_emit (G_OBJECT(tree_view),
2267 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2268 0, priv->cur_folder_store, TRUE);
2273 on_row_activated (GtkTreeView *treeview,
2274 GtkTreePath *treepath,
2275 GtkTreeViewColumn *column,
2278 GtkTreeModel *model = NULL;
2279 TnyFolderStore *folder = NULL;
2281 ModestFolderView *self = NULL;
2282 ModestFolderViewPrivate *priv = NULL;
2284 g_return_if_fail (treeview);
2285 g_return_if_fail (user_data);
2287 self = MODEST_FOLDER_VIEW (user_data);
2288 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2290 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2292 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2295 gtk_tree_model_get (model, &iter,
2296 INSTANCE_COLUMN, &folder,
2299 g_signal_emit (G_OBJECT(self),
2300 signals[FOLDER_ACTIVATED_SIGNAL],
2303 #ifdef MODEST_TOOLKIT_HILDON2
2304 HildonUIMode ui_mode;
2305 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2306 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2307 if (priv->cur_folder_store)
2308 g_object_unref (priv->cur_folder_store);
2309 priv->cur_folder_store = g_object_ref (folder);
2313 g_object_unref (folder);
2317 modest_folder_view_get_selected (ModestFolderView *self)
2319 ModestFolderViewPrivate *priv;
2321 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2323 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2324 if (priv->cur_folder_store)
2325 g_object_ref (priv->cur_folder_store);
2327 return priv->cur_folder_store;
2331 get_cmp_rows_type_pos (GObject *folder)
2333 /* Remote accounts -> Local account -> MMC account .*/
2336 if (TNY_IS_ACCOUNT (folder) &&
2337 modest_tny_account_is_virtual_local_folders (
2338 TNY_ACCOUNT (folder))) {
2340 } else if (TNY_IS_ACCOUNT (folder)) {
2341 TnyAccount *account = TNY_ACCOUNT (folder);
2342 const gchar *account_id = tny_account_get_id (account);
2343 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2349 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2350 return -1; /* Should never happen */
2355 inbox_is_special (TnyFolderStore *folder_store)
2357 gboolean is_special = TRUE;
2359 if (TNY_IS_FOLDER (folder_store)) {
2363 gchar *last_inbox_bar;
2365 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2366 downcase = g_utf8_strdown (id, -1);
2367 last_bar = g_strrstr (downcase, "/");
2369 last_inbox_bar = g_strrstr (downcase, "inbox/");
2370 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2381 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2383 TnyAccount *account;
2384 gboolean is_special;
2385 /* Inbox, Outbox, Drafts, Sent, User */
2388 if (!TNY_IS_FOLDER (folder_store))
2391 case TNY_FOLDER_TYPE_INBOX:
2393 account = tny_folder_get_account (folder_store);
2394 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2396 /* In inbox case we need to know if the inbox is really the top
2397 * inbox of the account, or if it's a submailbox inbox. To do
2398 * this we'll apply an heuristic rule: Find last "/" and check
2399 * if it's preceeded by another Inbox */
2400 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2401 g_object_unref (account);
2402 return is_special?0:4;
2405 case TNY_FOLDER_TYPE_OUTBOX:
2406 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2408 case TNY_FOLDER_TYPE_DRAFTS:
2410 account = tny_folder_get_account (folder_store);
2411 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2412 g_object_unref (account);
2413 return is_special?1:4;
2416 case TNY_FOLDER_TYPE_SENT:
2418 account = tny_folder_get_account (folder_store);
2419 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2420 g_object_unref (account);
2421 return is_special?3:4;
2430 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2432 const gchar *a1_name, *a2_name;
2434 a1_name = tny_account_get_name (a1);
2435 a2_name = tny_account_get_name (a2);
2437 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2441 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2443 TnyAccount *a1 = NULL, *a2 = NULL;
2446 if (TNY_IS_ACCOUNT (s1)) {
2447 a1 = TNY_ACCOUNT (g_object_ref (s1));
2448 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2449 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2452 if (TNY_IS_ACCOUNT (s2)) {
2453 a2 = TNY_ACCOUNT (g_object_ref (s2));
2454 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2455 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2472 /* First we sort with the type of account */
2473 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2477 cmp = compare_account_names (a1, a2);
2481 g_object_unref (a1);
2483 g_object_unref (a2);
2489 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2491 gint is_account1, is_account2;
2493 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2494 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2496 return is_account2 - is_account1;
2500 * This function orders the mail accounts according to these rules:
2501 * 1st - remote accounts
2502 * 2nd - local account
2506 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2510 gchar *name1 = NULL;
2511 gchar *name2 = NULL;
2512 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2513 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2514 GObject *folder1 = NULL;
2515 GObject *folder2 = NULL;
2517 gtk_tree_model_get (tree_model, iter1,
2518 NAME_COLUMN, &name1,
2520 INSTANCE_COLUMN, &folder1,
2522 gtk_tree_model_get (tree_model, iter2,
2523 NAME_COLUMN, &name2,
2524 TYPE_COLUMN, &type2,
2525 INSTANCE_COLUMN, &folder2,
2528 /* Return if we get no folder. This could happen when folder
2529 operations are happening. The model is updated after the
2530 folder copy/move actually occurs, so there could be
2531 situations where the model to be drawn is not correct */
2532 if (!folder1 || !folder2)
2535 /* Sort by type. First the special folders, then the archives */
2536 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2540 /* Now we sort using the account of each folder */
2541 if (TNY_IS_FOLDER_STORE (folder1) &&
2542 TNY_IS_FOLDER_STORE (folder2)) {
2543 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2547 /* Each group is preceeded by its account */
2548 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2553 /* Pure sort by name */
2554 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2557 g_object_unref(G_OBJECT(folder1));
2559 g_object_unref(G_OBJECT(folder2));
2567 /*****************************************************************************/
2568 /* DRAG and DROP stuff */
2569 /*****************************************************************************/
2571 * This function fills the #GtkSelectionData with the row and the
2572 * model that has been dragged. It's called when this widget is a
2573 * source for dnd after the event drop happened
2576 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2577 guint info, guint time, gpointer data)
2579 GtkTreeSelection *selection;
2580 GtkTreeModel *model;
2582 GtkTreePath *source_row;
2584 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2585 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2587 source_row = gtk_tree_model_get_path (model, &iter);
2588 gtk_tree_set_row_drag_data (selection_data,
2592 gtk_tree_path_free (source_row);
2596 typedef struct _DndHelper {
2597 ModestFolderView *folder_view;
2598 gboolean delete_source;
2599 GtkTreePath *source_row;
2603 dnd_helper_destroyer (DndHelper *helper)
2605 /* Free the helper */
2606 gtk_tree_path_free (helper->source_row);
2607 g_slice_free (DndHelper, helper);
2611 xfer_folder_cb (ModestMailOperation *mail_op,
2612 TnyFolder *new_folder,
2616 /* Select the folder */
2617 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2623 /* get the folder for the row the treepath refers to. */
2624 /* folder must be unref'd */
2625 static TnyFolderStore *
2626 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2629 TnyFolderStore *folder = NULL;
2631 if (gtk_tree_model_get_iter (model,&iter, path))
2632 gtk_tree_model_get (model, &iter,
2633 INSTANCE_COLUMN, &folder,
2640 * This function is used by drag_data_received_cb to manage drag and
2641 * drop of a header, i.e, and drag from the header view to the folder
2645 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2646 GtkTreeModel *dest_model,
2647 GtkTreePath *dest_row,
2648 GtkSelectionData *selection_data)
2650 TnyList *headers = NULL;
2651 TnyFolder *folder = NULL, *src_folder = NULL;
2652 TnyFolderType folder_type;
2653 GtkTreeIter source_iter, dest_iter;
2654 ModestWindowMgr *mgr = NULL;
2655 ModestWindow *main_win = NULL;
2656 gchar **uris, **tmp;
2658 /* Build the list of headers */
2659 mgr = modest_runtime_get_window_mgr ();
2660 headers = tny_simple_list_new ();
2661 uris = modest_dnd_selection_data_get_paths (selection_data);
2664 while (*tmp != NULL) {
2667 gboolean first = TRUE;
2670 path = gtk_tree_path_new_from_string (*tmp);
2671 gtk_tree_model_get_iter (source_model, &source_iter, path);
2672 gtk_tree_model_get (source_model, &source_iter,
2673 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2676 /* Do not enable d&d of headers already opened */
2677 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2678 tny_list_append (headers, G_OBJECT (header));
2680 if (G_UNLIKELY (first)) {
2681 src_folder = tny_header_get_folder (header);
2685 /* Free and go on */
2686 gtk_tree_path_free (path);
2687 g_object_unref (header);
2692 /* This could happen ig we perform a d&d very quickly over the
2693 same row that row could dissapear because message is
2695 if (!TNY_IS_FOLDER (src_folder))
2698 /* Get the target folder */
2699 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2700 gtk_tree_model_get (dest_model, &dest_iter,
2704 if (!folder || !TNY_IS_FOLDER(folder)) {
2705 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2709 folder_type = modest_tny_folder_guess_folder_type (folder);
2710 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2711 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2712 goto cleanup; /* cannot move messages there */
2715 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2716 /* g_warning ("folder not writable"); */
2717 goto cleanup; /* verboten! */
2720 /* Ask for confirmation to move */
2721 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2723 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2727 /* Transfer messages */
2728 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2733 if (G_IS_OBJECT (src_folder))
2734 g_object_unref (src_folder);
2735 if (G_IS_OBJECT(folder))
2736 g_object_unref (G_OBJECT (folder));
2737 if (G_IS_OBJECT(headers))
2738 g_object_unref (headers);
2742 TnyFolderStore *src_folder;
2743 TnyFolderStore *dst_folder;
2744 ModestFolderView *folder_view;
2749 dnd_folder_info_destroyer (DndFolderInfo *info)
2751 if (info->src_folder)
2752 g_object_unref (info->src_folder);
2753 if (info->dst_folder)
2754 g_object_unref (info->dst_folder);
2755 g_slice_free (DndFolderInfo, info);
2759 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2760 GtkWindow *parent_window,
2761 TnyAccount *account)
2764 modest_ui_actions_on_account_connection_error (parent_window, account);
2766 /* Free the helper & info */
2767 dnd_helper_destroyer (info->helper);
2768 dnd_folder_info_destroyer (info);
2772 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2774 GtkWindow *parent_window,
2775 TnyAccount *account,
2778 DndFolderInfo *info = NULL;
2779 ModestMailOperation *mail_op;
2781 info = (DndFolderInfo *) user_data;
2783 if (err || canceled) {
2784 dnd_on_connection_failed_destroyer (info, parent_window, account);
2788 /* Do the mail operation */
2789 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2790 modest_ui_actions_move_folder_error_handler,
2791 info->src_folder, NULL);
2793 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2796 /* Transfer the folder */
2797 modest_mail_operation_xfer_folder (mail_op,
2798 TNY_FOLDER (info->src_folder),
2800 info->helper->delete_source,
2802 info->helper->folder_view);
2805 g_object_unref (G_OBJECT (mail_op));
2806 dnd_helper_destroyer (info->helper);
2807 dnd_folder_info_destroyer (info);
2812 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2814 GtkWindow *parent_window,
2815 TnyAccount *account,
2818 DndFolderInfo *info = NULL;
2820 info = (DndFolderInfo *) user_data;
2822 if (err || canceled) {
2823 dnd_on_connection_failed_destroyer (info, parent_window, account);
2827 /* Connect to source folder and perform the copy/move */
2828 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2830 drag_and_drop_from_folder_view_src_folder_performer,
2835 * This function is used by drag_data_received_cb to manage drag and
2836 * drop of a folder, i.e, and drag from the folder view to the same
2840 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2841 GtkTreeModel *dest_model,
2842 GtkTreePath *dest_row,
2843 GtkSelectionData *selection_data,
2846 GtkTreeIter dest_iter, iter;
2847 TnyFolderStore *dest_folder = NULL;
2848 TnyFolderStore *folder = NULL;
2849 gboolean forbidden = FALSE;
2851 DndFolderInfo *info = NULL;
2853 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2855 g_warning ("%s: BUG: no main window", __FUNCTION__);
2856 dnd_helper_destroyer (helper);
2861 /* check the folder rules for the destination */
2862 folder = tree_path_to_folder (dest_model, dest_row);
2863 if (TNY_IS_FOLDER(folder)) {
2864 ModestTnyFolderRules rules =
2865 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2866 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2867 } else if (TNY_IS_FOLDER_STORE(folder)) {
2868 /* enable local root as destination for folders */
2869 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2870 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2873 g_object_unref (folder);
2876 /* check the folder rules for the source */
2877 folder = tree_path_to_folder (source_model, helper->source_row);
2878 if (TNY_IS_FOLDER(folder)) {
2879 ModestTnyFolderRules rules =
2880 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2881 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2884 g_object_unref (folder);
2888 /* Check if the drag is possible */
2889 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2891 modest_platform_run_information_dialog ((GtkWindow *) win,
2892 _("mail_in_ui_folder_move_target_error"),
2894 /* Restore the previous selection */
2895 folder = tree_path_to_folder (source_model, helper->source_row);
2897 if (TNY_IS_FOLDER (folder))
2898 modest_folder_view_select_folder (helper->folder_view,
2899 TNY_FOLDER (folder), FALSE);
2900 g_object_unref (folder);
2902 dnd_helper_destroyer (helper);
2907 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2908 gtk_tree_model_get (dest_model, &dest_iter,
2911 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2912 gtk_tree_model_get (source_model, &iter,
2916 /* Create the info for the performer */
2917 info = g_slice_new0 (DndFolderInfo);
2918 info->src_folder = g_object_ref (folder);
2919 info->dst_folder = g_object_ref (dest_folder);
2920 info->helper = helper;
2922 /* Connect to the destination folder and perform the copy/move */
2923 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2925 drag_and_drop_from_folder_view_dst_folder_performer,
2929 g_object_unref (dest_folder);
2930 g_object_unref (folder);
2934 * This function receives the data set by the "drag-data-get" signal
2935 * handler. This information comes within the #GtkSelectionData. This
2936 * function will manage both the drags of folders of the treeview and
2937 * drags of headers of the header view widget.
2940 on_drag_data_received (GtkWidget *widget,
2941 GdkDragContext *context,
2944 GtkSelectionData *selection_data,
2949 GtkWidget *source_widget;
2950 GtkTreeModel *dest_model, *source_model;
2951 GtkTreePath *source_row, *dest_row;
2952 GtkTreeViewDropPosition pos;
2953 gboolean delete_source = FALSE;
2954 gboolean success = FALSE;
2956 /* Do not allow further process */
2957 g_signal_stop_emission_by_name (widget, "drag-data-received");
2958 source_widget = gtk_drag_get_source_widget (context);
2960 /* Get the action */
2961 if (context->action == GDK_ACTION_MOVE) {
2962 delete_source = TRUE;
2964 /* Notify that there is no folder selected. We need to
2965 do this in order to update the headers view (and
2966 its monitors, because when moving, the old folder
2967 won't longer exist. We can not wait for the end of
2968 the operation, because the operation won't start if
2969 the folder is in use */
2970 if (source_widget == widget) {
2971 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2972 gtk_tree_selection_unselect_all (sel);
2976 /* Check if the get_data failed */
2977 if (selection_data == NULL || selection_data->length < 0)
2980 /* Select the destination model */
2981 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2983 /* Get the path to the destination row. Can not call
2984 gtk_tree_view_get_drag_dest_row() because the source row
2985 is not selected anymore */
2986 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2989 /* Only allow drops IN other rows */
2991 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2992 pos == GTK_TREE_VIEW_DROP_AFTER)
2996 /* Drags from the header view */
2997 if (source_widget != widget) {
2998 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3000 drag_and_drop_from_header_view (source_model,
3005 DndHelper *helper = NULL;
3007 /* Get the source model and row */
3008 gtk_tree_get_row_drag_data (selection_data,
3012 /* Create the helper */
3013 helper = g_slice_new0 (DndHelper);
3014 helper->delete_source = delete_source;
3015 helper->source_row = gtk_tree_path_copy (source_row);
3016 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3018 drag_and_drop_from_folder_view (source_model,
3024 gtk_tree_path_free (source_row);
3028 gtk_tree_path_free (dest_row);
3031 /* Finish the drag and drop */
3032 gtk_drag_finish (context, success, FALSE, time);
3036 * We define a "drag-drop" signal handler because we do not want to
3037 * use the default one, because the default one always calls
3038 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3039 * signal handler, because there we have all the information available
3040 * to know if the dnd was a success or not.
3043 drag_drop_cb (GtkWidget *widget,
3044 GdkDragContext *context,
3052 if (!context->targets)
3055 /* Check if we're dragging a folder row */
3056 target = gtk_drag_dest_find_target (widget, context, NULL);
3058 /* Request the data from the source. */
3059 gtk_drag_get_data(widget, context, target, time);
3065 * This function expands a node of a tree view if it's not expanded
3066 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3067 * does that, so that's why they're here.
3070 expand_row_timeout (gpointer data)
3072 GtkTreeView *tree_view = data;
3073 GtkTreePath *dest_path = NULL;
3074 GtkTreeViewDropPosition pos;
3075 gboolean result = FALSE;
3077 gdk_threads_enter ();
3079 gtk_tree_view_get_drag_dest_row (tree_view,
3084 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3085 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3086 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3087 gtk_tree_path_free (dest_path);
3091 gtk_tree_path_free (dest_path);
3096 gdk_threads_leave ();
3102 * This function is called whenever the pointer is moved over a widget
3103 * while dragging some data. It installs a timeout that will expand a
3104 * node of the treeview if not expanded yet. This function also calls
3105 * gdk_drag_status in order to set the suggested action that will be
3106 * used by the "drag-data-received" signal handler to know if we
3107 * should do a move or just a copy of the data.
3110 on_drag_motion (GtkWidget *widget,
3111 GdkDragContext *context,
3117 GtkTreeViewDropPosition pos;
3118 GtkTreePath *dest_row;
3119 GtkTreeModel *dest_model;
3120 ModestFolderViewPrivate *priv;
3121 GdkDragAction suggested_action;
3122 gboolean valid_location = FALSE;
3123 TnyFolderStore *folder = NULL;
3125 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3127 if (priv->timer_expander != 0) {
3128 g_source_remove (priv->timer_expander);
3129 priv->timer_expander = 0;
3132 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3137 /* Do not allow drops between folders */
3139 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3140 pos == GTK_TREE_VIEW_DROP_AFTER) {
3141 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3142 gdk_drag_status(context, 0, time);
3143 valid_location = FALSE;
3146 valid_location = TRUE;
3149 /* Check that the destination folder is writable */
3150 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3151 folder = tree_path_to_folder (dest_model, dest_row);
3152 if (folder && TNY_IS_FOLDER (folder)) {
3153 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3155 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3156 valid_location = FALSE;
3161 /* Expand the selected row after 1/2 second */
3162 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3163 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3165 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3167 /* Select the desired action. By default we pick MOVE */
3168 suggested_action = GDK_ACTION_MOVE;
3170 if (context->actions == GDK_ACTION_COPY)
3171 gdk_drag_status(context, GDK_ACTION_COPY, time);
3172 else if (context->actions == GDK_ACTION_MOVE)
3173 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3174 else if (context->actions & suggested_action)
3175 gdk_drag_status(context, suggested_action, time);
3177 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3181 g_object_unref (folder);
3183 gtk_tree_path_free (dest_row);
3185 g_signal_stop_emission_by_name (widget, "drag-motion");
3187 return valid_location;
3191 * This function sets the treeview as a source and a target for dnd
3192 * events. It also connects all the requirede signals.
3195 setup_drag_and_drop (GtkTreeView *self)
3197 /* Set up the folder view as a dnd destination. Set only the
3198 highlight flag, otherwise gtk will have a different
3200 #ifdef MODEST_TOOLKIT_HILDON2
3203 gtk_drag_dest_set (GTK_WIDGET (self),
3204 GTK_DEST_DEFAULT_HIGHLIGHT,
3205 folder_view_drag_types,
3206 G_N_ELEMENTS (folder_view_drag_types),
3207 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3209 g_signal_connect (G_OBJECT (self),
3210 "drag_data_received",
3211 G_CALLBACK (on_drag_data_received),
3215 /* Set up the treeview as a dnd source */
3216 gtk_drag_source_set (GTK_WIDGET (self),
3218 folder_view_drag_types,
3219 G_N_ELEMENTS (folder_view_drag_types),
3220 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3222 g_signal_connect (G_OBJECT (self),
3224 G_CALLBACK (on_drag_motion),
3227 g_signal_connect (G_OBJECT (self),
3229 G_CALLBACK (on_drag_data_get),
3232 g_signal_connect (G_OBJECT (self),
3234 G_CALLBACK (drag_drop_cb),
3239 * This function manages the navigation through the folders using the
3240 * keyboard or the hardware keys in the device
3243 on_key_pressed (GtkWidget *self,
3247 GtkTreeSelection *selection;
3249 GtkTreeModel *model;
3250 gboolean retval = FALSE;
3252 /* Up and Down are automatically managed by the treeview */
3253 if (event->keyval == GDK_Return) {
3254 /* Expand/Collapse the selected row */
3255 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3256 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3259 path = gtk_tree_model_get_path (model, &iter);
3261 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3262 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3264 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3265 gtk_tree_path_free (path);
3267 /* No further processing */
3275 * We listen to the changes in the local folder account name key,
3276 * because we want to show the right name in the view. The local
3277 * folder account name corresponds to the device name in the Maemo
3278 * version. We do this because we do not want to query gconf on each
3279 * tree view refresh. It's better to cache it and change whenever
3283 on_configuration_key_changed (ModestConf* conf,
3285 ModestConfEvent event,
3286 ModestConfNotificationId id,
3287 ModestFolderView *self)
3289 ModestFolderViewPrivate *priv;
3292 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3293 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3295 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3296 g_free (priv->local_account_name);
3298 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3299 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3301 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3302 MODEST_CONF_DEVICE_NAME, NULL);
3304 /* Force a redraw */
3305 #if GTK_CHECK_VERSION(2, 8, 0)
3306 GtkTreeViewColumn * tree_column;
3308 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3310 gtk_tree_view_column_queue_resize (tree_column);
3312 gtk_widget_queue_draw (GTK_WIDGET (self));
3318 modest_folder_view_set_style (ModestFolderView *self,
3319 ModestFolderViewStyle style)
3321 ModestFolderViewPrivate *priv;
3323 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3324 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3325 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3327 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3330 priv->style = style;
3334 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3335 const gchar *account_id)
3337 ModestFolderViewPrivate *priv;
3338 GtkTreeModel *model;
3340 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3342 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3344 /* This will be used by the filter_row callback,
3345 * to decided which rows to show: */
3346 if (priv->visible_account_id) {
3347 g_free (priv->visible_account_id);
3348 priv->visible_account_id = NULL;
3351 priv->visible_account_id = g_strdup (account_id);
3354 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3355 if (GTK_IS_TREE_MODEL_FILTER (model))
3356 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3358 /* Save settings to gconf */
3359 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3360 MODEST_CONF_FOLDER_VIEW_KEY);
3362 /* Notify observers */
3363 g_signal_emit (G_OBJECT(self),
3364 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3369 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3371 ModestFolderViewPrivate *priv;
3373 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3375 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3377 return (const gchar *) priv->visible_account_id;
3381 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3385 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3387 gtk_tree_model_get (model, iter,
3391 gboolean result = FALSE;
3392 if (type == TNY_FOLDER_TYPE_INBOX) {
3396 *inbox_iter = *iter;
3400 if (gtk_tree_model_iter_children (model, &child, iter)) {
3401 if (find_inbox_iter (model, &child, inbox_iter))
3405 } while (gtk_tree_model_iter_next (model, iter));
3414 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3416 GtkTreeModel *model;
3417 GtkTreeIter iter, inbox_iter;
3418 GtkTreeSelection *sel;
3419 GtkTreePath *path = NULL;
3421 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3423 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3427 expand_root_items (self);
3428 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3430 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3431 g_warning ("%s: model is empty", __FUNCTION__);
3435 if (find_inbox_iter (model, &iter, &inbox_iter))
3436 path = gtk_tree_model_get_path (model, &inbox_iter);
3438 path = gtk_tree_path_new_first ();
3440 /* Select the row and free */
3441 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3442 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3443 gtk_tree_path_free (path);
3446 gtk_widget_grab_focus (GTK_WIDGET(self));
3452 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3457 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3458 TnyFolder* a_folder;
3461 gtk_tree_model_get (model, iter,
3462 INSTANCE_COLUMN, &a_folder,
3468 if (folder == a_folder) {
3469 g_object_unref (a_folder);
3470 *folder_iter = *iter;
3473 g_object_unref (a_folder);
3475 if (gtk_tree_model_iter_children (model, &child, iter)) {
3476 if (find_folder_iter (model, &child, folder_iter, folder))
3480 } while (gtk_tree_model_iter_next (model, iter));
3485 #ifndef MODEST_TOOLKIT_HILDON2
3487 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3490 ModestFolderView *self)
3492 ModestFolderViewPrivate *priv = NULL;
3493 GtkTreeSelection *sel;
3494 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3495 GObject *instance = NULL;
3497 if (!MODEST_IS_FOLDER_VIEW(self))
3500 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3502 priv->reexpand = TRUE;
3504 gtk_tree_model_get (tree_model, iter,
3506 INSTANCE_COLUMN, &instance,
3512 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3513 priv->folder_to_select = g_object_ref (instance);
3515 g_object_unref (instance);
3517 if (priv->folder_to_select) {
3519 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3522 path = gtk_tree_model_get_path (tree_model, iter);
3523 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3525 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3527 gtk_tree_selection_select_iter (sel, iter);
3528 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3530 gtk_tree_path_free (path);
3534 modest_folder_view_disable_next_folder_selection (self);
3536 /* Refilter the model */
3537 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3543 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3545 ModestFolderViewPrivate *priv;
3547 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3549 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3551 if (priv->folder_to_select)
3552 g_object_unref(priv->folder_to_select);
3554 priv->folder_to_select = NULL;
3558 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3559 gboolean after_change)
3561 GtkTreeModel *model;
3562 GtkTreeIter iter, folder_iter;
3563 GtkTreeSelection *sel;
3564 ModestFolderViewPrivate *priv = NULL;
3566 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3567 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3569 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3572 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3573 gtk_tree_selection_unselect_all (sel);
3575 if (priv->folder_to_select)
3576 g_object_unref(priv->folder_to_select);
3577 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3581 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3586 /* Refilter the model, before selecting the folder */
3587 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3589 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3590 g_warning ("%s: model is empty", __FUNCTION__);
3594 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3597 path = gtk_tree_model_get_path (model, &folder_iter);
3598 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3600 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3601 gtk_tree_selection_select_iter (sel, &folder_iter);
3602 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3604 gtk_tree_path_free (path);
3612 modest_folder_view_copy_selection (ModestFolderView *self)
3614 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3616 /* Copy selection */
3617 _clipboard_set_selected_data (self, FALSE);
3621 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3623 ModestFolderViewPrivate *priv = NULL;
3624 GtkTreeModel *model = NULL;
3625 const gchar **hidding = NULL;
3626 guint i, n_selected;
3628 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3629 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3631 /* Copy selection */
3632 if (!_clipboard_set_selected_data (folder_view, TRUE))
3635 /* Get hidding ids */
3636 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3638 /* Clear hidding array created by previous cut operation */
3639 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3641 /* Copy hidding array */
3642 priv->n_selected = n_selected;
3643 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3644 for (i=0; i < n_selected; i++)
3645 priv->hidding_ids[i] = g_strdup(hidding[i]);
3647 /* Hide cut folders */
3648 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3649 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3653 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3654 ModestFolderView *folder_view_dst)
3656 GtkTreeModel *filter_model = NULL;
3657 GtkTreeModel *model = NULL;
3658 GtkTreeModel *new_filter_model = NULL;
3660 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3661 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3664 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3665 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3667 /* Build new filter model */
3668 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3669 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3673 /* Set copied model */
3674 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3675 #ifndef MODEST_TOOLKIT_HILDON2
3676 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3677 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3681 g_object_unref (new_filter_model);
3685 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3688 GtkTreeModel *model = NULL;
3689 ModestFolderViewPrivate* priv;
3691 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3693 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3694 priv->show_non_move = show;
3695 /* modest_folder_view_update_model(folder_view, */
3696 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3698 /* Hide special folders */
3699 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3700 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3701 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3705 /* Returns FALSE if it did not selected anything */
3707 _clipboard_set_selected_data (ModestFolderView *folder_view,
3710 ModestFolderViewPrivate *priv = NULL;
3711 TnyFolderStore *folder = NULL;
3712 gboolean retval = FALSE;
3714 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3715 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3717 /* Set selected data on clipboard */
3718 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3719 folder = modest_folder_view_get_selected (folder_view);
3721 /* Do not allow to select an account */
3722 if (TNY_IS_FOLDER (folder)) {
3723 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3728 g_object_unref (folder);
3734 _clear_hidding_filter (ModestFolderView *folder_view)
3736 ModestFolderViewPrivate *priv;
3739 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3740 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3742 if (priv->hidding_ids != NULL) {
3743 for (i=0; i < priv->n_selected; i++)
3744 g_free (priv->hidding_ids[i]);
3745 g_free(priv->hidding_ids);
3751 on_display_name_changed (ModestAccountMgr *mgr,
3752 const gchar *account,
3755 ModestFolderView *self;
3757 self = MODEST_FOLDER_VIEW (user_data);
3759 /* Force a redraw */
3760 #if GTK_CHECK_VERSION(2, 8, 0)
3761 GtkTreeViewColumn * tree_column;
3763 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3765 gtk_tree_view_column_queue_resize (tree_column);
3767 gtk_widget_queue_draw (GTK_WIDGET (self));
3772 modest_folder_view_set_cell_style (ModestFolderView *self,
3773 ModestFolderViewCellStyle cell_style)
3775 ModestFolderViewPrivate *priv = NULL;
3777 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3778 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3780 priv->cell_style = cell_style;
3782 g_object_set (G_OBJECT (priv->messages_renderer),
3783 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3786 gtk_widget_queue_draw (GTK_WIDGET (self));
3790 update_style (ModestFolderView *self)
3792 ModestFolderViewPrivate *priv;
3793 GdkColor style_color;
3794 PangoAttrList *attr_list;
3796 PangoAttribute *attr;
3798 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3799 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3803 attr_list = pango_attr_list_new ();
3804 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3805 gdk_color_parse ("grey", &style_color);
3807 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3808 pango_attr_list_insert (attr_list, attr);
3811 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3813 "SmallSystemFont", NULL,
3816 attr = pango_attr_font_desc_new (pango_font_description_copy
3817 (style->font_desc));
3818 pango_attr_list_insert (attr_list, attr);
3820 g_object_set (G_OBJECT (priv->messages_renderer),
3821 "foreground-gdk", &style_color,
3822 "foreground-set", TRUE,
3823 "attributes", attr_list,
3825 pango_attr_list_unref (attr_list);
3830 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3832 if (strcmp ("style", spec->name) == 0) {
3833 update_style (MODEST_FOLDER_VIEW (obj));
3834 gtk_widget_queue_draw (GTK_WIDGET (obj));
3839 modest_folder_view_set_filter (ModestFolderView *self,
3840 ModestFolderViewFilter filter)
3842 ModestFolderViewPrivate *priv;
3843 GtkTreeModel *filter_model;
3845 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3846 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3848 priv->filter |= filter;
3850 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3851 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3852 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3857 modest_folder_view_unset_filter (ModestFolderView *self,
3858 ModestFolderViewFilter filter)
3860 ModestFolderViewPrivate *priv;
3861 GtkTreeModel *filter_model;
3863 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3864 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3866 priv->filter &= ~filter;
3868 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3869 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3870 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3875 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3876 ModestTnyFolderRules rules)
3878 GtkTreeModel *filter_model;
3880 gboolean fulfil = FALSE;
3882 if (!get_inner_models (self, &filter_model, NULL, NULL))
3885 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3889 TnyFolderStore *folder;
3891 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3893 if (TNY_IS_FOLDER (folder)) {
3894 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3895 /* Folder rules are negative: non_writable, non_deletable... */
3896 if (!(folder_rules & rules))
3899 g_object_unref (folder);
3902 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3908 modest_folder_view_set_list_to_move (ModestFolderView *self,
3911 ModestFolderViewPrivate *priv;
3913 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3914 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3916 if (priv->list_to_move)
3917 g_object_unref (priv->list_to_move);
3920 g_object_ref (list);
3922 priv->list_to_move = list;
3926 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3928 ModestFolderViewPrivate *priv;
3930 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3931 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3934 g_free (priv->mailbox);
3936 priv->mailbox = g_strdup (mailbox);
3938 /* Notify observers */
3939 g_signal_emit (G_OBJECT(self),
3940 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3941 priv->visible_account_id);
3945 modest_folder_view_get_mailbox (ModestFolderView *self)
3947 ModestFolderViewPrivate *priv;
3949 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3950 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3952 return (const gchar *) priv->mailbox;