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));
547 *item_name = new_item_name;
551 if (!is_special || multiaccount) {
552 TnyAccount *account = tny_folder_get_account (folder);
553 const gchar *folder_name;
554 gboolean concat_folder_name = FALSE;
557 /* Should not happen */
561 /* convert parent folders to dots */
562 convert_parent_folders_to_dots (item_name);
564 folder_name = tny_folder_get_name (folder);
565 if (g_str_has_suffix (*item_name, folder_name)) {
566 gchar *offset = g_strrstr (*item_name, folder_name);
568 concat_folder_name = TRUE;
571 buffer = g_string_new ("");
573 buffer = g_string_append (buffer, *item_name);
574 if (concat_folder_name) {
575 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
576 buffer = g_string_append (buffer, folder_name);
577 if (bold) buffer = g_string_append (buffer, "</span>");
580 g_object_unref (account);
582 *item_name = g_string_free (buffer, FALSE);
590 text_cell_data (GtkTreeViewColumn *column,
591 GtkCellRenderer *renderer,
592 GtkTreeModel *tree_model,
596 ModestFolderViewPrivate *priv;
597 GObject *rendobj = (GObject *) renderer;
599 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
600 GObject *instance = NULL;
601 gboolean use_markup = FALSE;
603 gtk_tree_model_get (tree_model, iter,
606 INSTANCE_COLUMN, &instance,
608 if (!fname || !instance)
611 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
612 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
614 gchar *item_name = NULL;
615 gint item_weight = 400;
617 if (type != TNY_FOLDER_TYPE_ROOT) {
621 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
622 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
623 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
624 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
626 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
629 /* Sometimes an special folder is reported by the server as
630 NORMAL, like some versions of Dovecot */
631 if (type == TNY_FOLDER_TYPE_NORMAL ||
632 type == TNY_FOLDER_TYPE_UNKNOWN) {
633 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
637 /* note: we cannot reliably get the counts from the
638 * tree model, we need to use explicit calls on
639 * tny_folder for some reason. Select the number to
640 * show: the unread or unsent messages. in case of
641 * outbox/drafts, show all */
642 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
643 (type == TNY_FOLDER_TYPE_OUTBOX) ||
644 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
645 number = tny_folder_get_all_count (TNY_FOLDER(instance));
648 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
652 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
653 item_name = g_strdup (fname);
660 /* Use bold font style if there are unread or unset messages */
662 item_name = g_strdup_printf ("%s (%d)", fname, number);
665 item_name = g_strdup (fname);
670 } else if (TNY_IS_ACCOUNT (instance)) {
671 /* If it's a server account */
672 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
673 item_name = g_strdup (priv->local_account_name);
675 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
676 /* fname is only correct when the items are first
677 * added to the model, not when the account is
678 * changed later, so get the name from the account
680 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
683 item_name = g_strdup (fname);
689 item_name = g_strdup ("unknown");
691 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
692 gboolean multiaccount;
694 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
695 /* Convert item_name to markup */
696 format_compact_style (&item_name, instance, priv->mailbox,
698 multiaccount, &use_markup);
701 if (item_name && item_weight) {
702 /* Set the name in the treeview cell: */
704 g_object_set (rendobj, "markup", item_name, NULL);
706 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
708 /* Notify display name observers */
709 /* TODO: What listens for this signal, and how can it use only the new name? */
710 if (((GObject *) priv->cur_folder_store) == instance) {
711 g_signal_emit (G_OBJECT(self),
712 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
719 /* If it is a Memory card account, make sure that we have the correct name.
720 * This function will be trigerred again when the name has been retrieved: */
721 if (TNY_IS_STORE_ACCOUNT (instance) &&
722 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
724 /* Get the account name asynchronously: */
725 GetMmcAccountNameData *callback_data =
726 g_slice_new0(GetMmcAccountNameData);
727 callback_data->self = self;
729 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
731 callback_data->previous_name = g_strdup (name);
733 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
734 on_get_mmc_account_name, callback_data);
738 g_object_unref (G_OBJECT (instance));
744 messages_cell_data (GtkTreeViewColumn *column,
745 GtkCellRenderer *renderer,
746 GtkTreeModel *tree_model,
750 ModestFolderView *self;
751 ModestFolderViewPrivate *priv;
752 GObject *rendobj = (GObject *) renderer;
753 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
754 GObject *instance = NULL;
755 gchar *item_name = NULL;
757 gtk_tree_model_get (tree_model, iter,
759 INSTANCE_COLUMN, &instance,
764 self = MODEST_FOLDER_VIEW (data);
765 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
768 if (type != TNY_FOLDER_TYPE_ROOT) {
772 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
773 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
774 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
776 /* Sometimes an special folder is reported by the server as
777 NORMAL, like some versions of Dovecot */
778 if (type == TNY_FOLDER_TYPE_NORMAL ||
779 type == TNY_FOLDER_TYPE_UNKNOWN) {
780 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
784 /* note: we cannot reliably get the counts from the tree model, we need
785 * to use explicit calls on tny_folder for some reason.
787 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
788 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
789 (type == TNY_FOLDER_TYPE_OUTBOX) ||
790 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
791 number = tny_folder_get_all_count (TNY_FOLDER(instance));
794 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
798 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
800 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
808 item_name = g_strdup ("");
811 /* Set the name in the treeview cell: */
812 g_object_set (rendobj,"text", item_name, NULL);
820 g_object_unref (G_OBJECT (instance));
826 GdkPixbuf *pixbuf_open;
827 GdkPixbuf *pixbuf_close;
831 static inline GdkPixbuf *
832 get_composite_pixbuf (const gchar *icon_name,
834 GdkPixbuf *base_pixbuf)
836 GdkPixbuf *emblem, *retval = NULL;
838 emblem = modest_platform_get_icon (icon_name, size);
840 retval = gdk_pixbuf_copy (base_pixbuf);
841 gdk_pixbuf_composite (emblem, retval, 0, 0,
842 MIN (gdk_pixbuf_get_width (emblem),
843 gdk_pixbuf_get_width (retval)),
844 MIN (gdk_pixbuf_get_height (emblem),
845 gdk_pixbuf_get_height (retval)),
846 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
847 g_object_unref (emblem);
852 static inline ThreePixbufs *
853 get_composite_icons (const gchar *icon_code,
855 GdkPixbuf **pixbuf_open,
856 GdkPixbuf **pixbuf_close)
858 ThreePixbufs *retval;
861 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
864 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
869 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
873 retval = g_slice_new0 (ThreePixbufs);
875 retval->pixbuf = g_object_ref (*pixbuf);
877 retval->pixbuf_open = g_object_ref (*pixbuf_open);
879 retval->pixbuf_close = g_object_ref (*pixbuf_close);
884 static inline ThreePixbufs*
885 get_folder_icons (TnyFolderType type, GObject *instance)
887 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
888 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
889 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
890 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
891 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
893 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
894 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
895 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
896 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
897 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
899 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
900 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
901 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
902 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
903 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
905 ThreePixbufs *retval = NULL;
907 /* Sometimes an special folder is reported by the server as
908 NORMAL, like some versions of Dovecot */
909 if (type == TNY_FOLDER_TYPE_NORMAL ||
910 type == TNY_FOLDER_TYPE_UNKNOWN) {
911 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
914 /* It's not enough with check the folder type. We need to
915 ensure that we're not giving a special folder icon to a
916 normal folder with the same name than a special folder */
917 if (TNY_IS_FOLDER (instance) &&
918 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
919 type = TNY_FOLDER_TYPE_NORMAL;
921 /* Remote folders should not be treated as special folders */
922 if (TNY_IS_FOLDER_STORE (instance) &&
923 !TNY_IS_ACCOUNT (instance) &&
924 type != TNY_FOLDER_TYPE_INBOX &&
925 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
926 #ifdef MODEST_TOOLKIT_HILDON2
927 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
930 &anorm_pixbuf_close);
932 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
935 &normal_pixbuf_close);
941 case TNY_FOLDER_TYPE_INVALID:
942 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
945 case TNY_FOLDER_TYPE_ROOT:
946 if (TNY_IS_ACCOUNT (instance)) {
948 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
949 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
952 &avirt_pixbuf_close);
954 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
956 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
957 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
962 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
965 &anorm_pixbuf_close);
970 case TNY_FOLDER_TYPE_INBOX:
971 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
974 &inbox_pixbuf_close);
976 case TNY_FOLDER_TYPE_OUTBOX:
977 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
980 &outbox_pixbuf_close);
982 case TNY_FOLDER_TYPE_JUNK:
983 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
988 case TNY_FOLDER_TYPE_SENT:
989 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
994 case TNY_FOLDER_TYPE_TRASH:
995 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
998 &trash_pixbuf_close);
1000 case TNY_FOLDER_TYPE_DRAFTS:
1001 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1004 &draft_pixbuf_close);
1006 case TNY_FOLDER_TYPE_ARCHIVE:
1007 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1012 case TNY_FOLDER_TYPE_NORMAL:
1014 /* Memory card folders could have an special icon */
1015 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1016 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1021 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1023 &normal_pixbuf_open,
1024 &normal_pixbuf_close);
1033 free_pixbufs (ThreePixbufs *pixbufs)
1035 if (pixbufs->pixbuf)
1036 g_object_unref (pixbufs->pixbuf);
1037 if (pixbufs->pixbuf_open)
1038 g_object_unref (pixbufs->pixbuf_open);
1039 if (pixbufs->pixbuf_close)
1040 g_object_unref (pixbufs->pixbuf_close);
1041 g_slice_free (ThreePixbufs, pixbufs);
1045 icon_cell_data (GtkTreeViewColumn *column,
1046 GtkCellRenderer *renderer,
1047 GtkTreeModel *tree_model,
1051 GObject *rendobj = NULL, *instance = NULL;
1052 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1053 gboolean has_children;
1054 ThreePixbufs *pixbufs;
1056 rendobj = (GObject *) renderer;
1058 gtk_tree_model_get (tree_model, iter,
1060 INSTANCE_COLUMN, &instance,
1066 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1067 pixbufs = get_folder_icons (type, instance);
1068 g_object_unref (instance);
1071 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1074 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1075 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1078 free_pixbufs (pixbufs);
1082 add_columns (GtkWidget *treeview)
1084 GtkTreeViewColumn *column;
1085 GtkCellRenderer *renderer;
1086 GtkTreeSelection *sel;
1087 ModestFolderViewPrivate *priv;
1089 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1092 column = gtk_tree_view_column_new ();
1094 /* Set icon and text render function */
1095 renderer = gtk_cell_renderer_pixbuf_new();
1096 #ifdef MODEST_TOOLKIT_HILDON2
1097 g_object_set (renderer,
1098 "xpad", MODEST_MARGIN_DEFAULT,
1099 "ypad", MODEST_MARGIN_DEFAULT,
1102 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1103 gtk_tree_view_column_set_cell_data_func(column, renderer,
1104 icon_cell_data, treeview, NULL);
1106 renderer = gtk_cell_renderer_text_new();
1107 g_object_set (renderer,
1108 #ifdef MODEST_TOOLKIT_HILDON2
1109 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1110 "ypad", MODEST_MARGIN_DEFAULT,
1112 "ellipsize", PANGO_ELLIPSIZE_END,
1114 "ellipsize-set", TRUE, NULL);
1115 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1116 gtk_tree_view_column_set_cell_data_func(column, renderer,
1117 text_cell_data, treeview, NULL);
1119 priv->messages_renderer = gtk_cell_renderer_text_new ();
1120 g_object_set (priv->messages_renderer,
1121 #ifdef MODEST_TOOLKIT_HILDON2
1123 "ypad", MODEST_MARGIN_DEFAULT,
1124 "xpad", MODEST_MARGIN_DOUBLE,
1126 "scale", PANGO_SCALE_X_SMALL,
1129 "alignment", PANGO_ALIGN_RIGHT,
1133 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1134 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1135 messages_cell_data, treeview, NULL);
1137 /* Set selection mode */
1138 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1139 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1141 /* Set treeview appearance */
1142 gtk_tree_view_column_set_spacing (column, 2);
1143 gtk_tree_view_column_set_resizable (column, TRUE);
1144 gtk_tree_view_column_set_fixed_width (column, TRUE);
1145 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1146 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1149 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1153 modest_folder_view_init (ModestFolderView *obj)
1155 ModestFolderViewPrivate *priv;
1158 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1160 priv->timer_expander = 0;
1161 priv->account_store = NULL;
1163 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1164 priv->cur_folder_store = NULL;
1165 priv->visible_account_id = NULL;
1166 priv->mailbox = NULL;
1167 priv->folder_to_select = NULL;
1168 priv->outbox_deleted_handler = 0;
1169 priv->reexpand = TRUE;
1171 /* Initialize the local account name */
1172 conf = modest_runtime_get_conf();
1173 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1175 /* Init email clipboard */
1176 priv->clipboard = modest_runtime_get_email_clipboard ();
1177 priv->hidding_ids = NULL;
1178 priv->n_selected = 0;
1179 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1180 priv->reselect = FALSE;
1181 priv->show_non_move = TRUE;
1182 priv->list_to_move = NULL;
1184 /* Build treeview */
1185 add_columns (GTK_WIDGET (obj));
1187 /* Setup drag and drop */
1188 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1190 /* Connect signals */
1191 g_signal_connect (G_OBJECT (obj),
1193 G_CALLBACK (on_key_pressed), NULL);
1195 priv->display_name_changed_signal =
1196 g_signal_connect (modest_runtime_get_account_mgr (),
1197 "display_name_changed",
1198 G_CALLBACK (on_display_name_changed),
1202 * Track changes in the local account name (in the device it
1203 * will be the device name)
1205 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1207 G_CALLBACK(on_configuration_key_changed),
1211 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1217 tny_account_store_view_init (gpointer g, gpointer iface_data)
1219 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1221 klass->set_account_store = modest_folder_view_set_account_store;
1225 modest_folder_view_finalize (GObject *obj)
1227 ModestFolderViewPrivate *priv;
1228 GtkTreeSelection *sel;
1229 TnyAccount *local_account;
1231 g_return_if_fail (obj);
1233 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1235 if (priv->timer_expander != 0) {
1236 g_source_remove (priv->timer_expander);
1237 priv->timer_expander = 0;
1240 local_account = (TnyAccount *)
1241 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1242 if (local_account) {
1243 if (g_signal_handler_is_connected (local_account,
1244 priv->outbox_deleted_handler))
1245 g_signal_handler_disconnect (local_account,
1246 priv->outbox_deleted_handler);
1247 g_object_unref (local_account);
1250 if (priv->account_store) {
1251 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1252 priv->account_inserted_signal);
1253 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1254 priv->account_removed_signal);
1255 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1256 priv->account_changed_signal);
1257 g_object_unref (G_OBJECT(priv->account_store));
1258 priv->account_store = NULL;
1261 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1262 priv->display_name_changed_signal)) {
1263 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1264 priv->display_name_changed_signal);
1265 priv->display_name_changed_signal = 0;
1269 g_object_unref (G_OBJECT (priv->query));
1273 if (priv->folder_to_select) {
1274 g_object_unref (G_OBJECT(priv->folder_to_select));
1275 priv->folder_to_select = NULL;
1278 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1280 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1282 g_free (priv->local_account_name);
1283 g_free (priv->visible_account_id);
1284 g_free (priv->mailbox);
1286 if (priv->conf_key_signal) {
1287 g_signal_handler_disconnect (modest_runtime_get_conf (),
1288 priv->conf_key_signal);
1289 priv->conf_key_signal = 0;
1292 if (priv->cur_folder_store) {
1293 g_object_unref (priv->cur_folder_store);
1294 priv->cur_folder_store = NULL;
1297 if (priv->list_to_move) {
1298 g_object_unref (priv->list_to_move);
1299 priv->list_to_move = NULL;
1302 /* Clear hidding array created by cut operation */
1303 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1305 G_OBJECT_CLASS(parent_class)->finalize (obj);
1310 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1312 ModestFolderViewPrivate *priv;
1315 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1316 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1318 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1319 device = tny_account_store_get_device (account_store);
1321 if (G_UNLIKELY (priv->account_store)) {
1323 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1324 priv->account_inserted_signal))
1325 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1326 priv->account_inserted_signal);
1327 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1328 priv->account_removed_signal))
1329 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1330 priv->account_removed_signal);
1331 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1332 priv->account_changed_signal))
1333 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1334 priv->account_changed_signal);
1335 g_object_unref (G_OBJECT (priv->account_store));
1338 priv->account_store = g_object_ref (G_OBJECT (account_store));
1340 priv->account_removed_signal =
1341 g_signal_connect (G_OBJECT(account_store), "account_removed",
1342 G_CALLBACK (on_account_removed), self);
1344 priv->account_inserted_signal =
1345 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1346 G_CALLBACK (on_account_inserted), self);
1348 priv->account_changed_signal =
1349 g_signal_connect (G_OBJECT(account_store), "account_changed",
1350 G_CALLBACK (on_account_changed), self);
1352 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1353 priv->reselect = FALSE;
1354 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1356 g_object_unref (G_OBJECT (device));
1360 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1363 ModestFolderView *self;
1364 GtkTreeModel *model, *filter_model;
1367 self = MODEST_FOLDER_VIEW (user_data);
1369 if (!get_inner_models (self, &filter_model, NULL, &model))
1372 /* Remove outbox from model */
1373 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1374 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1375 g_object_unref (outbox);
1378 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1382 on_account_inserted (TnyAccountStore *account_store,
1383 TnyAccount *account,
1386 ModestFolderViewPrivate *priv;
1387 GtkTreeModel *model, *filter_model;
1389 /* Ignore transport account insertions, we're not showing them
1390 in the folder view */
1391 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1394 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1397 /* If we're adding a new account, and there is no previous
1398 one, we need to select the visible server account */
1399 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1400 !priv->visible_account_id)
1401 modest_widget_memory_restore (modest_runtime_get_conf(),
1402 G_OBJECT (user_data),
1403 MODEST_CONF_FOLDER_VIEW_KEY);
1407 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1408 &filter_model, NULL, &model))
1411 /* Insert the account in the model */
1412 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1414 /* When the model is a list store (plain representation) the
1415 outbox is not a child of any account so we have to manually
1416 delete it because removing the local folders account won't
1417 delete it (because tny_folder_get_account() is not defined
1418 for a merge folder */
1419 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1420 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1422 priv->outbox_deleted_handler =
1423 g_signal_connect (account,
1425 G_CALLBACK (on_outbox_deleted_cb),
1429 /* Refilter the model */
1430 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1435 same_account_selected (ModestFolderView *self,
1436 TnyAccount *account)
1438 ModestFolderViewPrivate *priv;
1439 gboolean same_account = FALSE;
1441 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1443 if (priv->cur_folder_store) {
1444 TnyAccount *selected_folder_account = NULL;
1446 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1447 selected_folder_account =
1448 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1450 selected_folder_account =
1451 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1454 if (selected_folder_account == account)
1455 same_account = TRUE;
1457 g_object_unref (selected_folder_account);
1459 return same_account;
1464 * Selects the first inbox or the local account in an idle
1467 on_idle_select_first_inbox_or_local (gpointer user_data)
1469 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1471 gdk_threads_enter ();
1472 modest_folder_view_select_first_inbox_or_local (self);
1473 gdk_threads_leave ();
1479 on_account_changed (TnyAccountStore *account_store,
1480 TnyAccount *tny_account,
1483 ModestFolderView *self;
1484 ModestFolderViewPrivate *priv;
1485 GtkTreeModel *model, *filter_model;
1486 GtkTreeSelection *sel;
1487 gboolean same_account;
1489 /* Ignore transport account insertions, we're not showing them
1490 in the folder view */
1491 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1494 self = MODEST_FOLDER_VIEW (user_data);
1495 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1497 /* Get the inner model */
1498 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1499 &filter_model, NULL, &model))
1502 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1504 /* Invalidate the cur_folder_store only if the selected folder
1505 belongs to the account that is being removed */
1506 same_account = same_account_selected (self, tny_account);
1508 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1509 gtk_tree_selection_unselect_all (sel);
1512 /* Remove the account from the model */
1513 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1515 /* Insert the account in the model */
1516 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1518 /* Refilter the model */
1519 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1521 /* Select the first INBOX if the currently selected folder
1522 belongs to the account that is being deleted */
1523 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1524 g_idle_add (on_idle_select_first_inbox_or_local, self);
1528 on_account_removed (TnyAccountStore *account_store,
1529 TnyAccount *account,
1532 ModestFolderView *self = NULL;
1533 ModestFolderViewPrivate *priv;
1534 GtkTreeModel *model, *filter_model;
1535 GtkTreeSelection *sel = NULL;
1536 gboolean same_account = FALSE;
1538 /* Ignore transport account removals, we're not showing them
1539 in the folder view */
1540 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1543 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1544 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1548 self = MODEST_FOLDER_VIEW (user_data);
1549 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1551 /* Invalidate the cur_folder_store only if the selected folder
1552 belongs to the account that is being removed */
1553 same_account = same_account_selected (self, account);
1555 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1556 gtk_tree_selection_unselect_all (sel);
1559 /* Invalidate row to select only if the folder to select
1560 belongs to the account that is being removed*/
1561 if (priv->folder_to_select) {
1562 TnyAccount *folder_to_select_account = NULL;
1564 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1565 if (folder_to_select_account == account) {
1566 modest_folder_view_disable_next_folder_selection (self);
1567 g_object_unref (priv->folder_to_select);
1568 priv->folder_to_select = NULL;
1570 g_object_unref (folder_to_select_account);
1573 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1574 &filter_model, NULL, &model))
1577 /* Disconnect the signal handler */
1578 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1579 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1580 if (g_signal_handler_is_connected (account,
1581 priv->outbox_deleted_handler))
1582 g_signal_handler_disconnect (account,
1583 priv->outbox_deleted_handler);
1586 /* Remove the account from the model */
1587 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1589 /* If the removed account is the currently viewed one then
1590 clear the configuration value. The new visible account will be the default account */
1591 if (priv->visible_account_id &&
1592 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1594 /* Clear the current visible account_id */
1595 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1596 modest_folder_view_set_mailbox (self, NULL);
1598 /* Call the restore method, this will set the new visible account */
1599 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1600 MODEST_CONF_FOLDER_VIEW_KEY);
1603 /* Refilter the model */
1604 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1606 /* Select the first INBOX if the currently selected folder
1607 belongs to the account that is being deleted */
1609 g_idle_add (on_idle_select_first_inbox_or_local, self);
1613 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1615 GtkTreeViewColumn *col;
1617 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1619 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1621 g_printerr ("modest: failed get column for title\n");
1625 gtk_tree_view_column_set_title (col, title);
1626 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1631 modest_folder_view_on_map (ModestFolderView *self,
1632 GdkEventExpose *event,
1635 ModestFolderViewPrivate *priv;
1637 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1639 /* This won't happen often */
1640 if (G_UNLIKELY (priv->reselect)) {
1641 /* Select the first inbox or the local account if not found */
1643 /* TODO: this could cause a lock at startup, so we
1644 comment it for the moment. We know that this will
1645 be a bug, because the INBOX is not selected, but we
1646 need to rewrite some parts of Modest to avoid the
1647 deathlock situation */
1648 /* TODO: check if this is still the case */
1649 priv->reselect = FALSE;
1650 modest_folder_view_select_first_inbox_or_local (self);
1651 /* Notify the display name observers */
1652 g_signal_emit (G_OBJECT(self),
1653 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1657 if (priv->reexpand) {
1658 expand_root_items (self);
1659 priv->reexpand = FALSE;
1666 modest_folder_view_new (TnyFolderStoreQuery *query)
1669 ModestFolderViewPrivate *priv;
1670 GtkTreeSelection *sel;
1672 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1673 #ifdef MODEST_TOOLKIT_HILDON2
1674 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1677 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1680 priv->query = g_object_ref (query);
1682 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1683 priv->changed_signal = g_signal_connect (sel, "changed",
1684 G_CALLBACK (on_selection_changed), self);
1686 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1688 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1690 return GTK_WIDGET(self);
1693 /* this feels dirty; any other way to expand all the root items? */
1695 expand_root_items (ModestFolderView *self)
1698 GtkTreeModel *model;
1701 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1702 path = gtk_tree_path_new_first ();
1704 /* all folders should have child items, so.. */
1706 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1707 gtk_tree_path_next (path);
1708 } while (gtk_tree_model_get_iter (model, &iter, path));
1710 gtk_tree_path_free (path);
1714 is_parent_of (TnyFolder *a, TnyFolder *b)
1717 gboolean retval = FALSE;
1719 a_id = tny_folder_get_id (a);
1721 gchar *string_to_match;
1724 string_to_match = g_strconcat (a_id, "/", NULL);
1725 b_id = tny_folder_get_id (b);
1726 retval = g_str_has_prefix (b_id, string_to_match);
1727 g_free (string_to_match);
1733 typedef struct _ForeachFolderInfo {
1736 } ForeachFolderInfo;
1739 foreach_folder_with_id (GtkTreeModel *model,
1744 ForeachFolderInfo *info;
1747 info = (ForeachFolderInfo *) data;
1748 gtk_tree_model_get (model, iter,
1749 INSTANCE_COLUMN, &instance,
1752 if (TNY_IS_FOLDER (instance)) {
1755 id = tny_folder_get_id (TNY_FOLDER (instance));
1757 collate = g_utf8_collate_key (id, -1);
1758 info->found = !strcmp (info->needle, collate);
1769 has_folder_with_id (ModestFolderView *self, const gchar *id)
1771 GtkTreeModel *model;
1772 ForeachFolderInfo info = {NULL, FALSE};
1774 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1775 info.needle = g_utf8_collate_key (id, -1);
1777 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1778 g_free (info.needle);
1784 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1787 gboolean retval = FALSE;
1789 a_id = tny_folder_get_id (a);
1792 b_id = tny_folder_get_id (b);
1795 const gchar *last_bar;
1796 gchar *string_to_match;
1797 last_bar = g_strrstr (b_id, "/");
1802 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1803 retval = has_folder_with_id (self, string_to_match);
1804 g_free (string_to_match);
1812 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1814 ModestFolderViewPrivate *priv;
1815 TnyIterator *iterator;
1816 gboolean retval = TRUE;
1818 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1819 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1821 for (iterator = tny_list_create_iterator (priv->list_to_move);
1822 retval && !tny_iterator_is_done (iterator);
1823 tny_iterator_next (iterator)) {
1825 instance = tny_iterator_get_current (iterator);
1826 if (instance == (GObject *) folder) {
1828 } else if (TNY_IS_FOLDER (instance)) {
1829 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1831 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1834 g_object_unref (instance);
1836 g_object_unref (iterator);
1843 * We use this function to implement the
1844 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1845 * account in this case, and the local folders.
1848 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1850 ModestFolderViewPrivate *priv;
1851 gboolean retval = TRUE;
1852 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1853 GObject *instance = NULL;
1854 const gchar *id = NULL;
1856 gboolean found = FALSE;
1857 gboolean cleared = FALSE;
1858 ModestTnyFolderRules rules = 0;
1861 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1862 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1864 gtk_tree_model_get (model, iter,
1865 NAME_COLUMN, &fname,
1867 INSTANCE_COLUMN, &instance,
1870 /* Do not show if there is no instance, this could indeed
1871 happen when the model is being modified while it's being
1872 drawn. This could occur for example when moving folders
1879 if (TNY_IS_ACCOUNT (instance)) {
1880 TnyAccount *acc = TNY_ACCOUNT (instance);
1881 const gchar *account_id = tny_account_get_id (acc);
1883 /* If it isn't a special folder,
1884 * don't show it unless it is the visible account: */
1885 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1886 !modest_tny_account_is_virtual_local_folders (acc) &&
1887 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1889 /* Show only the visible account id */
1890 if (priv->visible_account_id) {
1891 if (strcmp (account_id, priv->visible_account_id))
1898 /* Never show these to the user. They are merged into one folder
1899 * in the local-folders account instead: */
1900 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1903 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1904 /* Only show special folders for current account if needed */
1905 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1906 TnyAccount *account;
1908 account = tny_folder_get_account (TNY_FOLDER (instance));
1910 if (TNY_IS_ACCOUNT (account)) {
1911 const gchar *account_id = tny_account_get_id (account);
1913 if (!modest_tny_account_is_virtual_local_folders (account) &&
1914 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1915 /* Show only the visible account id */
1916 if (priv->visible_account_id) {
1917 if (strcmp (account_id, priv->visible_account_id)) {
1919 } else if (priv->mailbox) {
1920 /* Filter mailboxes */
1921 if (!g_str_has_prefix (fname, priv->mailbox)) {
1923 } else if (!strcmp (fname, priv->mailbox)) {
1924 /* Hide mailbox parent */
1930 g_object_unref (account);
1937 /* Check hiding (if necessary) */
1938 cleared = modest_email_clipboard_cleared (priv->clipboard);
1939 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1940 id = tny_folder_get_id (TNY_FOLDER(instance));
1941 if (priv->hidding_ids != NULL)
1942 for (i=0; i < priv->n_selected && !found; i++)
1943 if (priv->hidding_ids[i] != NULL && id != NULL)
1944 found = (!strcmp (priv->hidding_ids[i], id));
1949 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1950 folder as no message can be move there according to UI specs */
1951 if (retval && !priv->show_non_move) {
1952 if (priv->list_to_move &&
1953 tny_list_get_length (priv->list_to_move) > 0 &&
1954 TNY_IS_FOLDER (instance)) {
1955 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1957 if (retval && TNY_IS_FOLDER (instance) &&
1958 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1960 case TNY_FOLDER_TYPE_OUTBOX:
1961 case TNY_FOLDER_TYPE_SENT:
1962 case TNY_FOLDER_TYPE_DRAFTS:
1965 case TNY_FOLDER_TYPE_UNKNOWN:
1966 case TNY_FOLDER_TYPE_NORMAL:
1967 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1968 if (type == TNY_FOLDER_TYPE_INVALID)
1969 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1971 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1972 type == TNY_FOLDER_TYPE_SENT
1973 || type == TNY_FOLDER_TYPE_DRAFTS)
1982 /* apply special filters */
1983 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1984 if (TNY_IS_ACCOUNT (instance))
1988 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
1989 if (TNY_IS_FOLDER (instance))
1993 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
1994 if (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1999 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2000 /* A mailbox is a fake folder with an @ in the middle of the name */
2001 if (!TNY_IS_FOLDER (instance) ||
2002 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2005 const gchar *folder_name;
2006 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2007 if (!folder_name || strchr (folder_name, '@') == NULL)
2013 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2014 if (TNY_IS_FOLDER (instance)) {
2015 /* Check folder rules */
2016 ModestTnyFolderRules rules;
2018 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2019 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2020 } else if (TNY_IS_ACCOUNT (instance)) {
2021 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2029 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2030 if (TNY_IS_FOLDER (instance)) {
2031 TnyFolderType guess_type;
2033 if (TNY_FOLDER_TYPE_NORMAL) {
2034 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2040 case TNY_FOLDER_TYPE_OUTBOX:
2041 case TNY_FOLDER_TYPE_SENT:
2042 case TNY_FOLDER_TYPE_DRAFTS:
2043 case TNY_FOLDER_TYPE_ARCHIVE:
2044 case TNY_FOLDER_TYPE_INBOX:
2047 case TNY_FOLDER_TYPE_UNKNOWN:
2048 case TNY_FOLDER_TYPE_NORMAL:
2054 } else if (TNY_IS_ACCOUNT (instance)) {
2059 if (retval && TNY_IS_FOLDER (instance)) {
2060 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2063 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2064 if (TNY_IS_FOLDER (instance)) {
2065 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2066 } else if (TNY_IS_ACCOUNT (instance)) {
2071 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2072 if (TNY_IS_FOLDER (instance)) {
2073 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2074 } else if (TNY_IS_ACCOUNT (instance)) {
2079 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2080 if (TNY_IS_FOLDER (instance)) {
2081 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2082 } else if (TNY_IS_ACCOUNT (instance)) {
2088 g_object_unref (instance);
2096 modest_folder_view_update_model (ModestFolderView *self,
2097 TnyAccountStore *account_store)
2099 ModestFolderViewPrivate *priv;
2100 GtkTreeModel *model /* , *old_model */;
2101 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2103 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2104 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2107 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2109 /* Notify that there is no folder selected */
2110 g_signal_emit (G_OBJECT(self),
2111 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2113 if (priv->cur_folder_store) {
2114 g_object_unref (priv->cur_folder_store);
2115 priv->cur_folder_store = NULL;
2118 /* FIXME: the local accounts are not shown when the query
2119 selects only the subscribed folders */
2120 #ifdef MODEST_TOOLKIT_HILDON2
2121 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2122 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2123 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2124 MODEST_FOLDER_PATH_SEPARATOR);
2126 model = tny_gtk_folder_store_tree_model_new (NULL);
2129 /* When the model is a list store (plain representation) the
2130 outbox is not a child of any account so we have to manually
2131 delete it because removing the local folders account won't
2132 delete it (because tny_folder_get_account() is not defined
2133 for a merge folder */
2134 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2135 TnyAccount *account;
2136 ModestTnyAccountStore *acc_store;
2138 acc_store = modest_runtime_get_account_store ();
2139 account = modest_tny_account_store_get_local_folders_account (acc_store);
2141 if (g_signal_handler_is_connected (account,
2142 priv->outbox_deleted_handler))
2143 g_signal_handler_disconnect (account,
2144 priv->outbox_deleted_handler);
2146 priv->outbox_deleted_handler =
2147 g_signal_connect (account,
2149 G_CALLBACK (on_outbox_deleted_cb),
2151 g_object_unref (account);
2154 /* Get the accounts: */
2155 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2157 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2159 sortable = gtk_tree_model_sort_new_with_model (model);
2160 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2162 GTK_SORT_ASCENDING);
2163 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2165 cmp_rows, NULL, NULL);
2167 /* Create filter model */
2168 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2169 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2175 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2176 #ifndef MODEST_TOOLKIT_HILDON2
2177 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2178 (GCallback) on_row_inserted_maybe_select_folder, self);
2181 g_object_unref (model);
2182 g_object_unref (filter_model);
2183 g_object_unref (sortable);
2185 /* Force a reselection of the INBOX next time the widget is shown */
2186 priv->reselect = TRUE;
2193 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2195 GtkTreeModel *model = NULL;
2196 TnyFolderStore *folder = NULL;
2198 ModestFolderView *tree_view = NULL;
2199 ModestFolderViewPrivate *priv = NULL;
2200 gboolean selected = FALSE;
2202 g_return_if_fail (sel);
2203 g_return_if_fail (user_data);
2205 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2207 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2209 tree_view = MODEST_FOLDER_VIEW (user_data);
2212 gtk_tree_model_get (model, &iter,
2213 INSTANCE_COLUMN, &folder,
2216 /* If the folder is the same do not notify */
2217 if (folder && priv->cur_folder_store == folder) {
2218 g_object_unref (folder);
2223 /* Current folder was unselected */
2224 if (priv->cur_folder_store) {
2225 /* We must do this firstly because a libtinymail-camel
2226 implementation detail. If we issue the signal
2227 before doing the sync_async, then that signal could
2228 cause (and it actually does it) a free of the
2229 summary of the folder (because the main window will
2230 clear the headers view */
2231 if (TNY_IS_FOLDER(priv->cur_folder_store))
2232 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2233 FALSE, NULL, NULL, NULL);
2235 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2236 priv->cur_folder_store, FALSE);
2238 g_object_unref (priv->cur_folder_store);
2239 priv->cur_folder_store = NULL;
2242 /* New current references */
2243 priv->cur_folder_store = folder;
2245 /* New folder has been selected. Do not notify if there is
2246 nothing new selected */
2248 g_signal_emit (G_OBJECT(tree_view),
2249 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2250 0, priv->cur_folder_store, TRUE);
2255 on_row_activated (GtkTreeView *treeview,
2256 GtkTreePath *treepath,
2257 GtkTreeViewColumn *column,
2260 GtkTreeModel *model = NULL;
2261 TnyFolderStore *folder = NULL;
2263 ModestFolderView *self = NULL;
2264 ModestFolderViewPrivate *priv = NULL;
2266 g_return_if_fail (treeview);
2267 g_return_if_fail (user_data);
2269 self = MODEST_FOLDER_VIEW (user_data);
2270 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2272 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2274 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2277 gtk_tree_model_get (model, &iter,
2278 INSTANCE_COLUMN, &folder,
2281 g_signal_emit (G_OBJECT(self),
2282 signals[FOLDER_ACTIVATED_SIGNAL],
2285 #ifdef MODEST_TOOLKIT_HILDON2
2286 HildonUIMode ui_mode;
2287 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2288 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2289 if (priv->cur_folder_store)
2290 g_object_unref (priv->cur_folder_store);
2291 priv->cur_folder_store = g_object_ref (folder);
2295 g_object_unref (folder);
2299 modest_folder_view_get_selected (ModestFolderView *self)
2301 ModestFolderViewPrivate *priv;
2303 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2305 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2306 if (priv->cur_folder_store)
2307 g_object_ref (priv->cur_folder_store);
2309 return priv->cur_folder_store;
2313 get_cmp_rows_type_pos (GObject *folder)
2315 /* Remote accounts -> Local account -> MMC account .*/
2318 if (TNY_IS_ACCOUNT (folder) &&
2319 modest_tny_account_is_virtual_local_folders (
2320 TNY_ACCOUNT (folder))) {
2322 } else if (TNY_IS_ACCOUNT (folder)) {
2323 TnyAccount *account = TNY_ACCOUNT (folder);
2324 const gchar *account_id = tny_account_get_id (account);
2325 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2331 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2332 return -1; /* Should never happen */
2337 inbox_is_special (TnyFolderStore *folder_store)
2339 gboolean is_special = TRUE;
2341 if (TNY_IS_FOLDER (folder_store)) {
2345 gchar *last_inbox_bar;
2347 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2348 downcase = g_utf8_strdown (id, -1);
2349 last_bar = g_strrstr (downcase, "/");
2351 last_inbox_bar = g_strrstr (downcase, "inbox/");
2352 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2363 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2365 TnyAccount *account;
2366 gboolean is_special;
2367 /* Inbox, Outbox, Drafts, Sent, User */
2370 if (!TNY_IS_FOLDER (folder_store))
2373 case TNY_FOLDER_TYPE_INBOX:
2375 account = tny_folder_get_account (folder_store);
2376 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2378 /* In inbox case we need to know if the inbox is really the top
2379 * inbox of the account, or if it's a submailbox inbox. To do
2380 * this we'll apply an heuristic rule: Find last "/" and check
2381 * if it's preceeded by another Inbox */
2382 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2383 g_object_unref (account);
2384 return is_special?0:4;
2387 case TNY_FOLDER_TYPE_OUTBOX:
2388 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2390 case TNY_FOLDER_TYPE_DRAFTS:
2392 account = tny_folder_get_account (folder_store);
2393 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2394 g_object_unref (account);
2395 return is_special?1:4;
2398 case TNY_FOLDER_TYPE_SENT:
2400 account = tny_folder_get_account (folder_store);
2401 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2402 g_object_unref (account);
2403 return is_special?3:4;
2412 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2414 const gchar *a1_name, *a2_name;
2416 a1_name = tny_account_get_name (a1);
2417 a2_name = tny_account_get_name (a2);
2419 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2423 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2425 TnyAccount *a1 = NULL, *a2 = NULL;
2428 if (TNY_IS_ACCOUNT (s1)) {
2429 a1 = TNY_ACCOUNT (g_object_ref (s1));
2430 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2431 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2434 if (TNY_IS_ACCOUNT (s2)) {
2435 a2 = TNY_ACCOUNT (g_object_ref (s2));
2436 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2437 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2454 /* First we sort with the type of account */
2455 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2459 cmp = compare_account_names (a1, a2);
2463 g_object_unref (a1);
2465 g_object_unref (a2);
2471 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2473 gint is_account1, is_account2;
2475 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2476 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2478 return is_account2 - is_account1;
2482 * This function orders the mail accounts according to these rules:
2483 * 1st - remote accounts
2484 * 2nd - local account
2488 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2492 gchar *name1 = NULL;
2493 gchar *name2 = NULL;
2494 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2495 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2496 GObject *folder1 = NULL;
2497 GObject *folder2 = NULL;
2499 gtk_tree_model_get (tree_model, iter1,
2500 NAME_COLUMN, &name1,
2502 INSTANCE_COLUMN, &folder1,
2504 gtk_tree_model_get (tree_model, iter2,
2505 NAME_COLUMN, &name2,
2506 TYPE_COLUMN, &type2,
2507 INSTANCE_COLUMN, &folder2,
2510 /* Return if we get no folder. This could happen when folder
2511 operations are happening. The model is updated after the
2512 folder copy/move actually occurs, so there could be
2513 situations where the model to be drawn is not correct */
2514 if (!folder1 || !folder2)
2517 /* Sort by type. First the special folders, then the archives */
2518 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2522 /* Now we sort using the account of each folder */
2523 if (TNY_IS_FOLDER_STORE (folder1) &&
2524 TNY_IS_FOLDER_STORE (folder2)) {
2525 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2529 /* Each group is preceeded by its account */
2530 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2535 /* Pure sort by name */
2536 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2539 g_object_unref(G_OBJECT(folder1));
2541 g_object_unref(G_OBJECT(folder2));
2549 /*****************************************************************************/
2550 /* DRAG and DROP stuff */
2551 /*****************************************************************************/
2553 * This function fills the #GtkSelectionData with the row and the
2554 * model that has been dragged. It's called when this widget is a
2555 * source for dnd after the event drop happened
2558 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2559 guint info, guint time, gpointer data)
2561 GtkTreeSelection *selection;
2562 GtkTreeModel *model;
2564 GtkTreePath *source_row;
2566 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2567 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2569 source_row = gtk_tree_model_get_path (model, &iter);
2570 gtk_tree_set_row_drag_data (selection_data,
2574 gtk_tree_path_free (source_row);
2578 typedef struct _DndHelper {
2579 ModestFolderView *folder_view;
2580 gboolean delete_source;
2581 GtkTreePath *source_row;
2585 dnd_helper_destroyer (DndHelper *helper)
2587 /* Free the helper */
2588 gtk_tree_path_free (helper->source_row);
2589 g_slice_free (DndHelper, helper);
2593 xfer_folder_cb (ModestMailOperation *mail_op,
2594 TnyFolder *new_folder,
2598 /* Select the folder */
2599 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2605 /* get the folder for the row the treepath refers to. */
2606 /* folder must be unref'd */
2607 static TnyFolderStore *
2608 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2611 TnyFolderStore *folder = NULL;
2613 if (gtk_tree_model_get_iter (model,&iter, path))
2614 gtk_tree_model_get (model, &iter,
2615 INSTANCE_COLUMN, &folder,
2622 * This function is used by drag_data_received_cb to manage drag and
2623 * drop of a header, i.e, and drag from the header view to the folder
2627 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2628 GtkTreeModel *dest_model,
2629 GtkTreePath *dest_row,
2630 GtkSelectionData *selection_data)
2632 TnyList *headers = NULL;
2633 TnyFolder *folder = NULL, *src_folder = NULL;
2634 TnyFolderType folder_type;
2635 GtkTreeIter source_iter, dest_iter;
2636 ModestWindowMgr *mgr = NULL;
2637 ModestWindow *main_win = NULL;
2638 gchar **uris, **tmp;
2640 /* Build the list of headers */
2641 mgr = modest_runtime_get_window_mgr ();
2642 headers = tny_simple_list_new ();
2643 uris = modest_dnd_selection_data_get_paths (selection_data);
2646 while (*tmp != NULL) {
2649 gboolean first = TRUE;
2652 path = gtk_tree_path_new_from_string (*tmp);
2653 gtk_tree_model_get_iter (source_model, &source_iter, path);
2654 gtk_tree_model_get (source_model, &source_iter,
2655 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2658 /* Do not enable d&d of headers already opened */
2659 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2660 tny_list_append (headers, G_OBJECT (header));
2662 if (G_UNLIKELY (first)) {
2663 src_folder = tny_header_get_folder (header);
2667 /* Free and go on */
2668 gtk_tree_path_free (path);
2669 g_object_unref (header);
2674 /* This could happen ig we perform a d&d very quickly over the
2675 same row that row could dissapear because message is
2677 if (!TNY_IS_FOLDER (src_folder))
2680 /* Get the target folder */
2681 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2682 gtk_tree_model_get (dest_model, &dest_iter,
2686 if (!folder || !TNY_IS_FOLDER(folder)) {
2687 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2691 folder_type = modest_tny_folder_guess_folder_type (folder);
2692 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2693 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2694 goto cleanup; /* cannot move messages there */
2697 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2698 /* g_warning ("folder not writable"); */
2699 goto cleanup; /* verboten! */
2702 /* Ask for confirmation to move */
2703 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2705 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2709 /* Transfer messages */
2710 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2715 if (G_IS_OBJECT (src_folder))
2716 g_object_unref (src_folder);
2717 if (G_IS_OBJECT(folder))
2718 g_object_unref (G_OBJECT (folder));
2719 if (G_IS_OBJECT(headers))
2720 g_object_unref (headers);
2724 TnyFolderStore *src_folder;
2725 TnyFolderStore *dst_folder;
2726 ModestFolderView *folder_view;
2731 dnd_folder_info_destroyer (DndFolderInfo *info)
2733 if (info->src_folder)
2734 g_object_unref (info->src_folder);
2735 if (info->dst_folder)
2736 g_object_unref (info->dst_folder);
2737 g_slice_free (DndFolderInfo, info);
2741 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2742 GtkWindow *parent_window,
2743 TnyAccount *account)
2746 modest_ui_actions_on_account_connection_error (parent_window, account);
2748 /* Free the helper & info */
2749 dnd_helper_destroyer (info->helper);
2750 dnd_folder_info_destroyer (info);
2754 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2756 GtkWindow *parent_window,
2757 TnyAccount *account,
2760 DndFolderInfo *info = NULL;
2761 ModestMailOperation *mail_op;
2763 info = (DndFolderInfo *) user_data;
2765 if (err || canceled) {
2766 dnd_on_connection_failed_destroyer (info, parent_window, account);
2770 /* Do the mail operation */
2771 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2772 modest_ui_actions_move_folder_error_handler,
2773 info->src_folder, NULL);
2775 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2778 /* Transfer the folder */
2779 modest_mail_operation_xfer_folder (mail_op,
2780 TNY_FOLDER (info->src_folder),
2782 info->helper->delete_source,
2784 info->helper->folder_view);
2787 g_object_unref (G_OBJECT (mail_op));
2788 dnd_helper_destroyer (info->helper);
2789 dnd_folder_info_destroyer (info);
2794 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2796 GtkWindow *parent_window,
2797 TnyAccount *account,
2800 DndFolderInfo *info = NULL;
2802 info = (DndFolderInfo *) user_data;
2804 if (err || canceled) {
2805 dnd_on_connection_failed_destroyer (info, parent_window, account);
2809 /* Connect to source folder and perform the copy/move */
2810 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2812 drag_and_drop_from_folder_view_src_folder_performer,
2817 * This function is used by drag_data_received_cb to manage drag and
2818 * drop of a folder, i.e, and drag from the folder view to the same
2822 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2823 GtkTreeModel *dest_model,
2824 GtkTreePath *dest_row,
2825 GtkSelectionData *selection_data,
2828 GtkTreeIter dest_iter, iter;
2829 TnyFolderStore *dest_folder = NULL;
2830 TnyFolderStore *folder = NULL;
2831 gboolean forbidden = FALSE;
2833 DndFolderInfo *info = NULL;
2835 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2837 g_warning ("%s: BUG: no main window", __FUNCTION__);
2838 dnd_helper_destroyer (helper);
2843 /* check the folder rules for the destination */
2844 folder = tree_path_to_folder (dest_model, dest_row);
2845 if (TNY_IS_FOLDER(folder)) {
2846 ModestTnyFolderRules rules =
2847 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2848 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2849 } else if (TNY_IS_FOLDER_STORE(folder)) {
2850 /* enable local root as destination for folders */
2851 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2852 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2855 g_object_unref (folder);
2858 /* check the folder rules for the source */
2859 folder = tree_path_to_folder (source_model, helper->source_row);
2860 if (TNY_IS_FOLDER(folder)) {
2861 ModestTnyFolderRules rules =
2862 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2863 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2866 g_object_unref (folder);
2870 /* Check if the drag is possible */
2871 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2873 modest_platform_run_information_dialog ((GtkWindow *) win,
2874 _("mail_in_ui_folder_move_target_error"),
2876 /* Restore the previous selection */
2877 folder = tree_path_to_folder (source_model, helper->source_row);
2879 if (TNY_IS_FOLDER (folder))
2880 modest_folder_view_select_folder (helper->folder_view,
2881 TNY_FOLDER (folder), FALSE);
2882 g_object_unref (folder);
2884 dnd_helper_destroyer (helper);
2889 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2890 gtk_tree_model_get (dest_model, &dest_iter,
2893 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2894 gtk_tree_model_get (source_model, &iter,
2898 /* Create the info for the performer */
2899 info = g_slice_new0 (DndFolderInfo);
2900 info->src_folder = g_object_ref (folder);
2901 info->dst_folder = g_object_ref (dest_folder);
2902 info->helper = helper;
2904 /* Connect to the destination folder and perform the copy/move */
2905 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2907 drag_and_drop_from_folder_view_dst_folder_performer,
2911 g_object_unref (dest_folder);
2912 g_object_unref (folder);
2916 * This function receives the data set by the "drag-data-get" signal
2917 * handler. This information comes within the #GtkSelectionData. This
2918 * function will manage both the drags of folders of the treeview and
2919 * drags of headers of the header view widget.
2922 on_drag_data_received (GtkWidget *widget,
2923 GdkDragContext *context,
2926 GtkSelectionData *selection_data,
2931 GtkWidget *source_widget;
2932 GtkTreeModel *dest_model, *source_model;
2933 GtkTreePath *source_row, *dest_row;
2934 GtkTreeViewDropPosition pos;
2935 gboolean delete_source = FALSE;
2936 gboolean success = FALSE;
2938 /* Do not allow further process */
2939 g_signal_stop_emission_by_name (widget, "drag-data-received");
2940 source_widget = gtk_drag_get_source_widget (context);
2942 /* Get the action */
2943 if (context->action == GDK_ACTION_MOVE) {
2944 delete_source = TRUE;
2946 /* Notify that there is no folder selected. We need to
2947 do this in order to update the headers view (and
2948 its monitors, because when moving, the old folder
2949 won't longer exist. We can not wait for the end of
2950 the operation, because the operation won't start if
2951 the folder is in use */
2952 if (source_widget == widget) {
2953 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2954 gtk_tree_selection_unselect_all (sel);
2958 /* Check if the get_data failed */
2959 if (selection_data == NULL || selection_data->length < 0)
2962 /* Select the destination model */
2963 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2965 /* Get the path to the destination row. Can not call
2966 gtk_tree_view_get_drag_dest_row() because the source row
2967 is not selected anymore */
2968 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2971 /* Only allow drops IN other rows */
2973 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2974 pos == GTK_TREE_VIEW_DROP_AFTER)
2978 /* Drags from the header view */
2979 if (source_widget != widget) {
2980 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2982 drag_and_drop_from_header_view (source_model,
2987 DndHelper *helper = NULL;
2989 /* Get the source model and row */
2990 gtk_tree_get_row_drag_data (selection_data,
2994 /* Create the helper */
2995 helper = g_slice_new0 (DndHelper);
2996 helper->delete_source = delete_source;
2997 helper->source_row = gtk_tree_path_copy (source_row);
2998 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3000 drag_and_drop_from_folder_view (source_model,
3006 gtk_tree_path_free (source_row);
3010 gtk_tree_path_free (dest_row);
3013 /* Finish the drag and drop */
3014 gtk_drag_finish (context, success, FALSE, time);
3018 * We define a "drag-drop" signal handler because we do not want to
3019 * use the default one, because the default one always calls
3020 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3021 * signal handler, because there we have all the information available
3022 * to know if the dnd was a success or not.
3025 drag_drop_cb (GtkWidget *widget,
3026 GdkDragContext *context,
3034 if (!context->targets)
3037 /* Check if we're dragging a folder row */
3038 target = gtk_drag_dest_find_target (widget, context, NULL);
3040 /* Request the data from the source. */
3041 gtk_drag_get_data(widget, context, target, time);
3047 * This function expands a node of a tree view if it's not expanded
3048 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3049 * does that, so that's why they're here.
3052 expand_row_timeout (gpointer data)
3054 GtkTreeView *tree_view = data;
3055 GtkTreePath *dest_path = NULL;
3056 GtkTreeViewDropPosition pos;
3057 gboolean result = FALSE;
3059 gdk_threads_enter ();
3061 gtk_tree_view_get_drag_dest_row (tree_view,
3066 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3067 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3068 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3069 gtk_tree_path_free (dest_path);
3073 gtk_tree_path_free (dest_path);
3078 gdk_threads_leave ();
3084 * This function is called whenever the pointer is moved over a widget
3085 * while dragging some data. It installs a timeout that will expand a
3086 * node of the treeview if not expanded yet. This function also calls
3087 * gdk_drag_status in order to set the suggested action that will be
3088 * used by the "drag-data-received" signal handler to know if we
3089 * should do a move or just a copy of the data.
3092 on_drag_motion (GtkWidget *widget,
3093 GdkDragContext *context,
3099 GtkTreeViewDropPosition pos;
3100 GtkTreePath *dest_row;
3101 GtkTreeModel *dest_model;
3102 ModestFolderViewPrivate *priv;
3103 GdkDragAction suggested_action;
3104 gboolean valid_location = FALSE;
3105 TnyFolderStore *folder = NULL;
3107 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3109 if (priv->timer_expander != 0) {
3110 g_source_remove (priv->timer_expander);
3111 priv->timer_expander = 0;
3114 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3119 /* Do not allow drops between folders */
3121 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3122 pos == GTK_TREE_VIEW_DROP_AFTER) {
3123 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3124 gdk_drag_status(context, 0, time);
3125 valid_location = FALSE;
3128 valid_location = TRUE;
3131 /* Check that the destination folder is writable */
3132 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3133 folder = tree_path_to_folder (dest_model, dest_row);
3134 if (folder && TNY_IS_FOLDER (folder)) {
3135 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3137 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3138 valid_location = FALSE;
3143 /* Expand the selected row after 1/2 second */
3144 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3145 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3147 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3149 /* Select the desired action. By default we pick MOVE */
3150 suggested_action = GDK_ACTION_MOVE;
3152 if (context->actions == GDK_ACTION_COPY)
3153 gdk_drag_status(context, GDK_ACTION_COPY, time);
3154 else if (context->actions == GDK_ACTION_MOVE)
3155 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3156 else if (context->actions & suggested_action)
3157 gdk_drag_status(context, suggested_action, time);
3159 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3163 g_object_unref (folder);
3165 gtk_tree_path_free (dest_row);
3167 g_signal_stop_emission_by_name (widget, "drag-motion");
3169 return valid_location;
3173 * This function sets the treeview as a source and a target for dnd
3174 * events. It also connects all the requirede signals.
3177 setup_drag_and_drop (GtkTreeView *self)
3179 /* Set up the folder view as a dnd destination. Set only the
3180 highlight flag, otherwise gtk will have a different
3182 #ifdef MODEST_TOOLKIT_HILDON2
3185 gtk_drag_dest_set (GTK_WIDGET (self),
3186 GTK_DEST_DEFAULT_HIGHLIGHT,
3187 folder_view_drag_types,
3188 G_N_ELEMENTS (folder_view_drag_types),
3189 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3191 g_signal_connect (G_OBJECT (self),
3192 "drag_data_received",
3193 G_CALLBACK (on_drag_data_received),
3197 /* Set up the treeview as a dnd source */
3198 gtk_drag_source_set (GTK_WIDGET (self),
3200 folder_view_drag_types,
3201 G_N_ELEMENTS (folder_view_drag_types),
3202 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3204 g_signal_connect (G_OBJECT (self),
3206 G_CALLBACK (on_drag_motion),
3209 g_signal_connect (G_OBJECT (self),
3211 G_CALLBACK (on_drag_data_get),
3214 g_signal_connect (G_OBJECT (self),
3216 G_CALLBACK (drag_drop_cb),
3221 * This function manages the navigation through the folders using the
3222 * keyboard or the hardware keys in the device
3225 on_key_pressed (GtkWidget *self,
3229 GtkTreeSelection *selection;
3231 GtkTreeModel *model;
3232 gboolean retval = FALSE;
3234 /* Up and Down are automatically managed by the treeview */
3235 if (event->keyval == GDK_Return) {
3236 /* Expand/Collapse the selected row */
3237 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3238 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3241 path = gtk_tree_model_get_path (model, &iter);
3243 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3244 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3246 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3247 gtk_tree_path_free (path);
3249 /* No further processing */
3257 * We listen to the changes in the local folder account name key,
3258 * because we want to show the right name in the view. The local
3259 * folder account name corresponds to the device name in the Maemo
3260 * version. We do this because we do not want to query gconf on each
3261 * tree view refresh. It's better to cache it and change whenever
3265 on_configuration_key_changed (ModestConf* conf,
3267 ModestConfEvent event,
3268 ModestConfNotificationId id,
3269 ModestFolderView *self)
3271 ModestFolderViewPrivate *priv;
3274 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3275 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3277 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3278 g_free (priv->local_account_name);
3280 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3281 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3283 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3284 MODEST_CONF_DEVICE_NAME, NULL);
3286 /* Force a redraw */
3287 #if GTK_CHECK_VERSION(2, 8, 0)
3288 GtkTreeViewColumn * tree_column;
3290 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3292 gtk_tree_view_column_queue_resize (tree_column);
3294 gtk_widget_queue_draw (GTK_WIDGET (self));
3300 modest_folder_view_set_style (ModestFolderView *self,
3301 ModestFolderViewStyle style)
3303 ModestFolderViewPrivate *priv;
3305 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3306 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3307 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3309 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3312 priv->style = style;
3316 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3317 const gchar *account_id)
3319 ModestFolderViewPrivate *priv;
3320 GtkTreeModel *model;
3322 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3324 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3326 /* This will be used by the filter_row callback,
3327 * to decided which rows to show: */
3328 if (priv->visible_account_id) {
3329 g_free (priv->visible_account_id);
3330 priv->visible_account_id = NULL;
3333 priv->visible_account_id = g_strdup (account_id);
3336 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3337 if (GTK_IS_TREE_MODEL_FILTER (model))
3338 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3340 /* Save settings to gconf */
3341 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3342 MODEST_CONF_FOLDER_VIEW_KEY);
3344 /* Notify observers */
3345 g_signal_emit (G_OBJECT(self),
3346 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3351 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3353 ModestFolderViewPrivate *priv;
3355 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3357 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3359 return (const gchar *) priv->visible_account_id;
3363 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3367 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3369 gtk_tree_model_get (model, iter,
3373 gboolean result = FALSE;
3374 if (type == TNY_FOLDER_TYPE_INBOX) {
3378 *inbox_iter = *iter;
3382 if (gtk_tree_model_iter_children (model, &child, iter)) {
3383 if (find_inbox_iter (model, &child, inbox_iter))
3387 } while (gtk_tree_model_iter_next (model, iter));
3396 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3398 GtkTreeModel *model;
3399 GtkTreeIter iter, inbox_iter;
3400 GtkTreeSelection *sel;
3401 GtkTreePath *path = NULL;
3403 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3405 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3409 expand_root_items (self);
3410 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3412 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3413 g_warning ("%s: model is empty", __FUNCTION__);
3417 if (find_inbox_iter (model, &iter, &inbox_iter))
3418 path = gtk_tree_model_get_path (model, &inbox_iter);
3420 path = gtk_tree_path_new_first ();
3422 /* Select the row and free */
3423 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3424 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3425 gtk_tree_path_free (path);
3428 gtk_widget_grab_focus (GTK_WIDGET(self));
3434 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3439 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3440 TnyFolder* a_folder;
3443 gtk_tree_model_get (model, iter,
3444 INSTANCE_COLUMN, &a_folder,
3450 if (folder == a_folder) {
3451 g_object_unref (a_folder);
3452 *folder_iter = *iter;
3455 g_object_unref (a_folder);
3457 if (gtk_tree_model_iter_children (model, &child, iter)) {
3458 if (find_folder_iter (model, &child, folder_iter, folder))
3462 } while (gtk_tree_model_iter_next (model, iter));
3467 #ifndef MODEST_TOOLKIT_HILDON2
3469 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3472 ModestFolderView *self)
3474 ModestFolderViewPrivate *priv = NULL;
3475 GtkTreeSelection *sel;
3476 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3477 GObject *instance = NULL;
3479 if (!MODEST_IS_FOLDER_VIEW(self))
3482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3484 priv->reexpand = TRUE;
3486 gtk_tree_model_get (tree_model, iter,
3488 INSTANCE_COLUMN, &instance,
3494 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3495 priv->folder_to_select = g_object_ref (instance);
3497 g_object_unref (instance);
3499 if (priv->folder_to_select) {
3501 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3504 path = gtk_tree_model_get_path (tree_model, iter);
3505 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3507 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3509 gtk_tree_selection_select_iter (sel, iter);
3510 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3512 gtk_tree_path_free (path);
3516 modest_folder_view_disable_next_folder_selection (self);
3518 /* Refilter the model */
3519 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3525 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3527 ModestFolderViewPrivate *priv;
3529 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3531 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3533 if (priv->folder_to_select)
3534 g_object_unref(priv->folder_to_select);
3536 priv->folder_to_select = NULL;
3540 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3541 gboolean after_change)
3543 GtkTreeModel *model;
3544 GtkTreeIter iter, folder_iter;
3545 GtkTreeSelection *sel;
3546 ModestFolderViewPrivate *priv = NULL;
3548 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3549 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3551 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3554 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3555 gtk_tree_selection_unselect_all (sel);
3557 if (priv->folder_to_select)
3558 g_object_unref(priv->folder_to_select);
3559 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3563 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3568 /* Refilter the model, before selecting the folder */
3569 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3571 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3572 g_warning ("%s: model is empty", __FUNCTION__);
3576 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3579 path = gtk_tree_model_get_path (model, &folder_iter);
3580 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3582 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3583 gtk_tree_selection_select_iter (sel, &folder_iter);
3584 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3586 gtk_tree_path_free (path);
3594 modest_folder_view_copy_selection (ModestFolderView *self)
3596 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3598 /* Copy selection */
3599 _clipboard_set_selected_data (self, FALSE);
3603 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3605 ModestFolderViewPrivate *priv = NULL;
3606 GtkTreeModel *model = NULL;
3607 const gchar **hidding = NULL;
3608 guint i, n_selected;
3610 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3611 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3613 /* Copy selection */
3614 if (!_clipboard_set_selected_data (folder_view, TRUE))
3617 /* Get hidding ids */
3618 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3620 /* Clear hidding array created by previous cut operation */
3621 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3623 /* Copy hidding array */
3624 priv->n_selected = n_selected;
3625 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3626 for (i=0; i < n_selected; i++)
3627 priv->hidding_ids[i] = g_strdup(hidding[i]);
3629 /* Hide cut folders */
3630 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3631 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3635 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3636 ModestFolderView *folder_view_dst)
3638 GtkTreeModel *filter_model = NULL;
3639 GtkTreeModel *model = NULL;
3640 GtkTreeModel *new_filter_model = NULL;
3642 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3643 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3646 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3647 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3649 /* Build new filter model */
3650 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3651 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3655 /* Set copied model */
3656 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3657 #ifndef MODEST_TOOLKIT_HILDON2
3658 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3659 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3663 g_object_unref (new_filter_model);
3667 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3670 GtkTreeModel *model = NULL;
3671 ModestFolderViewPrivate* priv;
3673 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3675 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3676 priv->show_non_move = show;
3677 /* modest_folder_view_update_model(folder_view, */
3678 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3680 /* Hide special folders */
3681 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3682 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3683 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3687 /* Returns FALSE if it did not selected anything */
3689 _clipboard_set_selected_data (ModestFolderView *folder_view,
3692 ModestFolderViewPrivate *priv = NULL;
3693 TnyFolderStore *folder = NULL;
3694 gboolean retval = FALSE;
3696 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3697 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3699 /* Set selected data on clipboard */
3700 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3701 folder = modest_folder_view_get_selected (folder_view);
3703 /* Do not allow to select an account */
3704 if (TNY_IS_FOLDER (folder)) {
3705 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3710 g_object_unref (folder);
3716 _clear_hidding_filter (ModestFolderView *folder_view)
3718 ModestFolderViewPrivate *priv;
3721 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3722 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3724 if (priv->hidding_ids != NULL) {
3725 for (i=0; i < priv->n_selected; i++)
3726 g_free (priv->hidding_ids[i]);
3727 g_free(priv->hidding_ids);
3733 on_display_name_changed (ModestAccountMgr *mgr,
3734 const gchar *account,
3737 ModestFolderView *self;
3739 self = MODEST_FOLDER_VIEW (user_data);
3741 /* Force a redraw */
3742 #if GTK_CHECK_VERSION(2, 8, 0)
3743 GtkTreeViewColumn * tree_column;
3745 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3747 gtk_tree_view_column_queue_resize (tree_column);
3749 gtk_widget_queue_draw (GTK_WIDGET (self));
3754 modest_folder_view_set_cell_style (ModestFolderView *self,
3755 ModestFolderViewCellStyle cell_style)
3757 ModestFolderViewPrivate *priv = NULL;
3759 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3760 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3762 priv->cell_style = cell_style;
3764 g_object_set (G_OBJECT (priv->messages_renderer),
3765 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3768 gtk_widget_queue_draw (GTK_WIDGET (self));
3772 update_style (ModestFolderView *self)
3774 ModestFolderViewPrivate *priv;
3775 GdkColor style_color;
3776 PangoAttrList *attr_list;
3778 PangoAttribute *attr;
3780 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3781 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3785 attr_list = pango_attr_list_new ();
3786 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3787 gdk_color_parse ("grey", &style_color);
3789 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3790 pango_attr_list_insert (attr_list, attr);
3793 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3795 "SmallSystemFont", NULL,
3798 attr = pango_attr_font_desc_new (pango_font_description_copy
3799 (style->font_desc));
3800 pango_attr_list_insert (attr_list, attr);
3802 g_object_set (G_OBJECT (priv->messages_renderer),
3803 "foreground-gdk", &style_color,
3804 "foreground-set", TRUE,
3805 "attributes", attr_list,
3807 pango_attr_list_unref (attr_list);
3812 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3814 if (strcmp ("style", spec->name) == 0) {
3815 update_style (MODEST_FOLDER_VIEW (obj));
3816 gtk_widget_queue_draw (GTK_WIDGET (obj));
3821 modest_folder_view_set_filter (ModestFolderView *self,
3822 ModestFolderViewFilter filter)
3824 ModestFolderViewPrivate *priv;
3825 GtkTreeModel *filter_model;
3827 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3828 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3830 priv->filter |= filter;
3832 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3833 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3834 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3839 modest_folder_view_unset_filter (ModestFolderView *self,
3840 ModestFolderViewFilter filter)
3842 ModestFolderViewPrivate *priv;
3843 GtkTreeModel *filter_model;
3845 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3846 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3848 priv->filter &= ~filter;
3850 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3851 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3852 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3857 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3858 ModestTnyFolderRules rules)
3860 GtkTreeModel *filter_model;
3862 gboolean fulfil = FALSE;
3864 if (!get_inner_models (self, &filter_model, NULL, NULL))
3867 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3871 TnyFolderStore *folder;
3873 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3875 if (TNY_IS_FOLDER (folder)) {
3876 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3877 /* Folder rules are negative: non_writable, non_deletable... */
3878 if (!(folder_rules & rules))
3881 g_object_unref (folder);
3884 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3890 modest_folder_view_set_list_to_move (ModestFolderView *self,
3893 ModestFolderViewPrivate *priv;
3895 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3896 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3898 if (priv->list_to_move)
3899 g_object_unref (priv->list_to_move);
3902 g_object_ref (list);
3904 priv->list_to_move = list;
3908 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3910 ModestFolderViewPrivate *priv;
3912 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3913 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3916 g_free (priv->mailbox);
3918 priv->mailbox = g_strdup (mailbox);
3920 /* Notify observers */
3921 g_signal_emit (G_OBJECT(self),
3922 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3923 priv->visible_account_id);
3927 modest_folder_view_get_mailbox (ModestFolderView *self)
3929 ModestFolderViewPrivate *priv;
3931 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3932 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3934 return (const gchar *) priv->mailbox;