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_debug ("%s: emtpy model or not 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,
1115 "xpad", MODEST_MARGIN_DEFAULT,
1117 "ellipsize", PANGO_ELLIPSIZE_END,
1119 "ellipsize-set", TRUE, NULL);
1120 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1121 gtk_tree_view_column_set_cell_data_func(column, renderer,
1122 text_cell_data, treeview, NULL);
1124 priv->messages_renderer = gtk_cell_renderer_text_new ();
1125 g_object_set (priv->messages_renderer,
1126 #ifdef MODEST_TOOLKIT_HILDON2
1128 "ypad", MODEST_MARGIN_DEFAULT,
1129 "xpad", MODEST_MARGIN_DOUBLE,
1131 "scale", PANGO_SCALE_X_SMALL,
1134 "alignment", PANGO_ALIGN_RIGHT,
1138 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1139 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1140 messages_cell_data, treeview, NULL);
1142 /* Set selection mode */
1143 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1144 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1146 /* Set treeview appearance */
1147 gtk_tree_view_column_set_spacing (column, 2);
1148 gtk_tree_view_column_set_resizable (column, TRUE);
1149 gtk_tree_view_column_set_fixed_width (column, TRUE);
1150 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1151 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1154 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1158 modest_folder_view_init (ModestFolderView *obj)
1160 ModestFolderViewPrivate *priv;
1163 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1165 priv->timer_expander = 0;
1166 priv->account_store = NULL;
1168 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1169 priv->cur_folder_store = NULL;
1170 priv->visible_account_id = NULL;
1171 priv->mailbox = NULL;
1172 priv->folder_to_select = NULL;
1173 priv->outbox_deleted_handler = 0;
1174 priv->reexpand = TRUE;
1176 /* Initialize the local account name */
1177 conf = modest_runtime_get_conf();
1178 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1180 /* Init email clipboard */
1181 priv->clipboard = modest_runtime_get_email_clipboard ();
1182 priv->hidding_ids = NULL;
1183 priv->n_selected = 0;
1184 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1185 priv->reselect = FALSE;
1186 priv->show_non_move = TRUE;
1187 priv->list_to_move = NULL;
1189 /* Build treeview */
1190 add_columns (GTK_WIDGET (obj));
1192 /* Setup drag and drop */
1193 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1195 /* Connect signals */
1196 g_signal_connect (G_OBJECT (obj),
1198 G_CALLBACK (on_key_pressed), NULL);
1200 priv->display_name_changed_signal =
1201 g_signal_connect (modest_runtime_get_account_mgr (),
1202 "display_name_changed",
1203 G_CALLBACK (on_display_name_changed),
1207 * Track changes in the local account name (in the device it
1208 * will be the device name)
1210 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1212 G_CALLBACK(on_configuration_key_changed),
1216 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1222 tny_account_store_view_init (gpointer g, gpointer iface_data)
1224 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1226 klass->set_account_store = modest_folder_view_set_account_store;
1230 modest_folder_view_finalize (GObject *obj)
1232 ModestFolderViewPrivate *priv;
1233 GtkTreeSelection *sel;
1234 TnyAccount *local_account;
1236 g_return_if_fail (obj);
1238 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1240 if (priv->timer_expander != 0) {
1241 g_source_remove (priv->timer_expander);
1242 priv->timer_expander = 0;
1245 local_account = (TnyAccount *)
1246 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1247 if (local_account) {
1248 if (g_signal_handler_is_connected (local_account,
1249 priv->outbox_deleted_handler))
1250 g_signal_handler_disconnect (local_account,
1251 priv->outbox_deleted_handler);
1252 g_object_unref (local_account);
1255 if (priv->account_store) {
1256 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1257 priv->account_inserted_signal);
1258 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1259 priv->account_removed_signal);
1260 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1261 priv->account_changed_signal);
1262 g_object_unref (G_OBJECT(priv->account_store));
1263 priv->account_store = NULL;
1266 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1267 priv->display_name_changed_signal)) {
1268 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1269 priv->display_name_changed_signal);
1270 priv->display_name_changed_signal = 0;
1274 g_object_unref (G_OBJECT (priv->query));
1278 if (priv->folder_to_select) {
1279 g_object_unref (G_OBJECT(priv->folder_to_select));
1280 priv->folder_to_select = NULL;
1283 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1285 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1287 g_free (priv->local_account_name);
1288 g_free (priv->visible_account_id);
1289 g_free (priv->mailbox);
1291 if (priv->conf_key_signal) {
1292 g_signal_handler_disconnect (modest_runtime_get_conf (),
1293 priv->conf_key_signal);
1294 priv->conf_key_signal = 0;
1297 if (priv->cur_folder_store) {
1298 g_object_unref (priv->cur_folder_store);
1299 priv->cur_folder_store = NULL;
1302 if (priv->list_to_move) {
1303 g_object_unref (priv->list_to_move);
1304 priv->list_to_move = NULL;
1307 /* Clear hidding array created by cut operation */
1308 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1310 G_OBJECT_CLASS(parent_class)->finalize (obj);
1315 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1317 ModestFolderViewPrivate *priv;
1320 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1321 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1323 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1324 device = tny_account_store_get_device (account_store);
1326 if (G_UNLIKELY (priv->account_store)) {
1328 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1329 priv->account_inserted_signal))
1330 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1331 priv->account_inserted_signal);
1332 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1333 priv->account_removed_signal))
1334 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1335 priv->account_removed_signal);
1336 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1337 priv->account_changed_signal))
1338 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1339 priv->account_changed_signal);
1340 g_object_unref (G_OBJECT (priv->account_store));
1343 priv->account_store = g_object_ref (G_OBJECT (account_store));
1345 priv->account_removed_signal =
1346 g_signal_connect (G_OBJECT(account_store), "account_removed",
1347 G_CALLBACK (on_account_removed), self);
1349 priv->account_inserted_signal =
1350 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1351 G_CALLBACK (on_account_inserted), self);
1353 priv->account_changed_signal =
1354 g_signal_connect (G_OBJECT(account_store), "account_changed",
1355 G_CALLBACK (on_account_changed), self);
1357 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1358 priv->reselect = FALSE;
1359 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1361 g_object_unref (G_OBJECT (device));
1365 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1368 ModestFolderView *self;
1369 GtkTreeModel *model, *filter_model;
1372 self = MODEST_FOLDER_VIEW (user_data);
1374 if (!get_inner_models (self, &filter_model, NULL, &model))
1377 /* Remove outbox from model */
1378 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1379 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1380 g_object_unref (outbox);
1383 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1387 on_account_inserted (TnyAccountStore *account_store,
1388 TnyAccount *account,
1391 ModestFolderViewPrivate *priv;
1392 GtkTreeModel *model, *filter_model;
1394 /* Ignore transport account insertions, we're not showing them
1395 in the folder view */
1396 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1399 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1402 /* If we're adding a new account, and there is no previous
1403 one, we need to select the visible server account */
1404 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1405 !priv->visible_account_id)
1406 modest_widget_memory_restore (modest_runtime_get_conf(),
1407 G_OBJECT (user_data),
1408 MODEST_CONF_FOLDER_VIEW_KEY);
1412 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1413 &filter_model, NULL, &model))
1416 /* Insert the account in the model */
1417 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1419 /* When the model is a list store (plain representation) the
1420 outbox is not a child of any account so we have to manually
1421 delete it because removing the local folders account won't
1422 delete it (because tny_folder_get_account() is not defined
1423 for a merge folder */
1424 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1425 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1427 priv->outbox_deleted_handler =
1428 g_signal_connect (account,
1430 G_CALLBACK (on_outbox_deleted_cb),
1434 /* Refilter the model */
1435 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1440 same_account_selected (ModestFolderView *self,
1441 TnyAccount *account)
1443 ModestFolderViewPrivate *priv;
1444 gboolean same_account = FALSE;
1446 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1448 if (priv->cur_folder_store) {
1449 TnyAccount *selected_folder_account = NULL;
1451 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1452 selected_folder_account =
1453 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1455 selected_folder_account =
1456 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1459 if (selected_folder_account == account)
1460 same_account = TRUE;
1462 g_object_unref (selected_folder_account);
1464 return same_account;
1469 * Selects the first inbox or the local account in an idle
1472 on_idle_select_first_inbox_or_local (gpointer user_data)
1474 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1476 gdk_threads_enter ();
1477 modest_folder_view_select_first_inbox_or_local (self);
1478 gdk_threads_leave ();
1484 on_account_changed (TnyAccountStore *account_store,
1485 TnyAccount *tny_account,
1488 ModestFolderView *self;
1489 ModestFolderViewPrivate *priv;
1490 GtkTreeModel *model, *filter_model;
1491 GtkTreeSelection *sel;
1492 gboolean same_account;
1494 /* Ignore transport account insertions, we're not showing them
1495 in the folder view */
1496 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1499 self = MODEST_FOLDER_VIEW (user_data);
1500 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1502 /* Get the inner model */
1503 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1504 &filter_model, NULL, &model))
1507 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1509 /* Invalidate the cur_folder_store only if the selected folder
1510 belongs to the account that is being removed */
1511 same_account = same_account_selected (self, tny_account);
1513 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1514 gtk_tree_selection_unselect_all (sel);
1517 /* Remove the account from the model */
1518 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1520 /* Insert the account in the model */
1521 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1523 /* Refilter the model */
1524 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1526 /* Select the first INBOX if the currently selected folder
1527 belongs to the account that is being deleted */
1528 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1529 g_idle_add (on_idle_select_first_inbox_or_local, self);
1533 on_account_removed (TnyAccountStore *account_store,
1534 TnyAccount *account,
1537 ModestFolderView *self = NULL;
1538 ModestFolderViewPrivate *priv;
1539 GtkTreeModel *model, *filter_model;
1540 GtkTreeSelection *sel = NULL;
1541 gboolean same_account = FALSE;
1543 /* Ignore transport account removals, we're not showing them
1544 in the folder view */
1545 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1548 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1549 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1553 self = MODEST_FOLDER_VIEW (user_data);
1554 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1556 /* Invalidate the cur_folder_store only if the selected folder
1557 belongs to the account that is being removed */
1558 same_account = same_account_selected (self, account);
1560 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1561 gtk_tree_selection_unselect_all (sel);
1564 /* Invalidate row to select only if the folder to select
1565 belongs to the account that is being removed*/
1566 if (priv->folder_to_select) {
1567 TnyAccount *folder_to_select_account = NULL;
1569 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1570 if (folder_to_select_account == account) {
1571 modest_folder_view_disable_next_folder_selection (self);
1572 g_object_unref (priv->folder_to_select);
1573 priv->folder_to_select = NULL;
1575 g_object_unref (folder_to_select_account);
1578 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1579 &filter_model, NULL, &model))
1582 /* Disconnect the signal handler */
1583 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1584 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1585 if (g_signal_handler_is_connected (account,
1586 priv->outbox_deleted_handler))
1587 g_signal_handler_disconnect (account,
1588 priv->outbox_deleted_handler);
1591 /* Remove the account from the model */
1592 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1594 /* If the removed account is the currently viewed one then
1595 clear the configuration value. The new visible account will be the default account */
1596 if (priv->visible_account_id &&
1597 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1599 /* Clear the current visible account_id */
1600 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1601 modest_folder_view_set_mailbox (self, NULL);
1603 /* Call the restore method, this will set the new visible account */
1604 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1605 MODEST_CONF_FOLDER_VIEW_KEY);
1608 /* Refilter the model */
1609 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1611 /* Select the first INBOX if the currently selected folder
1612 belongs to the account that is being deleted */
1614 g_idle_add (on_idle_select_first_inbox_or_local, self);
1618 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1620 GtkTreeViewColumn *col;
1622 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1624 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1626 g_printerr ("modest: failed get column for title\n");
1630 gtk_tree_view_column_set_title (col, title);
1631 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1636 modest_folder_view_on_map (ModestFolderView *self,
1637 GdkEventExpose *event,
1640 ModestFolderViewPrivate *priv;
1642 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1644 /* This won't happen often */
1645 if (G_UNLIKELY (priv->reselect)) {
1646 /* Select the first inbox or the local account if not found */
1648 /* TODO: this could cause a lock at startup, so we
1649 comment it for the moment. We know that this will
1650 be a bug, because the INBOX is not selected, but we
1651 need to rewrite some parts of Modest to avoid the
1652 deathlock situation */
1653 /* TODO: check if this is still the case */
1654 priv->reselect = FALSE;
1655 modest_folder_view_select_first_inbox_or_local (self);
1656 /* Notify the display name observers */
1657 g_signal_emit (G_OBJECT(self),
1658 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1662 if (priv->reexpand) {
1663 expand_root_items (self);
1664 priv->reexpand = FALSE;
1671 modest_folder_view_new (TnyFolderStoreQuery *query)
1674 ModestFolderViewPrivate *priv;
1675 GtkTreeSelection *sel;
1677 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1678 #ifdef MODEST_TOOLKIT_HILDON2
1679 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1682 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1685 priv->query = g_object_ref (query);
1687 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1688 priv->changed_signal = g_signal_connect (sel, "changed",
1689 G_CALLBACK (on_selection_changed), self);
1691 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1693 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1695 return GTK_WIDGET(self);
1698 /* this feels dirty; any other way to expand all the root items? */
1700 expand_root_items (ModestFolderView *self)
1703 GtkTreeModel *model;
1706 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1707 path = gtk_tree_path_new_first ();
1709 /* all folders should have child items, so.. */
1711 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1712 gtk_tree_path_next (path);
1713 } while (gtk_tree_model_get_iter (model, &iter, path));
1715 gtk_tree_path_free (path);
1719 is_parent_of (TnyFolder *a, TnyFolder *b)
1722 gboolean retval = FALSE;
1724 a_id = tny_folder_get_id (a);
1726 gchar *string_to_match;
1729 string_to_match = g_strconcat (a_id, "/", NULL);
1730 b_id = tny_folder_get_id (b);
1731 retval = g_str_has_prefix (b_id, string_to_match);
1732 g_free (string_to_match);
1738 typedef struct _ForeachFolderInfo {
1741 } ForeachFolderInfo;
1744 foreach_folder_with_id (GtkTreeModel *model,
1749 ForeachFolderInfo *info;
1752 info = (ForeachFolderInfo *) data;
1753 gtk_tree_model_get (model, iter,
1754 INSTANCE_COLUMN, &instance,
1757 if (TNY_IS_FOLDER (instance)) {
1760 id = tny_folder_get_id (TNY_FOLDER (instance));
1762 collate = g_utf8_collate_key (id, -1);
1763 info->found = !strcmp (info->needle, collate);
1769 g_object_unref (instance);
1777 has_folder_with_id (ModestFolderView *self, const gchar *id)
1779 GtkTreeModel *model;
1780 ForeachFolderInfo info = {NULL, FALSE};
1782 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1783 info.needle = g_utf8_collate_key (id, -1);
1785 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1786 g_free (info.needle);
1792 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1795 gboolean retval = FALSE;
1797 a_id = tny_folder_get_id (a);
1800 b_id = tny_folder_get_id (b);
1803 const gchar *last_bar;
1804 gchar *string_to_match;
1805 last_bar = g_strrstr (b_id, "/");
1810 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1811 retval = has_folder_with_id (self, string_to_match);
1812 g_free (string_to_match);
1820 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1822 ModestFolderViewPrivate *priv;
1823 TnyIterator *iterator;
1824 gboolean retval = TRUE;
1826 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1827 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1829 for (iterator = tny_list_create_iterator (priv->list_to_move);
1830 retval && !tny_iterator_is_done (iterator);
1831 tny_iterator_next (iterator)) {
1833 instance = tny_iterator_get_current (iterator);
1834 if (instance == (GObject *) folder) {
1836 } else if (TNY_IS_FOLDER (instance)) {
1837 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1839 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1842 g_object_unref (instance);
1844 g_object_unref (iterator);
1851 * We use this function to implement the
1852 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1853 * account in this case, and the local folders.
1856 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1858 ModestFolderViewPrivate *priv;
1859 gboolean retval = TRUE;
1860 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1861 GObject *instance = NULL;
1862 const gchar *id = NULL;
1864 gboolean found = FALSE;
1865 gboolean cleared = FALSE;
1866 ModestTnyFolderRules rules = 0;
1869 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1870 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1872 gtk_tree_model_get (model, iter,
1873 NAME_COLUMN, &fname,
1875 INSTANCE_COLUMN, &instance,
1878 /* Do not show if there is no instance, this could indeed
1879 happen when the model is being modified while it's being
1880 drawn. This could occur for example when moving folders
1887 if (TNY_IS_ACCOUNT (instance)) {
1888 TnyAccount *acc = TNY_ACCOUNT (instance);
1889 const gchar *account_id = tny_account_get_id (acc);
1891 /* If it isn't a special folder,
1892 * don't show it unless it is the visible account: */
1893 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1894 !modest_tny_account_is_virtual_local_folders (acc) &&
1895 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1897 /* Show only the visible account id */
1898 if (priv->visible_account_id) {
1899 if (strcmp (account_id, priv->visible_account_id))
1906 /* Never show these to the user. They are merged into one folder
1907 * in the local-folders account instead: */
1908 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1911 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1912 /* Only show special folders for current account if needed */
1913 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1914 TnyAccount *account;
1916 account = tny_folder_get_account (TNY_FOLDER (instance));
1918 if (TNY_IS_ACCOUNT (account)) {
1919 const gchar *account_id = tny_account_get_id (account);
1921 if (!modest_tny_account_is_virtual_local_folders (account) &&
1922 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1923 /* Show only the visible account id */
1924 if (priv->visible_account_id) {
1925 if (strcmp (account_id, priv->visible_account_id)) {
1927 } else if (priv->mailbox) {
1928 /* Filter mailboxes */
1929 if (!g_str_has_prefix (fname, priv->mailbox)) {
1931 } else if (!strcmp (fname, priv->mailbox)) {
1932 /* Hide mailbox parent */
1938 g_object_unref (account);
1945 /* Check hiding (if necessary) */
1946 cleared = modest_email_clipboard_cleared (priv->clipboard);
1947 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1948 id = tny_folder_get_id (TNY_FOLDER(instance));
1949 if (priv->hidding_ids != NULL)
1950 for (i=0; i < priv->n_selected && !found; i++)
1951 if (priv->hidding_ids[i] != NULL && id != NULL)
1952 found = (!strcmp (priv->hidding_ids[i], id));
1957 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1958 folder as no message can be move there according to UI specs */
1959 if (retval && !priv->show_non_move) {
1960 if (priv->list_to_move &&
1961 tny_list_get_length (priv->list_to_move) > 0 &&
1962 TNY_IS_FOLDER (instance)) {
1963 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1965 if (retval && TNY_IS_FOLDER (instance) &&
1966 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1968 case TNY_FOLDER_TYPE_OUTBOX:
1969 case TNY_FOLDER_TYPE_SENT:
1970 case TNY_FOLDER_TYPE_DRAFTS:
1973 case TNY_FOLDER_TYPE_UNKNOWN:
1974 case TNY_FOLDER_TYPE_NORMAL:
1975 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1976 if (type == TNY_FOLDER_TYPE_INVALID)
1977 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1979 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1980 type == TNY_FOLDER_TYPE_SENT
1981 || type == TNY_FOLDER_TYPE_DRAFTS)
1990 /* apply special filters */
1991 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1992 if (TNY_IS_ACCOUNT (instance))
1996 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
1997 if (TNY_IS_FOLDER (instance))
2001 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2002 if (TNY_IS_ACCOUNT (instance)) {
2003 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2005 } else if (TNY_IS_FOLDER (instance)) {
2006 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2011 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2012 if (TNY_IS_ACCOUNT (instance)) {
2013 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2015 } else if (TNY_IS_FOLDER (instance)) {
2016 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2021 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2022 /* A mailbox is a fake folder with an @ in the middle of the name */
2023 if (!TNY_IS_FOLDER (instance) ||
2024 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2027 const gchar *folder_name;
2028 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2029 if (!folder_name || strchr (folder_name, '@') == NULL)
2035 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2036 if (TNY_IS_FOLDER (instance)) {
2037 /* Check folder rules */
2038 ModestTnyFolderRules rules;
2040 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2041 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2042 } else if (TNY_IS_ACCOUNT (instance)) {
2043 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2051 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2052 if (TNY_IS_FOLDER (instance)) {
2053 TnyFolderType guess_type;
2055 if (TNY_FOLDER_TYPE_NORMAL) {
2056 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2062 case TNY_FOLDER_TYPE_OUTBOX:
2063 case TNY_FOLDER_TYPE_SENT:
2064 case TNY_FOLDER_TYPE_DRAFTS:
2065 case TNY_FOLDER_TYPE_ARCHIVE:
2066 case TNY_FOLDER_TYPE_INBOX:
2069 case TNY_FOLDER_TYPE_UNKNOWN:
2070 case TNY_FOLDER_TYPE_NORMAL:
2076 } else if (TNY_IS_ACCOUNT (instance)) {
2081 if (retval && TNY_IS_FOLDER (instance)) {
2082 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2085 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2086 if (TNY_IS_FOLDER (instance)) {
2087 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2088 } else if (TNY_IS_ACCOUNT (instance)) {
2093 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2094 if (TNY_IS_FOLDER (instance)) {
2095 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2096 } else if (TNY_IS_ACCOUNT (instance)) {
2101 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2102 if (TNY_IS_FOLDER (instance)) {
2103 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2104 } else if (TNY_IS_ACCOUNT (instance)) {
2110 g_object_unref (instance);
2118 modest_folder_view_update_model (ModestFolderView *self,
2119 TnyAccountStore *account_store)
2121 ModestFolderViewPrivate *priv;
2122 GtkTreeModel *model /* , *old_model */;
2123 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2125 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2126 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2129 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2131 /* Notify that there is no folder selected */
2132 g_signal_emit (G_OBJECT(self),
2133 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2135 if (priv->cur_folder_store) {
2136 g_object_unref (priv->cur_folder_store);
2137 priv->cur_folder_store = NULL;
2140 /* FIXME: the local accounts are not shown when the query
2141 selects only the subscribed folders */
2142 #ifdef MODEST_TOOLKIT_HILDON2
2143 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2144 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2145 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2146 MODEST_FOLDER_PATH_SEPARATOR);
2148 model = tny_gtk_folder_store_tree_model_new (NULL);
2151 /* When the model is a list store (plain representation) the
2152 outbox is not a child of any account so we have to manually
2153 delete it because removing the local folders account won't
2154 delete it (because tny_folder_get_account() is not defined
2155 for a merge folder */
2156 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2157 TnyAccount *account;
2158 ModestTnyAccountStore *acc_store;
2160 acc_store = modest_runtime_get_account_store ();
2161 account = modest_tny_account_store_get_local_folders_account (acc_store);
2163 if (g_signal_handler_is_connected (account,
2164 priv->outbox_deleted_handler))
2165 g_signal_handler_disconnect (account,
2166 priv->outbox_deleted_handler);
2168 priv->outbox_deleted_handler =
2169 g_signal_connect (account,
2171 G_CALLBACK (on_outbox_deleted_cb),
2173 g_object_unref (account);
2176 /* Get the accounts: */
2177 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2179 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2181 sortable = gtk_tree_model_sort_new_with_model (model);
2182 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2184 GTK_SORT_ASCENDING);
2185 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2187 cmp_rows, NULL, NULL);
2189 /* Create filter model */
2190 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2191 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2197 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2198 #ifndef MODEST_TOOLKIT_HILDON2
2199 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2200 (GCallback) on_row_inserted_maybe_select_folder, self);
2203 g_object_unref (model);
2204 g_object_unref (filter_model);
2205 g_object_unref (sortable);
2207 /* Force a reselection of the INBOX next time the widget is shown */
2208 priv->reselect = TRUE;
2215 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2217 GtkTreeModel *model = NULL;
2218 TnyFolderStore *folder = NULL;
2220 ModestFolderView *tree_view = NULL;
2221 ModestFolderViewPrivate *priv = NULL;
2222 gboolean selected = FALSE;
2224 g_return_if_fail (sel);
2225 g_return_if_fail (user_data);
2227 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2229 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2231 tree_view = MODEST_FOLDER_VIEW (user_data);
2234 gtk_tree_model_get (model, &iter,
2235 INSTANCE_COLUMN, &folder,
2238 /* If the folder is the same do not notify */
2239 if (folder && priv->cur_folder_store == folder) {
2240 g_object_unref (folder);
2245 /* Current folder was unselected */
2246 if (priv->cur_folder_store) {
2247 /* We must do this firstly because a libtinymail-camel
2248 implementation detail. If we issue the signal
2249 before doing the sync_async, then that signal could
2250 cause (and it actually does it) a free of the
2251 summary of the folder (because the main window will
2252 clear the headers view */
2253 if (TNY_IS_FOLDER(priv->cur_folder_store))
2254 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2255 FALSE, NULL, NULL, NULL);
2257 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2258 priv->cur_folder_store, FALSE);
2260 g_object_unref (priv->cur_folder_store);
2261 priv->cur_folder_store = NULL;
2264 /* New current references */
2265 priv->cur_folder_store = folder;
2267 /* New folder has been selected. Do not notify if there is
2268 nothing new selected */
2270 g_signal_emit (G_OBJECT(tree_view),
2271 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2272 0, priv->cur_folder_store, TRUE);
2277 on_row_activated (GtkTreeView *treeview,
2278 GtkTreePath *treepath,
2279 GtkTreeViewColumn *column,
2282 GtkTreeModel *model = NULL;
2283 TnyFolderStore *folder = NULL;
2285 ModestFolderView *self = NULL;
2286 ModestFolderViewPrivate *priv = NULL;
2288 g_return_if_fail (treeview);
2289 g_return_if_fail (user_data);
2291 self = MODEST_FOLDER_VIEW (user_data);
2292 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2294 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2296 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2299 gtk_tree_model_get (model, &iter,
2300 INSTANCE_COLUMN, &folder,
2303 g_signal_emit (G_OBJECT(self),
2304 signals[FOLDER_ACTIVATED_SIGNAL],
2307 #ifdef MODEST_TOOLKIT_HILDON2
2308 HildonUIMode ui_mode;
2309 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2310 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2311 if (priv->cur_folder_store)
2312 g_object_unref (priv->cur_folder_store);
2313 priv->cur_folder_store = g_object_ref (folder);
2317 g_object_unref (folder);
2321 modest_folder_view_get_selected (ModestFolderView *self)
2323 ModestFolderViewPrivate *priv;
2325 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2327 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2328 if (priv->cur_folder_store)
2329 g_object_ref (priv->cur_folder_store);
2331 return priv->cur_folder_store;
2335 get_cmp_rows_type_pos (GObject *folder)
2337 /* Remote accounts -> Local account -> MMC account .*/
2340 if (TNY_IS_ACCOUNT (folder) &&
2341 modest_tny_account_is_virtual_local_folders (
2342 TNY_ACCOUNT (folder))) {
2344 } else if (TNY_IS_ACCOUNT (folder)) {
2345 TnyAccount *account = TNY_ACCOUNT (folder);
2346 const gchar *account_id = tny_account_get_id (account);
2347 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2353 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2354 return -1; /* Should never happen */
2359 inbox_is_special (TnyFolderStore *folder_store)
2361 gboolean is_special = TRUE;
2363 if (TNY_IS_FOLDER (folder_store)) {
2367 gchar *last_inbox_bar;
2369 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2370 downcase = g_utf8_strdown (id, -1);
2371 last_bar = g_strrstr (downcase, "/");
2373 last_inbox_bar = g_strrstr (downcase, "inbox/");
2374 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2385 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2387 TnyAccount *account;
2388 gboolean is_special;
2389 /* Inbox, Outbox, Drafts, Sent, User */
2392 if (!TNY_IS_FOLDER (folder_store))
2395 case TNY_FOLDER_TYPE_INBOX:
2397 account = tny_folder_get_account (folder_store);
2398 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2400 /* In inbox case we need to know if the inbox is really the top
2401 * inbox of the account, or if it's a submailbox inbox. To do
2402 * this we'll apply an heuristic rule: Find last "/" and check
2403 * if it's preceeded by another Inbox */
2404 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2405 g_object_unref (account);
2406 return is_special?0:4;
2409 case TNY_FOLDER_TYPE_OUTBOX:
2410 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2412 case TNY_FOLDER_TYPE_DRAFTS:
2414 account = tny_folder_get_account (folder_store);
2415 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2416 g_object_unref (account);
2417 return is_special?1:4;
2420 case TNY_FOLDER_TYPE_SENT:
2422 account = tny_folder_get_account (folder_store);
2423 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2424 g_object_unref (account);
2425 return is_special?3:4;
2434 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2436 const gchar *a1_name, *a2_name;
2438 a1_name = tny_account_get_name (a1);
2439 a2_name = tny_account_get_name (a2);
2441 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2445 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2447 TnyAccount *a1 = NULL, *a2 = NULL;
2450 if (TNY_IS_ACCOUNT (s1)) {
2451 a1 = TNY_ACCOUNT (g_object_ref (s1));
2452 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2453 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2456 if (TNY_IS_ACCOUNT (s2)) {
2457 a2 = TNY_ACCOUNT (g_object_ref (s2));
2458 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2459 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2476 /* First we sort with the type of account */
2477 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2481 cmp = compare_account_names (a1, a2);
2485 g_object_unref (a1);
2487 g_object_unref (a2);
2493 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2495 gint is_account1, is_account2;
2497 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2498 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2500 return is_account2 - is_account1;
2504 * This function orders the mail accounts according to these rules:
2505 * 1st - remote accounts
2506 * 2nd - local account
2510 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2514 gchar *name1 = NULL;
2515 gchar *name2 = NULL;
2516 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2517 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2518 GObject *folder1 = NULL;
2519 GObject *folder2 = NULL;
2521 gtk_tree_model_get (tree_model, iter1,
2522 NAME_COLUMN, &name1,
2524 INSTANCE_COLUMN, &folder1,
2526 gtk_tree_model_get (tree_model, iter2,
2527 NAME_COLUMN, &name2,
2528 TYPE_COLUMN, &type2,
2529 INSTANCE_COLUMN, &folder2,
2532 /* Return if we get no folder. This could happen when folder
2533 operations are happening. The model is updated after the
2534 folder copy/move actually occurs, so there could be
2535 situations where the model to be drawn is not correct */
2536 if (!folder1 || !folder2)
2539 /* Sort by type. First the special folders, then the archives */
2540 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2544 /* Now we sort using the account of each folder */
2545 if (TNY_IS_FOLDER_STORE (folder1) &&
2546 TNY_IS_FOLDER_STORE (folder2)) {
2547 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2551 /* Each group is preceeded by its account */
2552 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2557 /* Pure sort by name */
2558 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2561 g_object_unref(G_OBJECT(folder1));
2563 g_object_unref(G_OBJECT(folder2));
2571 /*****************************************************************************/
2572 /* DRAG and DROP stuff */
2573 /*****************************************************************************/
2575 * This function fills the #GtkSelectionData with the row and the
2576 * model that has been dragged. It's called when this widget is a
2577 * source for dnd after the event drop happened
2580 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2581 guint info, guint time, gpointer data)
2583 GtkTreeSelection *selection;
2584 GtkTreeModel *model;
2586 GtkTreePath *source_row;
2588 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2589 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2591 source_row = gtk_tree_model_get_path (model, &iter);
2592 gtk_tree_set_row_drag_data (selection_data,
2596 gtk_tree_path_free (source_row);
2600 typedef struct _DndHelper {
2601 ModestFolderView *folder_view;
2602 gboolean delete_source;
2603 GtkTreePath *source_row;
2607 dnd_helper_destroyer (DndHelper *helper)
2609 /* Free the helper */
2610 gtk_tree_path_free (helper->source_row);
2611 g_slice_free (DndHelper, helper);
2615 xfer_folder_cb (ModestMailOperation *mail_op,
2616 TnyFolder *new_folder,
2620 /* Select the folder */
2621 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2627 /* get the folder for the row the treepath refers to. */
2628 /* folder must be unref'd */
2629 static TnyFolderStore *
2630 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2633 TnyFolderStore *folder = NULL;
2635 if (gtk_tree_model_get_iter (model,&iter, path))
2636 gtk_tree_model_get (model, &iter,
2637 INSTANCE_COLUMN, &folder,
2644 * This function is used by drag_data_received_cb to manage drag and
2645 * drop of a header, i.e, and drag from the header view to the folder
2649 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2650 GtkTreeModel *dest_model,
2651 GtkTreePath *dest_row,
2652 GtkSelectionData *selection_data)
2654 TnyList *headers = NULL;
2655 TnyFolder *folder = NULL, *src_folder = NULL;
2656 TnyFolderType folder_type;
2657 GtkTreeIter source_iter, dest_iter;
2658 ModestWindowMgr *mgr = NULL;
2659 ModestWindow *main_win = NULL;
2660 gchar **uris, **tmp;
2662 /* Build the list of headers */
2663 mgr = modest_runtime_get_window_mgr ();
2664 headers = tny_simple_list_new ();
2665 uris = modest_dnd_selection_data_get_paths (selection_data);
2668 while (*tmp != NULL) {
2671 gboolean first = TRUE;
2674 path = gtk_tree_path_new_from_string (*tmp);
2675 gtk_tree_model_get_iter (source_model, &source_iter, path);
2676 gtk_tree_model_get (source_model, &source_iter,
2677 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2680 /* Do not enable d&d of headers already opened */
2681 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2682 tny_list_append (headers, G_OBJECT (header));
2684 if (G_UNLIKELY (first)) {
2685 src_folder = tny_header_get_folder (header);
2689 /* Free and go on */
2690 gtk_tree_path_free (path);
2691 g_object_unref (header);
2696 /* This could happen ig we perform a d&d very quickly over the
2697 same row that row could dissapear because message is
2699 if (!TNY_IS_FOLDER (src_folder))
2702 /* Get the target folder */
2703 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2704 gtk_tree_model_get (dest_model, &dest_iter,
2708 if (!folder || !TNY_IS_FOLDER(folder)) {
2709 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2713 folder_type = modest_tny_folder_guess_folder_type (folder);
2714 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2715 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2716 goto cleanup; /* cannot move messages there */
2719 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2720 /* g_warning ("folder not writable"); */
2721 goto cleanup; /* verboten! */
2724 /* Ask for confirmation to move */
2725 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2727 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2731 /* Transfer messages */
2732 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2737 if (G_IS_OBJECT (src_folder))
2738 g_object_unref (src_folder);
2739 if (G_IS_OBJECT(folder))
2740 g_object_unref (G_OBJECT (folder));
2741 if (G_IS_OBJECT(headers))
2742 g_object_unref (headers);
2746 TnyFolderStore *src_folder;
2747 TnyFolderStore *dst_folder;
2748 ModestFolderView *folder_view;
2753 dnd_folder_info_destroyer (DndFolderInfo *info)
2755 if (info->src_folder)
2756 g_object_unref (info->src_folder);
2757 if (info->dst_folder)
2758 g_object_unref (info->dst_folder);
2759 g_slice_free (DndFolderInfo, info);
2763 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2764 GtkWindow *parent_window,
2765 TnyAccount *account)
2768 modest_ui_actions_on_account_connection_error (parent_window, account);
2770 /* Free the helper & info */
2771 dnd_helper_destroyer (info->helper);
2772 dnd_folder_info_destroyer (info);
2776 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2778 GtkWindow *parent_window,
2779 TnyAccount *account,
2782 DndFolderInfo *info = NULL;
2783 ModestMailOperation *mail_op;
2785 info = (DndFolderInfo *) user_data;
2787 if (err || canceled) {
2788 dnd_on_connection_failed_destroyer (info, parent_window, account);
2792 /* Do the mail operation */
2793 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2794 modest_ui_actions_move_folder_error_handler,
2795 info->src_folder, NULL);
2797 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2800 /* Transfer the folder */
2801 modest_mail_operation_xfer_folder (mail_op,
2802 TNY_FOLDER (info->src_folder),
2804 info->helper->delete_source,
2806 info->helper->folder_view);
2809 g_object_unref (G_OBJECT (mail_op));
2810 dnd_helper_destroyer (info->helper);
2811 dnd_folder_info_destroyer (info);
2816 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2818 GtkWindow *parent_window,
2819 TnyAccount *account,
2822 DndFolderInfo *info = NULL;
2824 info = (DndFolderInfo *) user_data;
2826 if (err || canceled) {
2827 dnd_on_connection_failed_destroyer (info, parent_window, account);
2831 /* Connect to source folder and perform the copy/move */
2832 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2834 drag_and_drop_from_folder_view_src_folder_performer,
2839 * This function is used by drag_data_received_cb to manage drag and
2840 * drop of a folder, i.e, and drag from the folder view to the same
2844 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2845 GtkTreeModel *dest_model,
2846 GtkTreePath *dest_row,
2847 GtkSelectionData *selection_data,
2850 GtkTreeIter dest_iter, iter;
2851 TnyFolderStore *dest_folder = NULL;
2852 TnyFolderStore *folder = NULL;
2853 gboolean forbidden = FALSE;
2855 DndFolderInfo *info = NULL;
2857 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2859 g_warning ("%s: BUG: no main window", __FUNCTION__);
2860 dnd_helper_destroyer (helper);
2865 /* check the folder rules for the destination */
2866 folder = tree_path_to_folder (dest_model, dest_row);
2867 if (TNY_IS_FOLDER(folder)) {
2868 ModestTnyFolderRules rules =
2869 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2870 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2871 } else if (TNY_IS_FOLDER_STORE(folder)) {
2872 /* enable local root as destination for folders */
2873 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2874 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2877 g_object_unref (folder);
2880 /* check the folder rules for the source */
2881 folder = tree_path_to_folder (source_model, helper->source_row);
2882 if (TNY_IS_FOLDER(folder)) {
2883 ModestTnyFolderRules rules =
2884 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2885 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2888 g_object_unref (folder);
2892 /* Check if the drag is possible */
2893 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2895 modest_platform_run_information_dialog ((GtkWindow *) win,
2896 _("mail_in_ui_folder_move_target_error"),
2898 /* Restore the previous selection */
2899 folder = tree_path_to_folder (source_model, helper->source_row);
2901 if (TNY_IS_FOLDER (folder))
2902 modest_folder_view_select_folder (helper->folder_view,
2903 TNY_FOLDER (folder), FALSE);
2904 g_object_unref (folder);
2906 dnd_helper_destroyer (helper);
2911 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2912 gtk_tree_model_get (dest_model, &dest_iter,
2915 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2916 gtk_tree_model_get (source_model, &iter,
2920 /* Create the info for the performer */
2921 info = g_slice_new0 (DndFolderInfo);
2922 info->src_folder = g_object_ref (folder);
2923 info->dst_folder = g_object_ref (dest_folder);
2924 info->helper = helper;
2926 /* Connect to the destination folder and perform the copy/move */
2927 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2929 drag_and_drop_from_folder_view_dst_folder_performer,
2933 g_object_unref (dest_folder);
2934 g_object_unref (folder);
2938 * This function receives the data set by the "drag-data-get" signal
2939 * handler. This information comes within the #GtkSelectionData. This
2940 * function will manage both the drags of folders of the treeview and
2941 * drags of headers of the header view widget.
2944 on_drag_data_received (GtkWidget *widget,
2945 GdkDragContext *context,
2948 GtkSelectionData *selection_data,
2953 GtkWidget *source_widget;
2954 GtkTreeModel *dest_model, *source_model;
2955 GtkTreePath *source_row, *dest_row;
2956 GtkTreeViewDropPosition pos;
2957 gboolean delete_source = FALSE;
2958 gboolean success = FALSE;
2960 /* Do not allow further process */
2961 g_signal_stop_emission_by_name (widget, "drag-data-received");
2962 source_widget = gtk_drag_get_source_widget (context);
2964 /* Get the action */
2965 if (context->action == GDK_ACTION_MOVE) {
2966 delete_source = TRUE;
2968 /* Notify that there is no folder selected. We need to
2969 do this in order to update the headers view (and
2970 its monitors, because when moving, the old folder
2971 won't longer exist. We can not wait for the end of
2972 the operation, because the operation won't start if
2973 the folder is in use */
2974 if (source_widget == widget) {
2975 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2976 gtk_tree_selection_unselect_all (sel);
2980 /* Check if the get_data failed */
2981 if (selection_data == NULL || selection_data->length < 0)
2984 /* Select the destination model */
2985 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2987 /* Get the path to the destination row. Can not call
2988 gtk_tree_view_get_drag_dest_row() because the source row
2989 is not selected anymore */
2990 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2993 /* Only allow drops IN other rows */
2995 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2996 pos == GTK_TREE_VIEW_DROP_AFTER)
3000 /* Drags from the header view */
3001 if (source_widget != widget) {
3002 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3004 drag_and_drop_from_header_view (source_model,
3009 DndHelper *helper = NULL;
3011 /* Get the source model and row */
3012 gtk_tree_get_row_drag_data (selection_data,
3016 /* Create the helper */
3017 helper = g_slice_new0 (DndHelper);
3018 helper->delete_source = delete_source;
3019 helper->source_row = gtk_tree_path_copy (source_row);
3020 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3022 drag_and_drop_from_folder_view (source_model,
3028 gtk_tree_path_free (source_row);
3032 gtk_tree_path_free (dest_row);
3035 /* Finish the drag and drop */
3036 gtk_drag_finish (context, success, FALSE, time);
3040 * We define a "drag-drop" signal handler because we do not want to
3041 * use the default one, because the default one always calls
3042 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3043 * signal handler, because there we have all the information available
3044 * to know if the dnd was a success or not.
3047 drag_drop_cb (GtkWidget *widget,
3048 GdkDragContext *context,
3056 if (!context->targets)
3059 /* Check if we're dragging a folder row */
3060 target = gtk_drag_dest_find_target (widget, context, NULL);
3062 /* Request the data from the source. */
3063 gtk_drag_get_data(widget, context, target, time);
3069 * This function expands a node of a tree view if it's not expanded
3070 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3071 * does that, so that's why they're here.
3074 expand_row_timeout (gpointer data)
3076 GtkTreeView *tree_view = data;
3077 GtkTreePath *dest_path = NULL;
3078 GtkTreeViewDropPosition pos;
3079 gboolean result = FALSE;
3081 gdk_threads_enter ();
3083 gtk_tree_view_get_drag_dest_row (tree_view,
3088 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3089 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3090 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3091 gtk_tree_path_free (dest_path);
3095 gtk_tree_path_free (dest_path);
3100 gdk_threads_leave ();
3106 * This function is called whenever the pointer is moved over a widget
3107 * while dragging some data. It installs a timeout that will expand a
3108 * node of the treeview if not expanded yet. This function also calls
3109 * gdk_drag_status in order to set the suggested action that will be
3110 * used by the "drag-data-received" signal handler to know if we
3111 * should do a move or just a copy of the data.
3114 on_drag_motion (GtkWidget *widget,
3115 GdkDragContext *context,
3121 GtkTreeViewDropPosition pos;
3122 GtkTreePath *dest_row;
3123 GtkTreeModel *dest_model;
3124 ModestFolderViewPrivate *priv;
3125 GdkDragAction suggested_action;
3126 gboolean valid_location = FALSE;
3127 TnyFolderStore *folder = NULL;
3129 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3131 if (priv->timer_expander != 0) {
3132 g_source_remove (priv->timer_expander);
3133 priv->timer_expander = 0;
3136 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3141 /* Do not allow drops between folders */
3143 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3144 pos == GTK_TREE_VIEW_DROP_AFTER) {
3145 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3146 gdk_drag_status(context, 0, time);
3147 valid_location = FALSE;
3150 valid_location = TRUE;
3153 /* Check that the destination folder is writable */
3154 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3155 folder = tree_path_to_folder (dest_model, dest_row);
3156 if (folder && TNY_IS_FOLDER (folder)) {
3157 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3159 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3160 valid_location = FALSE;
3165 /* Expand the selected row after 1/2 second */
3166 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3167 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3169 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3171 /* Select the desired action. By default we pick MOVE */
3172 suggested_action = GDK_ACTION_MOVE;
3174 if (context->actions == GDK_ACTION_COPY)
3175 gdk_drag_status(context, GDK_ACTION_COPY, time);
3176 else if (context->actions == GDK_ACTION_MOVE)
3177 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3178 else if (context->actions & suggested_action)
3179 gdk_drag_status(context, suggested_action, time);
3181 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3185 g_object_unref (folder);
3187 gtk_tree_path_free (dest_row);
3189 g_signal_stop_emission_by_name (widget, "drag-motion");
3191 return valid_location;
3195 * This function sets the treeview as a source and a target for dnd
3196 * events. It also connects all the requirede signals.
3199 setup_drag_and_drop (GtkTreeView *self)
3201 /* Set up the folder view as a dnd destination. Set only the
3202 highlight flag, otherwise gtk will have a different
3204 #ifdef MODEST_TOOLKIT_HILDON2
3207 gtk_drag_dest_set (GTK_WIDGET (self),
3208 GTK_DEST_DEFAULT_HIGHLIGHT,
3209 folder_view_drag_types,
3210 G_N_ELEMENTS (folder_view_drag_types),
3211 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3213 g_signal_connect (G_OBJECT (self),
3214 "drag_data_received",
3215 G_CALLBACK (on_drag_data_received),
3219 /* Set up the treeview as a dnd source */
3220 gtk_drag_source_set (GTK_WIDGET (self),
3222 folder_view_drag_types,
3223 G_N_ELEMENTS (folder_view_drag_types),
3224 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3226 g_signal_connect (G_OBJECT (self),
3228 G_CALLBACK (on_drag_motion),
3231 g_signal_connect (G_OBJECT (self),
3233 G_CALLBACK (on_drag_data_get),
3236 g_signal_connect (G_OBJECT (self),
3238 G_CALLBACK (drag_drop_cb),
3243 * This function manages the navigation through the folders using the
3244 * keyboard or the hardware keys in the device
3247 on_key_pressed (GtkWidget *self,
3251 GtkTreeSelection *selection;
3253 GtkTreeModel *model;
3254 gboolean retval = FALSE;
3256 /* Up and Down are automatically managed by the treeview */
3257 if (event->keyval == GDK_Return) {
3258 /* Expand/Collapse the selected row */
3259 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3260 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3263 path = gtk_tree_model_get_path (model, &iter);
3265 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3266 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3268 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3269 gtk_tree_path_free (path);
3271 /* No further processing */
3279 * We listen to the changes in the local folder account name key,
3280 * because we want to show the right name in the view. The local
3281 * folder account name corresponds to the device name in the Maemo
3282 * version. We do this because we do not want to query gconf on each
3283 * tree view refresh. It's better to cache it and change whenever
3287 on_configuration_key_changed (ModestConf* conf,
3289 ModestConfEvent event,
3290 ModestConfNotificationId id,
3291 ModestFolderView *self)
3293 ModestFolderViewPrivate *priv;
3296 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3297 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3299 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3300 g_free (priv->local_account_name);
3302 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3303 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3305 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3306 MODEST_CONF_DEVICE_NAME, NULL);
3308 /* Force a redraw */
3309 #if GTK_CHECK_VERSION(2, 8, 0)
3310 GtkTreeViewColumn * tree_column;
3312 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3314 gtk_tree_view_column_queue_resize (tree_column);
3316 gtk_widget_queue_draw (GTK_WIDGET (self));
3322 modest_folder_view_set_style (ModestFolderView *self,
3323 ModestFolderViewStyle style)
3325 ModestFolderViewPrivate *priv;
3327 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3328 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3329 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3331 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3334 priv->style = style;
3338 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3339 const gchar *account_id)
3341 ModestFolderViewPrivate *priv;
3342 GtkTreeModel *model;
3344 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3346 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3348 /* This will be used by the filter_row callback,
3349 * to decided which rows to show: */
3350 if (priv->visible_account_id) {
3351 g_free (priv->visible_account_id);
3352 priv->visible_account_id = NULL;
3355 priv->visible_account_id = g_strdup (account_id);
3358 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3359 if (GTK_IS_TREE_MODEL_FILTER (model))
3360 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3362 /* Save settings to gconf */
3363 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3364 MODEST_CONF_FOLDER_VIEW_KEY);
3366 /* Notify observers */
3367 g_signal_emit (G_OBJECT(self),
3368 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3373 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3375 ModestFolderViewPrivate *priv;
3377 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3379 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3381 return (const gchar *) priv->visible_account_id;
3385 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3389 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3391 gtk_tree_model_get (model, iter,
3395 gboolean result = FALSE;
3396 if (type == TNY_FOLDER_TYPE_INBOX) {
3400 *inbox_iter = *iter;
3404 if (gtk_tree_model_iter_children (model, &child, iter)) {
3405 if (find_inbox_iter (model, &child, inbox_iter))
3409 } while (gtk_tree_model_iter_next (model, iter));
3418 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3420 #ifndef MODEST_TOOLKIT_HILDON2
3421 GtkTreeModel *model;
3422 GtkTreeIter iter, inbox_iter;
3423 GtkTreeSelection *sel;
3424 GtkTreePath *path = NULL;
3426 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3428 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3432 expand_root_items (self);
3433 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3435 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3436 g_warning ("%s: model is empty", __FUNCTION__);
3440 if (find_inbox_iter (model, &iter, &inbox_iter))
3441 path = gtk_tree_model_get_path (model, &inbox_iter);
3443 path = gtk_tree_path_new_first ();
3445 /* Select the row and free */
3446 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3447 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3448 gtk_tree_path_free (path);
3451 gtk_widget_grab_focus (GTK_WIDGET(self));
3458 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3463 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3464 TnyFolder* a_folder;
3467 gtk_tree_model_get (model, iter,
3468 INSTANCE_COLUMN, &a_folder,
3474 if (folder == a_folder) {
3475 g_object_unref (a_folder);
3476 *folder_iter = *iter;
3479 g_object_unref (a_folder);
3481 if (gtk_tree_model_iter_children (model, &child, iter)) {
3482 if (find_folder_iter (model, &child, folder_iter, folder))
3486 } while (gtk_tree_model_iter_next (model, iter));
3491 #ifndef MODEST_TOOLKIT_HILDON2
3493 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3496 ModestFolderView *self)
3498 ModestFolderViewPrivate *priv = NULL;
3499 GtkTreeSelection *sel;
3500 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3501 GObject *instance = NULL;
3503 if (!MODEST_IS_FOLDER_VIEW(self))
3506 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3508 priv->reexpand = TRUE;
3510 gtk_tree_model_get (tree_model, iter,
3512 INSTANCE_COLUMN, &instance,
3518 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3519 priv->folder_to_select = g_object_ref (instance);
3521 g_object_unref (instance);
3523 if (priv->folder_to_select) {
3525 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3528 path = gtk_tree_model_get_path (tree_model, iter);
3529 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3531 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3533 gtk_tree_selection_select_iter (sel, iter);
3534 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3536 gtk_tree_path_free (path);
3540 modest_folder_view_disable_next_folder_selection (self);
3542 /* Refilter the model */
3543 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3549 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3551 ModestFolderViewPrivate *priv;
3553 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3555 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3557 if (priv->folder_to_select)
3558 g_object_unref(priv->folder_to_select);
3560 priv->folder_to_select = NULL;
3564 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3565 gboolean after_change)
3567 GtkTreeModel *model;
3568 GtkTreeIter iter, folder_iter;
3569 GtkTreeSelection *sel;
3570 ModestFolderViewPrivate *priv = NULL;
3572 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3573 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3575 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3578 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3579 gtk_tree_selection_unselect_all (sel);
3581 if (priv->folder_to_select)
3582 g_object_unref(priv->folder_to_select);
3583 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3587 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3592 /* Refilter the model, before selecting the folder */
3593 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3595 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3596 g_warning ("%s: model is empty", __FUNCTION__);
3600 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3603 path = gtk_tree_model_get_path (model, &folder_iter);
3604 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3606 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3607 gtk_tree_selection_select_iter (sel, &folder_iter);
3608 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3610 gtk_tree_path_free (path);
3618 modest_folder_view_copy_selection (ModestFolderView *self)
3620 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3622 /* Copy selection */
3623 _clipboard_set_selected_data (self, FALSE);
3627 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3629 ModestFolderViewPrivate *priv = NULL;
3630 GtkTreeModel *model = NULL;
3631 const gchar **hidding = NULL;
3632 guint i, n_selected;
3634 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3635 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3637 /* Copy selection */
3638 if (!_clipboard_set_selected_data (folder_view, TRUE))
3641 /* Get hidding ids */
3642 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3644 /* Clear hidding array created by previous cut operation */
3645 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3647 /* Copy hidding array */
3648 priv->n_selected = n_selected;
3649 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3650 for (i=0; i < n_selected; i++)
3651 priv->hidding_ids[i] = g_strdup(hidding[i]);
3653 /* Hide cut folders */
3654 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3655 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3659 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3660 ModestFolderView *folder_view_dst)
3662 GtkTreeModel *filter_model = NULL;
3663 GtkTreeModel *model = NULL;
3664 GtkTreeModel *new_filter_model = NULL;
3666 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3667 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3670 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3671 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3673 /* Build new filter model */
3674 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3675 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3679 /* Set copied model */
3680 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3681 #ifndef MODEST_TOOLKIT_HILDON2
3682 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3683 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3687 g_object_unref (new_filter_model);
3691 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3694 GtkTreeModel *model = NULL;
3695 ModestFolderViewPrivate* priv;
3697 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3699 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3700 priv->show_non_move = show;
3701 /* modest_folder_view_update_model(folder_view, */
3702 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3704 /* Hide special folders */
3705 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3706 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3707 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3711 /* Returns FALSE if it did not selected anything */
3713 _clipboard_set_selected_data (ModestFolderView *folder_view,
3716 ModestFolderViewPrivate *priv = NULL;
3717 TnyFolderStore *folder = NULL;
3718 gboolean retval = FALSE;
3720 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3721 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3723 /* Set selected data on clipboard */
3724 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3725 folder = modest_folder_view_get_selected (folder_view);
3727 /* Do not allow to select an account */
3728 if (TNY_IS_FOLDER (folder)) {
3729 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3734 g_object_unref (folder);
3740 _clear_hidding_filter (ModestFolderView *folder_view)
3742 ModestFolderViewPrivate *priv;
3745 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3746 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3748 if (priv->hidding_ids != NULL) {
3749 for (i=0; i < priv->n_selected; i++)
3750 g_free (priv->hidding_ids[i]);
3751 g_free(priv->hidding_ids);
3757 on_display_name_changed (ModestAccountMgr *mgr,
3758 const gchar *account,
3761 ModestFolderView *self;
3763 self = MODEST_FOLDER_VIEW (user_data);
3765 /* Force a redraw */
3766 #if GTK_CHECK_VERSION(2, 8, 0)
3767 GtkTreeViewColumn * tree_column;
3769 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3771 gtk_tree_view_column_queue_resize (tree_column);
3773 gtk_widget_queue_draw (GTK_WIDGET (self));
3778 modest_folder_view_set_cell_style (ModestFolderView *self,
3779 ModestFolderViewCellStyle cell_style)
3781 ModestFolderViewPrivate *priv = NULL;
3783 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3784 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3786 priv->cell_style = cell_style;
3788 g_object_set (G_OBJECT (priv->messages_renderer),
3789 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3792 gtk_widget_queue_draw (GTK_WIDGET (self));
3796 update_style (ModestFolderView *self)
3798 ModestFolderViewPrivate *priv;
3799 GdkColor style_color;
3800 PangoAttrList *attr_list;
3802 PangoAttribute *attr;
3804 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3805 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3809 attr_list = pango_attr_list_new ();
3810 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3811 gdk_color_parse ("grey", &style_color);
3813 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3814 pango_attr_list_insert (attr_list, attr);
3817 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3819 "SmallSystemFont", NULL,
3822 attr = pango_attr_font_desc_new (pango_font_description_copy
3823 (style->font_desc));
3824 pango_attr_list_insert (attr_list, attr);
3826 g_object_set (G_OBJECT (priv->messages_renderer),
3827 "foreground-gdk", &style_color,
3828 "foreground-set", TRUE,
3829 "attributes", attr_list,
3831 pango_attr_list_unref (attr_list);
3836 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3838 if (strcmp ("style", spec->name) == 0) {
3839 update_style (MODEST_FOLDER_VIEW (obj));
3840 gtk_widget_queue_draw (GTK_WIDGET (obj));
3845 modest_folder_view_set_filter (ModestFolderView *self,
3846 ModestFolderViewFilter filter)
3848 ModestFolderViewPrivate *priv;
3849 GtkTreeModel *filter_model;
3851 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3852 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3854 priv->filter |= filter;
3856 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3857 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3858 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3863 modest_folder_view_unset_filter (ModestFolderView *self,
3864 ModestFolderViewFilter filter)
3866 ModestFolderViewPrivate *priv;
3867 GtkTreeModel *filter_model;
3869 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3870 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3872 priv->filter &= ~filter;
3874 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3875 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3876 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3881 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3882 ModestTnyFolderRules rules)
3884 GtkTreeModel *filter_model;
3886 gboolean fulfil = FALSE;
3888 if (!get_inner_models (self, &filter_model, NULL, NULL))
3891 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3895 TnyFolderStore *folder;
3897 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3899 if (TNY_IS_FOLDER (folder)) {
3900 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3901 /* Folder rules are negative: non_writable, non_deletable... */
3902 if (!(folder_rules & rules))
3905 g_object_unref (folder);
3908 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3914 modest_folder_view_set_list_to_move (ModestFolderView *self,
3917 ModestFolderViewPrivate *priv;
3919 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3920 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3922 if (priv->list_to_move)
3923 g_object_unref (priv->list_to_move);
3926 g_object_ref (list);
3928 priv->list_to_move = list;
3932 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3934 ModestFolderViewPrivate *priv;
3936 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3937 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3940 g_free (priv->mailbox);
3942 priv->mailbox = g_strdup (mailbox);
3944 /* Notify observers */
3945 g_signal_emit (G_OBJECT(self),
3946 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3947 priv->visible_account_id);
3951 modest_folder_view_get_mailbox (ModestFolderView *self)
3953 ModestFolderViewPrivate *priv;
3955 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3956 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3958 return (const gchar *) priv->mailbox;