1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
66 /* Folder view drag types */
67 const GtkTargetEntry folder_view_drag_types[] =
69 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
70 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
73 /* Default icon sizes for Fremantle style are different */
74 #ifdef MODEST_TOOLKIT_HILDON2
75 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
77 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
80 /* Column names depending on we use list store or tree store */
81 #ifdef MODEST_TOOLKIT_HILDON2
82 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
83 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
84 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
85 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
86 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
88 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
89 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
90 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
91 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
92 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
95 /* 'private'/'protected' functions */
96 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
97 static void modest_folder_view_init (ModestFolderView *obj);
98 static void modest_folder_view_finalize (GObject *obj);
100 static void tny_account_store_view_init (gpointer g,
101 gpointer iface_data);
103 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
104 TnyAccountStore *account_store);
106 static void on_selection_changed (GtkTreeSelection *sel,
109 static void on_row_activated (GtkTreeView *treeview,
111 GtkTreeViewColumn *column,
114 static void on_account_removed (TnyAccountStore *self,
118 static void on_account_inserted (TnyAccountStore *self,
122 static void on_account_changed (TnyAccountStore *self,
126 static gint cmp_rows (GtkTreeModel *tree_model,
131 static gboolean filter_row (GtkTreeModel *model,
135 static gboolean on_key_pressed (GtkWidget *self,
139 static void on_configuration_key_changed (ModestConf* conf,
141 ModestConfEvent event,
142 ModestConfNotificationId notification_id,
143 ModestFolderView *self);
146 static void on_drag_data_get (GtkWidget *widget,
147 GdkDragContext *context,
148 GtkSelectionData *selection_data,
153 static void on_drag_data_received (GtkWidget *widget,
154 GdkDragContext *context,
157 GtkSelectionData *selection_data,
162 static gboolean on_drag_motion (GtkWidget *widget,
163 GdkDragContext *context,
169 static void expand_root_items (ModestFolderView *self);
171 static gint expand_row_timeout (gpointer data);
173 static void setup_drag_and_drop (GtkTreeView *self);
175 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
178 static void _clear_hidding_filter (ModestFolderView *folder_view);
180 #ifndef MODEST_TOOLKIT_HILDON2
181 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
184 ModestFolderView *self);
187 static void on_display_name_changed (ModestAccountMgr *self,
188 const gchar *account,
190 static void update_style (ModestFolderView *self);
191 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
192 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
193 static gboolean inbox_is_special (TnyFolderStore *folder_store);
195 static gboolean get_inner_models (ModestFolderView *self,
196 GtkTreeModel **filter_model,
197 GtkTreeModel **sort_model,
198 GtkTreeModel **tny_model);
201 FOLDER_SELECTION_CHANGED_SIGNAL,
202 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
203 FOLDER_ACTIVATED_SIGNAL,
204 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
208 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
209 struct _ModestFolderViewPrivate {
210 TnyAccountStore *account_store;
211 TnyFolderStore *cur_folder_store;
213 TnyFolder *folder_to_select; /* folder to select after the next update */
215 gulong changed_signal;
216 gulong account_inserted_signal;
217 gulong account_removed_signal;
218 gulong account_changed_signal;
219 gulong conf_key_signal;
220 gulong display_name_changed_signal;
222 /* not unref this object, its a singlenton */
223 ModestEmailClipboard *clipboard;
225 /* Filter tree model */
228 ModestFolderViewFilter filter;
230 TnyFolderStoreQuery *query;
231 guint timer_expander;
233 gchar *local_account_name;
234 gchar *visible_account_id;
236 ModestFolderViewStyle style;
237 ModestFolderViewCellStyle cell_style;
239 gboolean reselect; /* we use this to force a reselection of the INBOX */
240 gboolean show_non_move;
241 TnyList *list_to_move;
242 gboolean reexpand; /* next time we expose, we'll expand all root folders */
244 GtkCellRenderer *messages_renderer;
246 gulong outbox_deleted_handler;
248 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
249 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
250 MODEST_TYPE_FOLDER_VIEW, \
251 ModestFolderViewPrivate))
253 static GObjectClass *parent_class = NULL;
255 static guint signals[LAST_SIGNAL] = {0};
258 modest_folder_view_get_type (void)
260 static GType my_type = 0;
262 static const GTypeInfo my_info = {
263 sizeof(ModestFolderViewClass),
264 NULL, /* base init */
265 NULL, /* base finalize */
266 (GClassInitFunc) modest_folder_view_class_init,
267 NULL, /* class finalize */
268 NULL, /* class data */
269 sizeof(ModestFolderView),
271 (GInstanceInitFunc) modest_folder_view_init,
275 static const GInterfaceInfo tny_account_store_view_info = {
276 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
277 NULL, /* interface_finalize */
278 NULL /* interface_data */
282 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
286 g_type_add_interface_static (my_type,
287 TNY_TYPE_ACCOUNT_STORE_VIEW,
288 &tny_account_store_view_info);
294 modest_folder_view_class_init (ModestFolderViewClass *klass)
296 GObjectClass *gobject_class;
297 GtkTreeViewClass *treeview_class;
298 gobject_class = (GObjectClass*) klass;
299 treeview_class = (GtkTreeViewClass*) klass;
301 parent_class = g_type_class_peek_parent (klass);
302 gobject_class->finalize = modest_folder_view_finalize;
304 g_type_class_add_private (gobject_class,
305 sizeof(ModestFolderViewPrivate));
307 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
308 g_signal_new ("folder_selection_changed",
309 G_TYPE_FROM_CLASS (gobject_class),
311 G_STRUCT_OFFSET (ModestFolderViewClass,
312 folder_selection_changed),
314 modest_marshal_VOID__POINTER_BOOLEAN,
315 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
318 * This signal is emitted whenever the currently selected
319 * folder display name is computed. Note that the name could
320 * be different to the folder name, because we could append
321 * the unread messages count to the folder name to build the
322 * folder display name
324 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
325 g_signal_new ("folder-display-name-changed",
326 G_TYPE_FROM_CLASS (gobject_class),
328 G_STRUCT_OFFSET (ModestFolderViewClass,
329 folder_display_name_changed),
331 g_cclosure_marshal_VOID__STRING,
332 G_TYPE_NONE, 1, G_TYPE_STRING);
334 signals[FOLDER_ACTIVATED_SIGNAL] =
335 g_signal_new ("folder_activated",
336 G_TYPE_FROM_CLASS (gobject_class),
338 G_STRUCT_OFFSET (ModestFolderViewClass,
341 g_cclosure_marshal_VOID__POINTER,
342 G_TYPE_NONE, 1, G_TYPE_POINTER);
345 * Emitted whenever the visible account changes
347 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
348 g_signal_new ("visible-account-changed",
349 G_TYPE_FROM_CLASS (gobject_class),
351 G_STRUCT_OFFSET (ModestFolderViewClass,
352 visible_account_changed),
354 g_cclosure_marshal_VOID__STRING,
355 G_TYPE_NONE, 1, G_TYPE_STRING);
357 treeview_class->select_cursor_parent = NULL;
359 #ifdef MODEST_TOOLKIT_HILDON2
360 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
366 /* Retrieves the filter, sort and tny models of the folder view. If
367 any of these does not exist then it returns FALSE */
369 get_inner_models (ModestFolderView *self,
370 GtkTreeModel **filter_model,
371 GtkTreeModel **sort_model,
372 GtkTreeModel **tny_model)
374 GtkTreeModel *s_model, *f_model, *t_model;
376 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
377 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
378 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
382 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
383 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
384 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
388 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
392 *filter_model = f_model;
394 *sort_model = s_model;
396 *tny_model = t_model;
401 /* Simplify checks for NULLs: */
403 strings_are_equal (const gchar *a, const gchar *b)
409 return (strcmp (a, b) == 0);
416 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
418 GObject *instance = NULL;
420 gtk_tree_model_get (model, iter,
421 INSTANCE_COLUMN, &instance,
425 return FALSE; /* keep walking */
427 if (!TNY_IS_ACCOUNT (instance)) {
428 g_object_unref (instance);
429 return FALSE; /* keep walking */
432 /* Check if this is the looked-for account: */
433 TnyAccount *this_account = TNY_ACCOUNT (instance);
434 TnyAccount *account = TNY_ACCOUNT (data);
436 const gchar *this_account_id = tny_account_get_id(this_account);
437 const gchar *account_id = tny_account_get_id(account);
438 g_object_unref (instance);
441 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
442 if (strings_are_equal(this_account_id, account_id)) {
443 /* Tell the model that the data has changed, so that
444 * it calls the cell_data_func callbacks again: */
445 /* TODO: This does not seem to actually cause the new string to be shown: */
446 gtk_tree_model_row_changed (model, path, iter);
448 return TRUE; /* stop walking */
451 return FALSE; /* keep walking */
456 ModestFolderView *self;
457 gchar *previous_name;
458 } GetMmcAccountNameData;
461 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
463 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
465 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
467 if (!strings_are_equal (
468 tny_account_get_name(TNY_ACCOUNT(account)),
469 data->previous_name)) {
471 /* Tell the model that the data has changed, so that
472 * it calls the cell_data_func callbacks again: */
473 ModestFolderView *self = data->self;
474 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
476 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
479 g_free (data->previous_name);
480 g_slice_free (GetMmcAccountNameData, data);
484 convert_parent_folders_to_dots (gchar **item_name)
488 gchar *last_separator;
490 if (item_name == NULL)
493 for (c = *item_name; *c != '\0'; c++) {
494 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
499 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
500 if (last_separator != NULL) {
501 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
508 buffer = g_string_new ("");
509 for (i = 0; i < n_parents; i++) {
510 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
512 buffer = g_string_append (buffer, last_separator);
514 *item_name = g_string_free (buffer, FALSE);
520 format_compact_style (gchar **item_name,
522 const gchar *mailbox,
524 gboolean multiaccount,
525 gboolean *use_markup)
529 TnyFolderType folder_type;
531 if (!TNY_IS_FOLDER (instance))
534 folder = (TnyFolder *) instance;
536 folder_type = tny_folder_get_folder_type (folder);
537 is_special = (get_cmp_pos (folder_type, folder)!= 4);
540 /* Remove mailbox prefix if any */
541 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
542 if (g_str_has_prefix (*item_name, prefix)) {
543 gchar *new_item_name;
545 new_item_name = g_strdup (*item_name + strlen (prefix));
546 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
547 g_free (new_item_name);
548 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
551 *item_name = new_item_name;
555 if (!is_special || multiaccount) {
556 TnyAccount *account = tny_folder_get_account (folder);
557 const gchar *folder_name;
558 gboolean concat_folder_name = FALSE;
561 /* Should not happen */
565 /* convert parent folders to dots */
566 convert_parent_folders_to_dots (item_name);
568 folder_name = tny_folder_get_name (folder);
569 if (g_str_has_suffix (*item_name, folder_name)) {
570 gchar *offset = g_strrstr (*item_name, folder_name);
572 concat_folder_name = TRUE;
575 buffer = g_string_new ("");
577 buffer = g_string_append (buffer, *item_name);
578 if (concat_folder_name) {
579 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
580 buffer = g_string_append (buffer, folder_name);
581 if (bold) buffer = g_string_append (buffer, "</span>");
584 g_object_unref (account);
586 *item_name = g_string_free (buffer, FALSE);
594 text_cell_data (GtkTreeViewColumn *column,
595 GtkCellRenderer *renderer,
596 GtkTreeModel *tree_model,
600 ModestFolderViewPrivate *priv;
601 GObject *rendobj = (GObject *) renderer;
603 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
604 GObject *instance = NULL;
605 gboolean use_markup = FALSE;
607 gtk_tree_model_get (tree_model, iter,
610 INSTANCE_COLUMN, &instance,
612 if (!fname || !instance)
615 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
616 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
618 gchar *item_name = NULL;
619 gint item_weight = 400;
621 if (type != TNY_FOLDER_TYPE_ROOT) {
625 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
626 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
627 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
628 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
630 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
633 /* Sometimes an special folder is reported by the server as
634 NORMAL, like some versions of Dovecot */
635 if (type == TNY_FOLDER_TYPE_NORMAL ||
636 type == TNY_FOLDER_TYPE_UNKNOWN) {
637 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
641 /* note: we cannot reliably get the counts from the
642 * tree model, we need to use explicit calls on
643 * tny_folder for some reason. Select the number to
644 * show: the unread or unsent messages. in case of
645 * outbox/drafts, show all */
646 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
647 (type == TNY_FOLDER_TYPE_OUTBOX) ||
648 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
649 number = tny_folder_get_all_count (TNY_FOLDER(instance));
652 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
656 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
657 item_name = g_strdup (fname);
664 /* Use bold font style if there are unread or unset messages */
666 item_name = g_strdup_printf ("%s (%d)", fname, number);
669 item_name = g_strdup (fname);
674 } else if (TNY_IS_ACCOUNT (instance)) {
675 /* If it's a server account */
676 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
677 item_name = g_strdup (priv->local_account_name);
679 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
680 /* fname is only correct when the items are first
681 * added to the model, not when the account is
682 * changed later, so get the name from the account
684 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
687 item_name = g_strdup (fname);
693 item_name = g_strdup ("unknown");
695 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
696 gboolean multiaccount;
698 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
699 /* Convert item_name to markup */
700 format_compact_style (&item_name, instance, priv->mailbox,
702 multiaccount, &use_markup);
705 if (item_name && item_weight) {
706 /* Set the name in the treeview cell: */
708 g_object_set (rendobj, "markup", item_name, NULL);
710 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
712 /* Notify display name observers */
713 /* TODO: What listens for this signal, and how can it use only the new name? */
714 if (((GObject *) priv->cur_folder_store) == instance) {
715 g_signal_emit (G_OBJECT(self),
716 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
723 /* If it is a Memory card account, make sure that we have the correct name.
724 * This function will be trigerred again when the name has been retrieved: */
725 if (TNY_IS_STORE_ACCOUNT (instance) &&
726 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
728 /* Get the account name asynchronously: */
729 GetMmcAccountNameData *callback_data =
730 g_slice_new0(GetMmcAccountNameData);
731 callback_data->self = self;
733 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
735 callback_data->previous_name = g_strdup (name);
737 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
738 on_get_mmc_account_name, callback_data);
742 g_object_unref (G_OBJECT (instance));
748 messages_cell_data (GtkTreeViewColumn *column,
749 GtkCellRenderer *renderer,
750 GtkTreeModel *tree_model,
754 ModestFolderView *self;
755 ModestFolderViewPrivate *priv;
756 GObject *rendobj = (GObject *) renderer;
757 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
758 GObject *instance = NULL;
759 gchar *item_name = NULL;
761 gtk_tree_model_get (tree_model, iter,
763 INSTANCE_COLUMN, &instance,
768 self = MODEST_FOLDER_VIEW (data);
769 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
772 if (type != TNY_FOLDER_TYPE_ROOT) {
776 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
777 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
778 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
780 /* Sometimes an special folder is reported by the server as
781 NORMAL, like some versions of Dovecot */
782 if (type == TNY_FOLDER_TYPE_NORMAL ||
783 type == TNY_FOLDER_TYPE_UNKNOWN) {
784 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
788 /* note: we cannot reliably get the counts from the tree model, we need
789 * to use explicit calls on tny_folder for some reason.
791 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
792 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
793 (type == TNY_FOLDER_TYPE_OUTBOX) ||
794 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
795 number = tny_folder_get_all_count (TNY_FOLDER(instance));
798 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
802 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
804 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
812 item_name = g_strdup ("");
815 /* Set the name in the treeview cell: */
816 g_object_set (rendobj,"text", item_name, NULL);
824 g_object_unref (G_OBJECT (instance));
830 GdkPixbuf *pixbuf_open;
831 GdkPixbuf *pixbuf_close;
835 static inline GdkPixbuf *
836 get_composite_pixbuf (const gchar *icon_name,
838 GdkPixbuf *base_pixbuf)
840 GdkPixbuf *emblem, *retval = NULL;
842 emblem = modest_platform_get_icon (icon_name, size);
844 retval = gdk_pixbuf_copy (base_pixbuf);
845 gdk_pixbuf_composite (emblem, retval, 0, 0,
846 MIN (gdk_pixbuf_get_width (emblem),
847 gdk_pixbuf_get_width (retval)),
848 MIN (gdk_pixbuf_get_height (emblem),
849 gdk_pixbuf_get_height (retval)),
850 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
851 g_object_unref (emblem);
856 static inline ThreePixbufs *
857 get_composite_icons (const gchar *icon_code,
859 GdkPixbuf **pixbuf_open,
860 GdkPixbuf **pixbuf_close)
862 ThreePixbufs *retval;
865 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
868 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
873 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
877 retval = g_slice_new0 (ThreePixbufs);
879 retval->pixbuf = g_object_ref (*pixbuf);
881 retval->pixbuf_open = g_object_ref (*pixbuf_open);
883 retval->pixbuf_close = g_object_ref (*pixbuf_close);
888 static inline ThreePixbufs*
889 get_folder_icons (TnyFolderType type, GObject *instance)
891 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
892 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
893 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
894 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
895 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
897 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
898 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
899 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
900 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
901 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
903 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
904 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
905 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
906 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
907 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
909 ThreePixbufs *retval = NULL;
911 /* Sometimes an special folder is reported by the server as
912 NORMAL, like some versions of Dovecot */
913 if (type == TNY_FOLDER_TYPE_NORMAL ||
914 type == TNY_FOLDER_TYPE_UNKNOWN) {
915 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
918 /* It's not enough with check the folder type. We need to
919 ensure that we're not giving a special folder icon to a
920 normal folder with the same name than a special folder */
921 if (TNY_IS_FOLDER (instance) &&
922 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
923 type = TNY_FOLDER_TYPE_NORMAL;
925 /* Remote folders should not be treated as special folders */
926 if (TNY_IS_FOLDER_STORE (instance) &&
927 !TNY_IS_ACCOUNT (instance) &&
928 type != TNY_FOLDER_TYPE_INBOX &&
929 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
930 #ifdef MODEST_TOOLKIT_HILDON2
931 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
934 &anorm_pixbuf_close);
936 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
939 &normal_pixbuf_close);
945 case TNY_FOLDER_TYPE_INVALID:
946 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
949 case TNY_FOLDER_TYPE_ROOT:
950 if (TNY_IS_ACCOUNT (instance)) {
952 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
953 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
956 &avirt_pixbuf_close);
958 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
960 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
961 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
966 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
969 &anorm_pixbuf_close);
974 case TNY_FOLDER_TYPE_INBOX:
975 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
978 &inbox_pixbuf_close);
980 case TNY_FOLDER_TYPE_OUTBOX:
981 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
984 &outbox_pixbuf_close);
986 case TNY_FOLDER_TYPE_JUNK:
987 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
992 case TNY_FOLDER_TYPE_SENT:
993 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
998 case TNY_FOLDER_TYPE_TRASH:
999 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1002 &trash_pixbuf_close);
1004 case TNY_FOLDER_TYPE_DRAFTS:
1005 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1008 &draft_pixbuf_close);
1010 case TNY_FOLDER_TYPE_ARCHIVE:
1011 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1016 case TNY_FOLDER_TYPE_NORMAL:
1018 /* Memory card folders could have an special icon */
1019 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1020 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1025 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1027 &normal_pixbuf_open,
1028 &normal_pixbuf_close);
1037 free_pixbufs (ThreePixbufs *pixbufs)
1039 if (pixbufs->pixbuf)
1040 g_object_unref (pixbufs->pixbuf);
1041 if (pixbufs->pixbuf_open)
1042 g_object_unref (pixbufs->pixbuf_open);
1043 if (pixbufs->pixbuf_close)
1044 g_object_unref (pixbufs->pixbuf_close);
1045 g_slice_free (ThreePixbufs, pixbufs);
1049 icon_cell_data (GtkTreeViewColumn *column,
1050 GtkCellRenderer *renderer,
1051 GtkTreeModel *tree_model,
1055 GObject *rendobj = NULL, *instance = NULL;
1056 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1057 gboolean has_children;
1058 ThreePixbufs *pixbufs;
1060 rendobj = (GObject *) renderer;
1062 gtk_tree_model_get (tree_model, iter,
1064 INSTANCE_COLUMN, &instance,
1070 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1071 pixbufs = get_folder_icons (type, instance);
1072 g_object_unref (instance);
1075 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1078 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1079 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1082 free_pixbufs (pixbufs);
1086 add_columns (GtkWidget *treeview)
1088 GtkTreeViewColumn *column;
1089 GtkCellRenderer *renderer;
1090 GtkTreeSelection *sel;
1091 ModestFolderViewPrivate *priv;
1093 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1096 column = gtk_tree_view_column_new ();
1098 /* Set icon and text render function */
1099 renderer = gtk_cell_renderer_pixbuf_new();
1100 #ifdef MODEST_TOOLKIT_HILDON2
1101 g_object_set (renderer,
1102 "xpad", MODEST_MARGIN_DEFAULT,
1103 "ypad", MODEST_MARGIN_DEFAULT,
1106 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1107 gtk_tree_view_column_set_cell_data_func(column, renderer,
1108 icon_cell_data, treeview, NULL);
1110 renderer = gtk_cell_renderer_text_new();
1111 g_object_set (renderer,
1112 #ifdef MODEST_TOOLKIT_HILDON2
1113 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1114 "ypad", MODEST_MARGIN_DEFAULT,
1116 "ellipsize", PANGO_ELLIPSIZE_END,
1118 "ellipsize-set", TRUE, NULL);
1119 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1120 gtk_tree_view_column_set_cell_data_func(column, renderer,
1121 text_cell_data, treeview, NULL);
1123 priv->messages_renderer = gtk_cell_renderer_text_new ();
1124 g_object_set (priv->messages_renderer,
1125 #ifdef MODEST_TOOLKIT_HILDON2
1127 "ypad", MODEST_MARGIN_DEFAULT,
1128 "xpad", MODEST_MARGIN_DOUBLE,
1130 "scale", PANGO_SCALE_X_SMALL,
1133 "alignment", PANGO_ALIGN_RIGHT,
1137 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1138 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1139 messages_cell_data, treeview, NULL);
1141 /* Set selection mode */
1142 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1143 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1145 /* Set treeview appearance */
1146 gtk_tree_view_column_set_spacing (column, 2);
1147 gtk_tree_view_column_set_resizable (column, TRUE);
1148 gtk_tree_view_column_set_fixed_width (column, TRUE);
1149 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1150 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1153 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1157 modest_folder_view_init (ModestFolderView *obj)
1159 ModestFolderViewPrivate *priv;
1162 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1164 priv->timer_expander = 0;
1165 priv->account_store = NULL;
1167 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1168 priv->cur_folder_store = NULL;
1169 priv->visible_account_id = NULL;
1170 priv->mailbox = NULL;
1171 priv->folder_to_select = NULL;
1172 priv->outbox_deleted_handler = 0;
1173 priv->reexpand = TRUE;
1175 /* Initialize the local account name */
1176 conf = modest_runtime_get_conf();
1177 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1179 /* Init email clipboard */
1180 priv->clipboard = modest_runtime_get_email_clipboard ();
1181 priv->hidding_ids = NULL;
1182 priv->n_selected = 0;
1183 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1184 priv->reselect = FALSE;
1185 priv->show_non_move = TRUE;
1186 priv->list_to_move = NULL;
1188 /* Build treeview */
1189 add_columns (GTK_WIDGET (obj));
1191 /* Setup drag and drop */
1192 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1194 /* Connect signals */
1195 g_signal_connect (G_OBJECT (obj),
1197 G_CALLBACK (on_key_pressed), NULL);
1199 priv->display_name_changed_signal =
1200 g_signal_connect (modest_runtime_get_account_mgr (),
1201 "display_name_changed",
1202 G_CALLBACK (on_display_name_changed),
1206 * Track changes in the local account name (in the device it
1207 * will be the device name)
1209 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1211 G_CALLBACK(on_configuration_key_changed),
1215 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1221 tny_account_store_view_init (gpointer g, gpointer iface_data)
1223 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1225 klass->set_account_store = modest_folder_view_set_account_store;
1229 modest_folder_view_finalize (GObject *obj)
1231 ModestFolderViewPrivate *priv;
1232 GtkTreeSelection *sel;
1233 TnyAccount *local_account;
1235 g_return_if_fail (obj);
1237 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1239 if (priv->timer_expander != 0) {
1240 g_source_remove (priv->timer_expander);
1241 priv->timer_expander = 0;
1244 local_account = (TnyAccount *)
1245 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1246 if (local_account) {
1247 if (g_signal_handler_is_connected (local_account,
1248 priv->outbox_deleted_handler))
1249 g_signal_handler_disconnect (local_account,
1250 priv->outbox_deleted_handler);
1251 g_object_unref (local_account);
1254 if (priv->account_store) {
1255 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1256 priv->account_inserted_signal);
1257 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1258 priv->account_removed_signal);
1259 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1260 priv->account_changed_signal);
1261 g_object_unref (G_OBJECT(priv->account_store));
1262 priv->account_store = NULL;
1265 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1266 priv->display_name_changed_signal)) {
1267 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1268 priv->display_name_changed_signal);
1269 priv->display_name_changed_signal = 0;
1273 g_object_unref (G_OBJECT (priv->query));
1277 if (priv->folder_to_select) {
1278 g_object_unref (G_OBJECT(priv->folder_to_select));
1279 priv->folder_to_select = NULL;
1282 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1284 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1286 g_free (priv->local_account_name);
1287 g_free (priv->visible_account_id);
1288 g_free (priv->mailbox);
1290 if (priv->conf_key_signal) {
1291 g_signal_handler_disconnect (modest_runtime_get_conf (),
1292 priv->conf_key_signal);
1293 priv->conf_key_signal = 0;
1296 if (priv->cur_folder_store) {
1297 g_object_unref (priv->cur_folder_store);
1298 priv->cur_folder_store = NULL;
1301 if (priv->list_to_move) {
1302 g_object_unref (priv->list_to_move);
1303 priv->list_to_move = NULL;
1306 /* Clear hidding array created by cut operation */
1307 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1309 G_OBJECT_CLASS(parent_class)->finalize (obj);
1314 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1316 ModestFolderViewPrivate *priv;
1319 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1320 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1322 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1323 device = tny_account_store_get_device (account_store);
1325 if (G_UNLIKELY (priv->account_store)) {
1327 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1328 priv->account_inserted_signal))
1329 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1330 priv->account_inserted_signal);
1331 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1332 priv->account_removed_signal))
1333 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1334 priv->account_removed_signal);
1335 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1336 priv->account_changed_signal))
1337 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1338 priv->account_changed_signal);
1339 g_object_unref (G_OBJECT (priv->account_store));
1342 priv->account_store = g_object_ref (G_OBJECT (account_store));
1344 priv->account_removed_signal =
1345 g_signal_connect (G_OBJECT(account_store), "account_removed",
1346 G_CALLBACK (on_account_removed), self);
1348 priv->account_inserted_signal =
1349 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1350 G_CALLBACK (on_account_inserted), self);
1352 priv->account_changed_signal =
1353 g_signal_connect (G_OBJECT(account_store), "account_changed",
1354 G_CALLBACK (on_account_changed), self);
1356 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1357 priv->reselect = FALSE;
1358 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1360 g_object_unref (G_OBJECT (device));
1364 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1367 ModestFolderView *self;
1368 GtkTreeModel *model, *filter_model;
1371 self = MODEST_FOLDER_VIEW (user_data);
1373 if (!get_inner_models (self, &filter_model, NULL, &model))
1376 /* Remove outbox from model */
1377 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1378 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1379 g_object_unref (outbox);
1382 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1386 on_account_inserted (TnyAccountStore *account_store,
1387 TnyAccount *account,
1390 ModestFolderViewPrivate *priv;
1391 GtkTreeModel *model, *filter_model;
1393 /* Ignore transport account insertions, we're not showing them
1394 in the folder view */
1395 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1398 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1401 /* If we're adding a new account, and there is no previous
1402 one, we need to select the visible server account */
1403 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1404 !priv->visible_account_id)
1405 modest_widget_memory_restore (modest_runtime_get_conf(),
1406 G_OBJECT (user_data),
1407 MODEST_CONF_FOLDER_VIEW_KEY);
1411 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1412 &filter_model, NULL, &model))
1415 /* Insert the account in the model */
1416 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1418 /* When the model is a list store (plain representation) the
1419 outbox is not a child of any account so we have to manually
1420 delete it because removing the local folders account won't
1421 delete it (because tny_folder_get_account() is not defined
1422 for a merge folder */
1423 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1424 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1426 priv->outbox_deleted_handler =
1427 g_signal_connect (account,
1429 G_CALLBACK (on_outbox_deleted_cb),
1433 /* Refilter the model */
1434 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1439 same_account_selected (ModestFolderView *self,
1440 TnyAccount *account)
1442 ModestFolderViewPrivate *priv;
1443 gboolean same_account = FALSE;
1445 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1447 if (priv->cur_folder_store) {
1448 TnyAccount *selected_folder_account = NULL;
1450 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1451 selected_folder_account =
1452 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1454 selected_folder_account =
1455 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1458 if (selected_folder_account == account)
1459 same_account = TRUE;
1461 g_object_unref (selected_folder_account);
1463 return same_account;
1468 * Selects the first inbox or the local account in an idle
1471 on_idle_select_first_inbox_or_local (gpointer user_data)
1473 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1475 gdk_threads_enter ();
1476 modest_folder_view_select_first_inbox_or_local (self);
1477 gdk_threads_leave ();
1483 on_account_changed (TnyAccountStore *account_store,
1484 TnyAccount *tny_account,
1487 ModestFolderView *self;
1488 ModestFolderViewPrivate *priv;
1489 GtkTreeModel *model, *filter_model;
1490 GtkTreeSelection *sel;
1491 gboolean same_account;
1493 /* Ignore transport account insertions, we're not showing them
1494 in the folder view */
1495 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1498 self = MODEST_FOLDER_VIEW (user_data);
1499 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1501 /* Get the inner model */
1502 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1503 &filter_model, NULL, &model))
1506 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1508 /* Invalidate the cur_folder_store only if the selected folder
1509 belongs to the account that is being removed */
1510 same_account = same_account_selected (self, tny_account);
1512 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1513 gtk_tree_selection_unselect_all (sel);
1516 /* Remove the account from the model */
1517 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1519 /* Insert the account in the model */
1520 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1522 /* Refilter the model */
1523 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1525 /* Select the first INBOX if the currently selected folder
1526 belongs to the account that is being deleted */
1527 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1528 g_idle_add (on_idle_select_first_inbox_or_local, self);
1532 on_account_removed (TnyAccountStore *account_store,
1533 TnyAccount *account,
1536 ModestFolderView *self = NULL;
1537 ModestFolderViewPrivate *priv;
1538 GtkTreeModel *model, *filter_model;
1539 GtkTreeSelection *sel = NULL;
1540 gboolean same_account = FALSE;
1542 /* Ignore transport account removals, we're not showing them
1543 in the folder view */
1544 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1547 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1548 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1552 self = MODEST_FOLDER_VIEW (user_data);
1553 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1555 /* Invalidate the cur_folder_store only if the selected folder
1556 belongs to the account that is being removed */
1557 same_account = same_account_selected (self, account);
1559 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1560 gtk_tree_selection_unselect_all (sel);
1563 /* Invalidate row to select only if the folder to select
1564 belongs to the account that is being removed*/
1565 if (priv->folder_to_select) {
1566 TnyAccount *folder_to_select_account = NULL;
1568 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1569 if (folder_to_select_account == account) {
1570 modest_folder_view_disable_next_folder_selection (self);
1571 g_object_unref (priv->folder_to_select);
1572 priv->folder_to_select = NULL;
1574 g_object_unref (folder_to_select_account);
1577 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1578 &filter_model, NULL, &model))
1581 /* Disconnect the signal handler */
1582 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1583 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1584 if (g_signal_handler_is_connected (account,
1585 priv->outbox_deleted_handler))
1586 g_signal_handler_disconnect (account,
1587 priv->outbox_deleted_handler);
1590 /* Remove the account from the model */
1591 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1593 /* If the removed account is the currently viewed one then
1594 clear the configuration value. The new visible account will be the default account */
1595 if (priv->visible_account_id &&
1596 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1598 /* Clear the current visible account_id */
1599 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1600 modest_folder_view_set_mailbox (self, NULL);
1602 /* Call the restore method, this will set the new visible account */
1603 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1604 MODEST_CONF_FOLDER_VIEW_KEY);
1607 /* Refilter the model */
1608 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1610 /* Select the first INBOX if the currently selected folder
1611 belongs to the account that is being deleted */
1613 g_idle_add (on_idle_select_first_inbox_or_local, self);
1617 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1619 GtkTreeViewColumn *col;
1621 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1623 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1625 g_printerr ("modest: failed get column for title\n");
1629 gtk_tree_view_column_set_title (col, title);
1630 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1635 modest_folder_view_on_map (ModestFolderView *self,
1636 GdkEventExpose *event,
1639 ModestFolderViewPrivate *priv;
1641 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1643 /* This won't happen often */
1644 if (G_UNLIKELY (priv->reselect)) {
1645 /* Select the first inbox or the local account if not found */
1647 /* TODO: this could cause a lock at startup, so we
1648 comment it for the moment. We know that this will
1649 be a bug, because the INBOX is not selected, but we
1650 need to rewrite some parts of Modest to avoid the
1651 deathlock situation */
1652 /* TODO: check if this is still the case */
1653 priv->reselect = FALSE;
1654 modest_folder_view_select_first_inbox_or_local (self);
1655 /* Notify the display name observers */
1656 g_signal_emit (G_OBJECT(self),
1657 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1661 if (priv->reexpand) {
1662 expand_root_items (self);
1663 priv->reexpand = FALSE;
1670 modest_folder_view_new (TnyFolderStoreQuery *query)
1673 ModestFolderViewPrivate *priv;
1674 GtkTreeSelection *sel;
1676 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1677 #ifdef MODEST_TOOLKIT_HILDON2
1678 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1681 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1684 priv->query = g_object_ref (query);
1686 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1687 priv->changed_signal = g_signal_connect (sel, "changed",
1688 G_CALLBACK (on_selection_changed), self);
1690 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1692 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1694 return GTK_WIDGET(self);
1697 /* this feels dirty; any other way to expand all the root items? */
1699 expand_root_items (ModestFolderView *self)
1702 GtkTreeModel *model;
1705 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1706 path = gtk_tree_path_new_first ();
1708 /* all folders should have child items, so.. */
1710 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1711 gtk_tree_path_next (path);
1712 } while (gtk_tree_model_get_iter (model, &iter, path));
1714 gtk_tree_path_free (path);
1718 is_parent_of (TnyFolder *a, TnyFolder *b)
1721 gboolean retval = FALSE;
1723 a_id = tny_folder_get_id (a);
1725 gchar *string_to_match;
1728 string_to_match = g_strconcat (a_id, "/", NULL);
1729 b_id = tny_folder_get_id (b);
1730 retval = g_str_has_prefix (b_id, string_to_match);
1731 g_free (string_to_match);
1737 typedef struct _ForeachFolderInfo {
1740 } ForeachFolderInfo;
1743 foreach_folder_with_id (GtkTreeModel *model,
1748 ForeachFolderInfo *info;
1751 info = (ForeachFolderInfo *) data;
1752 gtk_tree_model_get (model, iter,
1753 INSTANCE_COLUMN, &instance,
1756 if (TNY_IS_FOLDER (instance)) {
1759 id = tny_folder_get_id (TNY_FOLDER (instance));
1761 collate = g_utf8_collate_key (id, -1);
1762 info->found = !strcmp (info->needle, collate);
1773 has_folder_with_id (ModestFolderView *self, const gchar *id)
1775 GtkTreeModel *model;
1776 ForeachFolderInfo info = {NULL, FALSE};
1778 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1779 info.needle = g_utf8_collate_key (id, -1);
1781 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1782 g_free (info.needle);
1788 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1791 gboolean retval = FALSE;
1793 a_id = tny_folder_get_id (a);
1796 b_id = tny_folder_get_id (b);
1799 const gchar *last_bar;
1800 gchar *string_to_match;
1801 last_bar = g_strrstr (b_id, "/");
1806 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1807 retval = has_folder_with_id (self, string_to_match);
1808 g_free (string_to_match);
1816 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1818 ModestFolderViewPrivate *priv;
1819 TnyIterator *iterator;
1820 gboolean retval = TRUE;
1822 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1823 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1825 for (iterator = tny_list_create_iterator (priv->list_to_move);
1826 retval && !tny_iterator_is_done (iterator);
1827 tny_iterator_next (iterator)) {
1829 instance = tny_iterator_get_current (iterator);
1830 if (instance == (GObject *) folder) {
1832 } else if (TNY_IS_FOLDER (instance)) {
1833 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1835 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1838 g_object_unref (instance);
1840 g_object_unref (iterator);
1847 * We use this function to implement the
1848 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1849 * account in this case, and the local folders.
1852 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1854 ModestFolderViewPrivate *priv;
1855 gboolean retval = TRUE;
1856 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1857 GObject *instance = NULL;
1858 const gchar *id = NULL;
1860 gboolean found = FALSE;
1861 gboolean cleared = FALSE;
1862 ModestTnyFolderRules rules = 0;
1865 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1866 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1868 gtk_tree_model_get (model, iter,
1869 NAME_COLUMN, &fname,
1871 INSTANCE_COLUMN, &instance,
1874 /* Do not show if there is no instance, this could indeed
1875 happen when the model is being modified while it's being
1876 drawn. This could occur for example when moving folders
1883 if (TNY_IS_ACCOUNT (instance)) {
1884 TnyAccount *acc = TNY_ACCOUNT (instance);
1885 const gchar *account_id = tny_account_get_id (acc);
1887 /* If it isn't a special folder,
1888 * don't show it unless it is the visible account: */
1889 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1890 !modest_tny_account_is_virtual_local_folders (acc) &&
1891 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1893 /* Show only the visible account id */
1894 if (priv->visible_account_id) {
1895 if (strcmp (account_id, priv->visible_account_id))
1902 /* Never show these to the user. They are merged into one folder
1903 * in the local-folders account instead: */
1904 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1907 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1908 /* Only show special folders for current account if needed */
1909 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1910 TnyAccount *account;
1912 account = tny_folder_get_account (TNY_FOLDER (instance));
1914 if (TNY_IS_ACCOUNT (account)) {
1915 const gchar *account_id = tny_account_get_id (account);
1917 if (!modest_tny_account_is_virtual_local_folders (account) &&
1918 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1919 /* Show only the visible account id */
1920 if (priv->visible_account_id) {
1921 if (strcmp (account_id, priv->visible_account_id)) {
1923 } else if (priv->mailbox) {
1924 /* Filter mailboxes */
1925 if (!g_str_has_prefix (fname, priv->mailbox)) {
1927 } else if (!strcmp (fname, priv->mailbox)) {
1928 /* Hide mailbox parent */
1934 g_object_unref (account);
1941 /* Check hiding (if necessary) */
1942 cleared = modest_email_clipboard_cleared (priv->clipboard);
1943 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1944 id = tny_folder_get_id (TNY_FOLDER(instance));
1945 if (priv->hidding_ids != NULL)
1946 for (i=0; i < priv->n_selected && !found; i++)
1947 if (priv->hidding_ids[i] != NULL && id != NULL)
1948 found = (!strcmp (priv->hidding_ids[i], id));
1953 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1954 folder as no message can be move there according to UI specs */
1955 if (retval && !priv->show_non_move) {
1956 if (priv->list_to_move &&
1957 tny_list_get_length (priv->list_to_move) > 0 &&
1958 TNY_IS_FOLDER (instance)) {
1959 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1961 if (retval && TNY_IS_FOLDER (instance) &&
1962 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1964 case TNY_FOLDER_TYPE_OUTBOX:
1965 case TNY_FOLDER_TYPE_SENT:
1966 case TNY_FOLDER_TYPE_DRAFTS:
1969 case TNY_FOLDER_TYPE_UNKNOWN:
1970 case TNY_FOLDER_TYPE_NORMAL:
1971 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1972 if (type == TNY_FOLDER_TYPE_INVALID)
1973 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1975 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1976 type == TNY_FOLDER_TYPE_SENT
1977 || type == TNY_FOLDER_TYPE_DRAFTS)
1986 /* apply special filters */
1987 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1988 if (TNY_IS_ACCOUNT (instance))
1992 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
1993 if (TNY_IS_FOLDER (instance))
1997 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
1998 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2003 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2004 /* A mailbox is a fake folder with an @ in the middle of the name */
2005 if (!TNY_IS_FOLDER (instance) ||
2006 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2009 const gchar *folder_name;
2010 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2011 if (!folder_name || strchr (folder_name, '@') == NULL)
2017 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2018 if (TNY_IS_FOLDER (instance)) {
2019 /* Check folder rules */
2020 ModestTnyFolderRules rules;
2022 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2023 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2024 } else if (TNY_IS_ACCOUNT (instance)) {
2025 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2033 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2034 if (TNY_IS_FOLDER (instance)) {
2035 TnyFolderType guess_type;
2037 if (TNY_FOLDER_TYPE_NORMAL) {
2038 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2044 case TNY_FOLDER_TYPE_OUTBOX:
2045 case TNY_FOLDER_TYPE_SENT:
2046 case TNY_FOLDER_TYPE_DRAFTS:
2047 case TNY_FOLDER_TYPE_ARCHIVE:
2048 case TNY_FOLDER_TYPE_INBOX:
2051 case TNY_FOLDER_TYPE_UNKNOWN:
2052 case TNY_FOLDER_TYPE_NORMAL:
2058 } else if (TNY_IS_ACCOUNT (instance)) {
2063 if (retval && TNY_IS_FOLDER (instance)) {
2064 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2067 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2068 if (TNY_IS_FOLDER (instance)) {
2069 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2070 } else if (TNY_IS_ACCOUNT (instance)) {
2075 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2076 if (TNY_IS_FOLDER (instance)) {
2077 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2078 } else if (TNY_IS_ACCOUNT (instance)) {
2083 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2084 if (TNY_IS_FOLDER (instance)) {
2085 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2086 } else if (TNY_IS_ACCOUNT (instance)) {
2092 g_object_unref (instance);
2100 modest_folder_view_update_model (ModestFolderView *self,
2101 TnyAccountStore *account_store)
2103 ModestFolderViewPrivate *priv;
2104 GtkTreeModel *model /* , *old_model */;
2105 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2107 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2108 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2111 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2113 /* Notify that there is no folder selected */
2114 g_signal_emit (G_OBJECT(self),
2115 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2117 if (priv->cur_folder_store) {
2118 g_object_unref (priv->cur_folder_store);
2119 priv->cur_folder_store = NULL;
2122 /* FIXME: the local accounts are not shown when the query
2123 selects only the subscribed folders */
2124 #ifdef MODEST_TOOLKIT_HILDON2
2125 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2126 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2127 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2128 MODEST_FOLDER_PATH_SEPARATOR);
2130 model = tny_gtk_folder_store_tree_model_new (NULL);
2133 /* When the model is a list store (plain representation) the
2134 outbox is not a child of any account so we have to manually
2135 delete it because removing the local folders account won't
2136 delete it (because tny_folder_get_account() is not defined
2137 for a merge folder */
2138 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2139 TnyAccount *account;
2140 ModestTnyAccountStore *acc_store;
2142 acc_store = modest_runtime_get_account_store ();
2143 account = modest_tny_account_store_get_local_folders_account (acc_store);
2145 if (g_signal_handler_is_connected (account,
2146 priv->outbox_deleted_handler))
2147 g_signal_handler_disconnect (account,
2148 priv->outbox_deleted_handler);
2150 priv->outbox_deleted_handler =
2151 g_signal_connect (account,
2153 G_CALLBACK (on_outbox_deleted_cb),
2155 g_object_unref (account);
2158 /* Get the accounts: */
2159 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2161 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2163 sortable = gtk_tree_model_sort_new_with_model (model);
2164 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2166 GTK_SORT_ASCENDING);
2167 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2169 cmp_rows, NULL, NULL);
2171 /* Create filter model */
2172 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2173 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2179 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2180 #ifndef MODEST_TOOLKIT_HILDON2
2181 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2182 (GCallback) on_row_inserted_maybe_select_folder, self);
2185 g_object_unref (model);
2186 g_object_unref (filter_model);
2187 g_object_unref (sortable);
2189 /* Force a reselection of the INBOX next time the widget is shown */
2190 priv->reselect = TRUE;
2197 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2199 GtkTreeModel *model = NULL;
2200 TnyFolderStore *folder = NULL;
2202 ModestFolderView *tree_view = NULL;
2203 ModestFolderViewPrivate *priv = NULL;
2204 gboolean selected = FALSE;
2206 g_return_if_fail (sel);
2207 g_return_if_fail (user_data);
2209 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2211 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2213 tree_view = MODEST_FOLDER_VIEW (user_data);
2216 gtk_tree_model_get (model, &iter,
2217 INSTANCE_COLUMN, &folder,
2220 /* If the folder is the same do not notify */
2221 if (folder && priv->cur_folder_store == folder) {
2222 g_object_unref (folder);
2227 /* Current folder was unselected */
2228 if (priv->cur_folder_store) {
2229 /* We must do this firstly because a libtinymail-camel
2230 implementation detail. If we issue the signal
2231 before doing the sync_async, then that signal could
2232 cause (and it actually does it) a free of the
2233 summary of the folder (because the main window will
2234 clear the headers view */
2235 if (TNY_IS_FOLDER(priv->cur_folder_store))
2236 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2237 FALSE, NULL, NULL, NULL);
2239 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2240 priv->cur_folder_store, FALSE);
2242 g_object_unref (priv->cur_folder_store);
2243 priv->cur_folder_store = NULL;
2246 /* New current references */
2247 priv->cur_folder_store = folder;
2249 /* New folder has been selected. Do not notify if there is
2250 nothing new selected */
2252 g_signal_emit (G_OBJECT(tree_view),
2253 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2254 0, priv->cur_folder_store, TRUE);
2259 on_row_activated (GtkTreeView *treeview,
2260 GtkTreePath *treepath,
2261 GtkTreeViewColumn *column,
2264 GtkTreeModel *model = NULL;
2265 TnyFolderStore *folder = NULL;
2267 ModestFolderView *self = NULL;
2268 ModestFolderViewPrivate *priv = NULL;
2270 g_return_if_fail (treeview);
2271 g_return_if_fail (user_data);
2273 self = MODEST_FOLDER_VIEW (user_data);
2274 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2276 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2278 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2281 gtk_tree_model_get (model, &iter,
2282 INSTANCE_COLUMN, &folder,
2285 g_signal_emit (G_OBJECT(self),
2286 signals[FOLDER_ACTIVATED_SIGNAL],
2289 #ifdef MODEST_TOOLKIT_HILDON2
2290 HildonUIMode ui_mode;
2291 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2292 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2293 if (priv->cur_folder_store)
2294 g_object_unref (priv->cur_folder_store);
2295 priv->cur_folder_store = g_object_ref (folder);
2299 g_object_unref (folder);
2303 modest_folder_view_get_selected (ModestFolderView *self)
2305 ModestFolderViewPrivate *priv;
2307 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2309 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2310 if (priv->cur_folder_store)
2311 g_object_ref (priv->cur_folder_store);
2313 return priv->cur_folder_store;
2317 get_cmp_rows_type_pos (GObject *folder)
2319 /* Remote accounts -> Local account -> MMC account .*/
2322 if (TNY_IS_ACCOUNT (folder) &&
2323 modest_tny_account_is_virtual_local_folders (
2324 TNY_ACCOUNT (folder))) {
2326 } else if (TNY_IS_ACCOUNT (folder)) {
2327 TnyAccount *account = TNY_ACCOUNT (folder);
2328 const gchar *account_id = tny_account_get_id (account);
2329 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2335 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2336 return -1; /* Should never happen */
2341 inbox_is_special (TnyFolderStore *folder_store)
2343 gboolean is_special = TRUE;
2345 if (TNY_IS_FOLDER (folder_store)) {
2349 gchar *last_inbox_bar;
2351 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2352 downcase = g_utf8_strdown (id, -1);
2353 last_bar = g_strrstr (downcase, "/");
2355 last_inbox_bar = g_strrstr (downcase, "inbox/");
2356 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2367 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2369 TnyAccount *account;
2370 gboolean is_special;
2371 /* Inbox, Outbox, Drafts, Sent, User */
2374 if (!TNY_IS_FOLDER (folder_store))
2377 case TNY_FOLDER_TYPE_INBOX:
2379 account = tny_folder_get_account (folder_store);
2380 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2382 /* In inbox case we need to know if the inbox is really the top
2383 * inbox of the account, or if it's a submailbox inbox. To do
2384 * this we'll apply an heuristic rule: Find last "/" and check
2385 * if it's preceeded by another Inbox */
2386 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2387 g_object_unref (account);
2388 return is_special?0:4;
2391 case TNY_FOLDER_TYPE_OUTBOX:
2392 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2394 case TNY_FOLDER_TYPE_DRAFTS:
2396 account = tny_folder_get_account (folder_store);
2397 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2398 g_object_unref (account);
2399 return is_special?1:4;
2402 case TNY_FOLDER_TYPE_SENT:
2404 account = tny_folder_get_account (folder_store);
2405 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2406 g_object_unref (account);
2407 return is_special?3:4;
2416 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2418 const gchar *a1_name, *a2_name;
2420 a1_name = tny_account_get_name (a1);
2421 a2_name = tny_account_get_name (a2);
2423 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2427 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2429 TnyAccount *a1 = NULL, *a2 = NULL;
2432 if (TNY_IS_ACCOUNT (s1)) {
2433 a1 = TNY_ACCOUNT (g_object_ref (s1));
2434 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2435 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2438 if (TNY_IS_ACCOUNT (s2)) {
2439 a2 = TNY_ACCOUNT (g_object_ref (s2));
2440 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2441 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2458 /* First we sort with the type of account */
2459 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2463 cmp = compare_account_names (a1, a2);
2467 g_object_unref (a1);
2469 g_object_unref (a2);
2475 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2477 gint is_account1, is_account2;
2479 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2480 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2482 return is_account2 - is_account1;
2486 * This function orders the mail accounts according to these rules:
2487 * 1st - remote accounts
2488 * 2nd - local account
2492 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2496 gchar *name1 = NULL;
2497 gchar *name2 = NULL;
2498 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2499 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2500 GObject *folder1 = NULL;
2501 GObject *folder2 = NULL;
2503 gtk_tree_model_get (tree_model, iter1,
2504 NAME_COLUMN, &name1,
2506 INSTANCE_COLUMN, &folder1,
2508 gtk_tree_model_get (tree_model, iter2,
2509 NAME_COLUMN, &name2,
2510 TYPE_COLUMN, &type2,
2511 INSTANCE_COLUMN, &folder2,
2514 /* Return if we get no folder. This could happen when folder
2515 operations are happening. The model is updated after the
2516 folder copy/move actually occurs, so there could be
2517 situations where the model to be drawn is not correct */
2518 if (!folder1 || !folder2)
2521 /* Sort by type. First the special folders, then the archives */
2522 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2526 /* Now we sort using the account of each folder */
2527 if (TNY_IS_FOLDER_STORE (folder1) &&
2528 TNY_IS_FOLDER_STORE (folder2)) {
2529 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2533 /* Each group is preceeded by its account */
2534 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2539 /* Pure sort by name */
2540 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2543 g_object_unref(G_OBJECT(folder1));
2545 g_object_unref(G_OBJECT(folder2));
2553 /*****************************************************************************/
2554 /* DRAG and DROP stuff */
2555 /*****************************************************************************/
2557 * This function fills the #GtkSelectionData with the row and the
2558 * model that has been dragged. It's called when this widget is a
2559 * source for dnd after the event drop happened
2562 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2563 guint info, guint time, gpointer data)
2565 GtkTreeSelection *selection;
2566 GtkTreeModel *model;
2568 GtkTreePath *source_row;
2570 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2571 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2573 source_row = gtk_tree_model_get_path (model, &iter);
2574 gtk_tree_set_row_drag_data (selection_data,
2578 gtk_tree_path_free (source_row);
2582 typedef struct _DndHelper {
2583 ModestFolderView *folder_view;
2584 gboolean delete_source;
2585 GtkTreePath *source_row;
2589 dnd_helper_destroyer (DndHelper *helper)
2591 /* Free the helper */
2592 gtk_tree_path_free (helper->source_row);
2593 g_slice_free (DndHelper, helper);
2597 xfer_folder_cb (ModestMailOperation *mail_op,
2598 TnyFolder *new_folder,
2602 /* Select the folder */
2603 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2609 /* get the folder for the row the treepath refers to. */
2610 /* folder must be unref'd */
2611 static TnyFolderStore *
2612 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2615 TnyFolderStore *folder = NULL;
2617 if (gtk_tree_model_get_iter (model,&iter, path))
2618 gtk_tree_model_get (model, &iter,
2619 INSTANCE_COLUMN, &folder,
2626 * This function is used by drag_data_received_cb to manage drag and
2627 * drop of a header, i.e, and drag from the header view to the folder
2631 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2632 GtkTreeModel *dest_model,
2633 GtkTreePath *dest_row,
2634 GtkSelectionData *selection_data)
2636 TnyList *headers = NULL;
2637 TnyFolder *folder = NULL, *src_folder = NULL;
2638 TnyFolderType folder_type;
2639 GtkTreeIter source_iter, dest_iter;
2640 ModestWindowMgr *mgr = NULL;
2641 ModestWindow *main_win = NULL;
2642 gchar **uris, **tmp;
2644 /* Build the list of headers */
2645 mgr = modest_runtime_get_window_mgr ();
2646 headers = tny_simple_list_new ();
2647 uris = modest_dnd_selection_data_get_paths (selection_data);
2650 while (*tmp != NULL) {
2653 gboolean first = TRUE;
2656 path = gtk_tree_path_new_from_string (*tmp);
2657 gtk_tree_model_get_iter (source_model, &source_iter, path);
2658 gtk_tree_model_get (source_model, &source_iter,
2659 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2662 /* Do not enable d&d of headers already opened */
2663 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2664 tny_list_append (headers, G_OBJECT (header));
2666 if (G_UNLIKELY (first)) {
2667 src_folder = tny_header_get_folder (header);
2671 /* Free and go on */
2672 gtk_tree_path_free (path);
2673 g_object_unref (header);
2678 /* This could happen ig we perform a d&d very quickly over the
2679 same row that row could dissapear because message is
2681 if (!TNY_IS_FOLDER (src_folder))
2684 /* Get the target folder */
2685 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2686 gtk_tree_model_get (dest_model, &dest_iter,
2690 if (!folder || !TNY_IS_FOLDER(folder)) {
2691 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2695 folder_type = modest_tny_folder_guess_folder_type (folder);
2696 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2697 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2698 goto cleanup; /* cannot move messages there */
2701 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2702 /* g_warning ("folder not writable"); */
2703 goto cleanup; /* verboten! */
2706 /* Ask for confirmation to move */
2707 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2709 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2713 /* Transfer messages */
2714 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2719 if (G_IS_OBJECT (src_folder))
2720 g_object_unref (src_folder);
2721 if (G_IS_OBJECT(folder))
2722 g_object_unref (G_OBJECT (folder));
2723 if (G_IS_OBJECT(headers))
2724 g_object_unref (headers);
2728 TnyFolderStore *src_folder;
2729 TnyFolderStore *dst_folder;
2730 ModestFolderView *folder_view;
2735 dnd_folder_info_destroyer (DndFolderInfo *info)
2737 if (info->src_folder)
2738 g_object_unref (info->src_folder);
2739 if (info->dst_folder)
2740 g_object_unref (info->dst_folder);
2741 g_slice_free (DndFolderInfo, info);
2745 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2746 GtkWindow *parent_window,
2747 TnyAccount *account)
2750 modest_ui_actions_on_account_connection_error (parent_window, account);
2752 /* Free the helper & info */
2753 dnd_helper_destroyer (info->helper);
2754 dnd_folder_info_destroyer (info);
2758 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2760 GtkWindow *parent_window,
2761 TnyAccount *account,
2764 DndFolderInfo *info = NULL;
2765 ModestMailOperation *mail_op;
2767 info = (DndFolderInfo *) user_data;
2769 if (err || canceled) {
2770 dnd_on_connection_failed_destroyer (info, parent_window, account);
2774 /* Do the mail operation */
2775 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2776 modest_ui_actions_move_folder_error_handler,
2777 info->src_folder, NULL);
2779 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2782 /* Transfer the folder */
2783 modest_mail_operation_xfer_folder (mail_op,
2784 TNY_FOLDER (info->src_folder),
2786 info->helper->delete_source,
2788 info->helper->folder_view);
2791 g_object_unref (G_OBJECT (mail_op));
2792 dnd_helper_destroyer (info->helper);
2793 dnd_folder_info_destroyer (info);
2798 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2800 GtkWindow *parent_window,
2801 TnyAccount *account,
2804 DndFolderInfo *info = NULL;
2806 info = (DndFolderInfo *) user_data;
2808 if (err || canceled) {
2809 dnd_on_connection_failed_destroyer (info, parent_window, account);
2813 /* Connect to source folder and perform the copy/move */
2814 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2816 drag_and_drop_from_folder_view_src_folder_performer,
2821 * This function is used by drag_data_received_cb to manage drag and
2822 * drop of a folder, i.e, and drag from the folder view to the same
2826 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2827 GtkTreeModel *dest_model,
2828 GtkTreePath *dest_row,
2829 GtkSelectionData *selection_data,
2832 GtkTreeIter dest_iter, iter;
2833 TnyFolderStore *dest_folder = NULL;
2834 TnyFolderStore *folder = NULL;
2835 gboolean forbidden = FALSE;
2837 DndFolderInfo *info = NULL;
2839 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2841 g_warning ("%s: BUG: no main window", __FUNCTION__);
2842 dnd_helper_destroyer (helper);
2847 /* check the folder rules for the destination */
2848 folder = tree_path_to_folder (dest_model, dest_row);
2849 if (TNY_IS_FOLDER(folder)) {
2850 ModestTnyFolderRules rules =
2851 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2852 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2853 } else if (TNY_IS_FOLDER_STORE(folder)) {
2854 /* enable local root as destination for folders */
2855 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2856 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2859 g_object_unref (folder);
2862 /* check the folder rules for the source */
2863 folder = tree_path_to_folder (source_model, helper->source_row);
2864 if (TNY_IS_FOLDER(folder)) {
2865 ModestTnyFolderRules rules =
2866 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2867 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2870 g_object_unref (folder);
2874 /* Check if the drag is possible */
2875 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2877 modest_platform_run_information_dialog ((GtkWindow *) win,
2878 _("mail_in_ui_folder_move_target_error"),
2880 /* Restore the previous selection */
2881 folder = tree_path_to_folder (source_model, helper->source_row);
2883 if (TNY_IS_FOLDER (folder))
2884 modest_folder_view_select_folder (helper->folder_view,
2885 TNY_FOLDER (folder), FALSE);
2886 g_object_unref (folder);
2888 dnd_helper_destroyer (helper);
2893 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2894 gtk_tree_model_get (dest_model, &dest_iter,
2897 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2898 gtk_tree_model_get (source_model, &iter,
2902 /* Create the info for the performer */
2903 info = g_slice_new0 (DndFolderInfo);
2904 info->src_folder = g_object_ref (folder);
2905 info->dst_folder = g_object_ref (dest_folder);
2906 info->helper = helper;
2908 /* Connect to the destination folder and perform the copy/move */
2909 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2911 drag_and_drop_from_folder_view_dst_folder_performer,
2915 g_object_unref (dest_folder);
2916 g_object_unref (folder);
2920 * This function receives the data set by the "drag-data-get" signal
2921 * handler. This information comes within the #GtkSelectionData. This
2922 * function will manage both the drags of folders of the treeview and
2923 * drags of headers of the header view widget.
2926 on_drag_data_received (GtkWidget *widget,
2927 GdkDragContext *context,
2930 GtkSelectionData *selection_data,
2935 GtkWidget *source_widget;
2936 GtkTreeModel *dest_model, *source_model;
2937 GtkTreePath *source_row, *dest_row;
2938 GtkTreeViewDropPosition pos;
2939 gboolean delete_source = FALSE;
2940 gboolean success = FALSE;
2942 /* Do not allow further process */
2943 g_signal_stop_emission_by_name (widget, "drag-data-received");
2944 source_widget = gtk_drag_get_source_widget (context);
2946 /* Get the action */
2947 if (context->action == GDK_ACTION_MOVE) {
2948 delete_source = TRUE;
2950 /* Notify that there is no folder selected. We need to
2951 do this in order to update the headers view (and
2952 its monitors, because when moving, the old folder
2953 won't longer exist. We can not wait for the end of
2954 the operation, because the operation won't start if
2955 the folder is in use */
2956 if (source_widget == widget) {
2957 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2958 gtk_tree_selection_unselect_all (sel);
2962 /* Check if the get_data failed */
2963 if (selection_data == NULL || selection_data->length < 0)
2966 /* Select the destination model */
2967 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2969 /* Get the path to the destination row. Can not call
2970 gtk_tree_view_get_drag_dest_row() because the source row
2971 is not selected anymore */
2972 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2975 /* Only allow drops IN other rows */
2977 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2978 pos == GTK_TREE_VIEW_DROP_AFTER)
2982 /* Drags from the header view */
2983 if (source_widget != widget) {
2984 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2986 drag_and_drop_from_header_view (source_model,
2991 DndHelper *helper = NULL;
2993 /* Get the source model and row */
2994 gtk_tree_get_row_drag_data (selection_data,
2998 /* Create the helper */
2999 helper = g_slice_new0 (DndHelper);
3000 helper->delete_source = delete_source;
3001 helper->source_row = gtk_tree_path_copy (source_row);
3002 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3004 drag_and_drop_from_folder_view (source_model,
3010 gtk_tree_path_free (source_row);
3014 gtk_tree_path_free (dest_row);
3017 /* Finish the drag and drop */
3018 gtk_drag_finish (context, success, FALSE, time);
3022 * We define a "drag-drop" signal handler because we do not want to
3023 * use the default one, because the default one always calls
3024 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3025 * signal handler, because there we have all the information available
3026 * to know if the dnd was a success or not.
3029 drag_drop_cb (GtkWidget *widget,
3030 GdkDragContext *context,
3038 if (!context->targets)
3041 /* Check if we're dragging a folder row */
3042 target = gtk_drag_dest_find_target (widget, context, NULL);
3044 /* Request the data from the source. */
3045 gtk_drag_get_data(widget, context, target, time);
3051 * This function expands a node of a tree view if it's not expanded
3052 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3053 * does that, so that's why they're here.
3056 expand_row_timeout (gpointer data)
3058 GtkTreeView *tree_view = data;
3059 GtkTreePath *dest_path = NULL;
3060 GtkTreeViewDropPosition pos;
3061 gboolean result = FALSE;
3063 gdk_threads_enter ();
3065 gtk_tree_view_get_drag_dest_row (tree_view,
3070 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3071 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3072 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3073 gtk_tree_path_free (dest_path);
3077 gtk_tree_path_free (dest_path);
3082 gdk_threads_leave ();
3088 * This function is called whenever the pointer is moved over a widget
3089 * while dragging some data. It installs a timeout that will expand a
3090 * node of the treeview if not expanded yet. This function also calls
3091 * gdk_drag_status in order to set the suggested action that will be
3092 * used by the "drag-data-received" signal handler to know if we
3093 * should do a move or just a copy of the data.
3096 on_drag_motion (GtkWidget *widget,
3097 GdkDragContext *context,
3103 GtkTreeViewDropPosition pos;
3104 GtkTreePath *dest_row;
3105 GtkTreeModel *dest_model;
3106 ModestFolderViewPrivate *priv;
3107 GdkDragAction suggested_action;
3108 gboolean valid_location = FALSE;
3109 TnyFolderStore *folder = NULL;
3111 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3113 if (priv->timer_expander != 0) {
3114 g_source_remove (priv->timer_expander);
3115 priv->timer_expander = 0;
3118 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3123 /* Do not allow drops between folders */
3125 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3126 pos == GTK_TREE_VIEW_DROP_AFTER) {
3127 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3128 gdk_drag_status(context, 0, time);
3129 valid_location = FALSE;
3132 valid_location = TRUE;
3135 /* Check that the destination folder is writable */
3136 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3137 folder = tree_path_to_folder (dest_model, dest_row);
3138 if (folder && TNY_IS_FOLDER (folder)) {
3139 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3141 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3142 valid_location = FALSE;
3147 /* Expand the selected row after 1/2 second */
3148 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3149 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3151 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3153 /* Select the desired action. By default we pick MOVE */
3154 suggested_action = GDK_ACTION_MOVE;
3156 if (context->actions == GDK_ACTION_COPY)
3157 gdk_drag_status(context, GDK_ACTION_COPY, time);
3158 else if (context->actions == GDK_ACTION_MOVE)
3159 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3160 else if (context->actions & suggested_action)
3161 gdk_drag_status(context, suggested_action, time);
3163 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3167 g_object_unref (folder);
3169 gtk_tree_path_free (dest_row);
3171 g_signal_stop_emission_by_name (widget, "drag-motion");
3173 return valid_location;
3177 * This function sets the treeview as a source and a target for dnd
3178 * events. It also connects all the requirede signals.
3181 setup_drag_and_drop (GtkTreeView *self)
3183 /* Set up the folder view as a dnd destination. Set only the
3184 highlight flag, otherwise gtk will have a different
3186 #ifdef MODEST_TOOLKIT_HILDON2
3189 gtk_drag_dest_set (GTK_WIDGET (self),
3190 GTK_DEST_DEFAULT_HIGHLIGHT,
3191 folder_view_drag_types,
3192 G_N_ELEMENTS (folder_view_drag_types),
3193 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3195 g_signal_connect (G_OBJECT (self),
3196 "drag_data_received",
3197 G_CALLBACK (on_drag_data_received),
3201 /* Set up the treeview as a dnd source */
3202 gtk_drag_source_set (GTK_WIDGET (self),
3204 folder_view_drag_types,
3205 G_N_ELEMENTS (folder_view_drag_types),
3206 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3208 g_signal_connect (G_OBJECT (self),
3210 G_CALLBACK (on_drag_motion),
3213 g_signal_connect (G_OBJECT (self),
3215 G_CALLBACK (on_drag_data_get),
3218 g_signal_connect (G_OBJECT (self),
3220 G_CALLBACK (drag_drop_cb),
3225 * This function manages the navigation through the folders using the
3226 * keyboard or the hardware keys in the device
3229 on_key_pressed (GtkWidget *self,
3233 GtkTreeSelection *selection;
3235 GtkTreeModel *model;
3236 gboolean retval = FALSE;
3238 /* Up and Down are automatically managed by the treeview */
3239 if (event->keyval == GDK_Return) {
3240 /* Expand/Collapse the selected row */
3241 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3242 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3245 path = gtk_tree_model_get_path (model, &iter);
3247 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3248 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3250 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3251 gtk_tree_path_free (path);
3253 /* No further processing */
3261 * We listen to the changes in the local folder account name key,
3262 * because we want to show the right name in the view. The local
3263 * folder account name corresponds to the device name in the Maemo
3264 * version. We do this because we do not want to query gconf on each
3265 * tree view refresh. It's better to cache it and change whenever
3269 on_configuration_key_changed (ModestConf* conf,
3271 ModestConfEvent event,
3272 ModestConfNotificationId id,
3273 ModestFolderView *self)
3275 ModestFolderViewPrivate *priv;
3278 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3279 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3281 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3282 g_free (priv->local_account_name);
3284 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3285 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3287 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3288 MODEST_CONF_DEVICE_NAME, NULL);
3290 /* Force a redraw */
3291 #if GTK_CHECK_VERSION(2, 8, 0)
3292 GtkTreeViewColumn * tree_column;
3294 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3296 gtk_tree_view_column_queue_resize (tree_column);
3298 gtk_widget_queue_draw (GTK_WIDGET (self));
3304 modest_folder_view_set_style (ModestFolderView *self,
3305 ModestFolderViewStyle style)
3307 ModestFolderViewPrivate *priv;
3309 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3310 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3311 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3313 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3316 priv->style = style;
3320 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3321 const gchar *account_id)
3323 ModestFolderViewPrivate *priv;
3324 GtkTreeModel *model;
3326 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3328 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3330 /* This will be used by the filter_row callback,
3331 * to decided which rows to show: */
3332 if (priv->visible_account_id) {
3333 g_free (priv->visible_account_id);
3334 priv->visible_account_id = NULL;
3337 priv->visible_account_id = g_strdup (account_id);
3340 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3341 if (GTK_IS_TREE_MODEL_FILTER (model))
3342 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3344 /* Save settings to gconf */
3345 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3346 MODEST_CONF_FOLDER_VIEW_KEY);
3348 /* Notify observers */
3349 g_signal_emit (G_OBJECT(self),
3350 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3355 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3357 ModestFolderViewPrivate *priv;
3359 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3361 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3363 return (const gchar *) priv->visible_account_id;
3367 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3371 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3373 gtk_tree_model_get (model, iter,
3377 gboolean result = FALSE;
3378 if (type == TNY_FOLDER_TYPE_INBOX) {
3382 *inbox_iter = *iter;
3386 if (gtk_tree_model_iter_children (model, &child, iter)) {
3387 if (find_inbox_iter (model, &child, inbox_iter))
3391 } while (gtk_tree_model_iter_next (model, iter));
3400 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3402 GtkTreeModel *model;
3403 GtkTreeIter iter, inbox_iter;
3404 GtkTreeSelection *sel;
3405 GtkTreePath *path = NULL;
3407 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3409 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3413 expand_root_items (self);
3414 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3416 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3417 g_warning ("%s: model is empty", __FUNCTION__);
3421 if (find_inbox_iter (model, &iter, &inbox_iter))
3422 path = gtk_tree_model_get_path (model, &inbox_iter);
3424 path = gtk_tree_path_new_first ();
3426 /* Select the row and free */
3427 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3428 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3429 gtk_tree_path_free (path);
3432 gtk_widget_grab_focus (GTK_WIDGET(self));
3438 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3443 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3444 TnyFolder* a_folder;
3447 gtk_tree_model_get (model, iter,
3448 INSTANCE_COLUMN, &a_folder,
3454 if (folder == a_folder) {
3455 g_object_unref (a_folder);
3456 *folder_iter = *iter;
3459 g_object_unref (a_folder);
3461 if (gtk_tree_model_iter_children (model, &child, iter)) {
3462 if (find_folder_iter (model, &child, folder_iter, folder))
3466 } while (gtk_tree_model_iter_next (model, iter));
3471 #ifndef MODEST_TOOLKIT_HILDON2
3473 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3476 ModestFolderView *self)
3478 ModestFolderViewPrivate *priv = NULL;
3479 GtkTreeSelection *sel;
3480 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3481 GObject *instance = NULL;
3483 if (!MODEST_IS_FOLDER_VIEW(self))
3486 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3488 priv->reexpand = TRUE;
3490 gtk_tree_model_get (tree_model, iter,
3492 INSTANCE_COLUMN, &instance,
3498 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3499 priv->folder_to_select = g_object_ref (instance);
3501 g_object_unref (instance);
3503 if (priv->folder_to_select) {
3505 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3508 path = gtk_tree_model_get_path (tree_model, iter);
3509 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3511 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3513 gtk_tree_selection_select_iter (sel, iter);
3514 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3516 gtk_tree_path_free (path);
3520 modest_folder_view_disable_next_folder_selection (self);
3522 /* Refilter the model */
3523 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3529 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3531 ModestFolderViewPrivate *priv;
3533 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3535 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3537 if (priv->folder_to_select)
3538 g_object_unref(priv->folder_to_select);
3540 priv->folder_to_select = NULL;
3544 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3545 gboolean after_change)
3547 GtkTreeModel *model;
3548 GtkTreeIter iter, folder_iter;
3549 GtkTreeSelection *sel;
3550 ModestFolderViewPrivate *priv = NULL;
3552 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3553 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3555 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3558 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3559 gtk_tree_selection_unselect_all (sel);
3561 if (priv->folder_to_select)
3562 g_object_unref(priv->folder_to_select);
3563 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3567 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3572 /* Refilter the model, before selecting the folder */
3573 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3575 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3576 g_warning ("%s: model is empty", __FUNCTION__);
3580 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3583 path = gtk_tree_model_get_path (model, &folder_iter);
3584 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3586 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3587 gtk_tree_selection_select_iter (sel, &folder_iter);
3588 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3590 gtk_tree_path_free (path);
3598 modest_folder_view_copy_selection (ModestFolderView *self)
3600 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3602 /* Copy selection */
3603 _clipboard_set_selected_data (self, FALSE);
3607 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3609 ModestFolderViewPrivate *priv = NULL;
3610 GtkTreeModel *model = NULL;
3611 const gchar **hidding = NULL;
3612 guint i, n_selected;
3614 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3615 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3617 /* Copy selection */
3618 if (!_clipboard_set_selected_data (folder_view, TRUE))
3621 /* Get hidding ids */
3622 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3624 /* Clear hidding array created by previous cut operation */
3625 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3627 /* Copy hidding array */
3628 priv->n_selected = n_selected;
3629 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3630 for (i=0; i < n_selected; i++)
3631 priv->hidding_ids[i] = g_strdup(hidding[i]);
3633 /* Hide cut folders */
3634 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3635 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3639 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3640 ModestFolderView *folder_view_dst)
3642 GtkTreeModel *filter_model = NULL;
3643 GtkTreeModel *model = NULL;
3644 GtkTreeModel *new_filter_model = NULL;
3646 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3647 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3650 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3651 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3653 /* Build new filter model */
3654 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3655 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3659 /* Set copied model */
3660 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3661 #ifndef MODEST_TOOLKIT_HILDON2
3662 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3663 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3667 g_object_unref (new_filter_model);
3671 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3674 GtkTreeModel *model = NULL;
3675 ModestFolderViewPrivate* priv;
3677 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3679 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3680 priv->show_non_move = show;
3681 /* modest_folder_view_update_model(folder_view, */
3682 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3684 /* Hide special folders */
3685 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3686 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3687 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3691 /* Returns FALSE if it did not selected anything */
3693 _clipboard_set_selected_data (ModestFolderView *folder_view,
3696 ModestFolderViewPrivate *priv = NULL;
3697 TnyFolderStore *folder = NULL;
3698 gboolean retval = FALSE;
3700 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3701 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3703 /* Set selected data on clipboard */
3704 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3705 folder = modest_folder_view_get_selected (folder_view);
3707 /* Do not allow to select an account */
3708 if (TNY_IS_FOLDER (folder)) {
3709 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3714 g_object_unref (folder);
3720 _clear_hidding_filter (ModestFolderView *folder_view)
3722 ModestFolderViewPrivate *priv;
3725 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3726 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3728 if (priv->hidding_ids != NULL) {
3729 for (i=0; i < priv->n_selected; i++)
3730 g_free (priv->hidding_ids[i]);
3731 g_free(priv->hidding_ids);
3737 on_display_name_changed (ModestAccountMgr *mgr,
3738 const gchar *account,
3741 ModestFolderView *self;
3743 self = MODEST_FOLDER_VIEW (user_data);
3745 /* Force a redraw */
3746 #if GTK_CHECK_VERSION(2, 8, 0)
3747 GtkTreeViewColumn * tree_column;
3749 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3751 gtk_tree_view_column_queue_resize (tree_column);
3753 gtk_widget_queue_draw (GTK_WIDGET (self));
3758 modest_folder_view_set_cell_style (ModestFolderView *self,
3759 ModestFolderViewCellStyle cell_style)
3761 ModestFolderViewPrivate *priv = NULL;
3763 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3764 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3766 priv->cell_style = cell_style;
3768 g_object_set (G_OBJECT (priv->messages_renderer),
3769 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3772 gtk_widget_queue_draw (GTK_WIDGET (self));
3776 update_style (ModestFolderView *self)
3778 ModestFolderViewPrivate *priv;
3779 GdkColor style_color;
3780 PangoAttrList *attr_list;
3782 PangoAttribute *attr;
3784 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3785 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3789 attr_list = pango_attr_list_new ();
3790 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3791 gdk_color_parse ("grey", &style_color);
3793 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3794 pango_attr_list_insert (attr_list, attr);
3797 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3799 "SmallSystemFont", NULL,
3802 attr = pango_attr_font_desc_new (pango_font_description_copy
3803 (style->font_desc));
3804 pango_attr_list_insert (attr_list, attr);
3806 g_object_set (G_OBJECT (priv->messages_renderer),
3807 "foreground-gdk", &style_color,
3808 "foreground-set", TRUE,
3809 "attributes", attr_list,
3811 pango_attr_list_unref (attr_list);
3816 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3818 if (strcmp ("style", spec->name) == 0) {
3819 update_style (MODEST_FOLDER_VIEW (obj));
3820 gtk_widget_queue_draw (GTK_WIDGET (obj));
3825 modest_folder_view_set_filter (ModestFolderView *self,
3826 ModestFolderViewFilter filter)
3828 ModestFolderViewPrivate *priv;
3829 GtkTreeModel *filter_model;
3831 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3832 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3834 priv->filter |= filter;
3836 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3837 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3838 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3843 modest_folder_view_unset_filter (ModestFolderView *self,
3844 ModestFolderViewFilter filter)
3846 ModestFolderViewPrivate *priv;
3847 GtkTreeModel *filter_model;
3849 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3850 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3852 priv->filter &= ~filter;
3854 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3855 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3856 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3861 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3862 ModestTnyFolderRules rules)
3864 GtkTreeModel *filter_model;
3866 gboolean fulfil = FALSE;
3868 if (!get_inner_models (self, &filter_model, NULL, NULL))
3871 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3875 TnyFolderStore *folder;
3877 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3879 if (TNY_IS_FOLDER (folder)) {
3880 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3881 /* Folder rules are negative: non_writable, non_deletable... */
3882 if (!(folder_rules & rules))
3885 g_object_unref (folder);
3888 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3894 modest_folder_view_set_list_to_move (ModestFolderView *self,
3897 ModestFolderViewPrivate *priv;
3899 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3900 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3902 if (priv->list_to_move)
3903 g_object_unref (priv->list_to_move);
3906 g_object_ref (list);
3908 priv->list_to_move = list;
3912 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3914 ModestFolderViewPrivate *priv;
3916 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3917 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3920 g_free (priv->mailbox);
3922 priv->mailbox = g_strdup (mailbox);
3924 /* Notify observers */
3925 g_signal_emit (G_OBJECT(self),
3926 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3927 priv->visible_account_id);
3931 modest_folder_view_get_mailbox (ModestFolderView *self)
3933 ModestFolderViewPrivate *priv;
3935 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3936 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3938 return (const gchar *) priv->mailbox;