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)
1988 if (retval && TNY_IS_ACCOUNT (instance) &&
1989 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1990 ModestProtocolType protocol_type;
1992 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
1993 retval = !modest_protocol_registry_protocol_type_has_tag
1994 (modest_runtime_get_protocol_registry (),
1996 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2000 /* apply special filters */
2001 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2002 if (TNY_IS_ACCOUNT (instance))
2006 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2007 if (TNY_IS_FOLDER (instance))
2011 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2012 if (TNY_IS_ACCOUNT (instance)) {
2013 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2015 } else if (TNY_IS_FOLDER (instance)) {
2016 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2021 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2022 if (TNY_IS_ACCOUNT (instance)) {
2023 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2025 } else if (TNY_IS_FOLDER (instance)) {
2026 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2031 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2032 /* A mailbox is a fake folder with an @ in the middle of the name */
2033 if (!TNY_IS_FOLDER (instance) ||
2034 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2037 const gchar *folder_name;
2038 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2039 if (!folder_name || strchr (folder_name, '@') == NULL)
2045 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2046 if (TNY_IS_FOLDER (instance)) {
2047 /* Check folder rules */
2048 ModestTnyFolderRules rules;
2050 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2051 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2052 } else if (TNY_IS_ACCOUNT (instance)) {
2053 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2061 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2062 if (TNY_IS_FOLDER (instance)) {
2063 TnyFolderType guess_type;
2065 if (TNY_FOLDER_TYPE_NORMAL) {
2066 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2072 case TNY_FOLDER_TYPE_OUTBOX:
2073 case TNY_FOLDER_TYPE_SENT:
2074 case TNY_FOLDER_TYPE_DRAFTS:
2075 case TNY_FOLDER_TYPE_ARCHIVE:
2076 case TNY_FOLDER_TYPE_INBOX:
2079 case TNY_FOLDER_TYPE_UNKNOWN:
2080 case TNY_FOLDER_TYPE_NORMAL:
2086 } else if (TNY_IS_ACCOUNT (instance)) {
2091 if (retval && TNY_IS_FOLDER (instance)) {
2092 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2095 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2096 if (TNY_IS_FOLDER (instance)) {
2097 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2098 } else if (TNY_IS_ACCOUNT (instance)) {
2103 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2104 if (TNY_IS_FOLDER (instance)) {
2105 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2106 } else if (TNY_IS_ACCOUNT (instance)) {
2111 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2112 if (TNY_IS_FOLDER (instance)) {
2113 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2114 } else if (TNY_IS_ACCOUNT (instance)) {
2120 g_object_unref (instance);
2128 modest_folder_view_update_model (ModestFolderView *self,
2129 TnyAccountStore *account_store)
2131 ModestFolderViewPrivate *priv;
2132 GtkTreeModel *model /* , *old_model */;
2133 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2135 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2136 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2139 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2141 /* Notify that there is no folder selected */
2142 g_signal_emit (G_OBJECT(self),
2143 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2145 if (priv->cur_folder_store) {
2146 g_object_unref (priv->cur_folder_store);
2147 priv->cur_folder_store = NULL;
2150 /* FIXME: the local accounts are not shown when the query
2151 selects only the subscribed folders */
2152 #ifdef MODEST_TOOLKIT_HILDON2
2153 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2154 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2155 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2156 MODEST_FOLDER_PATH_SEPARATOR);
2158 model = tny_gtk_folder_store_tree_model_new (NULL);
2161 /* When the model is a list store (plain representation) the
2162 outbox is not a child of any account so we have to manually
2163 delete it because removing the local folders account won't
2164 delete it (because tny_folder_get_account() is not defined
2165 for a merge folder */
2166 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2167 TnyAccount *account;
2168 ModestTnyAccountStore *acc_store;
2170 acc_store = modest_runtime_get_account_store ();
2171 account = modest_tny_account_store_get_local_folders_account (acc_store);
2173 if (g_signal_handler_is_connected (account,
2174 priv->outbox_deleted_handler))
2175 g_signal_handler_disconnect (account,
2176 priv->outbox_deleted_handler);
2178 priv->outbox_deleted_handler =
2179 g_signal_connect (account,
2181 G_CALLBACK (on_outbox_deleted_cb),
2183 g_object_unref (account);
2186 /* Get the accounts: */
2187 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2189 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2191 sortable = gtk_tree_model_sort_new_with_model (model);
2192 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2194 GTK_SORT_ASCENDING);
2195 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2197 cmp_rows, NULL, NULL);
2199 /* Create filter model */
2200 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2201 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2207 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2208 #ifndef MODEST_TOOLKIT_HILDON2
2209 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2210 (GCallback) on_row_inserted_maybe_select_folder, self);
2213 g_object_unref (model);
2214 g_object_unref (filter_model);
2215 g_object_unref (sortable);
2217 /* Force a reselection of the INBOX next time the widget is shown */
2218 priv->reselect = TRUE;
2225 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2227 GtkTreeModel *model = NULL;
2228 TnyFolderStore *folder = NULL;
2230 ModestFolderView *tree_view = NULL;
2231 ModestFolderViewPrivate *priv = NULL;
2232 gboolean selected = FALSE;
2234 g_return_if_fail (sel);
2235 g_return_if_fail (user_data);
2237 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2239 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2241 tree_view = MODEST_FOLDER_VIEW (user_data);
2244 gtk_tree_model_get (model, &iter,
2245 INSTANCE_COLUMN, &folder,
2248 /* If the folder is the same do not notify */
2249 if (folder && priv->cur_folder_store == folder) {
2250 g_object_unref (folder);
2255 /* Current folder was unselected */
2256 if (priv->cur_folder_store) {
2257 /* We must do this firstly because a libtinymail-camel
2258 implementation detail. If we issue the signal
2259 before doing the sync_async, then that signal could
2260 cause (and it actually does it) a free of the
2261 summary of the folder (because the main window will
2262 clear the headers view */
2263 if (TNY_IS_FOLDER(priv->cur_folder_store))
2264 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2265 FALSE, NULL, NULL, NULL);
2267 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2268 priv->cur_folder_store, FALSE);
2270 g_object_unref (priv->cur_folder_store);
2271 priv->cur_folder_store = NULL;
2274 /* New current references */
2275 priv->cur_folder_store = folder;
2277 /* New folder has been selected. Do not notify if there is
2278 nothing new selected */
2280 g_signal_emit (G_OBJECT(tree_view),
2281 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2282 0, priv->cur_folder_store, TRUE);
2287 on_row_activated (GtkTreeView *treeview,
2288 GtkTreePath *treepath,
2289 GtkTreeViewColumn *column,
2292 GtkTreeModel *model = NULL;
2293 TnyFolderStore *folder = NULL;
2295 ModestFolderView *self = NULL;
2296 ModestFolderViewPrivate *priv = NULL;
2298 g_return_if_fail (treeview);
2299 g_return_if_fail (user_data);
2301 self = MODEST_FOLDER_VIEW (user_data);
2302 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2304 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2306 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2309 gtk_tree_model_get (model, &iter,
2310 INSTANCE_COLUMN, &folder,
2313 g_signal_emit (G_OBJECT(self),
2314 signals[FOLDER_ACTIVATED_SIGNAL],
2317 #ifdef MODEST_TOOLKIT_HILDON2
2318 HildonUIMode ui_mode;
2319 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2320 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2321 if (priv->cur_folder_store)
2322 g_object_unref (priv->cur_folder_store);
2323 priv->cur_folder_store = g_object_ref (folder);
2327 g_object_unref (folder);
2331 modest_folder_view_get_selected (ModestFolderView *self)
2333 ModestFolderViewPrivate *priv;
2335 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2337 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2338 if (priv->cur_folder_store)
2339 g_object_ref (priv->cur_folder_store);
2341 return priv->cur_folder_store;
2345 get_cmp_rows_type_pos (GObject *folder)
2347 /* Remote accounts -> Local account -> MMC account .*/
2350 if (TNY_IS_ACCOUNT (folder) &&
2351 modest_tny_account_is_virtual_local_folders (
2352 TNY_ACCOUNT (folder))) {
2354 } else if (TNY_IS_ACCOUNT (folder)) {
2355 TnyAccount *account = TNY_ACCOUNT (folder);
2356 const gchar *account_id = tny_account_get_id (account);
2357 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2363 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2364 return -1; /* Should never happen */
2369 inbox_is_special (TnyFolderStore *folder_store)
2371 gboolean is_special = TRUE;
2373 if (TNY_IS_FOLDER (folder_store)) {
2377 gchar *last_inbox_bar;
2379 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2380 downcase = g_utf8_strdown (id, -1);
2381 last_bar = g_strrstr (downcase, "/");
2383 last_inbox_bar = g_strrstr (downcase, "inbox/");
2384 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2395 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2397 TnyAccount *account;
2398 gboolean is_special;
2399 /* Inbox, Outbox, Drafts, Sent, User */
2402 if (!TNY_IS_FOLDER (folder_store))
2405 case TNY_FOLDER_TYPE_INBOX:
2407 account = tny_folder_get_account (folder_store);
2408 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2410 /* In inbox case we need to know if the inbox is really the top
2411 * inbox of the account, or if it's a submailbox inbox. To do
2412 * this we'll apply an heuristic rule: Find last "/" and check
2413 * if it's preceeded by another Inbox */
2414 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2415 g_object_unref (account);
2416 return is_special?0:4;
2419 case TNY_FOLDER_TYPE_OUTBOX:
2420 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2422 case TNY_FOLDER_TYPE_DRAFTS:
2424 account = tny_folder_get_account (folder_store);
2425 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2426 g_object_unref (account);
2427 return is_special?1:4;
2430 case TNY_FOLDER_TYPE_SENT:
2432 account = tny_folder_get_account (folder_store);
2433 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2434 g_object_unref (account);
2435 return is_special?3:4;
2444 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2446 const gchar *a1_name, *a2_name;
2448 a1_name = tny_account_get_name (a1);
2449 a2_name = tny_account_get_name (a2);
2451 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2455 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2457 TnyAccount *a1 = NULL, *a2 = NULL;
2460 if (TNY_IS_ACCOUNT (s1)) {
2461 a1 = TNY_ACCOUNT (g_object_ref (s1));
2462 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2463 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2466 if (TNY_IS_ACCOUNT (s2)) {
2467 a2 = TNY_ACCOUNT (g_object_ref (s2));
2468 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2469 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2486 /* First we sort with the type of account */
2487 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2491 cmp = compare_account_names (a1, a2);
2495 g_object_unref (a1);
2497 g_object_unref (a2);
2503 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2505 gint is_account1, is_account2;
2507 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2508 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2510 return is_account2 - is_account1;
2514 * This function orders the mail accounts according to these rules:
2515 * 1st - remote accounts
2516 * 2nd - local account
2520 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2524 gchar *name1 = NULL;
2525 gchar *name2 = NULL;
2526 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2527 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2528 GObject *folder1 = NULL;
2529 GObject *folder2 = NULL;
2531 gtk_tree_model_get (tree_model, iter1,
2532 NAME_COLUMN, &name1,
2534 INSTANCE_COLUMN, &folder1,
2536 gtk_tree_model_get (tree_model, iter2,
2537 NAME_COLUMN, &name2,
2538 TYPE_COLUMN, &type2,
2539 INSTANCE_COLUMN, &folder2,
2542 /* Return if we get no folder. This could happen when folder
2543 operations are happening. The model is updated after the
2544 folder copy/move actually occurs, so there could be
2545 situations where the model to be drawn is not correct */
2546 if (!folder1 || !folder2)
2549 /* Sort by type. First the special folders, then the archives */
2550 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2554 /* Now we sort using the account of each folder */
2555 if (TNY_IS_FOLDER_STORE (folder1) &&
2556 TNY_IS_FOLDER_STORE (folder2)) {
2557 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2561 /* Each group is preceeded by its account */
2562 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2567 /* Pure sort by name */
2568 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2571 g_object_unref(G_OBJECT(folder1));
2573 g_object_unref(G_OBJECT(folder2));
2581 /*****************************************************************************/
2582 /* DRAG and DROP stuff */
2583 /*****************************************************************************/
2585 * This function fills the #GtkSelectionData with the row and the
2586 * model that has been dragged. It's called when this widget is a
2587 * source for dnd after the event drop happened
2590 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2591 guint info, guint time, gpointer data)
2593 GtkTreeSelection *selection;
2594 GtkTreeModel *model;
2596 GtkTreePath *source_row;
2598 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2599 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2601 source_row = gtk_tree_model_get_path (model, &iter);
2602 gtk_tree_set_row_drag_data (selection_data,
2606 gtk_tree_path_free (source_row);
2610 typedef struct _DndHelper {
2611 ModestFolderView *folder_view;
2612 gboolean delete_source;
2613 GtkTreePath *source_row;
2617 dnd_helper_destroyer (DndHelper *helper)
2619 /* Free the helper */
2620 gtk_tree_path_free (helper->source_row);
2621 g_slice_free (DndHelper, helper);
2625 xfer_folder_cb (ModestMailOperation *mail_op,
2626 TnyFolder *new_folder,
2630 /* Select the folder */
2631 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2637 /* get the folder for the row the treepath refers to. */
2638 /* folder must be unref'd */
2639 static TnyFolderStore *
2640 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2643 TnyFolderStore *folder = NULL;
2645 if (gtk_tree_model_get_iter (model,&iter, path))
2646 gtk_tree_model_get (model, &iter,
2647 INSTANCE_COLUMN, &folder,
2654 * This function is used by drag_data_received_cb to manage drag and
2655 * drop of a header, i.e, and drag from the header view to the folder
2659 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2660 GtkTreeModel *dest_model,
2661 GtkTreePath *dest_row,
2662 GtkSelectionData *selection_data)
2664 TnyList *headers = NULL;
2665 TnyFolder *folder = NULL, *src_folder = NULL;
2666 TnyFolderType folder_type;
2667 GtkTreeIter source_iter, dest_iter;
2668 ModestWindowMgr *mgr = NULL;
2669 ModestWindow *main_win = NULL;
2670 gchar **uris, **tmp;
2672 /* Build the list of headers */
2673 mgr = modest_runtime_get_window_mgr ();
2674 headers = tny_simple_list_new ();
2675 uris = modest_dnd_selection_data_get_paths (selection_data);
2678 while (*tmp != NULL) {
2681 gboolean first = TRUE;
2684 path = gtk_tree_path_new_from_string (*tmp);
2685 gtk_tree_model_get_iter (source_model, &source_iter, path);
2686 gtk_tree_model_get (source_model, &source_iter,
2687 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2690 /* Do not enable d&d of headers already opened */
2691 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2692 tny_list_append (headers, G_OBJECT (header));
2694 if (G_UNLIKELY (first)) {
2695 src_folder = tny_header_get_folder (header);
2699 /* Free and go on */
2700 gtk_tree_path_free (path);
2701 g_object_unref (header);
2706 /* This could happen ig we perform a d&d very quickly over the
2707 same row that row could dissapear because message is
2709 if (!TNY_IS_FOLDER (src_folder))
2712 /* Get the target folder */
2713 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2714 gtk_tree_model_get (dest_model, &dest_iter,
2718 if (!folder || !TNY_IS_FOLDER(folder)) {
2719 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2723 folder_type = modest_tny_folder_guess_folder_type (folder);
2724 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2725 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2726 goto cleanup; /* cannot move messages there */
2729 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2730 /* g_warning ("folder not writable"); */
2731 goto cleanup; /* verboten! */
2734 /* Ask for confirmation to move */
2735 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2737 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2741 /* Transfer messages */
2742 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2747 if (G_IS_OBJECT (src_folder))
2748 g_object_unref (src_folder);
2749 if (G_IS_OBJECT(folder))
2750 g_object_unref (G_OBJECT (folder));
2751 if (G_IS_OBJECT(headers))
2752 g_object_unref (headers);
2756 TnyFolderStore *src_folder;
2757 TnyFolderStore *dst_folder;
2758 ModestFolderView *folder_view;
2763 dnd_folder_info_destroyer (DndFolderInfo *info)
2765 if (info->src_folder)
2766 g_object_unref (info->src_folder);
2767 if (info->dst_folder)
2768 g_object_unref (info->dst_folder);
2769 g_slice_free (DndFolderInfo, info);
2773 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2774 GtkWindow *parent_window,
2775 TnyAccount *account)
2778 modest_ui_actions_on_account_connection_error (parent_window, account);
2780 /* Free the helper & info */
2781 dnd_helper_destroyer (info->helper);
2782 dnd_folder_info_destroyer (info);
2786 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2788 GtkWindow *parent_window,
2789 TnyAccount *account,
2792 DndFolderInfo *info = NULL;
2793 ModestMailOperation *mail_op;
2795 info = (DndFolderInfo *) user_data;
2797 if (err || canceled) {
2798 dnd_on_connection_failed_destroyer (info, parent_window, account);
2802 /* Do the mail operation */
2803 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2804 modest_ui_actions_move_folder_error_handler,
2805 info->src_folder, NULL);
2807 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2810 /* Transfer the folder */
2811 modest_mail_operation_xfer_folder (mail_op,
2812 TNY_FOLDER (info->src_folder),
2814 info->helper->delete_source,
2816 info->helper->folder_view);
2819 g_object_unref (G_OBJECT (mail_op));
2820 dnd_helper_destroyer (info->helper);
2821 dnd_folder_info_destroyer (info);
2826 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2828 GtkWindow *parent_window,
2829 TnyAccount *account,
2832 DndFolderInfo *info = NULL;
2834 info = (DndFolderInfo *) user_data;
2836 if (err || canceled) {
2837 dnd_on_connection_failed_destroyer (info, parent_window, account);
2841 /* Connect to source folder and perform the copy/move */
2842 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2844 drag_and_drop_from_folder_view_src_folder_performer,
2849 * This function is used by drag_data_received_cb to manage drag and
2850 * drop of a folder, i.e, and drag from the folder view to the same
2854 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2855 GtkTreeModel *dest_model,
2856 GtkTreePath *dest_row,
2857 GtkSelectionData *selection_data,
2860 GtkTreeIter dest_iter, iter;
2861 TnyFolderStore *dest_folder = NULL;
2862 TnyFolderStore *folder = NULL;
2863 gboolean forbidden = FALSE;
2865 DndFolderInfo *info = NULL;
2867 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2869 g_warning ("%s: BUG: no main window", __FUNCTION__);
2870 dnd_helper_destroyer (helper);
2875 /* check the folder rules for the destination */
2876 folder = tree_path_to_folder (dest_model, dest_row);
2877 if (TNY_IS_FOLDER(folder)) {
2878 ModestTnyFolderRules rules =
2879 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2880 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2881 } else if (TNY_IS_FOLDER_STORE(folder)) {
2882 /* enable local root as destination for folders */
2883 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2884 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2887 g_object_unref (folder);
2890 /* check the folder rules for the source */
2891 folder = tree_path_to_folder (source_model, helper->source_row);
2892 if (TNY_IS_FOLDER(folder)) {
2893 ModestTnyFolderRules rules =
2894 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2895 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2898 g_object_unref (folder);
2902 /* Check if the drag is possible */
2903 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2905 modest_platform_run_information_dialog ((GtkWindow *) win,
2906 _("mail_in_ui_folder_move_target_error"),
2908 /* Restore the previous selection */
2909 folder = tree_path_to_folder (source_model, helper->source_row);
2911 if (TNY_IS_FOLDER (folder))
2912 modest_folder_view_select_folder (helper->folder_view,
2913 TNY_FOLDER (folder), FALSE);
2914 g_object_unref (folder);
2916 dnd_helper_destroyer (helper);
2921 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2922 gtk_tree_model_get (dest_model, &dest_iter,
2925 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2926 gtk_tree_model_get (source_model, &iter,
2930 /* Create the info for the performer */
2931 info = g_slice_new0 (DndFolderInfo);
2932 info->src_folder = g_object_ref (folder);
2933 info->dst_folder = g_object_ref (dest_folder);
2934 info->helper = helper;
2936 /* Connect to the destination folder and perform the copy/move */
2937 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2939 drag_and_drop_from_folder_view_dst_folder_performer,
2943 g_object_unref (dest_folder);
2944 g_object_unref (folder);
2948 * This function receives the data set by the "drag-data-get" signal
2949 * handler. This information comes within the #GtkSelectionData. This
2950 * function will manage both the drags of folders of the treeview and
2951 * drags of headers of the header view widget.
2954 on_drag_data_received (GtkWidget *widget,
2955 GdkDragContext *context,
2958 GtkSelectionData *selection_data,
2963 GtkWidget *source_widget;
2964 GtkTreeModel *dest_model, *source_model;
2965 GtkTreePath *source_row, *dest_row;
2966 GtkTreeViewDropPosition pos;
2967 gboolean delete_source = FALSE;
2968 gboolean success = FALSE;
2970 /* Do not allow further process */
2971 g_signal_stop_emission_by_name (widget, "drag-data-received");
2972 source_widget = gtk_drag_get_source_widget (context);
2974 /* Get the action */
2975 if (context->action == GDK_ACTION_MOVE) {
2976 delete_source = TRUE;
2978 /* Notify that there is no folder selected. We need to
2979 do this in order to update the headers view (and
2980 its monitors, because when moving, the old folder
2981 won't longer exist. We can not wait for the end of
2982 the operation, because the operation won't start if
2983 the folder is in use */
2984 if (source_widget == widget) {
2985 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2986 gtk_tree_selection_unselect_all (sel);
2990 /* Check if the get_data failed */
2991 if (selection_data == NULL || selection_data->length < 0)
2994 /* Select the destination model */
2995 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2997 /* Get the path to the destination row. Can not call
2998 gtk_tree_view_get_drag_dest_row() because the source row
2999 is not selected anymore */
3000 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3003 /* Only allow drops IN other rows */
3005 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3006 pos == GTK_TREE_VIEW_DROP_AFTER)
3010 /* Drags from the header view */
3011 if (source_widget != widget) {
3012 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3014 drag_and_drop_from_header_view (source_model,
3019 DndHelper *helper = NULL;
3021 /* Get the source model and row */
3022 gtk_tree_get_row_drag_data (selection_data,
3026 /* Create the helper */
3027 helper = g_slice_new0 (DndHelper);
3028 helper->delete_source = delete_source;
3029 helper->source_row = gtk_tree_path_copy (source_row);
3030 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3032 drag_and_drop_from_folder_view (source_model,
3038 gtk_tree_path_free (source_row);
3042 gtk_tree_path_free (dest_row);
3045 /* Finish the drag and drop */
3046 gtk_drag_finish (context, success, FALSE, time);
3050 * We define a "drag-drop" signal handler because we do not want to
3051 * use the default one, because the default one always calls
3052 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3053 * signal handler, because there we have all the information available
3054 * to know if the dnd was a success or not.
3057 drag_drop_cb (GtkWidget *widget,
3058 GdkDragContext *context,
3066 if (!context->targets)
3069 /* Check if we're dragging a folder row */
3070 target = gtk_drag_dest_find_target (widget, context, NULL);
3072 /* Request the data from the source. */
3073 gtk_drag_get_data(widget, context, target, time);
3079 * This function expands a node of a tree view if it's not expanded
3080 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3081 * does that, so that's why they're here.
3084 expand_row_timeout (gpointer data)
3086 GtkTreeView *tree_view = data;
3087 GtkTreePath *dest_path = NULL;
3088 GtkTreeViewDropPosition pos;
3089 gboolean result = FALSE;
3091 gdk_threads_enter ();
3093 gtk_tree_view_get_drag_dest_row (tree_view,
3098 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3099 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3100 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3101 gtk_tree_path_free (dest_path);
3105 gtk_tree_path_free (dest_path);
3110 gdk_threads_leave ();
3116 * This function is called whenever the pointer is moved over a widget
3117 * while dragging some data. It installs a timeout that will expand a
3118 * node of the treeview if not expanded yet. This function also calls
3119 * gdk_drag_status in order to set the suggested action that will be
3120 * used by the "drag-data-received" signal handler to know if we
3121 * should do a move or just a copy of the data.
3124 on_drag_motion (GtkWidget *widget,
3125 GdkDragContext *context,
3131 GtkTreeViewDropPosition pos;
3132 GtkTreePath *dest_row;
3133 GtkTreeModel *dest_model;
3134 ModestFolderViewPrivate *priv;
3135 GdkDragAction suggested_action;
3136 gboolean valid_location = FALSE;
3137 TnyFolderStore *folder = NULL;
3139 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3141 if (priv->timer_expander != 0) {
3142 g_source_remove (priv->timer_expander);
3143 priv->timer_expander = 0;
3146 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3151 /* Do not allow drops between folders */
3153 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3154 pos == GTK_TREE_VIEW_DROP_AFTER) {
3155 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3156 gdk_drag_status(context, 0, time);
3157 valid_location = FALSE;
3160 valid_location = TRUE;
3163 /* Check that the destination folder is writable */
3164 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3165 folder = tree_path_to_folder (dest_model, dest_row);
3166 if (folder && TNY_IS_FOLDER (folder)) {
3167 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3169 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3170 valid_location = FALSE;
3175 /* Expand the selected row after 1/2 second */
3176 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3177 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3179 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3181 /* Select the desired action. By default we pick MOVE */
3182 suggested_action = GDK_ACTION_MOVE;
3184 if (context->actions == GDK_ACTION_COPY)
3185 gdk_drag_status(context, GDK_ACTION_COPY, time);
3186 else if (context->actions == GDK_ACTION_MOVE)
3187 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3188 else if (context->actions & suggested_action)
3189 gdk_drag_status(context, suggested_action, time);
3191 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3195 g_object_unref (folder);
3197 gtk_tree_path_free (dest_row);
3199 g_signal_stop_emission_by_name (widget, "drag-motion");
3201 return valid_location;
3205 * This function sets the treeview as a source and a target for dnd
3206 * events. It also connects all the requirede signals.
3209 setup_drag_and_drop (GtkTreeView *self)
3211 /* Set up the folder view as a dnd destination. Set only the
3212 highlight flag, otherwise gtk will have a different
3214 #ifdef MODEST_TOOLKIT_HILDON2
3217 gtk_drag_dest_set (GTK_WIDGET (self),
3218 GTK_DEST_DEFAULT_HIGHLIGHT,
3219 folder_view_drag_types,
3220 G_N_ELEMENTS (folder_view_drag_types),
3221 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3223 g_signal_connect (G_OBJECT (self),
3224 "drag_data_received",
3225 G_CALLBACK (on_drag_data_received),
3229 /* Set up the treeview as a dnd source */
3230 gtk_drag_source_set (GTK_WIDGET (self),
3232 folder_view_drag_types,
3233 G_N_ELEMENTS (folder_view_drag_types),
3234 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3236 g_signal_connect (G_OBJECT (self),
3238 G_CALLBACK (on_drag_motion),
3241 g_signal_connect (G_OBJECT (self),
3243 G_CALLBACK (on_drag_data_get),
3246 g_signal_connect (G_OBJECT (self),
3248 G_CALLBACK (drag_drop_cb),
3253 * This function manages the navigation through the folders using the
3254 * keyboard or the hardware keys in the device
3257 on_key_pressed (GtkWidget *self,
3261 GtkTreeSelection *selection;
3263 GtkTreeModel *model;
3264 gboolean retval = FALSE;
3266 /* Up and Down are automatically managed by the treeview */
3267 if (event->keyval == GDK_Return) {
3268 /* Expand/Collapse the selected row */
3269 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3270 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3273 path = gtk_tree_model_get_path (model, &iter);
3275 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3276 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3278 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3279 gtk_tree_path_free (path);
3281 /* No further processing */
3289 * We listen to the changes in the local folder account name key,
3290 * because we want to show the right name in the view. The local
3291 * folder account name corresponds to the device name in the Maemo
3292 * version. We do this because we do not want to query gconf on each
3293 * tree view refresh. It's better to cache it and change whenever
3297 on_configuration_key_changed (ModestConf* conf,
3299 ModestConfEvent event,
3300 ModestConfNotificationId id,
3301 ModestFolderView *self)
3303 ModestFolderViewPrivate *priv;
3306 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3307 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3309 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3310 g_free (priv->local_account_name);
3312 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3313 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3315 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3316 MODEST_CONF_DEVICE_NAME, NULL);
3318 /* Force a redraw */
3319 #if GTK_CHECK_VERSION(2, 8, 0)
3320 GtkTreeViewColumn * tree_column;
3322 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3324 gtk_tree_view_column_queue_resize (tree_column);
3326 gtk_widget_queue_draw (GTK_WIDGET (self));
3332 modest_folder_view_set_style (ModestFolderView *self,
3333 ModestFolderViewStyle style)
3335 ModestFolderViewPrivate *priv;
3337 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3338 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3339 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3341 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3344 priv->style = style;
3348 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3349 const gchar *account_id)
3351 ModestFolderViewPrivate *priv;
3352 GtkTreeModel *model;
3354 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3356 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3358 /* This will be used by the filter_row callback,
3359 * to decided which rows to show: */
3360 if (priv->visible_account_id) {
3361 g_free (priv->visible_account_id);
3362 priv->visible_account_id = NULL;
3365 priv->visible_account_id = g_strdup (account_id);
3368 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3369 if (GTK_IS_TREE_MODEL_FILTER (model))
3370 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3372 /* Save settings to gconf */
3373 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3374 MODEST_CONF_FOLDER_VIEW_KEY);
3376 /* Notify observers */
3377 g_signal_emit (G_OBJECT(self),
3378 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3383 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3385 ModestFolderViewPrivate *priv;
3387 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3389 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3391 return (const gchar *) priv->visible_account_id;
3395 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3399 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3401 gtk_tree_model_get (model, iter,
3405 gboolean result = FALSE;
3406 if (type == TNY_FOLDER_TYPE_INBOX) {
3410 *inbox_iter = *iter;
3414 if (gtk_tree_model_iter_children (model, &child, iter)) {
3415 if (find_inbox_iter (model, &child, inbox_iter))
3419 } while (gtk_tree_model_iter_next (model, iter));
3428 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3430 #ifndef MODEST_TOOLKIT_HILDON2
3431 GtkTreeModel *model;
3432 GtkTreeIter iter, inbox_iter;
3433 GtkTreeSelection *sel;
3434 GtkTreePath *path = NULL;
3436 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3438 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3442 expand_root_items (self);
3443 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3445 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3446 g_warning ("%s: model is empty", __FUNCTION__);
3450 if (find_inbox_iter (model, &iter, &inbox_iter))
3451 path = gtk_tree_model_get_path (model, &inbox_iter);
3453 path = gtk_tree_path_new_first ();
3455 /* Select the row and free */
3456 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3457 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3458 gtk_tree_path_free (path);
3461 gtk_widget_grab_focus (GTK_WIDGET(self));
3468 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3473 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3474 TnyFolder* a_folder;
3477 gtk_tree_model_get (model, iter,
3478 INSTANCE_COLUMN, &a_folder,
3484 if (folder == a_folder) {
3485 g_object_unref (a_folder);
3486 *folder_iter = *iter;
3489 g_object_unref (a_folder);
3491 if (gtk_tree_model_iter_children (model, &child, iter)) {
3492 if (find_folder_iter (model, &child, folder_iter, folder))
3496 } while (gtk_tree_model_iter_next (model, iter));
3501 #ifndef MODEST_TOOLKIT_HILDON2
3503 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3506 ModestFolderView *self)
3508 ModestFolderViewPrivate *priv = NULL;
3509 GtkTreeSelection *sel;
3510 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3511 GObject *instance = NULL;
3513 if (!MODEST_IS_FOLDER_VIEW(self))
3516 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3518 priv->reexpand = TRUE;
3520 gtk_tree_model_get (tree_model, iter,
3522 INSTANCE_COLUMN, &instance,
3528 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3529 priv->folder_to_select = g_object_ref (instance);
3531 g_object_unref (instance);
3533 if (priv->folder_to_select) {
3535 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3538 path = gtk_tree_model_get_path (tree_model, iter);
3539 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3541 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3543 gtk_tree_selection_select_iter (sel, iter);
3544 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3546 gtk_tree_path_free (path);
3550 modest_folder_view_disable_next_folder_selection (self);
3552 /* Refilter the model */
3553 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3559 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3561 ModestFolderViewPrivate *priv;
3563 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3565 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3567 if (priv->folder_to_select)
3568 g_object_unref(priv->folder_to_select);
3570 priv->folder_to_select = NULL;
3574 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3575 gboolean after_change)
3577 GtkTreeModel *model;
3578 GtkTreeIter iter, folder_iter;
3579 GtkTreeSelection *sel;
3580 ModestFolderViewPrivate *priv = NULL;
3582 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3583 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3585 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3588 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3589 gtk_tree_selection_unselect_all (sel);
3591 if (priv->folder_to_select)
3592 g_object_unref(priv->folder_to_select);
3593 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3597 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3602 /* Refilter the model, before selecting the folder */
3603 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3605 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3606 g_warning ("%s: model is empty", __FUNCTION__);
3610 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3613 path = gtk_tree_model_get_path (model, &folder_iter);
3614 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3616 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3617 gtk_tree_selection_select_iter (sel, &folder_iter);
3618 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3620 gtk_tree_path_free (path);
3628 modest_folder_view_copy_selection (ModestFolderView *self)
3630 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3632 /* Copy selection */
3633 _clipboard_set_selected_data (self, FALSE);
3637 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3639 ModestFolderViewPrivate *priv = NULL;
3640 GtkTreeModel *model = NULL;
3641 const gchar **hidding = NULL;
3642 guint i, n_selected;
3644 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3645 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3647 /* Copy selection */
3648 if (!_clipboard_set_selected_data (folder_view, TRUE))
3651 /* Get hidding ids */
3652 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3654 /* Clear hidding array created by previous cut operation */
3655 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3657 /* Copy hidding array */
3658 priv->n_selected = n_selected;
3659 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3660 for (i=0; i < n_selected; i++)
3661 priv->hidding_ids[i] = g_strdup(hidding[i]);
3663 /* Hide cut folders */
3664 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3665 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3669 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3670 ModestFolderView *folder_view_dst)
3672 GtkTreeModel *filter_model = NULL;
3673 GtkTreeModel *model = NULL;
3674 GtkTreeModel *new_filter_model = NULL;
3676 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3677 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3680 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3681 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3683 /* Build new filter model */
3684 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3685 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3689 /* Set copied model */
3690 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3691 #ifndef MODEST_TOOLKIT_HILDON2
3692 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3693 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3697 g_object_unref (new_filter_model);
3701 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3704 GtkTreeModel *model = NULL;
3705 ModestFolderViewPrivate* priv;
3707 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3709 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3710 priv->show_non_move = show;
3711 /* modest_folder_view_update_model(folder_view, */
3712 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3714 /* Hide special folders */
3715 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3716 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3717 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3721 /* Returns FALSE if it did not selected anything */
3723 _clipboard_set_selected_data (ModestFolderView *folder_view,
3726 ModestFolderViewPrivate *priv = NULL;
3727 TnyFolderStore *folder = NULL;
3728 gboolean retval = FALSE;
3730 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3731 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3733 /* Set selected data on clipboard */
3734 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3735 folder = modest_folder_view_get_selected (folder_view);
3737 /* Do not allow to select an account */
3738 if (TNY_IS_FOLDER (folder)) {
3739 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3744 g_object_unref (folder);
3750 _clear_hidding_filter (ModestFolderView *folder_view)
3752 ModestFolderViewPrivate *priv;
3755 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3756 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3758 if (priv->hidding_ids != NULL) {
3759 for (i=0; i < priv->n_selected; i++)
3760 g_free (priv->hidding_ids[i]);
3761 g_free(priv->hidding_ids);
3767 on_display_name_changed (ModestAccountMgr *mgr,
3768 const gchar *account,
3771 ModestFolderView *self;
3773 self = MODEST_FOLDER_VIEW (user_data);
3775 /* Force a redraw */
3776 #if GTK_CHECK_VERSION(2, 8, 0)
3777 GtkTreeViewColumn * tree_column;
3779 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3781 gtk_tree_view_column_queue_resize (tree_column);
3783 gtk_widget_queue_draw (GTK_WIDGET (self));
3788 modest_folder_view_set_cell_style (ModestFolderView *self,
3789 ModestFolderViewCellStyle cell_style)
3791 ModestFolderViewPrivate *priv = NULL;
3793 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3794 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3796 priv->cell_style = cell_style;
3798 g_object_set (G_OBJECT (priv->messages_renderer),
3799 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3802 gtk_widget_queue_draw (GTK_WIDGET (self));
3806 update_style (ModestFolderView *self)
3808 ModestFolderViewPrivate *priv;
3809 GdkColor style_color;
3810 PangoAttrList *attr_list;
3812 PangoAttribute *attr;
3814 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3815 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3819 attr_list = pango_attr_list_new ();
3820 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3821 gdk_color_parse ("grey", &style_color);
3823 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3824 pango_attr_list_insert (attr_list, attr);
3827 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3829 "SmallSystemFont", NULL,
3832 attr = pango_attr_font_desc_new (pango_font_description_copy
3833 (style->font_desc));
3834 pango_attr_list_insert (attr_list, attr);
3836 g_object_set (G_OBJECT (priv->messages_renderer),
3837 "foreground-gdk", &style_color,
3838 "foreground-set", TRUE,
3839 "attributes", attr_list,
3841 pango_attr_list_unref (attr_list);
3846 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3848 if (strcmp ("style", spec->name) == 0) {
3849 update_style (MODEST_FOLDER_VIEW (obj));
3850 gtk_widget_queue_draw (GTK_WIDGET (obj));
3855 modest_folder_view_set_filter (ModestFolderView *self,
3856 ModestFolderViewFilter filter)
3858 ModestFolderViewPrivate *priv;
3859 GtkTreeModel *filter_model;
3861 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3862 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3864 priv->filter |= filter;
3866 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3867 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3868 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3873 modest_folder_view_unset_filter (ModestFolderView *self,
3874 ModestFolderViewFilter filter)
3876 ModestFolderViewPrivate *priv;
3877 GtkTreeModel *filter_model;
3879 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3880 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3882 priv->filter &= ~filter;
3884 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3885 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3886 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3891 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3892 ModestTnyFolderRules rules)
3894 GtkTreeModel *filter_model;
3896 gboolean fulfil = FALSE;
3898 if (!get_inner_models (self, &filter_model, NULL, NULL))
3901 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3905 TnyFolderStore *folder;
3907 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3909 if (TNY_IS_FOLDER (folder)) {
3910 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3911 /* Folder rules are negative: non_writable, non_deletable... */
3912 if (!(folder_rules & rules))
3915 g_object_unref (folder);
3918 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3924 modest_folder_view_set_list_to_move (ModestFolderView *self,
3927 ModestFolderViewPrivate *priv;
3929 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3930 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3932 if (priv->list_to_move)
3933 g_object_unref (priv->list_to_move);
3936 g_object_ref (list);
3938 priv->list_to_move = list;
3942 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3944 ModestFolderViewPrivate *priv;
3946 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3947 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3950 g_free (priv->mailbox);
3952 priv->mailbox = g_strdup (mailbox);
3954 /* Notify observers */
3955 g_signal_emit (G_OBJECT(self),
3956 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3957 priv->visible_account_id);
3961 modest_folder_view_get_mailbox (ModestFolderView *self)
3963 ModestFolderViewPrivate *priv;
3965 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3966 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3968 return (const gchar *) priv->mailbox;