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);
1768 g_object_unref (instance);
1776 has_folder_with_id (ModestFolderView *self, const gchar *id)
1778 GtkTreeModel *model;
1779 ForeachFolderInfo info = {NULL, FALSE};
1781 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1782 info.needle = g_utf8_collate_key (id, -1);
1784 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1785 g_free (info.needle);
1791 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1794 gboolean retval = FALSE;
1796 a_id = tny_folder_get_id (a);
1799 b_id = tny_folder_get_id (b);
1802 const gchar *last_bar;
1803 gchar *string_to_match;
1804 last_bar = g_strrstr (b_id, "/");
1809 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1810 retval = has_folder_with_id (self, string_to_match);
1811 g_free (string_to_match);
1819 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1821 ModestFolderViewPrivate *priv;
1822 TnyIterator *iterator;
1823 gboolean retval = TRUE;
1825 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1826 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1828 for (iterator = tny_list_create_iterator (priv->list_to_move);
1829 retval && !tny_iterator_is_done (iterator);
1830 tny_iterator_next (iterator)) {
1832 instance = tny_iterator_get_current (iterator);
1833 if (instance == (GObject *) folder) {
1835 } else if (TNY_IS_FOLDER (instance)) {
1836 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1838 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1841 g_object_unref (instance);
1843 g_object_unref (iterator);
1850 * We use this function to implement the
1851 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1852 * account in this case, and the local folders.
1855 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1857 ModestFolderViewPrivate *priv;
1858 gboolean retval = TRUE;
1859 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1860 GObject *instance = NULL;
1861 const gchar *id = NULL;
1863 gboolean found = FALSE;
1864 gboolean cleared = FALSE;
1865 ModestTnyFolderRules rules = 0;
1868 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1869 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1871 gtk_tree_model_get (model, iter,
1872 NAME_COLUMN, &fname,
1874 INSTANCE_COLUMN, &instance,
1877 /* Do not show if there is no instance, this could indeed
1878 happen when the model is being modified while it's being
1879 drawn. This could occur for example when moving folders
1886 if (TNY_IS_ACCOUNT (instance)) {
1887 TnyAccount *acc = TNY_ACCOUNT (instance);
1888 const gchar *account_id = tny_account_get_id (acc);
1890 /* If it isn't a special folder,
1891 * don't show it unless it is the visible account: */
1892 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1893 !modest_tny_account_is_virtual_local_folders (acc) &&
1894 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1896 /* Show only the visible account id */
1897 if (priv->visible_account_id) {
1898 if (strcmp (account_id, priv->visible_account_id))
1905 /* Never show these to the user. They are merged into one folder
1906 * in the local-folders account instead: */
1907 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1910 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1911 /* Only show special folders for current account if needed */
1912 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1913 TnyAccount *account;
1915 account = tny_folder_get_account (TNY_FOLDER (instance));
1917 if (TNY_IS_ACCOUNT (account)) {
1918 const gchar *account_id = tny_account_get_id (account);
1920 if (!modest_tny_account_is_virtual_local_folders (account) &&
1921 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1922 /* Show only the visible account id */
1923 if (priv->visible_account_id) {
1924 if (strcmp (account_id, priv->visible_account_id)) {
1926 } else if (priv->mailbox) {
1927 /* Filter mailboxes */
1928 if (!g_str_has_prefix (fname, priv->mailbox)) {
1930 } else if (!strcmp (fname, priv->mailbox)) {
1931 /* Hide mailbox parent */
1937 g_object_unref (account);
1944 /* Check hiding (if necessary) */
1945 cleared = modest_email_clipboard_cleared (priv->clipboard);
1946 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1947 id = tny_folder_get_id (TNY_FOLDER(instance));
1948 if (priv->hidding_ids != NULL)
1949 for (i=0; i < priv->n_selected && !found; i++)
1950 if (priv->hidding_ids[i] != NULL && id != NULL)
1951 found = (!strcmp (priv->hidding_ids[i], id));
1956 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1957 folder as no message can be move there according to UI specs */
1958 if (retval && !priv->show_non_move) {
1959 if (priv->list_to_move &&
1960 tny_list_get_length (priv->list_to_move) > 0 &&
1961 TNY_IS_FOLDER (instance)) {
1962 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1964 if (retval && TNY_IS_FOLDER (instance) &&
1965 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1967 case TNY_FOLDER_TYPE_OUTBOX:
1968 case TNY_FOLDER_TYPE_SENT:
1969 case TNY_FOLDER_TYPE_DRAFTS:
1972 case TNY_FOLDER_TYPE_UNKNOWN:
1973 case TNY_FOLDER_TYPE_NORMAL:
1974 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1975 if (type == TNY_FOLDER_TYPE_INVALID)
1976 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1978 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1979 type == TNY_FOLDER_TYPE_SENT
1980 || type == TNY_FOLDER_TYPE_DRAFTS)
1989 /* apply special filters */
1990 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1991 if (TNY_IS_ACCOUNT (instance))
1995 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
1996 if (TNY_IS_FOLDER (instance))
2000 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2001 if (TNY_IS_ACCOUNT (instance)) {
2002 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2004 } else if (TNY_IS_FOLDER (instance)) {
2005 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2010 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2011 if (TNY_IS_ACCOUNT (instance)) {
2012 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2014 } else if (TNY_IS_FOLDER (instance)) {
2015 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2020 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2021 /* A mailbox is a fake folder with an @ in the middle of the name */
2022 if (!TNY_IS_FOLDER (instance) ||
2023 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2026 const gchar *folder_name;
2027 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2028 if (!folder_name || strchr (folder_name, '@') == NULL)
2034 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2035 if (TNY_IS_FOLDER (instance)) {
2036 /* Check folder rules */
2037 ModestTnyFolderRules rules;
2039 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2040 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2041 } else if (TNY_IS_ACCOUNT (instance)) {
2042 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2050 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2051 if (TNY_IS_FOLDER (instance)) {
2052 TnyFolderType guess_type;
2054 if (TNY_FOLDER_TYPE_NORMAL) {
2055 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2061 case TNY_FOLDER_TYPE_OUTBOX:
2062 case TNY_FOLDER_TYPE_SENT:
2063 case TNY_FOLDER_TYPE_DRAFTS:
2064 case TNY_FOLDER_TYPE_ARCHIVE:
2065 case TNY_FOLDER_TYPE_INBOX:
2068 case TNY_FOLDER_TYPE_UNKNOWN:
2069 case TNY_FOLDER_TYPE_NORMAL:
2075 } else if (TNY_IS_ACCOUNT (instance)) {
2080 if (retval && TNY_IS_FOLDER (instance)) {
2081 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2084 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2085 if (TNY_IS_FOLDER (instance)) {
2086 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2087 } else if (TNY_IS_ACCOUNT (instance)) {
2092 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2093 if (TNY_IS_FOLDER (instance)) {
2094 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2095 } else if (TNY_IS_ACCOUNT (instance)) {
2100 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2101 if (TNY_IS_FOLDER (instance)) {
2102 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2103 } else if (TNY_IS_ACCOUNT (instance)) {
2109 g_object_unref (instance);
2117 modest_folder_view_update_model (ModestFolderView *self,
2118 TnyAccountStore *account_store)
2120 ModestFolderViewPrivate *priv;
2121 GtkTreeModel *model /* , *old_model */;
2122 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2124 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2125 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2128 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2130 /* Notify that there is no folder selected */
2131 g_signal_emit (G_OBJECT(self),
2132 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2134 if (priv->cur_folder_store) {
2135 g_object_unref (priv->cur_folder_store);
2136 priv->cur_folder_store = NULL;
2139 /* FIXME: the local accounts are not shown when the query
2140 selects only the subscribed folders */
2141 #ifdef MODEST_TOOLKIT_HILDON2
2142 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2143 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2144 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2145 MODEST_FOLDER_PATH_SEPARATOR);
2147 model = tny_gtk_folder_store_tree_model_new (NULL);
2150 /* When the model is a list store (plain representation) the
2151 outbox is not a child of any account so we have to manually
2152 delete it because removing the local folders account won't
2153 delete it (because tny_folder_get_account() is not defined
2154 for a merge folder */
2155 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2156 TnyAccount *account;
2157 ModestTnyAccountStore *acc_store;
2159 acc_store = modest_runtime_get_account_store ();
2160 account = modest_tny_account_store_get_local_folders_account (acc_store);
2162 if (g_signal_handler_is_connected (account,
2163 priv->outbox_deleted_handler))
2164 g_signal_handler_disconnect (account,
2165 priv->outbox_deleted_handler);
2167 priv->outbox_deleted_handler =
2168 g_signal_connect (account,
2170 G_CALLBACK (on_outbox_deleted_cb),
2172 g_object_unref (account);
2175 /* Get the accounts: */
2176 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2178 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2180 sortable = gtk_tree_model_sort_new_with_model (model);
2181 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2183 GTK_SORT_ASCENDING);
2184 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2186 cmp_rows, NULL, NULL);
2188 /* Create filter model */
2189 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2190 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2196 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2197 #ifndef MODEST_TOOLKIT_HILDON2
2198 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2199 (GCallback) on_row_inserted_maybe_select_folder, self);
2202 g_object_unref (model);
2203 g_object_unref (filter_model);
2204 g_object_unref (sortable);
2206 /* Force a reselection of the INBOX next time the widget is shown */
2207 priv->reselect = TRUE;
2214 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2216 GtkTreeModel *model = NULL;
2217 TnyFolderStore *folder = NULL;
2219 ModestFolderView *tree_view = NULL;
2220 ModestFolderViewPrivate *priv = NULL;
2221 gboolean selected = FALSE;
2223 g_return_if_fail (sel);
2224 g_return_if_fail (user_data);
2226 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2228 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2230 tree_view = MODEST_FOLDER_VIEW (user_data);
2233 gtk_tree_model_get (model, &iter,
2234 INSTANCE_COLUMN, &folder,
2237 /* If the folder is the same do not notify */
2238 if (folder && priv->cur_folder_store == folder) {
2239 g_object_unref (folder);
2244 /* Current folder was unselected */
2245 if (priv->cur_folder_store) {
2246 /* We must do this firstly because a libtinymail-camel
2247 implementation detail. If we issue the signal
2248 before doing the sync_async, then that signal could
2249 cause (and it actually does it) a free of the
2250 summary of the folder (because the main window will
2251 clear the headers view */
2252 if (TNY_IS_FOLDER(priv->cur_folder_store))
2253 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2254 FALSE, NULL, NULL, NULL);
2256 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2257 priv->cur_folder_store, FALSE);
2259 g_object_unref (priv->cur_folder_store);
2260 priv->cur_folder_store = NULL;
2263 /* New current references */
2264 priv->cur_folder_store = folder;
2266 /* New folder has been selected. Do not notify if there is
2267 nothing new selected */
2269 g_signal_emit (G_OBJECT(tree_view),
2270 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2271 0, priv->cur_folder_store, TRUE);
2276 on_row_activated (GtkTreeView *treeview,
2277 GtkTreePath *treepath,
2278 GtkTreeViewColumn *column,
2281 GtkTreeModel *model = NULL;
2282 TnyFolderStore *folder = NULL;
2284 ModestFolderView *self = NULL;
2285 ModestFolderViewPrivate *priv = NULL;
2287 g_return_if_fail (treeview);
2288 g_return_if_fail (user_data);
2290 self = MODEST_FOLDER_VIEW (user_data);
2291 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2293 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2295 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2298 gtk_tree_model_get (model, &iter,
2299 INSTANCE_COLUMN, &folder,
2302 g_signal_emit (G_OBJECT(self),
2303 signals[FOLDER_ACTIVATED_SIGNAL],
2306 #ifdef MODEST_TOOLKIT_HILDON2
2307 HildonUIMode ui_mode;
2308 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2309 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2310 if (priv->cur_folder_store)
2311 g_object_unref (priv->cur_folder_store);
2312 priv->cur_folder_store = g_object_ref (folder);
2316 g_object_unref (folder);
2320 modest_folder_view_get_selected (ModestFolderView *self)
2322 ModestFolderViewPrivate *priv;
2324 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2326 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2327 if (priv->cur_folder_store)
2328 g_object_ref (priv->cur_folder_store);
2330 return priv->cur_folder_store;
2334 get_cmp_rows_type_pos (GObject *folder)
2336 /* Remote accounts -> Local account -> MMC account .*/
2339 if (TNY_IS_ACCOUNT (folder) &&
2340 modest_tny_account_is_virtual_local_folders (
2341 TNY_ACCOUNT (folder))) {
2343 } else if (TNY_IS_ACCOUNT (folder)) {
2344 TnyAccount *account = TNY_ACCOUNT (folder);
2345 const gchar *account_id = tny_account_get_id (account);
2346 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2352 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2353 return -1; /* Should never happen */
2358 inbox_is_special (TnyFolderStore *folder_store)
2360 gboolean is_special = TRUE;
2362 if (TNY_IS_FOLDER (folder_store)) {
2366 gchar *last_inbox_bar;
2368 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2369 downcase = g_utf8_strdown (id, -1);
2370 last_bar = g_strrstr (downcase, "/");
2372 last_inbox_bar = g_strrstr (downcase, "inbox/");
2373 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2384 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2386 TnyAccount *account;
2387 gboolean is_special;
2388 /* Inbox, Outbox, Drafts, Sent, User */
2391 if (!TNY_IS_FOLDER (folder_store))
2394 case TNY_FOLDER_TYPE_INBOX:
2396 account = tny_folder_get_account (folder_store);
2397 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2399 /* In inbox case we need to know if the inbox is really the top
2400 * inbox of the account, or if it's a submailbox inbox. To do
2401 * this we'll apply an heuristic rule: Find last "/" and check
2402 * if it's preceeded by another Inbox */
2403 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2404 g_object_unref (account);
2405 return is_special?0:4;
2408 case TNY_FOLDER_TYPE_OUTBOX:
2409 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2411 case TNY_FOLDER_TYPE_DRAFTS:
2413 account = tny_folder_get_account (folder_store);
2414 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2415 g_object_unref (account);
2416 return is_special?1:4;
2419 case TNY_FOLDER_TYPE_SENT:
2421 account = tny_folder_get_account (folder_store);
2422 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2423 g_object_unref (account);
2424 return is_special?3:4;
2433 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2435 const gchar *a1_name, *a2_name;
2437 a1_name = tny_account_get_name (a1);
2438 a2_name = tny_account_get_name (a2);
2440 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2444 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2446 TnyAccount *a1 = NULL, *a2 = NULL;
2449 if (TNY_IS_ACCOUNT (s1)) {
2450 a1 = TNY_ACCOUNT (g_object_ref (s1));
2451 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2452 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2455 if (TNY_IS_ACCOUNT (s2)) {
2456 a2 = TNY_ACCOUNT (g_object_ref (s2));
2457 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2458 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2475 /* First we sort with the type of account */
2476 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2480 cmp = compare_account_names (a1, a2);
2484 g_object_unref (a1);
2486 g_object_unref (a2);
2492 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2494 gint is_account1, is_account2;
2496 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2497 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2499 return is_account2 - is_account1;
2503 * This function orders the mail accounts according to these rules:
2504 * 1st - remote accounts
2505 * 2nd - local account
2509 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2513 gchar *name1 = NULL;
2514 gchar *name2 = NULL;
2515 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2516 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2517 GObject *folder1 = NULL;
2518 GObject *folder2 = NULL;
2520 gtk_tree_model_get (tree_model, iter1,
2521 NAME_COLUMN, &name1,
2523 INSTANCE_COLUMN, &folder1,
2525 gtk_tree_model_get (tree_model, iter2,
2526 NAME_COLUMN, &name2,
2527 TYPE_COLUMN, &type2,
2528 INSTANCE_COLUMN, &folder2,
2531 /* Return if we get no folder. This could happen when folder
2532 operations are happening. The model is updated after the
2533 folder copy/move actually occurs, so there could be
2534 situations where the model to be drawn is not correct */
2535 if (!folder1 || !folder2)
2538 /* Sort by type. First the special folders, then the archives */
2539 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2543 /* Now we sort using the account of each folder */
2544 if (TNY_IS_FOLDER_STORE (folder1) &&
2545 TNY_IS_FOLDER_STORE (folder2)) {
2546 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2550 /* Each group is preceeded by its account */
2551 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2556 /* Pure sort by name */
2557 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2560 g_object_unref(G_OBJECT(folder1));
2562 g_object_unref(G_OBJECT(folder2));
2570 /*****************************************************************************/
2571 /* DRAG and DROP stuff */
2572 /*****************************************************************************/
2574 * This function fills the #GtkSelectionData with the row and the
2575 * model that has been dragged. It's called when this widget is a
2576 * source for dnd after the event drop happened
2579 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2580 guint info, guint time, gpointer data)
2582 GtkTreeSelection *selection;
2583 GtkTreeModel *model;
2585 GtkTreePath *source_row;
2587 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2588 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2590 source_row = gtk_tree_model_get_path (model, &iter);
2591 gtk_tree_set_row_drag_data (selection_data,
2595 gtk_tree_path_free (source_row);
2599 typedef struct _DndHelper {
2600 ModestFolderView *folder_view;
2601 gboolean delete_source;
2602 GtkTreePath *source_row;
2606 dnd_helper_destroyer (DndHelper *helper)
2608 /* Free the helper */
2609 gtk_tree_path_free (helper->source_row);
2610 g_slice_free (DndHelper, helper);
2614 xfer_folder_cb (ModestMailOperation *mail_op,
2615 TnyFolder *new_folder,
2619 /* Select the folder */
2620 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2626 /* get the folder for the row the treepath refers to. */
2627 /* folder must be unref'd */
2628 static TnyFolderStore *
2629 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2632 TnyFolderStore *folder = NULL;
2634 if (gtk_tree_model_get_iter (model,&iter, path))
2635 gtk_tree_model_get (model, &iter,
2636 INSTANCE_COLUMN, &folder,
2643 * This function is used by drag_data_received_cb to manage drag and
2644 * drop of a header, i.e, and drag from the header view to the folder
2648 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2649 GtkTreeModel *dest_model,
2650 GtkTreePath *dest_row,
2651 GtkSelectionData *selection_data)
2653 TnyList *headers = NULL;
2654 TnyFolder *folder = NULL, *src_folder = NULL;
2655 TnyFolderType folder_type;
2656 GtkTreeIter source_iter, dest_iter;
2657 ModestWindowMgr *mgr = NULL;
2658 ModestWindow *main_win = NULL;
2659 gchar **uris, **tmp;
2661 /* Build the list of headers */
2662 mgr = modest_runtime_get_window_mgr ();
2663 headers = tny_simple_list_new ();
2664 uris = modest_dnd_selection_data_get_paths (selection_data);
2667 while (*tmp != NULL) {
2670 gboolean first = TRUE;
2673 path = gtk_tree_path_new_from_string (*tmp);
2674 gtk_tree_model_get_iter (source_model, &source_iter, path);
2675 gtk_tree_model_get (source_model, &source_iter,
2676 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2679 /* Do not enable d&d of headers already opened */
2680 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2681 tny_list_append (headers, G_OBJECT (header));
2683 if (G_UNLIKELY (first)) {
2684 src_folder = tny_header_get_folder (header);
2688 /* Free and go on */
2689 gtk_tree_path_free (path);
2690 g_object_unref (header);
2695 /* This could happen ig we perform a d&d very quickly over the
2696 same row that row could dissapear because message is
2698 if (!TNY_IS_FOLDER (src_folder))
2701 /* Get the target folder */
2702 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2703 gtk_tree_model_get (dest_model, &dest_iter,
2707 if (!folder || !TNY_IS_FOLDER(folder)) {
2708 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2712 folder_type = modest_tny_folder_guess_folder_type (folder);
2713 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2714 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2715 goto cleanup; /* cannot move messages there */
2718 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2719 /* g_warning ("folder not writable"); */
2720 goto cleanup; /* verboten! */
2723 /* Ask for confirmation to move */
2724 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2726 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2730 /* Transfer messages */
2731 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2736 if (G_IS_OBJECT (src_folder))
2737 g_object_unref (src_folder);
2738 if (G_IS_OBJECT(folder))
2739 g_object_unref (G_OBJECT (folder));
2740 if (G_IS_OBJECT(headers))
2741 g_object_unref (headers);
2745 TnyFolderStore *src_folder;
2746 TnyFolderStore *dst_folder;
2747 ModestFolderView *folder_view;
2752 dnd_folder_info_destroyer (DndFolderInfo *info)
2754 if (info->src_folder)
2755 g_object_unref (info->src_folder);
2756 if (info->dst_folder)
2757 g_object_unref (info->dst_folder);
2758 g_slice_free (DndFolderInfo, info);
2762 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2763 GtkWindow *parent_window,
2764 TnyAccount *account)
2767 modest_ui_actions_on_account_connection_error (parent_window, account);
2769 /* Free the helper & info */
2770 dnd_helper_destroyer (info->helper);
2771 dnd_folder_info_destroyer (info);
2775 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2777 GtkWindow *parent_window,
2778 TnyAccount *account,
2781 DndFolderInfo *info = NULL;
2782 ModestMailOperation *mail_op;
2784 info = (DndFolderInfo *) user_data;
2786 if (err || canceled) {
2787 dnd_on_connection_failed_destroyer (info, parent_window, account);
2791 /* Do the mail operation */
2792 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2793 modest_ui_actions_move_folder_error_handler,
2794 info->src_folder, NULL);
2796 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2799 /* Transfer the folder */
2800 modest_mail_operation_xfer_folder (mail_op,
2801 TNY_FOLDER (info->src_folder),
2803 info->helper->delete_source,
2805 info->helper->folder_view);
2808 g_object_unref (G_OBJECT (mail_op));
2809 dnd_helper_destroyer (info->helper);
2810 dnd_folder_info_destroyer (info);
2815 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2817 GtkWindow *parent_window,
2818 TnyAccount *account,
2821 DndFolderInfo *info = NULL;
2823 info = (DndFolderInfo *) user_data;
2825 if (err || canceled) {
2826 dnd_on_connection_failed_destroyer (info, parent_window, account);
2830 /* Connect to source folder and perform the copy/move */
2831 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2833 drag_and_drop_from_folder_view_src_folder_performer,
2838 * This function is used by drag_data_received_cb to manage drag and
2839 * drop of a folder, i.e, and drag from the folder view to the same
2843 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2844 GtkTreeModel *dest_model,
2845 GtkTreePath *dest_row,
2846 GtkSelectionData *selection_data,
2849 GtkTreeIter dest_iter, iter;
2850 TnyFolderStore *dest_folder = NULL;
2851 TnyFolderStore *folder = NULL;
2852 gboolean forbidden = FALSE;
2854 DndFolderInfo *info = NULL;
2856 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2858 g_warning ("%s: BUG: no main window", __FUNCTION__);
2859 dnd_helper_destroyer (helper);
2864 /* check the folder rules for the destination */
2865 folder = tree_path_to_folder (dest_model, dest_row);
2866 if (TNY_IS_FOLDER(folder)) {
2867 ModestTnyFolderRules rules =
2868 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2869 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2870 } else if (TNY_IS_FOLDER_STORE(folder)) {
2871 /* enable local root as destination for folders */
2872 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2873 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2876 g_object_unref (folder);
2879 /* check the folder rules for the source */
2880 folder = tree_path_to_folder (source_model, helper->source_row);
2881 if (TNY_IS_FOLDER(folder)) {
2882 ModestTnyFolderRules rules =
2883 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2884 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2887 g_object_unref (folder);
2891 /* Check if the drag is possible */
2892 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2894 modest_platform_run_information_dialog ((GtkWindow *) win,
2895 _("mail_in_ui_folder_move_target_error"),
2897 /* Restore the previous selection */
2898 folder = tree_path_to_folder (source_model, helper->source_row);
2900 if (TNY_IS_FOLDER (folder))
2901 modest_folder_view_select_folder (helper->folder_view,
2902 TNY_FOLDER (folder), FALSE);
2903 g_object_unref (folder);
2905 dnd_helper_destroyer (helper);
2910 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2911 gtk_tree_model_get (dest_model, &dest_iter,
2914 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2915 gtk_tree_model_get (source_model, &iter,
2919 /* Create the info for the performer */
2920 info = g_slice_new0 (DndFolderInfo);
2921 info->src_folder = g_object_ref (folder);
2922 info->dst_folder = g_object_ref (dest_folder);
2923 info->helper = helper;
2925 /* Connect to the destination folder and perform the copy/move */
2926 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2928 drag_and_drop_from_folder_view_dst_folder_performer,
2932 g_object_unref (dest_folder);
2933 g_object_unref (folder);
2937 * This function receives the data set by the "drag-data-get" signal
2938 * handler. This information comes within the #GtkSelectionData. This
2939 * function will manage both the drags of folders of the treeview and
2940 * drags of headers of the header view widget.
2943 on_drag_data_received (GtkWidget *widget,
2944 GdkDragContext *context,
2947 GtkSelectionData *selection_data,
2952 GtkWidget *source_widget;
2953 GtkTreeModel *dest_model, *source_model;
2954 GtkTreePath *source_row, *dest_row;
2955 GtkTreeViewDropPosition pos;
2956 gboolean delete_source = FALSE;
2957 gboolean success = FALSE;
2959 /* Do not allow further process */
2960 g_signal_stop_emission_by_name (widget, "drag-data-received");
2961 source_widget = gtk_drag_get_source_widget (context);
2963 /* Get the action */
2964 if (context->action == GDK_ACTION_MOVE) {
2965 delete_source = TRUE;
2967 /* Notify that there is no folder selected. We need to
2968 do this in order to update the headers view (and
2969 its monitors, because when moving, the old folder
2970 won't longer exist. We can not wait for the end of
2971 the operation, because the operation won't start if
2972 the folder is in use */
2973 if (source_widget == widget) {
2974 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2975 gtk_tree_selection_unselect_all (sel);
2979 /* Check if the get_data failed */
2980 if (selection_data == NULL || selection_data->length < 0)
2983 /* Select the destination model */
2984 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2986 /* Get the path to the destination row. Can not call
2987 gtk_tree_view_get_drag_dest_row() because the source row
2988 is not selected anymore */
2989 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2992 /* Only allow drops IN other rows */
2994 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2995 pos == GTK_TREE_VIEW_DROP_AFTER)
2999 /* Drags from the header view */
3000 if (source_widget != widget) {
3001 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3003 drag_and_drop_from_header_view (source_model,
3008 DndHelper *helper = NULL;
3010 /* Get the source model and row */
3011 gtk_tree_get_row_drag_data (selection_data,
3015 /* Create the helper */
3016 helper = g_slice_new0 (DndHelper);
3017 helper->delete_source = delete_source;
3018 helper->source_row = gtk_tree_path_copy (source_row);
3019 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3021 drag_and_drop_from_folder_view (source_model,
3027 gtk_tree_path_free (source_row);
3031 gtk_tree_path_free (dest_row);
3034 /* Finish the drag and drop */
3035 gtk_drag_finish (context, success, FALSE, time);
3039 * We define a "drag-drop" signal handler because we do not want to
3040 * use the default one, because the default one always calls
3041 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3042 * signal handler, because there we have all the information available
3043 * to know if the dnd was a success or not.
3046 drag_drop_cb (GtkWidget *widget,
3047 GdkDragContext *context,
3055 if (!context->targets)
3058 /* Check if we're dragging a folder row */
3059 target = gtk_drag_dest_find_target (widget, context, NULL);
3061 /* Request the data from the source. */
3062 gtk_drag_get_data(widget, context, target, time);
3068 * This function expands a node of a tree view if it's not expanded
3069 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3070 * does that, so that's why they're here.
3073 expand_row_timeout (gpointer data)
3075 GtkTreeView *tree_view = data;
3076 GtkTreePath *dest_path = NULL;
3077 GtkTreeViewDropPosition pos;
3078 gboolean result = FALSE;
3080 gdk_threads_enter ();
3082 gtk_tree_view_get_drag_dest_row (tree_view,
3087 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3088 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3089 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3090 gtk_tree_path_free (dest_path);
3094 gtk_tree_path_free (dest_path);
3099 gdk_threads_leave ();
3105 * This function is called whenever the pointer is moved over a widget
3106 * while dragging some data. It installs a timeout that will expand a
3107 * node of the treeview if not expanded yet. This function also calls
3108 * gdk_drag_status in order to set the suggested action that will be
3109 * used by the "drag-data-received" signal handler to know if we
3110 * should do a move or just a copy of the data.
3113 on_drag_motion (GtkWidget *widget,
3114 GdkDragContext *context,
3120 GtkTreeViewDropPosition pos;
3121 GtkTreePath *dest_row;
3122 GtkTreeModel *dest_model;
3123 ModestFolderViewPrivate *priv;
3124 GdkDragAction suggested_action;
3125 gboolean valid_location = FALSE;
3126 TnyFolderStore *folder = NULL;
3128 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3130 if (priv->timer_expander != 0) {
3131 g_source_remove (priv->timer_expander);
3132 priv->timer_expander = 0;
3135 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3140 /* Do not allow drops between folders */
3142 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3143 pos == GTK_TREE_VIEW_DROP_AFTER) {
3144 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3145 gdk_drag_status(context, 0, time);
3146 valid_location = FALSE;
3149 valid_location = TRUE;
3152 /* Check that the destination folder is writable */
3153 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3154 folder = tree_path_to_folder (dest_model, dest_row);
3155 if (folder && TNY_IS_FOLDER (folder)) {
3156 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3158 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3159 valid_location = FALSE;
3164 /* Expand the selected row after 1/2 second */
3165 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3166 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3168 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3170 /* Select the desired action. By default we pick MOVE */
3171 suggested_action = GDK_ACTION_MOVE;
3173 if (context->actions == GDK_ACTION_COPY)
3174 gdk_drag_status(context, GDK_ACTION_COPY, time);
3175 else if (context->actions == GDK_ACTION_MOVE)
3176 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3177 else if (context->actions & suggested_action)
3178 gdk_drag_status(context, suggested_action, time);
3180 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3184 g_object_unref (folder);
3186 gtk_tree_path_free (dest_row);
3188 g_signal_stop_emission_by_name (widget, "drag-motion");
3190 return valid_location;
3194 * This function sets the treeview as a source and a target for dnd
3195 * events. It also connects all the requirede signals.
3198 setup_drag_and_drop (GtkTreeView *self)
3200 /* Set up the folder view as a dnd destination. Set only the
3201 highlight flag, otherwise gtk will have a different
3203 #ifdef MODEST_TOOLKIT_HILDON2
3206 gtk_drag_dest_set (GTK_WIDGET (self),
3207 GTK_DEST_DEFAULT_HIGHLIGHT,
3208 folder_view_drag_types,
3209 G_N_ELEMENTS (folder_view_drag_types),
3210 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3212 g_signal_connect (G_OBJECT (self),
3213 "drag_data_received",
3214 G_CALLBACK (on_drag_data_received),
3218 /* Set up the treeview as a dnd source */
3219 gtk_drag_source_set (GTK_WIDGET (self),
3221 folder_view_drag_types,
3222 G_N_ELEMENTS (folder_view_drag_types),
3223 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3225 g_signal_connect (G_OBJECT (self),
3227 G_CALLBACK (on_drag_motion),
3230 g_signal_connect (G_OBJECT (self),
3232 G_CALLBACK (on_drag_data_get),
3235 g_signal_connect (G_OBJECT (self),
3237 G_CALLBACK (drag_drop_cb),
3242 * This function manages the navigation through the folders using the
3243 * keyboard or the hardware keys in the device
3246 on_key_pressed (GtkWidget *self,
3250 GtkTreeSelection *selection;
3252 GtkTreeModel *model;
3253 gboolean retval = FALSE;
3255 /* Up and Down are automatically managed by the treeview */
3256 if (event->keyval == GDK_Return) {
3257 /* Expand/Collapse the selected row */
3258 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3259 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3262 path = gtk_tree_model_get_path (model, &iter);
3264 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3265 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3267 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3268 gtk_tree_path_free (path);
3270 /* No further processing */
3278 * We listen to the changes in the local folder account name key,
3279 * because we want to show the right name in the view. The local
3280 * folder account name corresponds to the device name in the Maemo
3281 * version. We do this because we do not want to query gconf on each
3282 * tree view refresh. It's better to cache it and change whenever
3286 on_configuration_key_changed (ModestConf* conf,
3288 ModestConfEvent event,
3289 ModestConfNotificationId id,
3290 ModestFolderView *self)
3292 ModestFolderViewPrivate *priv;
3295 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3296 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3298 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3299 g_free (priv->local_account_name);
3301 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3302 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3304 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3305 MODEST_CONF_DEVICE_NAME, NULL);
3307 /* Force a redraw */
3308 #if GTK_CHECK_VERSION(2, 8, 0)
3309 GtkTreeViewColumn * tree_column;
3311 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3313 gtk_tree_view_column_queue_resize (tree_column);
3315 gtk_widget_queue_draw (GTK_WIDGET (self));
3321 modest_folder_view_set_style (ModestFolderView *self,
3322 ModestFolderViewStyle style)
3324 ModestFolderViewPrivate *priv;
3326 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3327 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3328 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3330 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3333 priv->style = style;
3337 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3338 const gchar *account_id)
3340 ModestFolderViewPrivate *priv;
3341 GtkTreeModel *model;
3343 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3345 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3347 /* This will be used by the filter_row callback,
3348 * to decided which rows to show: */
3349 if (priv->visible_account_id) {
3350 g_free (priv->visible_account_id);
3351 priv->visible_account_id = NULL;
3354 priv->visible_account_id = g_strdup (account_id);
3357 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3358 if (GTK_IS_TREE_MODEL_FILTER (model))
3359 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3361 /* Save settings to gconf */
3362 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3363 MODEST_CONF_FOLDER_VIEW_KEY);
3365 /* Notify observers */
3366 g_signal_emit (G_OBJECT(self),
3367 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3372 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3374 ModestFolderViewPrivate *priv;
3376 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3378 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3380 return (const gchar *) priv->visible_account_id;
3384 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3388 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3390 gtk_tree_model_get (model, iter,
3394 gboolean result = FALSE;
3395 if (type == TNY_FOLDER_TYPE_INBOX) {
3399 *inbox_iter = *iter;
3403 if (gtk_tree_model_iter_children (model, &child, iter)) {
3404 if (find_inbox_iter (model, &child, inbox_iter))
3408 } while (gtk_tree_model_iter_next (model, iter));
3417 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3419 GtkTreeModel *model;
3420 GtkTreeIter iter, inbox_iter;
3421 GtkTreeSelection *sel;
3422 GtkTreePath *path = NULL;
3424 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3426 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3430 expand_root_items (self);
3431 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3433 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3434 g_warning ("%s: model is empty", __FUNCTION__);
3438 if (find_inbox_iter (model, &iter, &inbox_iter))
3439 path = gtk_tree_model_get_path (model, &inbox_iter);
3441 path = gtk_tree_path_new_first ();
3443 /* Select the row and free */
3444 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3445 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3446 gtk_tree_path_free (path);
3449 gtk_widget_grab_focus (GTK_WIDGET(self));
3455 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3460 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3461 TnyFolder* a_folder;
3464 gtk_tree_model_get (model, iter,
3465 INSTANCE_COLUMN, &a_folder,
3471 if (folder == a_folder) {
3472 g_object_unref (a_folder);
3473 *folder_iter = *iter;
3476 g_object_unref (a_folder);
3478 if (gtk_tree_model_iter_children (model, &child, iter)) {
3479 if (find_folder_iter (model, &child, folder_iter, folder))
3483 } while (gtk_tree_model_iter_next (model, iter));
3488 #ifndef MODEST_TOOLKIT_HILDON2
3490 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3493 ModestFolderView *self)
3495 ModestFolderViewPrivate *priv = NULL;
3496 GtkTreeSelection *sel;
3497 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3498 GObject *instance = NULL;
3500 if (!MODEST_IS_FOLDER_VIEW(self))
3503 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3505 priv->reexpand = TRUE;
3507 gtk_tree_model_get (tree_model, iter,
3509 INSTANCE_COLUMN, &instance,
3515 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3516 priv->folder_to_select = g_object_ref (instance);
3518 g_object_unref (instance);
3520 if (priv->folder_to_select) {
3522 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3525 path = gtk_tree_model_get_path (tree_model, iter);
3526 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3528 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3530 gtk_tree_selection_select_iter (sel, iter);
3531 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3533 gtk_tree_path_free (path);
3537 modest_folder_view_disable_next_folder_selection (self);
3539 /* Refilter the model */
3540 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3546 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3548 ModestFolderViewPrivate *priv;
3550 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3552 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3554 if (priv->folder_to_select)
3555 g_object_unref(priv->folder_to_select);
3557 priv->folder_to_select = NULL;
3561 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3562 gboolean after_change)
3564 GtkTreeModel *model;
3565 GtkTreeIter iter, folder_iter;
3566 GtkTreeSelection *sel;
3567 ModestFolderViewPrivate *priv = NULL;
3569 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3570 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3572 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3575 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3576 gtk_tree_selection_unselect_all (sel);
3578 if (priv->folder_to_select)
3579 g_object_unref(priv->folder_to_select);
3580 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3584 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3589 /* Refilter the model, before selecting the folder */
3590 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3592 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3593 g_warning ("%s: model is empty", __FUNCTION__);
3597 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3600 path = gtk_tree_model_get_path (model, &folder_iter);
3601 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3603 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3604 gtk_tree_selection_select_iter (sel, &folder_iter);
3605 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3607 gtk_tree_path_free (path);
3615 modest_folder_view_copy_selection (ModestFolderView *self)
3617 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3619 /* Copy selection */
3620 _clipboard_set_selected_data (self, FALSE);
3624 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3626 ModestFolderViewPrivate *priv = NULL;
3627 GtkTreeModel *model = NULL;
3628 const gchar **hidding = NULL;
3629 guint i, n_selected;
3631 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3632 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3634 /* Copy selection */
3635 if (!_clipboard_set_selected_data (folder_view, TRUE))
3638 /* Get hidding ids */
3639 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3641 /* Clear hidding array created by previous cut operation */
3642 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3644 /* Copy hidding array */
3645 priv->n_selected = n_selected;
3646 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3647 for (i=0; i < n_selected; i++)
3648 priv->hidding_ids[i] = g_strdup(hidding[i]);
3650 /* Hide cut folders */
3651 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3652 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3656 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3657 ModestFolderView *folder_view_dst)
3659 GtkTreeModel *filter_model = NULL;
3660 GtkTreeModel *model = NULL;
3661 GtkTreeModel *new_filter_model = NULL;
3663 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3664 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3667 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3668 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3670 /* Build new filter model */
3671 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3672 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3676 /* Set copied model */
3677 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3678 #ifndef MODEST_TOOLKIT_HILDON2
3679 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3680 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3684 g_object_unref (new_filter_model);
3688 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3691 GtkTreeModel *model = NULL;
3692 ModestFolderViewPrivate* priv;
3694 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3696 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3697 priv->show_non_move = show;
3698 /* modest_folder_view_update_model(folder_view, */
3699 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3701 /* Hide special folders */
3702 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3703 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3704 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3708 /* Returns FALSE if it did not selected anything */
3710 _clipboard_set_selected_data (ModestFolderView *folder_view,
3713 ModestFolderViewPrivate *priv = NULL;
3714 TnyFolderStore *folder = NULL;
3715 gboolean retval = FALSE;
3717 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3718 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3720 /* Set selected data on clipboard */
3721 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3722 folder = modest_folder_view_get_selected (folder_view);
3724 /* Do not allow to select an account */
3725 if (TNY_IS_FOLDER (folder)) {
3726 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3731 g_object_unref (folder);
3737 _clear_hidding_filter (ModestFolderView *folder_view)
3739 ModestFolderViewPrivate *priv;
3742 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3743 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3745 if (priv->hidding_ids != NULL) {
3746 for (i=0; i < priv->n_selected; i++)
3747 g_free (priv->hidding_ids[i]);
3748 g_free(priv->hidding_ids);
3754 on_display_name_changed (ModestAccountMgr *mgr,
3755 const gchar *account,
3758 ModestFolderView *self;
3760 self = MODEST_FOLDER_VIEW (user_data);
3762 /* Force a redraw */
3763 #if GTK_CHECK_VERSION(2, 8, 0)
3764 GtkTreeViewColumn * tree_column;
3766 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3768 gtk_tree_view_column_queue_resize (tree_column);
3770 gtk_widget_queue_draw (GTK_WIDGET (self));
3775 modest_folder_view_set_cell_style (ModestFolderView *self,
3776 ModestFolderViewCellStyle cell_style)
3778 ModestFolderViewPrivate *priv = NULL;
3780 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3781 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3783 priv->cell_style = cell_style;
3785 g_object_set (G_OBJECT (priv->messages_renderer),
3786 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3789 gtk_widget_queue_draw (GTK_WIDGET (self));
3793 update_style (ModestFolderView *self)
3795 ModestFolderViewPrivate *priv;
3796 GdkColor style_color;
3797 PangoAttrList *attr_list;
3799 PangoAttribute *attr;
3801 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3802 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3806 attr_list = pango_attr_list_new ();
3807 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3808 gdk_color_parse ("grey", &style_color);
3810 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3811 pango_attr_list_insert (attr_list, attr);
3814 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3816 "SmallSystemFont", NULL,
3819 attr = pango_attr_font_desc_new (pango_font_description_copy
3820 (style->font_desc));
3821 pango_attr_list_insert (attr_list, attr);
3823 g_object_set (G_OBJECT (priv->messages_renderer),
3824 "foreground-gdk", &style_color,
3825 "foreground-set", TRUE,
3826 "attributes", attr_list,
3828 pango_attr_list_unref (attr_list);
3833 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3835 if (strcmp ("style", spec->name) == 0) {
3836 update_style (MODEST_FOLDER_VIEW (obj));
3837 gtk_widget_queue_draw (GTK_WIDGET (obj));
3842 modest_folder_view_set_filter (ModestFolderView *self,
3843 ModestFolderViewFilter filter)
3845 ModestFolderViewPrivate *priv;
3846 GtkTreeModel *filter_model;
3848 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3849 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3851 priv->filter |= filter;
3853 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3854 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3855 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3860 modest_folder_view_unset_filter (ModestFolderView *self,
3861 ModestFolderViewFilter filter)
3863 ModestFolderViewPrivate *priv;
3864 GtkTreeModel *filter_model;
3866 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3867 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3869 priv->filter &= ~filter;
3871 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3872 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3873 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3878 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3879 ModestTnyFolderRules rules)
3881 GtkTreeModel *filter_model;
3883 gboolean fulfil = FALSE;
3885 if (!get_inner_models (self, &filter_model, NULL, NULL))
3888 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3892 TnyFolderStore *folder;
3894 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3896 if (TNY_IS_FOLDER (folder)) {
3897 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3898 /* Folder rules are negative: non_writable, non_deletable... */
3899 if (!(folder_rules & rules))
3902 g_object_unref (folder);
3905 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3911 modest_folder_view_set_list_to_move (ModestFolderView *self,
3914 ModestFolderViewPrivate *priv;
3916 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3917 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3919 if (priv->list_to_move)
3920 g_object_unref (priv->list_to_move);
3923 g_object_ref (list);
3925 priv->list_to_move = list;
3929 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3931 ModestFolderViewPrivate *priv;
3933 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3934 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3937 g_free (priv->mailbox);
3939 priv->mailbox = g_strdup (mailbox);
3941 /* Notify observers */
3942 g_signal_emit (G_OBJECT(self),
3943 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3944 priv->visible_account_id);
3948 modest_folder_view_get_mailbox (ModestFolderView *self)
3950 ModestFolderViewPrivate *priv;
3952 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3953 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3955 return (const gchar *) priv->mailbox;