1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
66 /* Folder view drag types */
67 const GtkTargetEntry folder_view_drag_types[] =
69 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
70 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
73 /* Default icon sizes for Fremantle style are different */
74 #ifdef MODEST_TOOLKIT_HILDON2
75 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
77 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
80 /* Column names depending on we use list store or tree store */
81 #ifdef MODEST_TOOLKIT_HILDON2
82 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
83 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
84 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
85 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
86 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
88 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
89 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
90 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
91 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
92 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
95 /* 'private'/'protected' functions */
96 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
97 static void modest_folder_view_init (ModestFolderView *obj);
98 static void modest_folder_view_finalize (GObject *obj);
100 static void tny_account_store_view_init (gpointer g,
101 gpointer iface_data);
103 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
104 TnyAccountStore *account_store);
106 static void on_selection_changed (GtkTreeSelection *sel,
109 static void on_row_activated (GtkTreeView *treeview,
111 GtkTreeViewColumn *column,
114 static void on_account_removed (TnyAccountStore *self,
118 static void on_account_inserted (TnyAccountStore *self,
122 static void on_account_changed (TnyAccountStore *self,
126 static gint cmp_rows (GtkTreeModel *tree_model,
131 static gboolean filter_row (GtkTreeModel *model,
135 static gboolean on_key_pressed (GtkWidget *self,
139 static void on_configuration_key_changed (ModestConf* conf,
141 ModestConfEvent event,
142 ModestConfNotificationId notification_id,
143 ModestFolderView *self);
146 static void on_drag_data_get (GtkWidget *widget,
147 GdkDragContext *context,
148 GtkSelectionData *selection_data,
153 static void on_drag_data_received (GtkWidget *widget,
154 GdkDragContext *context,
157 GtkSelectionData *selection_data,
162 static gboolean on_drag_motion (GtkWidget *widget,
163 GdkDragContext *context,
169 static void expand_root_items (ModestFolderView *self);
171 static gint expand_row_timeout (gpointer data);
173 static void setup_drag_and_drop (GtkTreeView *self);
175 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
178 static void _clear_hidding_filter (ModestFolderView *folder_view);
180 #ifndef MODEST_TOOLKIT_HILDON2
181 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
184 ModestFolderView *self);
187 static void on_display_name_changed (ModestAccountMgr *self,
188 const gchar *account,
190 static void update_style (ModestFolderView *self);
191 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
192 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
193 static gboolean inbox_is_special (TnyFolderStore *folder_store);
195 static gboolean get_inner_models (ModestFolderView *self,
196 GtkTreeModel **filter_model,
197 GtkTreeModel **sort_model,
198 GtkTreeModel **tny_model);
201 FOLDER_SELECTION_CHANGED_SIGNAL,
202 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
203 FOLDER_ACTIVATED_SIGNAL,
204 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
208 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
209 struct _ModestFolderViewPrivate {
210 TnyAccountStore *account_store;
211 TnyFolderStore *cur_folder_store;
213 TnyFolder *folder_to_select; /* folder to select after the next update */
215 gulong changed_signal;
216 gulong account_inserted_signal;
217 gulong account_removed_signal;
218 gulong account_changed_signal;
219 gulong conf_key_signal;
220 gulong display_name_changed_signal;
222 /* not unref this object, its a singlenton */
223 ModestEmailClipboard *clipboard;
225 /* Filter tree model */
228 ModestFolderViewFilter filter;
230 TnyFolderStoreQuery *query;
231 guint timer_expander;
233 gchar *local_account_name;
234 gchar *visible_account_id;
236 ModestFolderViewStyle style;
237 ModestFolderViewCellStyle cell_style;
239 gboolean reselect; /* we use this to force a reselection of the INBOX */
240 gboolean show_non_move;
241 TnyList *list_to_move;
242 gboolean reexpand; /* next time we expose, we'll expand all root folders */
244 GtkCellRenderer *messages_renderer;
246 gulong outbox_deleted_handler;
248 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
249 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
250 MODEST_TYPE_FOLDER_VIEW, \
251 ModestFolderViewPrivate))
253 static GObjectClass *parent_class = NULL;
255 static guint signals[LAST_SIGNAL] = {0};
258 modest_folder_view_get_type (void)
260 static GType my_type = 0;
262 static const GTypeInfo my_info = {
263 sizeof(ModestFolderViewClass),
264 NULL, /* base init */
265 NULL, /* base finalize */
266 (GClassInitFunc) modest_folder_view_class_init,
267 NULL, /* class finalize */
268 NULL, /* class data */
269 sizeof(ModestFolderView),
271 (GInstanceInitFunc) modest_folder_view_init,
275 static const GInterfaceInfo tny_account_store_view_info = {
276 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
277 NULL, /* interface_finalize */
278 NULL /* interface_data */
282 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
286 g_type_add_interface_static (my_type,
287 TNY_TYPE_ACCOUNT_STORE_VIEW,
288 &tny_account_store_view_info);
294 modest_folder_view_class_init (ModestFolderViewClass *klass)
296 GObjectClass *gobject_class;
297 GtkTreeViewClass *treeview_class;
298 gobject_class = (GObjectClass*) klass;
299 treeview_class = (GtkTreeViewClass*) klass;
301 parent_class = g_type_class_peek_parent (klass);
302 gobject_class->finalize = modest_folder_view_finalize;
304 g_type_class_add_private (gobject_class,
305 sizeof(ModestFolderViewPrivate));
307 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
308 g_signal_new ("folder_selection_changed",
309 G_TYPE_FROM_CLASS (gobject_class),
311 G_STRUCT_OFFSET (ModestFolderViewClass,
312 folder_selection_changed),
314 modest_marshal_VOID__POINTER_BOOLEAN,
315 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
318 * This signal is emitted whenever the currently selected
319 * folder display name is computed. Note that the name could
320 * be different to the folder name, because we could append
321 * the unread messages count to the folder name to build the
322 * folder display name
324 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
325 g_signal_new ("folder-display-name-changed",
326 G_TYPE_FROM_CLASS (gobject_class),
328 G_STRUCT_OFFSET (ModestFolderViewClass,
329 folder_display_name_changed),
331 g_cclosure_marshal_VOID__STRING,
332 G_TYPE_NONE, 1, G_TYPE_STRING);
334 signals[FOLDER_ACTIVATED_SIGNAL] =
335 g_signal_new ("folder_activated",
336 G_TYPE_FROM_CLASS (gobject_class),
338 G_STRUCT_OFFSET (ModestFolderViewClass,
341 g_cclosure_marshal_VOID__POINTER,
342 G_TYPE_NONE, 1, G_TYPE_POINTER);
345 * Emitted whenever the visible account changes
347 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
348 g_signal_new ("visible-account-changed",
349 G_TYPE_FROM_CLASS (gobject_class),
351 G_STRUCT_OFFSET (ModestFolderViewClass,
352 visible_account_changed),
354 g_cclosure_marshal_VOID__STRING,
355 G_TYPE_NONE, 1, G_TYPE_STRING);
357 treeview_class->select_cursor_parent = NULL;
359 #ifdef MODEST_TOOLKIT_HILDON2
360 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
366 /* Retrieves the filter, sort and tny models of the folder view. If
367 any of these does not exist then it returns FALSE */
369 get_inner_models (ModestFolderView *self,
370 GtkTreeModel **filter_model,
371 GtkTreeModel **sort_model,
372 GtkTreeModel **tny_model)
374 GtkTreeModel *s_model, *f_model, *t_model;
376 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
377 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
378 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
382 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
383 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
384 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
388 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
392 *filter_model = f_model;
394 *sort_model = s_model;
396 *tny_model = t_model;
401 /* Simplify checks for NULLs: */
403 strings_are_equal (const gchar *a, const gchar *b)
409 return (strcmp (a, b) == 0);
416 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
418 GObject *instance = NULL;
420 gtk_tree_model_get (model, iter,
421 INSTANCE_COLUMN, &instance,
425 return FALSE; /* keep walking */
427 if (!TNY_IS_ACCOUNT (instance)) {
428 g_object_unref (instance);
429 return FALSE; /* keep walking */
432 /* Check if this is the looked-for account: */
433 TnyAccount *this_account = TNY_ACCOUNT (instance);
434 TnyAccount *account = TNY_ACCOUNT (data);
436 const gchar *this_account_id = tny_account_get_id(this_account);
437 const gchar *account_id = tny_account_get_id(account);
438 g_object_unref (instance);
441 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
442 if (strings_are_equal(this_account_id, account_id)) {
443 /* Tell the model that the data has changed, so that
444 * it calls the cell_data_func callbacks again: */
445 /* TODO: This does not seem to actually cause the new string to be shown: */
446 gtk_tree_model_row_changed (model, path, iter);
448 return TRUE; /* stop walking */
451 return FALSE; /* keep walking */
456 ModestFolderView *self;
457 gchar *previous_name;
458 } GetMmcAccountNameData;
461 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
463 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
465 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
467 if (!strings_are_equal (
468 tny_account_get_name(TNY_ACCOUNT(account)),
469 data->previous_name)) {
471 /* Tell the model that the data has changed, so that
472 * it calls the cell_data_func callbacks again: */
473 ModestFolderView *self = data->self;
474 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
476 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
479 g_free (data->previous_name);
480 g_slice_free (GetMmcAccountNameData, data);
484 convert_parent_folders_to_dots (gchar **item_name)
488 gchar *last_separator;
490 if (item_name == NULL)
493 for (c = *item_name; *c != '\0'; c++) {
494 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
499 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
500 if (last_separator != NULL) {
501 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
508 buffer = g_string_new ("");
509 for (i = 0; i < n_parents; i++) {
510 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
512 buffer = g_string_append (buffer, last_separator);
514 *item_name = g_string_free (buffer, FALSE);
520 format_compact_style (gchar **item_name,
522 const gchar *mailbox,
524 gboolean multiaccount,
525 gboolean *use_markup)
529 TnyFolderType folder_type;
531 if (!TNY_IS_FOLDER (instance))
534 folder = (TnyFolder *) instance;
536 folder_type = tny_folder_get_folder_type (folder);
537 is_special = (get_cmp_pos (folder_type, folder)!= 4);
540 /* Remove mailbox prefix if any */
541 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
542 if (g_str_has_prefix (*item_name, prefix)) {
543 gchar *new_item_name;
545 new_item_name = g_strdup (*item_name + strlen (prefix));
546 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
547 g_free (new_item_name);
548 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
551 *item_name = new_item_name;
553 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
556 *item_name = g_strdup (_("mcen_me_folder_inbox"));
559 if (!is_special || multiaccount) {
560 TnyAccount *account = tny_folder_get_account (folder);
561 const gchar *folder_name;
562 gboolean concat_folder_name = FALSE;
565 /* Should not happen */
569 /* convert parent folders to dots */
570 convert_parent_folders_to_dots (item_name);
572 folder_name = tny_folder_get_name (folder);
573 if (g_str_has_suffix (*item_name, folder_name)) {
574 gchar *offset = g_strrstr (*item_name, folder_name);
576 concat_folder_name = TRUE;
579 buffer = g_string_new ("");
581 buffer = g_string_append (buffer, *item_name);
582 if (concat_folder_name) {
583 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
584 buffer = g_string_append (buffer, folder_name);
585 if (bold) buffer = g_string_append (buffer, "</span>");
588 g_object_unref (account);
590 *item_name = g_string_free (buffer, FALSE);
598 text_cell_data (GtkTreeViewColumn *column,
599 GtkCellRenderer *renderer,
600 GtkTreeModel *tree_model,
604 ModestFolderViewPrivate *priv;
605 GObject *rendobj = (GObject *) renderer;
607 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
608 GObject *instance = NULL;
609 gboolean use_markup = FALSE;
611 gtk_tree_model_get (tree_model, iter,
614 INSTANCE_COLUMN, &instance,
616 if (!fname || !instance)
619 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
620 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
622 gchar *item_name = NULL;
623 gint item_weight = 400;
625 if (type != TNY_FOLDER_TYPE_ROOT) {
629 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
630 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
631 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
632 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
634 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
637 /* Sometimes an special folder is reported by the server as
638 NORMAL, like some versions of Dovecot */
639 if (type == TNY_FOLDER_TYPE_NORMAL ||
640 type == TNY_FOLDER_TYPE_UNKNOWN) {
641 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
645 /* note: we cannot reliably get the counts from the
646 * tree model, we need to use explicit calls on
647 * tny_folder for some reason. Select the number to
648 * show: the unread or unsent messages. in case of
649 * outbox/drafts, show all */
650 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
651 (type == TNY_FOLDER_TYPE_OUTBOX) ||
652 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
653 number = tny_folder_get_all_count (TNY_FOLDER(instance));
656 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
660 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
661 item_name = g_strdup (fname);
668 /* Use bold font style if there are unread or unset messages */
670 item_name = g_strdup_printf ("%s (%d)", fname, number);
673 item_name = g_strdup (fname);
678 } else if (TNY_IS_ACCOUNT (instance)) {
679 /* If it's a server account */
680 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
681 item_name = g_strdup (priv->local_account_name);
683 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
684 /* fname is only correct when the items are first
685 * added to the model, not when the account is
686 * changed later, so get the name from the account
688 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
691 item_name = g_strdup (fname);
697 item_name = g_strdup ("unknown");
699 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
700 gboolean multiaccount;
702 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
703 /* Convert item_name to markup */
704 format_compact_style (&item_name, instance, priv->mailbox,
706 multiaccount, &use_markup);
709 if (item_name && item_weight) {
710 /* Set the name in the treeview cell: */
712 g_object_set (rendobj, "markup", item_name, NULL);
714 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
716 /* Notify display name observers */
717 /* TODO: What listens for this signal, and how can it use only the new name? */
718 if (((GObject *) priv->cur_folder_store) == instance) {
719 g_signal_emit (G_OBJECT(self),
720 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
727 /* If it is a Memory card account, make sure that we have the correct name.
728 * This function will be trigerred again when the name has been retrieved: */
729 if (TNY_IS_STORE_ACCOUNT (instance) &&
730 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
732 /* Get the account name asynchronously: */
733 GetMmcAccountNameData *callback_data =
734 g_slice_new0(GetMmcAccountNameData);
735 callback_data->self = self;
737 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
739 callback_data->previous_name = g_strdup (name);
741 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
742 on_get_mmc_account_name, callback_data);
746 g_object_unref (G_OBJECT (instance));
752 messages_cell_data (GtkTreeViewColumn *column,
753 GtkCellRenderer *renderer,
754 GtkTreeModel *tree_model,
758 ModestFolderView *self;
759 ModestFolderViewPrivate *priv;
760 GObject *rendobj = (GObject *) renderer;
761 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
762 GObject *instance = NULL;
763 gchar *item_name = NULL;
765 gtk_tree_model_get (tree_model, iter,
767 INSTANCE_COLUMN, &instance,
772 self = MODEST_FOLDER_VIEW (data);
773 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
776 if (type != TNY_FOLDER_TYPE_ROOT) {
780 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
781 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
782 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
784 /* Sometimes an special folder is reported by the server as
785 NORMAL, like some versions of Dovecot */
786 if (type == TNY_FOLDER_TYPE_NORMAL ||
787 type == TNY_FOLDER_TYPE_UNKNOWN) {
788 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
792 /* note: we cannot reliably get the counts from the tree model, we need
793 * to use explicit calls on tny_folder for some reason.
795 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
796 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
797 (type == TNY_FOLDER_TYPE_OUTBOX) ||
798 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
799 number = tny_folder_get_all_count (TNY_FOLDER(instance));
802 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
806 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
808 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
816 item_name = g_strdup ("");
819 /* Set the name in the treeview cell: */
820 g_object_set (rendobj,"text", item_name, NULL);
828 g_object_unref (G_OBJECT (instance));
834 GdkPixbuf *pixbuf_open;
835 GdkPixbuf *pixbuf_close;
839 static inline GdkPixbuf *
840 get_composite_pixbuf (const gchar *icon_name,
842 GdkPixbuf *base_pixbuf)
844 GdkPixbuf *emblem, *retval = NULL;
846 emblem = modest_platform_get_icon (icon_name, size);
848 retval = gdk_pixbuf_copy (base_pixbuf);
849 gdk_pixbuf_composite (emblem, retval, 0, 0,
850 MIN (gdk_pixbuf_get_width (emblem),
851 gdk_pixbuf_get_width (retval)),
852 MIN (gdk_pixbuf_get_height (emblem),
853 gdk_pixbuf_get_height (retval)),
854 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
855 g_object_unref (emblem);
860 static inline ThreePixbufs *
861 get_composite_icons (const gchar *icon_code,
863 GdkPixbuf **pixbuf_open,
864 GdkPixbuf **pixbuf_close)
866 ThreePixbufs *retval;
869 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
872 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
877 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
881 retval = g_slice_new0 (ThreePixbufs);
883 retval->pixbuf = g_object_ref (*pixbuf);
885 retval->pixbuf_open = g_object_ref (*pixbuf_open);
887 retval->pixbuf_close = g_object_ref (*pixbuf_close);
892 static inline ThreePixbufs*
893 get_folder_icons (TnyFolderType type, GObject *instance)
895 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
896 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
897 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
898 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
899 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
901 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
902 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
903 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
904 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
905 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
907 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
908 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
909 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
910 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
911 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
913 ThreePixbufs *retval = NULL;
915 /* Sometimes an special folder is reported by the server as
916 NORMAL, like some versions of Dovecot */
917 if (type == TNY_FOLDER_TYPE_NORMAL ||
918 type == TNY_FOLDER_TYPE_UNKNOWN) {
919 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
922 /* It's not enough with check the folder type. We need to
923 ensure that we're not giving a special folder icon to a
924 normal folder with the same name than a special folder */
925 if (TNY_IS_FOLDER (instance) &&
926 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
927 type = TNY_FOLDER_TYPE_NORMAL;
929 /* Remote folders should not be treated as special folders */
930 if (TNY_IS_FOLDER_STORE (instance) &&
931 !TNY_IS_ACCOUNT (instance) &&
932 type != TNY_FOLDER_TYPE_INBOX &&
933 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
934 #ifdef MODEST_TOOLKIT_HILDON2
935 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
938 &anorm_pixbuf_close);
940 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
943 &normal_pixbuf_close);
949 case TNY_FOLDER_TYPE_INVALID:
950 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
953 case TNY_FOLDER_TYPE_ROOT:
954 if (TNY_IS_ACCOUNT (instance)) {
956 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
957 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
960 &avirt_pixbuf_close);
962 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
964 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
965 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
970 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
973 &anorm_pixbuf_close);
978 case TNY_FOLDER_TYPE_INBOX:
979 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
982 &inbox_pixbuf_close);
984 case TNY_FOLDER_TYPE_OUTBOX:
985 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
988 &outbox_pixbuf_close);
990 case TNY_FOLDER_TYPE_JUNK:
991 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
996 case TNY_FOLDER_TYPE_SENT:
997 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1000 &sent_pixbuf_close);
1002 case TNY_FOLDER_TYPE_TRASH:
1003 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1006 &trash_pixbuf_close);
1008 case TNY_FOLDER_TYPE_DRAFTS:
1009 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1012 &draft_pixbuf_close);
1014 case TNY_FOLDER_TYPE_ARCHIVE:
1015 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1020 case TNY_FOLDER_TYPE_NORMAL:
1022 /* Memory card folders could have an special icon */
1023 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1024 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1029 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1031 &normal_pixbuf_open,
1032 &normal_pixbuf_close);
1041 free_pixbufs (ThreePixbufs *pixbufs)
1043 if (pixbufs->pixbuf)
1044 g_object_unref (pixbufs->pixbuf);
1045 if (pixbufs->pixbuf_open)
1046 g_object_unref (pixbufs->pixbuf_open);
1047 if (pixbufs->pixbuf_close)
1048 g_object_unref (pixbufs->pixbuf_close);
1049 g_slice_free (ThreePixbufs, pixbufs);
1053 icon_cell_data (GtkTreeViewColumn *column,
1054 GtkCellRenderer *renderer,
1055 GtkTreeModel *tree_model,
1059 GObject *rendobj = NULL, *instance = NULL;
1060 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1061 gboolean has_children;
1062 ThreePixbufs *pixbufs;
1064 rendobj = (GObject *) renderer;
1066 gtk_tree_model_get (tree_model, iter,
1068 INSTANCE_COLUMN, &instance,
1074 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1075 pixbufs = get_folder_icons (type, instance);
1076 g_object_unref (instance);
1079 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1082 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1083 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1086 free_pixbufs (pixbufs);
1090 add_columns (GtkWidget *treeview)
1092 GtkTreeViewColumn *column;
1093 GtkCellRenderer *renderer;
1094 GtkTreeSelection *sel;
1095 ModestFolderViewPrivate *priv;
1097 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1100 column = gtk_tree_view_column_new ();
1102 /* Set icon and text render function */
1103 renderer = gtk_cell_renderer_pixbuf_new();
1104 #ifdef MODEST_TOOLKIT_HILDON2
1105 g_object_set (renderer,
1106 "xpad", MODEST_MARGIN_DEFAULT,
1107 "ypad", MODEST_MARGIN_DEFAULT,
1110 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1111 gtk_tree_view_column_set_cell_data_func(column, renderer,
1112 icon_cell_data, treeview, NULL);
1114 renderer = gtk_cell_renderer_text_new();
1115 g_object_set (renderer,
1116 #ifdef MODEST_TOOLKIT_HILDON2
1117 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1118 "ypad", MODEST_MARGIN_DEFAULT,
1119 "xpad", MODEST_MARGIN_DEFAULT,
1121 "ellipsize", PANGO_ELLIPSIZE_END,
1123 "ellipsize-set", TRUE, NULL);
1124 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1125 gtk_tree_view_column_set_cell_data_func(column, renderer,
1126 text_cell_data, treeview, NULL);
1128 priv->messages_renderer = gtk_cell_renderer_text_new ();
1129 g_object_set (priv->messages_renderer,
1130 #ifdef MODEST_TOOLKIT_HILDON2
1132 "ypad", MODEST_MARGIN_DEFAULT,
1133 "xpad", MODEST_MARGIN_DOUBLE,
1135 "scale", PANGO_SCALE_X_SMALL,
1138 "alignment", PANGO_ALIGN_RIGHT,
1142 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1143 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1144 messages_cell_data, treeview, NULL);
1146 /* Set selection mode */
1147 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1148 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1150 /* Set treeview appearance */
1151 gtk_tree_view_column_set_spacing (column, 2);
1152 gtk_tree_view_column_set_resizable (column, TRUE);
1153 gtk_tree_view_column_set_fixed_width (column, TRUE);
1154 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1155 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1158 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1162 modest_folder_view_init (ModestFolderView *obj)
1164 ModestFolderViewPrivate *priv;
1167 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1169 priv->timer_expander = 0;
1170 priv->account_store = NULL;
1172 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1173 priv->cur_folder_store = NULL;
1174 priv->visible_account_id = NULL;
1175 priv->mailbox = NULL;
1176 priv->folder_to_select = NULL;
1177 priv->outbox_deleted_handler = 0;
1178 priv->reexpand = TRUE;
1180 /* Initialize the local account name */
1181 conf = modest_runtime_get_conf();
1182 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1184 /* Init email clipboard */
1185 priv->clipboard = modest_runtime_get_email_clipboard ();
1186 priv->hidding_ids = NULL;
1187 priv->n_selected = 0;
1188 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1189 priv->reselect = FALSE;
1190 priv->show_non_move = TRUE;
1191 priv->list_to_move = NULL;
1193 /* Build treeview */
1194 add_columns (GTK_WIDGET (obj));
1196 /* Setup drag and drop */
1197 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1199 /* Connect signals */
1200 g_signal_connect (G_OBJECT (obj),
1202 G_CALLBACK (on_key_pressed), NULL);
1204 priv->display_name_changed_signal =
1205 g_signal_connect (modest_runtime_get_account_mgr (),
1206 "display_name_changed",
1207 G_CALLBACK (on_display_name_changed),
1211 * Track changes in the local account name (in the device it
1212 * will be the device name)
1214 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1216 G_CALLBACK(on_configuration_key_changed),
1220 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1226 tny_account_store_view_init (gpointer g, gpointer iface_data)
1228 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1230 klass->set_account_store = modest_folder_view_set_account_store;
1234 modest_folder_view_finalize (GObject *obj)
1236 ModestFolderViewPrivate *priv;
1237 GtkTreeSelection *sel;
1238 TnyAccount *local_account;
1240 g_return_if_fail (obj);
1242 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1244 if (priv->timer_expander != 0) {
1245 g_source_remove (priv->timer_expander);
1246 priv->timer_expander = 0;
1249 local_account = (TnyAccount *)
1250 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1251 if (local_account) {
1252 if (g_signal_handler_is_connected (local_account,
1253 priv->outbox_deleted_handler))
1254 g_signal_handler_disconnect (local_account,
1255 priv->outbox_deleted_handler);
1256 g_object_unref (local_account);
1259 if (priv->account_store) {
1260 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1261 priv->account_inserted_signal);
1262 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1263 priv->account_removed_signal);
1264 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1265 priv->account_changed_signal);
1266 g_object_unref (G_OBJECT(priv->account_store));
1267 priv->account_store = NULL;
1270 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1271 priv->display_name_changed_signal)) {
1272 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1273 priv->display_name_changed_signal);
1274 priv->display_name_changed_signal = 0;
1278 g_object_unref (G_OBJECT (priv->query));
1282 if (priv->folder_to_select) {
1283 g_object_unref (G_OBJECT(priv->folder_to_select));
1284 priv->folder_to_select = NULL;
1287 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1289 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1291 g_free (priv->local_account_name);
1292 g_free (priv->visible_account_id);
1293 g_free (priv->mailbox);
1295 if (priv->conf_key_signal) {
1296 g_signal_handler_disconnect (modest_runtime_get_conf (),
1297 priv->conf_key_signal);
1298 priv->conf_key_signal = 0;
1301 if (priv->cur_folder_store) {
1302 g_object_unref (priv->cur_folder_store);
1303 priv->cur_folder_store = NULL;
1306 if (priv->list_to_move) {
1307 g_object_unref (priv->list_to_move);
1308 priv->list_to_move = NULL;
1311 /* Clear hidding array created by cut operation */
1312 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1314 G_OBJECT_CLASS(parent_class)->finalize (obj);
1319 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1321 ModestFolderViewPrivate *priv;
1324 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1325 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1327 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1328 device = tny_account_store_get_device (account_store);
1330 if (G_UNLIKELY (priv->account_store)) {
1332 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1333 priv->account_inserted_signal))
1334 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1335 priv->account_inserted_signal);
1336 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1337 priv->account_removed_signal))
1338 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1339 priv->account_removed_signal);
1340 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1341 priv->account_changed_signal))
1342 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1343 priv->account_changed_signal);
1344 g_object_unref (G_OBJECT (priv->account_store));
1347 priv->account_store = g_object_ref (G_OBJECT (account_store));
1349 priv->account_removed_signal =
1350 g_signal_connect (G_OBJECT(account_store), "account_removed",
1351 G_CALLBACK (on_account_removed), self);
1353 priv->account_inserted_signal =
1354 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1355 G_CALLBACK (on_account_inserted), self);
1357 priv->account_changed_signal =
1358 g_signal_connect (G_OBJECT(account_store), "account_changed",
1359 G_CALLBACK (on_account_changed), self);
1361 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1362 priv->reselect = FALSE;
1363 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1365 g_object_unref (G_OBJECT (device));
1369 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1372 ModestFolderView *self;
1373 GtkTreeModel *model, *filter_model;
1376 self = MODEST_FOLDER_VIEW (user_data);
1378 if (!get_inner_models (self, &filter_model, NULL, &model))
1381 /* Remove outbox from model */
1382 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1383 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1384 g_object_unref (outbox);
1387 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1391 on_account_inserted (TnyAccountStore *account_store,
1392 TnyAccount *account,
1395 ModestFolderViewPrivate *priv;
1396 GtkTreeModel *model, *filter_model;
1398 /* Ignore transport account insertions, we're not showing them
1399 in the folder view */
1400 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1403 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1406 /* If we're adding a new account, and there is no previous
1407 one, we need to select the visible server account */
1408 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1409 !priv->visible_account_id)
1410 modest_widget_memory_restore (modest_runtime_get_conf(),
1411 G_OBJECT (user_data),
1412 MODEST_CONF_FOLDER_VIEW_KEY);
1416 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1417 &filter_model, NULL, &model))
1420 /* Insert the account in the model */
1421 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1423 /* When the model is a list store (plain representation) the
1424 outbox is not a child of any account so we have to manually
1425 delete it because removing the local folders account won't
1426 delete it (because tny_folder_get_account() is not defined
1427 for a merge folder */
1428 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1429 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1431 priv->outbox_deleted_handler =
1432 g_signal_connect (account,
1434 G_CALLBACK (on_outbox_deleted_cb),
1438 /* Refilter the model */
1439 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1444 same_account_selected (ModestFolderView *self,
1445 TnyAccount *account)
1447 ModestFolderViewPrivate *priv;
1448 gboolean same_account = FALSE;
1450 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1452 if (priv->cur_folder_store) {
1453 TnyAccount *selected_folder_account = NULL;
1455 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1456 selected_folder_account =
1457 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1459 selected_folder_account =
1460 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1463 if (selected_folder_account == account)
1464 same_account = TRUE;
1466 g_object_unref (selected_folder_account);
1468 return same_account;
1473 * Selects the first inbox or the local account in an idle
1476 on_idle_select_first_inbox_or_local (gpointer user_data)
1478 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1480 gdk_threads_enter ();
1481 modest_folder_view_select_first_inbox_or_local (self);
1482 gdk_threads_leave ();
1488 on_account_changed (TnyAccountStore *account_store,
1489 TnyAccount *tny_account,
1492 ModestFolderView *self;
1493 ModestFolderViewPrivate *priv;
1494 GtkTreeModel *model, *filter_model;
1495 GtkTreeSelection *sel;
1496 gboolean same_account;
1498 /* Ignore transport account insertions, we're not showing them
1499 in the folder view */
1500 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1503 self = MODEST_FOLDER_VIEW (user_data);
1504 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1506 /* Get the inner model */
1507 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1508 &filter_model, NULL, &model))
1511 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1513 /* Invalidate the cur_folder_store only if the selected folder
1514 belongs to the account that is being removed */
1515 same_account = same_account_selected (self, tny_account);
1517 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1518 gtk_tree_selection_unselect_all (sel);
1521 /* Remove the account from the model */
1522 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1524 /* Insert the account in the model */
1525 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1527 /* Refilter the model */
1528 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1530 /* Select the first INBOX if the currently selected folder
1531 belongs to the account that is being deleted */
1532 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1533 g_idle_add (on_idle_select_first_inbox_or_local, self);
1537 on_account_removed (TnyAccountStore *account_store,
1538 TnyAccount *account,
1541 ModestFolderView *self = NULL;
1542 ModestFolderViewPrivate *priv;
1543 GtkTreeModel *model, *filter_model;
1544 GtkTreeSelection *sel = NULL;
1545 gboolean same_account = FALSE;
1547 /* Ignore transport account removals, we're not showing them
1548 in the folder view */
1549 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1552 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1553 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1557 self = MODEST_FOLDER_VIEW (user_data);
1558 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1560 /* Invalidate the cur_folder_store only if the selected folder
1561 belongs to the account that is being removed */
1562 same_account = same_account_selected (self, account);
1564 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1565 gtk_tree_selection_unselect_all (sel);
1568 /* Invalidate row to select only if the folder to select
1569 belongs to the account that is being removed*/
1570 if (priv->folder_to_select) {
1571 TnyAccount *folder_to_select_account = NULL;
1573 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1574 if (folder_to_select_account == account) {
1575 modest_folder_view_disable_next_folder_selection (self);
1576 g_object_unref (priv->folder_to_select);
1577 priv->folder_to_select = NULL;
1579 g_object_unref (folder_to_select_account);
1582 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1583 &filter_model, NULL, &model))
1586 /* Disconnect the signal handler */
1587 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1588 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1589 if (g_signal_handler_is_connected (account,
1590 priv->outbox_deleted_handler))
1591 g_signal_handler_disconnect (account,
1592 priv->outbox_deleted_handler);
1595 /* Remove the account from the model */
1596 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1598 /* If the removed account is the currently viewed one then
1599 clear the configuration value. The new visible account will be the default account */
1600 if (priv->visible_account_id &&
1601 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1603 /* Clear the current visible account_id */
1604 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1605 modest_folder_view_set_mailbox (self, NULL);
1607 /* Call the restore method, this will set the new visible account */
1608 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1609 MODEST_CONF_FOLDER_VIEW_KEY);
1612 /* Refilter the model */
1613 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1615 /* Select the first INBOX if the currently selected folder
1616 belongs to the account that is being deleted */
1618 g_idle_add (on_idle_select_first_inbox_or_local, self);
1622 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1624 GtkTreeViewColumn *col;
1626 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1628 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1630 g_printerr ("modest: failed get column for title\n");
1634 gtk_tree_view_column_set_title (col, title);
1635 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1640 modest_folder_view_on_map (ModestFolderView *self,
1641 GdkEventExpose *event,
1644 ModestFolderViewPrivate *priv;
1646 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1648 /* This won't happen often */
1649 if (G_UNLIKELY (priv->reselect)) {
1650 /* Select the first inbox or the local account if not found */
1652 /* TODO: this could cause a lock at startup, so we
1653 comment it for the moment. We know that this will
1654 be a bug, because the INBOX is not selected, but we
1655 need to rewrite some parts of Modest to avoid the
1656 deathlock situation */
1657 /* TODO: check if this is still the case */
1658 priv->reselect = FALSE;
1659 modest_folder_view_select_first_inbox_or_local (self);
1660 /* Notify the display name observers */
1661 g_signal_emit (G_OBJECT(self),
1662 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1666 if (priv->reexpand) {
1667 expand_root_items (self);
1668 priv->reexpand = FALSE;
1675 modest_folder_view_new (TnyFolderStoreQuery *query)
1678 ModestFolderViewPrivate *priv;
1679 GtkTreeSelection *sel;
1681 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1682 #ifdef MODEST_TOOLKIT_HILDON2
1683 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1686 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1689 priv->query = g_object_ref (query);
1691 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1692 priv->changed_signal = g_signal_connect (sel, "changed",
1693 G_CALLBACK (on_selection_changed), self);
1695 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1697 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1699 return GTK_WIDGET(self);
1702 /* this feels dirty; any other way to expand all the root items? */
1704 expand_root_items (ModestFolderView *self)
1707 GtkTreeModel *model;
1710 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1711 path = gtk_tree_path_new_first ();
1713 /* all folders should have child items, so.. */
1715 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1716 gtk_tree_path_next (path);
1717 } while (gtk_tree_model_get_iter (model, &iter, path));
1719 gtk_tree_path_free (path);
1723 is_parent_of (TnyFolder *a, TnyFolder *b)
1726 gboolean retval = FALSE;
1728 a_id = tny_folder_get_id (a);
1730 gchar *string_to_match;
1733 string_to_match = g_strconcat (a_id, "/", NULL);
1734 b_id = tny_folder_get_id (b);
1735 retval = g_str_has_prefix (b_id, string_to_match);
1736 g_free (string_to_match);
1742 typedef struct _ForeachFolderInfo {
1745 } ForeachFolderInfo;
1748 foreach_folder_with_id (GtkTreeModel *model,
1753 ForeachFolderInfo *info;
1756 info = (ForeachFolderInfo *) data;
1757 gtk_tree_model_get (model, iter,
1758 INSTANCE_COLUMN, &instance,
1761 if (TNY_IS_FOLDER (instance)) {
1764 id = tny_folder_get_id (TNY_FOLDER (instance));
1766 collate = g_utf8_collate_key (id, -1);
1767 info->found = !strcmp (info->needle, collate);
1773 g_object_unref (instance);
1781 has_folder_with_id (ModestFolderView *self, const gchar *id)
1783 GtkTreeModel *model;
1784 ForeachFolderInfo info = {NULL, FALSE};
1786 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1787 info.needle = g_utf8_collate_key (id, -1);
1789 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1790 g_free (info.needle);
1796 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1799 gboolean retval = FALSE;
1801 a_id = tny_folder_get_id (a);
1804 b_id = tny_folder_get_id (b);
1807 const gchar *last_bar;
1808 gchar *string_to_match;
1809 last_bar = g_strrstr (b_id, "/");
1814 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1815 retval = has_folder_with_id (self, string_to_match);
1816 g_free (string_to_match);
1824 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1826 ModestFolderViewPrivate *priv;
1827 TnyIterator *iterator;
1828 gboolean retval = TRUE;
1830 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1831 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1833 for (iterator = tny_list_create_iterator (priv->list_to_move);
1834 retval && !tny_iterator_is_done (iterator);
1835 tny_iterator_next (iterator)) {
1837 instance = tny_iterator_get_current (iterator);
1838 if (instance == (GObject *) folder) {
1840 } else if (TNY_IS_FOLDER (instance)) {
1841 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1843 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1846 g_object_unref (instance);
1848 g_object_unref (iterator);
1855 * We use this function to implement the
1856 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1857 * account in this case, and the local folders.
1860 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1862 ModestFolderViewPrivate *priv;
1863 gboolean retval = TRUE;
1864 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1865 GObject *instance = NULL;
1866 const gchar *id = NULL;
1868 gboolean found = FALSE;
1869 gboolean cleared = FALSE;
1870 ModestTnyFolderRules rules = 0;
1873 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1874 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1876 gtk_tree_model_get (model, iter,
1877 NAME_COLUMN, &fname,
1879 INSTANCE_COLUMN, &instance,
1882 /* Do not show if there is no instance, this could indeed
1883 happen when the model is being modified while it's being
1884 drawn. This could occur for example when moving folders
1891 if (TNY_IS_ACCOUNT (instance)) {
1892 TnyAccount *acc = TNY_ACCOUNT (instance);
1893 const gchar *account_id = tny_account_get_id (acc);
1895 /* If it isn't a special folder,
1896 * don't show it unless it is the visible account: */
1897 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1898 !modest_tny_account_is_virtual_local_folders (acc) &&
1899 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1901 /* Show only the visible account id */
1902 if (priv->visible_account_id) {
1903 if (strcmp (account_id, priv->visible_account_id))
1910 /* Never show these to the user. They are merged into one folder
1911 * in the local-folders account instead: */
1912 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1915 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1916 /* Only show special folders for current account if needed */
1917 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1918 TnyAccount *account;
1920 account = tny_folder_get_account (TNY_FOLDER (instance));
1922 if (TNY_IS_ACCOUNT (account)) {
1923 const gchar *account_id = tny_account_get_id (account);
1925 if (!modest_tny_account_is_virtual_local_folders (account) &&
1926 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1927 /* Show only the visible account id */
1928 if (priv->visible_account_id) {
1929 if (strcmp (account_id, priv->visible_account_id)) {
1931 } else if (priv->mailbox) {
1932 /* Filter mailboxes */
1933 if (!g_str_has_prefix (fname, priv->mailbox)) {
1935 } else if (!strcmp (fname, priv->mailbox)) {
1936 /* Hide mailbox parent */
1942 g_object_unref (account);
1949 /* Check hiding (if necessary) */
1950 cleared = modest_email_clipboard_cleared (priv->clipboard);
1951 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1952 id = tny_folder_get_id (TNY_FOLDER(instance));
1953 if (priv->hidding_ids != NULL)
1954 for (i=0; i < priv->n_selected && !found; i++)
1955 if (priv->hidding_ids[i] != NULL && id != NULL)
1956 found = (!strcmp (priv->hidding_ids[i], id));
1961 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1962 folder as no message can be move there according to UI specs */
1963 if (retval && !priv->show_non_move) {
1964 if (priv->list_to_move &&
1965 tny_list_get_length (priv->list_to_move) > 0 &&
1966 TNY_IS_FOLDER (instance)) {
1967 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1969 if (retval && TNY_IS_FOLDER (instance) &&
1970 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1972 case TNY_FOLDER_TYPE_OUTBOX:
1973 case TNY_FOLDER_TYPE_SENT:
1974 case TNY_FOLDER_TYPE_DRAFTS:
1977 case TNY_FOLDER_TYPE_UNKNOWN:
1978 case TNY_FOLDER_TYPE_NORMAL:
1979 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1980 if (type == TNY_FOLDER_TYPE_INVALID)
1981 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1983 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1984 type == TNY_FOLDER_TYPE_SENT
1985 || type == TNY_FOLDER_TYPE_DRAFTS)
1992 if (retval && TNY_IS_ACCOUNT (instance) &&
1993 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1994 ModestProtocolType protocol_type;
1996 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
1997 retval = !modest_protocol_registry_protocol_type_has_tag
1998 (modest_runtime_get_protocol_registry (),
2000 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2004 /* apply special filters */
2005 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2006 if (TNY_IS_ACCOUNT (instance))
2010 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2011 if (TNY_IS_FOLDER (instance))
2015 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2016 if (TNY_IS_ACCOUNT (instance)) {
2017 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2019 } else if (TNY_IS_FOLDER (instance)) {
2020 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2025 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2026 if (TNY_IS_ACCOUNT (instance)) {
2027 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2029 } else if (TNY_IS_FOLDER (instance)) {
2030 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2035 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2036 /* A mailbox is a fake folder with an @ in the middle of the name */
2037 if (!TNY_IS_FOLDER (instance) ||
2038 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2041 const gchar *folder_name;
2042 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2043 if (!folder_name || strchr (folder_name, '@') == NULL)
2049 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2050 if (TNY_IS_FOLDER (instance)) {
2051 /* Check folder rules */
2052 ModestTnyFolderRules rules;
2054 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2055 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2056 } else if (TNY_IS_ACCOUNT (instance)) {
2057 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2065 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2066 if (TNY_IS_FOLDER (instance)) {
2067 TnyFolderType guess_type;
2069 if (TNY_FOLDER_TYPE_NORMAL) {
2070 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2076 case TNY_FOLDER_TYPE_OUTBOX:
2077 case TNY_FOLDER_TYPE_SENT:
2078 case TNY_FOLDER_TYPE_DRAFTS:
2079 case TNY_FOLDER_TYPE_ARCHIVE:
2080 case TNY_FOLDER_TYPE_INBOX:
2083 case TNY_FOLDER_TYPE_UNKNOWN:
2084 case TNY_FOLDER_TYPE_NORMAL:
2090 } else if (TNY_IS_ACCOUNT (instance)) {
2095 if (retval && TNY_IS_FOLDER (instance)) {
2096 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2099 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2100 if (TNY_IS_FOLDER (instance)) {
2101 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2102 } else if (TNY_IS_ACCOUNT (instance)) {
2107 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2108 if (TNY_IS_FOLDER (instance)) {
2109 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2110 } else if (TNY_IS_ACCOUNT (instance)) {
2115 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2116 if (TNY_IS_FOLDER (instance)) {
2117 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2118 } else if (TNY_IS_ACCOUNT (instance)) {
2124 g_object_unref (instance);
2132 modest_folder_view_update_model (ModestFolderView *self,
2133 TnyAccountStore *account_store)
2135 ModestFolderViewPrivate *priv;
2136 GtkTreeModel *model /* , *old_model */;
2137 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2139 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2140 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2143 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2145 /* Notify that there is no folder selected */
2146 g_signal_emit (G_OBJECT(self),
2147 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2149 if (priv->cur_folder_store) {
2150 g_object_unref (priv->cur_folder_store);
2151 priv->cur_folder_store = NULL;
2154 /* FIXME: the local accounts are not shown when the query
2155 selects only the subscribed folders */
2156 #ifdef MODEST_TOOLKIT_HILDON2
2157 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2158 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2159 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2160 MODEST_FOLDER_PATH_SEPARATOR);
2162 model = tny_gtk_folder_store_tree_model_new (NULL);
2165 /* When the model is a list store (plain representation) the
2166 outbox is not a child of any account so we have to manually
2167 delete it because removing the local folders account won't
2168 delete it (because tny_folder_get_account() is not defined
2169 for a merge folder */
2170 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2171 TnyAccount *account;
2172 ModestTnyAccountStore *acc_store;
2174 acc_store = modest_runtime_get_account_store ();
2175 account = modest_tny_account_store_get_local_folders_account (acc_store);
2177 if (g_signal_handler_is_connected (account,
2178 priv->outbox_deleted_handler))
2179 g_signal_handler_disconnect (account,
2180 priv->outbox_deleted_handler);
2182 priv->outbox_deleted_handler =
2183 g_signal_connect (account,
2185 G_CALLBACK (on_outbox_deleted_cb),
2187 g_object_unref (account);
2190 /* Get the accounts: */
2191 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2193 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2195 sortable = gtk_tree_model_sort_new_with_model (model);
2196 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2198 GTK_SORT_ASCENDING);
2199 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2201 cmp_rows, NULL, NULL);
2203 /* Create filter model */
2204 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2205 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2211 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2212 #ifndef MODEST_TOOLKIT_HILDON2
2213 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2214 (GCallback) on_row_inserted_maybe_select_folder, self);
2217 g_object_unref (model);
2218 g_object_unref (filter_model);
2219 g_object_unref (sortable);
2221 /* Force a reselection of the INBOX next time the widget is shown */
2222 priv->reselect = TRUE;
2229 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2231 GtkTreeModel *model = NULL;
2232 TnyFolderStore *folder = NULL;
2234 ModestFolderView *tree_view = NULL;
2235 ModestFolderViewPrivate *priv = NULL;
2236 gboolean selected = FALSE;
2238 g_return_if_fail (sel);
2239 g_return_if_fail (user_data);
2241 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2243 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2245 tree_view = MODEST_FOLDER_VIEW (user_data);
2248 gtk_tree_model_get (model, &iter,
2249 INSTANCE_COLUMN, &folder,
2252 /* If the folder is the same do not notify */
2253 if (folder && priv->cur_folder_store == folder) {
2254 g_object_unref (folder);
2259 /* Current folder was unselected */
2260 if (priv->cur_folder_store) {
2261 /* We must do this firstly because a libtinymail-camel
2262 implementation detail. If we issue the signal
2263 before doing the sync_async, then that signal could
2264 cause (and it actually does it) a free of the
2265 summary of the folder (because the main window will
2266 clear the headers view */
2267 if (TNY_IS_FOLDER(priv->cur_folder_store))
2268 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2269 FALSE, NULL, NULL, NULL);
2271 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2272 priv->cur_folder_store, FALSE);
2274 g_object_unref (priv->cur_folder_store);
2275 priv->cur_folder_store = NULL;
2278 /* New current references */
2279 priv->cur_folder_store = folder;
2281 /* New folder has been selected. Do not notify if there is
2282 nothing new selected */
2284 g_signal_emit (G_OBJECT(tree_view),
2285 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2286 0, priv->cur_folder_store, TRUE);
2291 on_row_activated (GtkTreeView *treeview,
2292 GtkTreePath *treepath,
2293 GtkTreeViewColumn *column,
2296 GtkTreeModel *model = NULL;
2297 TnyFolderStore *folder = NULL;
2299 ModestFolderView *self = NULL;
2300 ModestFolderViewPrivate *priv = NULL;
2302 g_return_if_fail (treeview);
2303 g_return_if_fail (user_data);
2305 self = MODEST_FOLDER_VIEW (user_data);
2306 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2308 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2310 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2313 gtk_tree_model_get (model, &iter,
2314 INSTANCE_COLUMN, &folder,
2317 g_signal_emit (G_OBJECT(self),
2318 signals[FOLDER_ACTIVATED_SIGNAL],
2321 #ifdef MODEST_TOOLKIT_HILDON2
2322 HildonUIMode ui_mode;
2323 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2324 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2325 if (priv->cur_folder_store)
2326 g_object_unref (priv->cur_folder_store);
2327 priv->cur_folder_store = g_object_ref (folder);
2331 g_object_unref (folder);
2335 modest_folder_view_get_selected (ModestFolderView *self)
2337 ModestFolderViewPrivate *priv;
2339 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2341 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2342 if (priv->cur_folder_store)
2343 g_object_ref (priv->cur_folder_store);
2345 return priv->cur_folder_store;
2349 get_cmp_rows_type_pos (GObject *folder)
2351 /* Remote accounts -> Local account -> MMC account .*/
2354 if (TNY_IS_ACCOUNT (folder) &&
2355 modest_tny_account_is_virtual_local_folders (
2356 TNY_ACCOUNT (folder))) {
2358 } else if (TNY_IS_ACCOUNT (folder)) {
2359 TnyAccount *account = TNY_ACCOUNT (folder);
2360 const gchar *account_id = tny_account_get_id (account);
2361 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2367 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2368 return -1; /* Should never happen */
2373 inbox_is_special (TnyFolderStore *folder_store)
2375 gboolean is_special = TRUE;
2377 if (TNY_IS_FOLDER (folder_store)) {
2381 gchar *last_inbox_bar;
2383 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2384 downcase = g_utf8_strdown (id, -1);
2385 last_bar = g_strrstr (downcase, "/");
2387 last_inbox_bar = g_strrstr (downcase, "inbox/");
2388 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2399 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2401 TnyAccount *account;
2402 gboolean is_special;
2403 /* Inbox, Outbox, Drafts, Sent, User */
2406 if (!TNY_IS_FOLDER (folder_store))
2409 case TNY_FOLDER_TYPE_INBOX:
2411 account = tny_folder_get_account (folder_store);
2412 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2414 /* In inbox case we need to know if the inbox is really the top
2415 * inbox of the account, or if it's a submailbox inbox. To do
2416 * this we'll apply an heuristic rule: Find last "/" and check
2417 * if it's preceeded by another Inbox */
2418 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2419 g_object_unref (account);
2420 return is_special?0:4;
2423 case TNY_FOLDER_TYPE_OUTBOX:
2424 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2426 case TNY_FOLDER_TYPE_DRAFTS:
2428 account = tny_folder_get_account (folder_store);
2429 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2430 g_object_unref (account);
2431 return is_special?1:4;
2434 case TNY_FOLDER_TYPE_SENT:
2436 account = tny_folder_get_account (folder_store);
2437 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2438 g_object_unref (account);
2439 return is_special?3:4;
2448 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2450 const gchar *a1_name, *a2_name;
2452 a1_name = tny_account_get_name (a1);
2453 a2_name = tny_account_get_name (a2);
2455 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2459 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2461 TnyAccount *a1 = NULL, *a2 = NULL;
2464 if (TNY_IS_ACCOUNT (s1)) {
2465 a1 = TNY_ACCOUNT (g_object_ref (s1));
2466 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2467 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2470 if (TNY_IS_ACCOUNT (s2)) {
2471 a2 = TNY_ACCOUNT (g_object_ref (s2));
2472 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2473 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2490 /* First we sort with the type of account */
2491 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2495 cmp = compare_account_names (a1, a2);
2499 g_object_unref (a1);
2501 g_object_unref (a2);
2507 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2509 gint is_account1, is_account2;
2511 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2512 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2514 return is_account2 - is_account1;
2518 * This function orders the mail accounts according to these rules:
2519 * 1st - remote accounts
2520 * 2nd - local account
2524 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2528 gchar *name1 = NULL;
2529 gchar *name2 = NULL;
2530 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2531 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2532 GObject *folder1 = NULL;
2533 GObject *folder2 = NULL;
2535 gtk_tree_model_get (tree_model, iter1,
2536 NAME_COLUMN, &name1,
2538 INSTANCE_COLUMN, &folder1,
2540 gtk_tree_model_get (tree_model, iter2,
2541 NAME_COLUMN, &name2,
2542 TYPE_COLUMN, &type2,
2543 INSTANCE_COLUMN, &folder2,
2546 /* Return if we get no folder. This could happen when folder
2547 operations are happening. The model is updated after the
2548 folder copy/move actually occurs, so there could be
2549 situations where the model to be drawn is not correct */
2550 if (!folder1 || !folder2)
2553 /* Sort by type. First the special folders, then the archives */
2554 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2558 /* Now we sort using the account of each folder */
2559 if (TNY_IS_FOLDER_STORE (folder1) &&
2560 TNY_IS_FOLDER_STORE (folder2)) {
2561 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2565 /* Each group is preceeded by its account */
2566 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2571 /* Pure sort by name */
2572 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2575 g_object_unref(G_OBJECT(folder1));
2577 g_object_unref(G_OBJECT(folder2));
2585 /*****************************************************************************/
2586 /* DRAG and DROP stuff */
2587 /*****************************************************************************/
2589 * This function fills the #GtkSelectionData with the row and the
2590 * model that has been dragged. It's called when this widget is a
2591 * source for dnd after the event drop happened
2594 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2595 guint info, guint time, gpointer data)
2597 GtkTreeSelection *selection;
2598 GtkTreeModel *model;
2600 GtkTreePath *source_row;
2602 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2603 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2605 source_row = gtk_tree_model_get_path (model, &iter);
2606 gtk_tree_set_row_drag_data (selection_data,
2610 gtk_tree_path_free (source_row);
2614 typedef struct _DndHelper {
2615 ModestFolderView *folder_view;
2616 gboolean delete_source;
2617 GtkTreePath *source_row;
2621 dnd_helper_destroyer (DndHelper *helper)
2623 /* Free the helper */
2624 gtk_tree_path_free (helper->source_row);
2625 g_slice_free (DndHelper, helper);
2629 xfer_folder_cb (ModestMailOperation *mail_op,
2630 TnyFolder *new_folder,
2634 /* Select the folder */
2635 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2641 /* get the folder for the row the treepath refers to. */
2642 /* folder must be unref'd */
2643 static TnyFolderStore *
2644 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2647 TnyFolderStore *folder = NULL;
2649 if (gtk_tree_model_get_iter (model,&iter, path))
2650 gtk_tree_model_get (model, &iter,
2651 INSTANCE_COLUMN, &folder,
2658 * This function is used by drag_data_received_cb to manage drag and
2659 * drop of a header, i.e, and drag from the header view to the folder
2663 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2664 GtkTreeModel *dest_model,
2665 GtkTreePath *dest_row,
2666 GtkSelectionData *selection_data)
2668 TnyList *headers = NULL;
2669 TnyFolder *folder = NULL, *src_folder = NULL;
2670 TnyFolderType folder_type;
2671 GtkTreeIter source_iter, dest_iter;
2672 ModestWindowMgr *mgr = NULL;
2673 ModestWindow *main_win = NULL;
2674 gchar **uris, **tmp;
2676 /* Build the list of headers */
2677 mgr = modest_runtime_get_window_mgr ();
2678 headers = tny_simple_list_new ();
2679 uris = modest_dnd_selection_data_get_paths (selection_data);
2682 while (*tmp != NULL) {
2685 gboolean first = TRUE;
2688 path = gtk_tree_path_new_from_string (*tmp);
2689 gtk_tree_model_get_iter (source_model, &source_iter, path);
2690 gtk_tree_model_get (source_model, &source_iter,
2691 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2694 /* Do not enable d&d of headers already opened */
2695 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2696 tny_list_append (headers, G_OBJECT (header));
2698 if (G_UNLIKELY (first)) {
2699 src_folder = tny_header_get_folder (header);
2703 /* Free and go on */
2704 gtk_tree_path_free (path);
2705 g_object_unref (header);
2710 /* This could happen ig we perform a d&d very quickly over the
2711 same row that row could dissapear because message is
2713 if (!TNY_IS_FOLDER (src_folder))
2716 /* Get the target folder */
2717 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2718 gtk_tree_model_get (dest_model, &dest_iter,
2722 if (!folder || !TNY_IS_FOLDER(folder)) {
2723 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2727 folder_type = modest_tny_folder_guess_folder_type (folder);
2728 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2729 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2730 goto cleanup; /* cannot move messages there */
2733 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2734 /* g_warning ("folder not writable"); */
2735 goto cleanup; /* verboten! */
2738 /* Ask for confirmation to move */
2739 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2741 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2745 /* Transfer messages */
2746 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2751 if (G_IS_OBJECT (src_folder))
2752 g_object_unref (src_folder);
2753 if (G_IS_OBJECT(folder))
2754 g_object_unref (G_OBJECT (folder));
2755 if (G_IS_OBJECT(headers))
2756 g_object_unref (headers);
2760 TnyFolderStore *src_folder;
2761 TnyFolderStore *dst_folder;
2762 ModestFolderView *folder_view;
2767 dnd_folder_info_destroyer (DndFolderInfo *info)
2769 if (info->src_folder)
2770 g_object_unref (info->src_folder);
2771 if (info->dst_folder)
2772 g_object_unref (info->dst_folder);
2773 g_slice_free (DndFolderInfo, info);
2777 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2778 GtkWindow *parent_window,
2779 TnyAccount *account)
2782 modest_ui_actions_on_account_connection_error (parent_window, account);
2784 /* Free the helper & info */
2785 dnd_helper_destroyer (info->helper);
2786 dnd_folder_info_destroyer (info);
2790 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2792 GtkWindow *parent_window,
2793 TnyAccount *account,
2796 DndFolderInfo *info = NULL;
2797 ModestMailOperation *mail_op;
2799 info = (DndFolderInfo *) user_data;
2801 if (err || canceled) {
2802 dnd_on_connection_failed_destroyer (info, parent_window, account);
2806 /* Do the mail operation */
2807 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2808 modest_ui_actions_move_folder_error_handler,
2809 info->src_folder, NULL);
2811 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2814 /* Transfer the folder */
2815 modest_mail_operation_xfer_folder (mail_op,
2816 TNY_FOLDER (info->src_folder),
2818 info->helper->delete_source,
2820 info->helper->folder_view);
2823 g_object_unref (G_OBJECT (mail_op));
2824 dnd_helper_destroyer (info->helper);
2825 dnd_folder_info_destroyer (info);
2830 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2832 GtkWindow *parent_window,
2833 TnyAccount *account,
2836 DndFolderInfo *info = NULL;
2838 info = (DndFolderInfo *) user_data;
2840 if (err || canceled) {
2841 dnd_on_connection_failed_destroyer (info, parent_window, account);
2845 /* Connect to source folder and perform the copy/move */
2846 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2848 drag_and_drop_from_folder_view_src_folder_performer,
2853 * This function is used by drag_data_received_cb to manage drag and
2854 * drop of a folder, i.e, and drag from the folder view to the same
2858 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2859 GtkTreeModel *dest_model,
2860 GtkTreePath *dest_row,
2861 GtkSelectionData *selection_data,
2864 GtkTreeIter dest_iter, iter;
2865 TnyFolderStore *dest_folder = NULL;
2866 TnyFolderStore *folder = NULL;
2867 gboolean forbidden = FALSE;
2869 DndFolderInfo *info = NULL;
2871 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2873 g_warning ("%s: BUG: no main window", __FUNCTION__);
2874 dnd_helper_destroyer (helper);
2879 /* check the folder rules for the destination */
2880 folder = tree_path_to_folder (dest_model, dest_row);
2881 if (TNY_IS_FOLDER(folder)) {
2882 ModestTnyFolderRules rules =
2883 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2884 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2885 } else if (TNY_IS_FOLDER_STORE(folder)) {
2886 /* enable local root as destination for folders */
2887 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2888 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2891 g_object_unref (folder);
2894 /* check the folder rules for the source */
2895 folder = tree_path_to_folder (source_model, helper->source_row);
2896 if (TNY_IS_FOLDER(folder)) {
2897 ModestTnyFolderRules rules =
2898 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2899 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2902 g_object_unref (folder);
2906 /* Check if the drag is possible */
2907 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2909 modest_platform_run_information_dialog ((GtkWindow *) win,
2910 _("mail_in_ui_folder_move_target_error"),
2912 /* Restore the previous selection */
2913 folder = tree_path_to_folder (source_model, helper->source_row);
2915 if (TNY_IS_FOLDER (folder))
2916 modest_folder_view_select_folder (helper->folder_view,
2917 TNY_FOLDER (folder), FALSE);
2918 g_object_unref (folder);
2920 dnd_helper_destroyer (helper);
2925 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2926 gtk_tree_model_get (dest_model, &dest_iter,
2929 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2930 gtk_tree_model_get (source_model, &iter,
2934 /* Create the info for the performer */
2935 info = g_slice_new0 (DndFolderInfo);
2936 info->src_folder = g_object_ref (folder);
2937 info->dst_folder = g_object_ref (dest_folder);
2938 info->helper = helper;
2940 /* Connect to the destination folder and perform the copy/move */
2941 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2943 drag_and_drop_from_folder_view_dst_folder_performer,
2947 g_object_unref (dest_folder);
2948 g_object_unref (folder);
2952 * This function receives the data set by the "drag-data-get" signal
2953 * handler. This information comes within the #GtkSelectionData. This
2954 * function will manage both the drags of folders of the treeview and
2955 * drags of headers of the header view widget.
2958 on_drag_data_received (GtkWidget *widget,
2959 GdkDragContext *context,
2962 GtkSelectionData *selection_data,
2967 GtkWidget *source_widget;
2968 GtkTreeModel *dest_model, *source_model;
2969 GtkTreePath *source_row, *dest_row;
2970 GtkTreeViewDropPosition pos;
2971 gboolean delete_source = FALSE;
2972 gboolean success = FALSE;
2974 /* Do not allow further process */
2975 g_signal_stop_emission_by_name (widget, "drag-data-received");
2976 source_widget = gtk_drag_get_source_widget (context);
2978 /* Get the action */
2979 if (context->action == GDK_ACTION_MOVE) {
2980 delete_source = TRUE;
2982 /* Notify that there is no folder selected. We need to
2983 do this in order to update the headers view (and
2984 its monitors, because when moving, the old folder
2985 won't longer exist. We can not wait for the end of
2986 the operation, because the operation won't start if
2987 the folder is in use */
2988 if (source_widget == widget) {
2989 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2990 gtk_tree_selection_unselect_all (sel);
2994 /* Check if the get_data failed */
2995 if (selection_data == NULL || selection_data->length < 0)
2998 /* Select the destination model */
2999 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3001 /* Get the path to the destination row. Can not call
3002 gtk_tree_view_get_drag_dest_row() because the source row
3003 is not selected anymore */
3004 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3007 /* Only allow drops IN other rows */
3009 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3010 pos == GTK_TREE_VIEW_DROP_AFTER)
3014 /* Drags from the header view */
3015 if (source_widget != widget) {
3016 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3018 drag_and_drop_from_header_view (source_model,
3023 DndHelper *helper = NULL;
3025 /* Get the source model and row */
3026 gtk_tree_get_row_drag_data (selection_data,
3030 /* Create the helper */
3031 helper = g_slice_new0 (DndHelper);
3032 helper->delete_source = delete_source;
3033 helper->source_row = gtk_tree_path_copy (source_row);
3034 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3036 drag_and_drop_from_folder_view (source_model,
3042 gtk_tree_path_free (source_row);
3046 gtk_tree_path_free (dest_row);
3049 /* Finish the drag and drop */
3050 gtk_drag_finish (context, success, FALSE, time);
3054 * We define a "drag-drop" signal handler because we do not want to
3055 * use the default one, because the default one always calls
3056 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3057 * signal handler, because there we have all the information available
3058 * to know if the dnd was a success or not.
3061 drag_drop_cb (GtkWidget *widget,
3062 GdkDragContext *context,
3070 if (!context->targets)
3073 /* Check if we're dragging a folder row */
3074 target = gtk_drag_dest_find_target (widget, context, NULL);
3076 /* Request the data from the source. */
3077 gtk_drag_get_data(widget, context, target, time);
3083 * This function expands a node of a tree view if it's not expanded
3084 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3085 * does that, so that's why they're here.
3088 expand_row_timeout (gpointer data)
3090 GtkTreeView *tree_view = data;
3091 GtkTreePath *dest_path = NULL;
3092 GtkTreeViewDropPosition pos;
3093 gboolean result = FALSE;
3095 gdk_threads_enter ();
3097 gtk_tree_view_get_drag_dest_row (tree_view,
3102 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3103 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3104 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3105 gtk_tree_path_free (dest_path);
3109 gtk_tree_path_free (dest_path);
3114 gdk_threads_leave ();
3120 * This function is called whenever the pointer is moved over a widget
3121 * while dragging some data. It installs a timeout that will expand a
3122 * node of the treeview if not expanded yet. This function also calls
3123 * gdk_drag_status in order to set the suggested action that will be
3124 * used by the "drag-data-received" signal handler to know if we
3125 * should do a move or just a copy of the data.
3128 on_drag_motion (GtkWidget *widget,
3129 GdkDragContext *context,
3135 GtkTreeViewDropPosition pos;
3136 GtkTreePath *dest_row;
3137 GtkTreeModel *dest_model;
3138 ModestFolderViewPrivate *priv;
3139 GdkDragAction suggested_action;
3140 gboolean valid_location = FALSE;
3141 TnyFolderStore *folder = NULL;
3143 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3145 if (priv->timer_expander != 0) {
3146 g_source_remove (priv->timer_expander);
3147 priv->timer_expander = 0;
3150 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3155 /* Do not allow drops between folders */
3157 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3158 pos == GTK_TREE_VIEW_DROP_AFTER) {
3159 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3160 gdk_drag_status(context, 0, time);
3161 valid_location = FALSE;
3164 valid_location = TRUE;
3167 /* Check that the destination folder is writable */
3168 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3169 folder = tree_path_to_folder (dest_model, dest_row);
3170 if (folder && TNY_IS_FOLDER (folder)) {
3171 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3173 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3174 valid_location = FALSE;
3179 /* Expand the selected row after 1/2 second */
3180 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3181 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3183 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3185 /* Select the desired action. By default we pick MOVE */
3186 suggested_action = GDK_ACTION_MOVE;
3188 if (context->actions == GDK_ACTION_COPY)
3189 gdk_drag_status(context, GDK_ACTION_COPY, time);
3190 else if (context->actions == GDK_ACTION_MOVE)
3191 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3192 else if (context->actions & suggested_action)
3193 gdk_drag_status(context, suggested_action, time);
3195 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3199 g_object_unref (folder);
3201 gtk_tree_path_free (dest_row);
3203 g_signal_stop_emission_by_name (widget, "drag-motion");
3205 return valid_location;
3209 * This function sets the treeview as a source and a target for dnd
3210 * events. It also connects all the requirede signals.
3213 setup_drag_and_drop (GtkTreeView *self)
3215 /* Set up the folder view as a dnd destination. Set only the
3216 highlight flag, otherwise gtk will have a different
3218 #ifdef MODEST_TOOLKIT_HILDON2
3221 gtk_drag_dest_set (GTK_WIDGET (self),
3222 GTK_DEST_DEFAULT_HIGHLIGHT,
3223 folder_view_drag_types,
3224 G_N_ELEMENTS (folder_view_drag_types),
3225 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3227 g_signal_connect (G_OBJECT (self),
3228 "drag_data_received",
3229 G_CALLBACK (on_drag_data_received),
3233 /* Set up the treeview as a dnd source */
3234 gtk_drag_source_set (GTK_WIDGET (self),
3236 folder_view_drag_types,
3237 G_N_ELEMENTS (folder_view_drag_types),
3238 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3240 g_signal_connect (G_OBJECT (self),
3242 G_CALLBACK (on_drag_motion),
3245 g_signal_connect (G_OBJECT (self),
3247 G_CALLBACK (on_drag_data_get),
3250 g_signal_connect (G_OBJECT (self),
3252 G_CALLBACK (drag_drop_cb),
3257 * This function manages the navigation through the folders using the
3258 * keyboard or the hardware keys in the device
3261 on_key_pressed (GtkWidget *self,
3265 GtkTreeSelection *selection;
3267 GtkTreeModel *model;
3268 gboolean retval = FALSE;
3270 /* Up and Down are automatically managed by the treeview */
3271 if (event->keyval == GDK_Return) {
3272 /* Expand/Collapse the selected row */
3273 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3274 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3277 path = gtk_tree_model_get_path (model, &iter);
3279 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3280 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3282 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3283 gtk_tree_path_free (path);
3285 /* No further processing */
3293 * We listen to the changes in the local folder account name key,
3294 * because we want to show the right name in the view. The local
3295 * folder account name corresponds to the device name in the Maemo
3296 * version. We do this because we do not want to query gconf on each
3297 * tree view refresh. It's better to cache it and change whenever
3301 on_configuration_key_changed (ModestConf* conf,
3303 ModestConfEvent event,
3304 ModestConfNotificationId id,
3305 ModestFolderView *self)
3307 ModestFolderViewPrivate *priv;
3310 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3311 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3313 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3314 g_free (priv->local_account_name);
3316 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3317 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3319 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3320 MODEST_CONF_DEVICE_NAME, NULL);
3322 /* Force a redraw */
3323 #if GTK_CHECK_VERSION(2, 8, 0)
3324 GtkTreeViewColumn * tree_column;
3326 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3328 gtk_tree_view_column_queue_resize (tree_column);
3330 gtk_widget_queue_draw (GTK_WIDGET (self));
3336 modest_folder_view_set_style (ModestFolderView *self,
3337 ModestFolderViewStyle style)
3339 ModestFolderViewPrivate *priv;
3341 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3342 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3343 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3345 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3348 priv->style = style;
3352 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3353 const gchar *account_id)
3355 ModestFolderViewPrivate *priv;
3356 GtkTreeModel *model;
3358 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3360 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3362 /* This will be used by the filter_row callback,
3363 * to decided which rows to show: */
3364 if (priv->visible_account_id) {
3365 g_free (priv->visible_account_id);
3366 priv->visible_account_id = NULL;
3369 priv->visible_account_id = g_strdup (account_id);
3372 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3373 if (GTK_IS_TREE_MODEL_FILTER (model))
3374 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3376 /* Save settings to gconf */
3377 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3378 MODEST_CONF_FOLDER_VIEW_KEY);
3380 /* Notify observers */
3381 g_signal_emit (G_OBJECT(self),
3382 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3387 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3389 ModestFolderViewPrivate *priv;
3391 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3393 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3395 return (const gchar *) priv->visible_account_id;
3399 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3403 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3405 gtk_tree_model_get (model, iter,
3409 gboolean result = FALSE;
3410 if (type == TNY_FOLDER_TYPE_INBOX) {
3414 *inbox_iter = *iter;
3418 if (gtk_tree_model_iter_children (model, &child, iter)) {
3419 if (find_inbox_iter (model, &child, inbox_iter))
3423 } while (gtk_tree_model_iter_next (model, iter));
3432 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3434 #ifndef MODEST_TOOLKIT_HILDON2
3435 GtkTreeModel *model;
3436 GtkTreeIter iter, inbox_iter;
3437 GtkTreeSelection *sel;
3438 GtkTreePath *path = NULL;
3440 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3442 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3446 expand_root_items (self);
3447 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3449 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3450 g_warning ("%s: model is empty", __FUNCTION__);
3454 if (find_inbox_iter (model, &iter, &inbox_iter))
3455 path = gtk_tree_model_get_path (model, &inbox_iter);
3457 path = gtk_tree_path_new_first ();
3459 /* Select the row and free */
3460 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3461 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3462 gtk_tree_path_free (path);
3465 gtk_widget_grab_focus (GTK_WIDGET(self));
3472 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3477 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3478 TnyFolder* a_folder;
3481 gtk_tree_model_get (model, iter,
3482 INSTANCE_COLUMN, &a_folder,
3488 if (folder == a_folder) {
3489 g_object_unref (a_folder);
3490 *folder_iter = *iter;
3493 g_object_unref (a_folder);
3495 if (gtk_tree_model_iter_children (model, &child, iter)) {
3496 if (find_folder_iter (model, &child, folder_iter, folder))
3500 } while (gtk_tree_model_iter_next (model, iter));
3505 #ifndef MODEST_TOOLKIT_HILDON2
3507 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3510 ModestFolderView *self)
3512 ModestFolderViewPrivate *priv = NULL;
3513 GtkTreeSelection *sel;
3514 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3515 GObject *instance = NULL;
3517 if (!MODEST_IS_FOLDER_VIEW(self))
3520 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3522 priv->reexpand = TRUE;
3524 gtk_tree_model_get (tree_model, iter,
3526 INSTANCE_COLUMN, &instance,
3532 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3533 priv->folder_to_select = g_object_ref (instance);
3535 g_object_unref (instance);
3537 if (priv->folder_to_select) {
3539 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3542 path = gtk_tree_model_get_path (tree_model, iter);
3543 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3545 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3547 gtk_tree_selection_select_iter (sel, iter);
3548 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3550 gtk_tree_path_free (path);
3554 modest_folder_view_disable_next_folder_selection (self);
3556 /* Refilter the model */
3557 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3563 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3565 ModestFolderViewPrivate *priv;
3567 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3569 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3571 if (priv->folder_to_select)
3572 g_object_unref(priv->folder_to_select);
3574 priv->folder_to_select = NULL;
3578 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3579 gboolean after_change)
3581 GtkTreeModel *model;
3582 GtkTreeIter iter, folder_iter;
3583 GtkTreeSelection *sel;
3584 ModestFolderViewPrivate *priv = NULL;
3586 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3587 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3589 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3592 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3593 gtk_tree_selection_unselect_all (sel);
3595 if (priv->folder_to_select)
3596 g_object_unref(priv->folder_to_select);
3597 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3601 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3606 /* Refilter the model, before selecting the folder */
3607 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3609 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3610 g_warning ("%s: model is empty", __FUNCTION__);
3614 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3617 path = gtk_tree_model_get_path (model, &folder_iter);
3618 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3620 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3621 gtk_tree_selection_select_iter (sel, &folder_iter);
3622 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3624 gtk_tree_path_free (path);
3632 modest_folder_view_copy_selection (ModestFolderView *self)
3634 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3636 /* Copy selection */
3637 _clipboard_set_selected_data (self, FALSE);
3641 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3643 ModestFolderViewPrivate *priv = NULL;
3644 GtkTreeModel *model = NULL;
3645 const gchar **hidding = NULL;
3646 guint i, n_selected;
3648 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3649 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3651 /* Copy selection */
3652 if (!_clipboard_set_selected_data (folder_view, TRUE))
3655 /* Get hidding ids */
3656 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3658 /* Clear hidding array created by previous cut operation */
3659 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3661 /* Copy hidding array */
3662 priv->n_selected = n_selected;
3663 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3664 for (i=0; i < n_selected; i++)
3665 priv->hidding_ids[i] = g_strdup(hidding[i]);
3667 /* Hide cut folders */
3668 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3669 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3673 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3674 ModestFolderView *folder_view_dst)
3676 GtkTreeModel *filter_model = NULL;
3677 GtkTreeModel *model = NULL;
3678 GtkTreeModel *new_filter_model = NULL;
3680 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3681 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3684 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3685 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3687 /* Build new filter model */
3688 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3689 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3693 /* Set copied model */
3694 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3695 #ifndef MODEST_TOOLKIT_HILDON2
3696 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3697 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3701 g_object_unref (new_filter_model);
3705 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3708 GtkTreeModel *model = NULL;
3709 ModestFolderViewPrivate* priv;
3711 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3713 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3714 priv->show_non_move = show;
3715 /* modest_folder_view_update_model(folder_view, */
3716 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3718 /* Hide special folders */
3719 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3720 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3721 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3725 /* Returns FALSE if it did not selected anything */
3727 _clipboard_set_selected_data (ModestFolderView *folder_view,
3730 ModestFolderViewPrivate *priv = NULL;
3731 TnyFolderStore *folder = NULL;
3732 gboolean retval = FALSE;
3734 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3735 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3737 /* Set selected data on clipboard */
3738 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3739 folder = modest_folder_view_get_selected (folder_view);
3741 /* Do not allow to select an account */
3742 if (TNY_IS_FOLDER (folder)) {
3743 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3748 g_object_unref (folder);
3754 _clear_hidding_filter (ModestFolderView *folder_view)
3756 ModestFolderViewPrivate *priv;
3759 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3760 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3762 if (priv->hidding_ids != NULL) {
3763 for (i=0; i < priv->n_selected; i++)
3764 g_free (priv->hidding_ids[i]);
3765 g_free(priv->hidding_ids);
3771 on_display_name_changed (ModestAccountMgr *mgr,
3772 const gchar *account,
3775 ModestFolderView *self;
3777 self = MODEST_FOLDER_VIEW (user_data);
3779 /* Force a redraw */
3780 #if GTK_CHECK_VERSION(2, 8, 0)
3781 GtkTreeViewColumn * tree_column;
3783 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3785 gtk_tree_view_column_queue_resize (tree_column);
3787 gtk_widget_queue_draw (GTK_WIDGET (self));
3792 modest_folder_view_set_cell_style (ModestFolderView *self,
3793 ModestFolderViewCellStyle cell_style)
3795 ModestFolderViewPrivate *priv = NULL;
3797 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3798 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3800 priv->cell_style = cell_style;
3802 g_object_set (G_OBJECT (priv->messages_renderer),
3803 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3806 gtk_widget_queue_draw (GTK_WIDGET (self));
3810 update_style (ModestFolderView *self)
3812 ModestFolderViewPrivate *priv;
3813 GdkColor style_color;
3814 PangoAttrList *attr_list;
3816 PangoAttribute *attr;
3818 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3819 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3823 attr_list = pango_attr_list_new ();
3824 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3825 gdk_color_parse ("grey", &style_color);
3827 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3828 pango_attr_list_insert (attr_list, attr);
3831 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3833 "SmallSystemFont", NULL,
3836 attr = pango_attr_font_desc_new (pango_font_description_copy
3837 (style->font_desc));
3838 pango_attr_list_insert (attr_list, attr);
3840 g_object_set (G_OBJECT (priv->messages_renderer),
3841 "foreground-gdk", &style_color,
3842 "foreground-set", TRUE,
3843 "attributes", attr_list,
3845 pango_attr_list_unref (attr_list);
3850 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3852 if (strcmp ("style", spec->name) == 0) {
3853 update_style (MODEST_FOLDER_VIEW (obj));
3854 gtk_widget_queue_draw (GTK_WIDGET (obj));
3859 modest_folder_view_set_filter (ModestFolderView *self,
3860 ModestFolderViewFilter filter)
3862 ModestFolderViewPrivate *priv;
3863 GtkTreeModel *filter_model;
3865 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3866 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3868 priv->filter |= filter;
3870 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3871 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3872 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3877 modest_folder_view_unset_filter (ModestFolderView *self,
3878 ModestFolderViewFilter filter)
3880 ModestFolderViewPrivate *priv;
3881 GtkTreeModel *filter_model;
3883 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3884 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3886 priv->filter &= ~filter;
3888 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3889 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3890 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3895 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3896 ModestTnyFolderRules rules)
3898 GtkTreeModel *filter_model;
3900 gboolean fulfil = FALSE;
3902 if (!get_inner_models (self, &filter_model, NULL, NULL))
3905 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3909 TnyFolderStore *folder;
3911 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3913 if (TNY_IS_FOLDER (folder)) {
3914 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3915 /* Folder rules are negative: non_writable, non_deletable... */
3916 if (!(folder_rules & rules))
3919 g_object_unref (folder);
3922 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3928 modest_folder_view_set_list_to_move (ModestFolderView *self,
3931 ModestFolderViewPrivate *priv;
3933 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3934 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3936 if (priv->list_to_move)
3937 g_object_unref (priv->list_to_move);
3940 g_object_ref (list);
3942 priv->list_to_move = list;
3946 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3948 ModestFolderViewPrivate *priv;
3950 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3951 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3954 g_free (priv->mailbox);
3956 priv->mailbox = g_strdup (mailbox);
3958 /* Notify observers */
3959 g_signal_emit (G_OBJECT(self),
3960 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3961 priv->visible_account_id);
3965 modest_folder_view_get_mailbox (ModestFolderView *self)
3967 ModestFolderViewPrivate *priv;
3969 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3970 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3972 return (const gchar *) priv->mailbox;