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"
65 #include <modest-account-protocol.h>
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
77 /* Column names depending on we use list store or tree store */
78 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
79 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
80 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
81 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
82 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
84 /* 'private'/'protected' functions */
85 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
86 static void modest_folder_view_init (ModestFolderView *obj);
87 static void modest_folder_view_finalize (GObject *obj);
88 static void modest_folder_view_dispose (GObject *obj);
90 static void tny_account_store_view_init (gpointer g,
93 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
94 TnyAccountStore *account_store);
96 static void on_selection_changed (GtkTreeSelection *sel,
99 static void on_row_activated (GtkTreeView *treeview,
101 GtkTreeViewColumn *column,
104 static void on_account_removed (TnyAccountStore *self,
108 static void on_account_inserted (TnyAccountStore *self,
112 static void on_account_changed (TnyAccountStore *self,
116 static gint cmp_rows (GtkTreeModel *tree_model,
121 static gboolean filter_row (GtkTreeModel *model,
125 static void on_configuration_key_changed (ModestConf* conf,
127 ModestConfEvent event,
128 ModestConfNotificationId notification_id,
129 ModestFolderView *self);
131 static void expand_root_items (ModestFolderView *self);
133 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
136 static void _clear_hidding_filter (ModestFolderView *folder_view);
138 static void on_display_name_changed (ModestAccountMgr *self,
139 const gchar *account,
141 static void update_style (ModestFolderView *self);
142 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
143 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
144 static gboolean inbox_is_special (TnyFolderStore *folder_store);
146 static gboolean get_inner_models (ModestFolderView *self,
147 GtkTreeModel **filter_model,
148 GtkTreeModel **sort_model,
149 GtkTreeModel **tny_model);
150 static void on_activity_changed (TnyGtkFolderListStore *store,
152 ModestFolderView *folder_view);
155 FOLDER_SELECTION_CHANGED_SIGNAL,
156 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
157 FOLDER_ACTIVATED_SIGNAL,
158 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
159 ACTIVITY_CHANGED_SIGNAL,
163 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
164 struct _ModestFolderViewPrivate {
165 TnyAccountStore *account_store;
166 TnyFolderStore *cur_folder_store;
168 TnyFolder *folder_to_select; /* folder to select after the next update */
170 gulong changed_signal;
171 gulong account_inserted_signal;
172 gulong account_removed_signal;
173 gulong account_changed_signal;
174 gulong conf_key_signal;
175 gulong display_name_changed_signal;
177 /* not unref this object, its a singlenton */
178 ModestEmailClipboard *clipboard;
180 /* Filter tree model */
183 ModestFolderViewFilter filter;
185 TnyFolderStoreQuery *query;
187 guint timer_expander;
189 gchar *local_account_name;
190 gchar *visible_account_id;
192 ModestFolderViewStyle style;
193 ModestFolderViewCellStyle cell_style;
194 gboolean show_message_count;
196 gboolean reselect; /* we use this to force a reselection of the INBOX */
197 gboolean show_non_move;
198 TnyList *list_to_move;
199 gboolean reexpand; /* next time we expose, we'll expand all root folders */
201 GtkCellRenderer *messages_renderer;
203 gulong outbox_deleted_handler;
205 GSList *signal_handlers;
206 GdkColor active_color;
208 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
209 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
210 MODEST_TYPE_FOLDER_VIEW, \
211 ModestFolderViewPrivate))
213 static GObjectClass *parent_class = NULL;
215 static guint signals[LAST_SIGNAL] = {0};
218 modest_folder_view_get_type (void)
220 static GType my_type = 0;
222 static const GTypeInfo my_info = {
223 sizeof(ModestFolderViewClass),
224 NULL, /* base init */
225 NULL, /* base finalize */
226 (GClassInitFunc) modest_folder_view_class_init,
227 NULL, /* class finalize */
228 NULL, /* class data */
229 sizeof(ModestFolderView),
231 (GInstanceInitFunc) modest_folder_view_init,
235 static const GInterfaceInfo tny_account_store_view_info = {
236 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
237 NULL, /* interface_finalize */
238 NULL /* interface_data */
242 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
246 g_type_add_interface_static (my_type,
247 TNY_TYPE_ACCOUNT_STORE_VIEW,
248 &tny_account_store_view_info);
254 modest_folder_view_class_init (ModestFolderViewClass *klass)
256 GObjectClass *gobject_class;
257 GtkTreeViewClass *treeview_class;
258 gobject_class = (GObjectClass*) klass;
259 treeview_class = (GtkTreeViewClass*) klass;
261 parent_class = g_type_class_peek_parent (klass);
262 gobject_class->finalize = modest_folder_view_finalize;
263 gobject_class->dispose = modest_folder_view_dispose;
265 g_type_class_add_private (gobject_class,
266 sizeof(ModestFolderViewPrivate));
268 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
269 g_signal_new ("folder_selection_changed",
270 G_TYPE_FROM_CLASS (gobject_class),
272 G_STRUCT_OFFSET (ModestFolderViewClass,
273 folder_selection_changed),
275 modest_marshal_VOID__POINTER_BOOLEAN,
276 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
279 * This signal is emitted whenever the currently selected
280 * folder display name is computed. Note that the name could
281 * be different to the folder name, because we could append
282 * the unread messages count to the folder name to build the
283 * folder display name
285 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
286 g_signal_new ("folder-display-name-changed",
287 G_TYPE_FROM_CLASS (gobject_class),
289 G_STRUCT_OFFSET (ModestFolderViewClass,
290 folder_display_name_changed),
292 g_cclosure_marshal_VOID__STRING,
293 G_TYPE_NONE, 1, G_TYPE_STRING);
295 signals[FOLDER_ACTIVATED_SIGNAL] =
296 g_signal_new ("folder_activated",
297 G_TYPE_FROM_CLASS (gobject_class),
299 G_STRUCT_OFFSET (ModestFolderViewClass,
302 g_cclosure_marshal_VOID__POINTER,
303 G_TYPE_NONE, 1, G_TYPE_POINTER);
306 * Emitted whenever the visible account changes
308 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
309 g_signal_new ("visible-account-changed",
310 G_TYPE_FROM_CLASS (gobject_class),
312 G_STRUCT_OFFSET (ModestFolderViewClass,
313 visible_account_changed),
315 g_cclosure_marshal_VOID__STRING,
316 G_TYPE_NONE, 1, G_TYPE_STRING);
319 * Emitted when the underlying GtkListStore is updating data
321 signals[ACTIVITY_CHANGED_SIGNAL] =
322 g_signal_new ("activity-changed",
323 G_TYPE_FROM_CLASS (gobject_class),
325 G_STRUCT_OFFSET (ModestFolderViewClass,
328 g_cclosure_marshal_VOID__BOOLEAN,
329 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
331 treeview_class->select_cursor_parent = NULL;
333 #ifdef MODEST_TOOLKIT_HILDON2
334 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
340 /* Retrieves the filter, sort and tny models of the folder view. If
341 any of these does not exist then it returns FALSE */
343 get_inner_models (ModestFolderView *self,
344 GtkTreeModel **filter_model,
345 GtkTreeModel **sort_model,
346 GtkTreeModel **tny_model)
348 GtkTreeModel *s_model, *f_model, *t_model;
350 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
351 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
352 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
356 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
357 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
358 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
362 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
366 *filter_model = f_model;
368 *sort_model = s_model;
370 *tny_model = t_model;
375 /* Simplify checks for NULLs: */
377 strings_are_equal (const gchar *a, const gchar *b)
383 return (strcmp (a, b) == 0);
390 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
392 GObject *instance = NULL;
394 gtk_tree_model_get (model, iter,
395 INSTANCE_COLUMN, &instance,
399 return FALSE; /* keep walking */
401 if (!TNY_IS_ACCOUNT (instance)) {
402 g_object_unref (instance);
403 return FALSE; /* keep walking */
406 /* Check if this is the looked-for account: */
407 TnyAccount *this_account = TNY_ACCOUNT (instance);
408 TnyAccount *account = TNY_ACCOUNT (data);
410 const gchar *this_account_id = tny_account_get_id(this_account);
411 const gchar *account_id = tny_account_get_id(account);
412 g_object_unref (instance);
415 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
416 if (strings_are_equal(this_account_id, account_id)) {
417 /* Tell the model that the data has changed, so that
418 * it calls the cell_data_func callbacks again: */
419 /* TODO: This does not seem to actually cause the new string to be shown: */
420 gtk_tree_model_row_changed (model, path, iter);
422 return TRUE; /* stop walking */
425 return FALSE; /* keep walking */
430 ModestFolderView *self;
431 gchar *previous_name;
432 } GetMmcAccountNameData;
435 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
437 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
439 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
441 if (!strings_are_equal (
442 tny_account_get_name(TNY_ACCOUNT(account)),
443 data->previous_name)) {
445 /* Tell the model that the data has changed, so that
446 * it calls the cell_data_func callbacks again: */
447 ModestFolderView *self = data->self;
448 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
450 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
453 g_free (data->previous_name);
454 g_slice_free (GetMmcAccountNameData, data);
458 convert_parent_folders_to_dots (gchar **item_name)
461 gint n_inbox_parents = 0;
464 gchar *last_separator;
466 if (item_name == NULL)
469 path_start = *item_name;
470 for (c = *item_name; *c != '\0'; c++) {
471 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
473 if (c != path_start) {
474 compare = g_strndup (path_start, c - path_start);
475 compare = g_strstrip (compare);
476 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
486 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
487 if (last_separator != NULL) {
488 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
495 buffer = g_string_new ("");
496 for (i = 0; i < n_parents - n_inbox_parents; i++) {
497 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
499 buffer = g_string_append (buffer, last_separator);
501 *item_name = g_string_free (buffer, FALSE);
507 format_compact_style (gchar **item_name,
509 const gchar *mailbox,
511 gboolean multiaccount,
512 gboolean *use_markup)
516 TnyFolderType folder_type;
518 if (!TNY_IS_FOLDER (instance))
521 folder = (TnyFolder *) instance;
523 folder_type = tny_folder_get_folder_type (folder);
524 is_special = (get_cmp_pos (folder_type, folder)!= 4);
527 /* Remove mailbox prefix if any */
528 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
529 if (g_str_has_prefix (*item_name, prefix)) {
530 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
532 *item_name = new_item_name;
536 if (!is_special || multiaccount) {
537 TnyAccount *account = tny_folder_get_account (folder);
538 const gchar *folder_name;
539 gboolean concat_folder_name = FALSE;
542 /* Should not happen */
546 /* convert parent folders to dots */
547 convert_parent_folders_to_dots (item_name);
549 folder_name = tny_folder_get_name (folder);
550 if (g_str_has_suffix (*item_name, folder_name)) {
551 gchar *offset = g_strrstr (*item_name, folder_name);
553 concat_folder_name = TRUE;
556 buffer = g_string_new ("");
558 buffer = g_string_append (buffer, *item_name);
559 if (concat_folder_name) {
560 if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
561 buffer = g_string_append (buffer, folder_name);
562 /* TODO: append a sensitive string to the remote drafts to
563 * be able to know it's the remote one */
564 /* buffer = g_string_append (buffer, " (TODO:remote)"); */
566 buffer = g_string_append (buffer, folder_name);
570 g_object_unref (account);
572 *item_name = g_string_free (buffer, FALSE);
580 replace_special_folder_prefix (gchar **item_name)
582 const gchar *separator;
585 if (item_name == NULL || *item_name == NULL || **item_name == '\0')
587 separator = g_strstr_len (*item_name, -1, MODEST_FOLDER_PATH_SEPARATOR);
588 if (separator == NULL)
591 prefix = g_strndup (*item_name, separator - *item_name);
594 if (prefix && *prefix != '\0') {
595 TnyFolderType folder_type;
599 downcase = g_utf8_strdown (prefix, -1);
603 if (strcmp (downcase, "inbox") == 0) {
604 folder_type = TNY_FOLDER_TYPE_INBOX;
605 new_name = g_strconcat (_("mcen_me_folder_inbox"), separator, NULL);
607 *item_name = new_name;
609 folder_type = modest_local_folder_info_get_type (prefix);
610 switch (folder_type) {
611 case TNY_FOLDER_TYPE_INBOX:
612 case TNY_FOLDER_TYPE_ARCHIVE:
613 new_name = g_strconcat (modest_local_folder_info_get_type_display_name (folder_type), separator, NULL);
615 *item_name = new_name;
626 text_cell_data (GtkTreeViewColumn *column,
627 GtkCellRenderer *renderer,
628 GtkTreeModel *tree_model,
632 ModestFolderViewPrivate *priv;
633 GObject *rendobj = (GObject *) renderer;
635 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
636 GObject *instance = NULL;
637 gboolean use_markup = FALSE;
639 gtk_tree_model_get (tree_model, iter,
642 INSTANCE_COLUMN, &instance,
644 if (!fname || !instance)
647 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
648 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
650 gchar *item_name = NULL;
651 gint item_weight = 400;
653 if (type != TNY_FOLDER_TYPE_ROOT) {
658 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
659 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
662 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
663 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
665 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
668 /* Sometimes an special folder is reported by the server as
669 NORMAL, like some versions of Dovecot */
670 if (type == TNY_FOLDER_TYPE_NORMAL ||
671 type == TNY_FOLDER_TYPE_UNKNOWN) {
672 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
676 /* note: we cannot reliably get the counts from the
677 * tree model, we need to use explicit calls on
678 * tny_folder for some reason. Select the number to
679 * show: the unread or unsent messages. in case of
680 * outbox/drafts, show all */
681 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
682 (type == TNY_FOLDER_TYPE_OUTBOX) ||
683 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
684 number = tny_folder_get_all_count (TNY_FOLDER(instance));
687 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
691 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
692 item_name = g_strdup (fname);
699 /* Use bold font style if there are unread or unset messages */
701 if (priv->show_message_count) {
702 item_name = g_strdup_printf ("%s (%d)", fname, number);
704 item_name = g_strdup (fname);
708 item_name = g_strdup (fname);
713 } else if (TNY_IS_ACCOUNT (instance)) {
714 /* If it's a server account */
715 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
716 item_name = g_strdup (priv->local_account_name);
718 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
719 /* fname is only correct when the items are first
720 * added to the model, not when the account is
721 * changed later, so get the name from the account
723 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
726 item_name = g_strdup (fname);
732 if (type == TNY_FOLDER_TYPE_INBOX &&
733 g_str_has_suffix (fname, "Inbox")) {
735 item_name = g_strdup (_("mcen_me_folder_inbox"));
739 item_name = g_strdup ("unknown");
741 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
742 gboolean multiaccount;
744 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
745 /* Convert item_name to markup */
746 format_compact_style (&item_name, instance, priv->mailbox,
748 multiaccount, &use_markup);
750 replace_special_folder_prefix (&item_name);
753 if (item_name && item_weight) {
754 /* Set the name in the treeview cell: */
755 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
756 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
757 g_object_set (rendobj,
760 "foreground-set", TRUE,
761 "foreground-gdk", &(priv->active_color),
764 g_object_set (rendobj,
766 "foreground-set", FALSE,
768 "weight", item_weight,
772 /* Notify display name observers */
773 /* TODO: What listens for this signal, and how can it use only the new name? */
774 if (((GObject *) priv->cur_folder_store) == instance) {
775 g_signal_emit (G_OBJECT(self),
776 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
783 /* If it is a Memory card account, make sure that we have the correct name.
784 * This function will be trigerred again when the name has been retrieved: */
785 if (TNY_IS_STORE_ACCOUNT (instance) &&
786 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
788 /* Get the account name asynchronously: */
789 GetMmcAccountNameData *callback_data =
790 g_slice_new0(GetMmcAccountNameData);
791 callback_data->self = self;
793 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
795 callback_data->previous_name = g_strdup (name);
797 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
798 on_get_mmc_account_name, callback_data);
802 g_object_unref (G_OBJECT (instance));
808 messages_cell_data (GtkTreeViewColumn *column,
809 GtkCellRenderer *renderer,
810 GtkTreeModel *tree_model,
814 ModestFolderView *self;
815 ModestFolderViewPrivate *priv;
816 GObject *rendobj = (GObject *) renderer;
817 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
818 GObject *instance = NULL;
819 gchar *item_name = NULL;
821 gtk_tree_model_get (tree_model, iter,
823 INSTANCE_COLUMN, &instance,
828 self = MODEST_FOLDER_VIEW (data);
829 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
832 if (type != TNY_FOLDER_TYPE_ROOT) {
837 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
838 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
841 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
843 /* Sometimes an special folder is reported by the server as
844 NORMAL, like some versions of Dovecot */
845 if (type == TNY_FOLDER_TYPE_NORMAL ||
846 type == TNY_FOLDER_TYPE_UNKNOWN) {
847 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
851 /* note: we cannot reliably get the counts from the tree model, we need
852 * to use explicit calls on tny_folder for some reason.
854 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
855 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
856 (type == TNY_FOLDER_TYPE_OUTBOX) ||
857 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
858 number = tny_folder_get_all_count (TNY_FOLDER(instance));
861 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
865 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
867 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
868 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
874 item_name = g_strdup ("");
877 /* Set the name in the treeview cell: */
878 g_object_set (rendobj,"text", item_name, NULL);
886 g_object_unref (G_OBJECT (instance));
892 GdkPixbuf *pixbuf_open;
893 GdkPixbuf *pixbuf_close;
897 static inline GdkPixbuf *
898 get_composite_pixbuf (const gchar *icon_name,
900 GdkPixbuf *base_pixbuf)
902 GdkPixbuf *emblem, *retval = NULL;
904 emblem = modest_platform_get_icon (icon_name, size);
906 retval = gdk_pixbuf_copy (base_pixbuf);
907 gdk_pixbuf_composite (emblem, retval, 0, 0,
908 MIN (gdk_pixbuf_get_width (emblem),
909 gdk_pixbuf_get_width (retval)),
910 MIN (gdk_pixbuf_get_height (emblem),
911 gdk_pixbuf_get_height (retval)),
912 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
913 g_object_unref (emblem);
918 static inline ThreePixbufs *
919 get_composite_icons (const gchar *icon_code,
921 GdkPixbuf **pixbuf_open,
922 GdkPixbuf **pixbuf_close)
924 ThreePixbufs *retval;
926 if (pixbuf && !*pixbuf) {
928 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
930 *pixbuf = gdk_pixbuf_copy (icon);
936 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
937 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
941 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
942 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
946 retval = g_slice_new0 (ThreePixbufs);
947 if (pixbuf && *pixbuf)
948 retval->pixbuf = g_object_ref (*pixbuf);
950 retval->pixbuf = NULL;
951 if (pixbuf_open && *pixbuf_open)
952 retval->pixbuf_open = g_object_ref (*pixbuf_open);
954 retval->pixbuf_open = NULL;
955 if (pixbuf_close && *pixbuf_close)
956 retval->pixbuf_close = g_object_ref (*pixbuf_close);
958 retval->pixbuf_close = NULL;
963 static inline ThreePixbufs *
964 get_account_protocol_pixbufs (ModestFolderView *folder_view,
965 ModestProtocolType protocol_type,
968 ModestProtocol *protocol;
969 const GdkPixbuf *pixbuf = NULL;
970 ModestFolderViewPrivate *priv;
972 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
974 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
977 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
978 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
979 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
980 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
981 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
982 object, FOLDER_ICON_SIZE);
986 ThreePixbufs *retval;
987 retval = g_slice_new0 (ThreePixbufs);
988 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
989 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
990 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
997 static inline ThreePixbufs*
998 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
1000 TnyAccount *account = NULL;
1001 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
1002 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
1003 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
1004 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
1005 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
1007 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1008 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1009 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1010 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1011 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1013 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1014 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1015 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1016 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1017 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1019 ThreePixbufs *retval = NULL;
1021 if (TNY_IS_ACCOUNT (instance)) {
1022 account = g_object_ref (instance);
1023 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1024 account = tny_folder_get_account (TNY_FOLDER (instance));
1028 ModestProtocolType account_store_protocol;
1030 account_store_protocol = modest_tny_account_get_protocol_type (account);
1031 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1032 g_object_unref (account);
1038 /* Sometimes an special folder is reported by the server as
1039 NORMAL, like some versions of Dovecot */
1040 if (type == TNY_FOLDER_TYPE_NORMAL ||
1041 type == TNY_FOLDER_TYPE_UNKNOWN) {
1042 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1045 /* It's not enough with check the folder type. We need to
1046 ensure that we're not giving a special folder icon to a
1047 normal folder with the same name than a special folder */
1048 if (TNY_IS_FOLDER (instance) &&
1049 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1050 type = TNY_FOLDER_TYPE_NORMAL;
1052 /* Remote folders should not be treated as special folders */
1053 if (TNY_IS_FOLDER_STORE (instance) &&
1054 !TNY_IS_ACCOUNT (instance) &&
1055 type != TNY_FOLDER_TYPE_INBOX &&
1056 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1057 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1060 &anorm_pixbuf_close);
1065 case TNY_FOLDER_TYPE_INVALID:
1066 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1069 case TNY_FOLDER_TYPE_ROOT:
1070 if (TNY_IS_ACCOUNT (instance)) {
1072 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1073 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1076 &avirt_pixbuf_close);
1078 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1080 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1081 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1084 &ammc_pixbuf_close);
1086 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1089 &anorm_pixbuf_close);
1094 case TNY_FOLDER_TYPE_INBOX:
1095 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1098 &inbox_pixbuf_close);
1100 case TNY_FOLDER_TYPE_OUTBOX:
1101 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1103 &outbox_pixbuf_open,
1104 &outbox_pixbuf_close);
1106 case TNY_FOLDER_TYPE_JUNK:
1107 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1110 &junk_pixbuf_close);
1112 case TNY_FOLDER_TYPE_SENT:
1113 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1116 &sent_pixbuf_close);
1118 case TNY_FOLDER_TYPE_TRASH:
1119 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1122 &trash_pixbuf_close);
1124 case TNY_FOLDER_TYPE_DRAFTS:
1125 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1128 &draft_pixbuf_close);
1130 case TNY_FOLDER_TYPE_ARCHIVE:
1131 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1136 case TNY_FOLDER_TYPE_NORMAL:
1138 /* Memory card folders could have an special icon */
1139 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1140 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1145 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1147 &normal_pixbuf_open,
1148 &normal_pixbuf_close);
1157 free_pixbufs (ThreePixbufs *pixbufs)
1159 if (pixbufs->pixbuf)
1160 g_object_unref (pixbufs->pixbuf);
1161 if (pixbufs->pixbuf_open)
1162 g_object_unref (pixbufs->pixbuf_open);
1163 if (pixbufs->pixbuf_close)
1164 g_object_unref (pixbufs->pixbuf_close);
1165 g_slice_free (ThreePixbufs, pixbufs);
1169 icon_cell_data (GtkTreeViewColumn *column,
1170 GtkCellRenderer *renderer,
1171 GtkTreeModel *tree_model,
1175 GObject *rendobj = NULL, *instance = NULL;
1176 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1177 gboolean has_children;
1178 ThreePixbufs *pixbufs;
1179 ModestFolderView *folder_view = (ModestFolderView *) data;
1181 rendobj = (GObject *) renderer;
1183 gtk_tree_model_get (tree_model, iter,
1185 INSTANCE_COLUMN, &instance,
1191 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1192 pixbufs = get_folder_icons (folder_view, type, instance);
1193 g_object_unref (instance);
1196 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1199 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1200 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1203 free_pixbufs (pixbufs);
1207 add_columns (GtkWidget *treeview)
1209 GtkTreeViewColumn *column;
1210 GtkCellRenderer *renderer;
1211 GtkTreeSelection *sel;
1212 ModestFolderViewPrivate *priv;
1214 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1217 column = gtk_tree_view_column_new ();
1219 /* Set icon and text render function */
1220 renderer = gtk_cell_renderer_pixbuf_new();
1221 g_object_set (renderer,
1222 "xpad", MODEST_MARGIN_DEFAULT,
1223 "ypad", MODEST_MARGIN_DEFAULT,
1225 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1226 gtk_tree_view_column_set_cell_data_func(column, renderer,
1227 icon_cell_data, treeview, NULL);
1229 renderer = gtk_cell_renderer_text_new();
1230 g_object_set (renderer,
1231 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1232 "ypad", MODEST_MARGIN_DEFAULT,
1233 "xpad", MODEST_MARGIN_DEFAULT,
1234 "ellipsize-set", TRUE, NULL);
1235 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1236 gtk_tree_view_column_set_cell_data_func(column, renderer,
1237 text_cell_data, treeview, NULL);
1239 priv->messages_renderer = gtk_cell_renderer_text_new ();
1240 g_object_set (priv->messages_renderer,
1242 "ypad", MODEST_MARGIN_DEFAULT,
1243 "xpad", MODEST_MARGIN_DOUBLE,
1244 "alignment", PANGO_ALIGN_RIGHT,
1248 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1249 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1250 messages_cell_data, treeview, NULL);
1252 /* Set selection mode */
1253 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1254 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1256 /* Set treeview appearance */
1257 gtk_tree_view_column_set_spacing (column, 2);
1258 gtk_tree_view_column_set_resizable (column, TRUE);
1259 gtk_tree_view_column_set_fixed_width (column, TRUE);
1260 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1261 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1262 gtk_tree_view_set_rules_hint ((GtkTreeView *) treeview, TRUE);
1265 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1269 modest_folder_view_init (ModestFolderView *obj)
1271 ModestFolderViewPrivate *priv;
1274 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1276 priv->timer_expander = 0;
1277 priv->account_store = NULL;
1279 priv->do_refresh = TRUE;
1280 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1281 priv->cur_folder_store = NULL;
1282 priv->visible_account_id = NULL;
1283 priv->mailbox = NULL;
1284 priv->folder_to_select = NULL;
1285 priv->outbox_deleted_handler = 0;
1286 priv->reexpand = TRUE;
1287 priv->signal_handlers = 0;
1289 /* Initialize the local account name */
1290 conf = modest_runtime_get_conf();
1291 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1293 /* Init email clipboard */
1294 priv->clipboard = modest_runtime_get_email_clipboard ();
1295 priv->hidding_ids = NULL;
1296 priv->n_selected = 0;
1297 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1298 priv->reselect = FALSE;
1299 priv->show_non_move = TRUE;
1300 priv->list_to_move = NULL;
1301 priv->show_message_count = TRUE;
1303 /* Build treeview */
1304 add_columns (GTK_WIDGET (obj));
1306 priv->display_name_changed_signal =
1307 g_signal_connect (modest_runtime_get_account_mgr (),
1308 "display_name_changed",
1309 G_CALLBACK (on_display_name_changed),
1313 * Track changes in the local account name (in the device it
1314 * will be the device name)
1316 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1318 G_CALLBACK(on_configuration_key_changed),
1321 gdk_color_parse ("000", &priv->active_color);
1324 g_signal_connect (G_OBJECT (obj), "notify::style",
1325 G_CALLBACK (on_notify_style), (gpointer) obj);
1329 tny_account_store_view_init (gpointer g, gpointer iface_data)
1331 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1333 klass->set_account_store = modest_folder_view_set_account_store;
1337 modest_folder_view_dispose (GObject *obj)
1339 ModestFolderViewPrivate *priv;
1341 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1343 if (priv->signal_handlers) {
1344 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1345 priv->signal_handlers = NULL;
1348 /* Free external references */
1349 if (priv->account_store) {
1350 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1351 priv->account_inserted_signal);
1352 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1353 priv->account_removed_signal);
1354 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1355 priv->account_changed_signal);
1356 g_object_unref (G_OBJECT(priv->account_store));
1357 priv->account_store = NULL;
1361 g_object_unref (G_OBJECT (priv->query));
1365 if (priv->folder_to_select) {
1366 g_object_unref (G_OBJECT(priv->folder_to_select));
1367 priv->folder_to_select = NULL;
1370 if (priv->cur_folder_store) {
1371 g_object_unref (priv->cur_folder_store);
1372 priv->cur_folder_store = NULL;
1375 if (priv->list_to_move) {
1376 g_object_unref (priv->list_to_move);
1377 priv->list_to_move = NULL;
1380 G_OBJECT_CLASS(parent_class)->dispose (obj);
1384 modest_folder_view_finalize (GObject *obj)
1386 ModestFolderViewPrivate *priv;
1387 GtkTreeSelection *sel;
1388 TnyAccount *local_account;
1390 g_return_if_fail (obj);
1392 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1394 if (priv->timer_expander != 0) {
1395 g_source_remove (priv->timer_expander);
1396 priv->timer_expander = 0;
1399 local_account = (TnyAccount *)
1400 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1401 if (local_account) {
1402 if (g_signal_handler_is_connected (local_account,
1403 priv->outbox_deleted_handler))
1404 g_signal_handler_disconnect (local_account,
1405 priv->outbox_deleted_handler);
1406 g_object_unref (local_account);
1409 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1410 priv->display_name_changed_signal)) {
1411 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1412 priv->display_name_changed_signal);
1413 priv->display_name_changed_signal = 0;
1416 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1418 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1420 g_free (priv->local_account_name);
1421 g_free (priv->visible_account_id);
1422 g_free (priv->mailbox);
1424 if (priv->conf_key_signal) {
1425 g_signal_handler_disconnect (modest_runtime_get_conf (),
1426 priv->conf_key_signal);
1427 priv->conf_key_signal = 0;
1430 /* Clear hidding array created by cut operation */
1431 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1433 gdk_color_parse ("000", &priv->active_color);
1435 G_OBJECT_CLASS(parent_class)->finalize (obj);
1440 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1442 ModestFolderViewPrivate *priv;
1445 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1446 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1448 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1449 device = tny_account_store_get_device (account_store);
1451 if (G_UNLIKELY (priv->account_store)) {
1453 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1454 priv->account_inserted_signal))
1455 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1456 priv->account_inserted_signal);
1457 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1458 priv->account_removed_signal))
1459 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1460 priv->account_removed_signal);
1461 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1462 priv->account_changed_signal))
1463 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1464 priv->account_changed_signal);
1465 g_object_unref (G_OBJECT (priv->account_store));
1468 priv->account_store = g_object_ref (G_OBJECT (account_store));
1470 priv->account_removed_signal =
1471 g_signal_connect (G_OBJECT(account_store), "account_removed",
1472 G_CALLBACK (on_account_removed), self);
1474 priv->account_inserted_signal =
1475 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1476 G_CALLBACK (on_account_inserted), self);
1478 priv->account_changed_signal =
1479 g_signal_connect (G_OBJECT(account_store), "account_changed",
1480 G_CALLBACK (on_account_changed), self);
1482 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1483 priv->reselect = FALSE;
1485 g_object_unref (G_OBJECT (device));
1489 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1492 ModestFolderView *self;
1493 GtkTreeModel *model, *filter_model;
1496 self = MODEST_FOLDER_VIEW (user_data);
1498 if (!get_inner_models (self, &filter_model, NULL, &model))
1501 /* Remove outbox from model */
1502 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1503 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1504 g_object_unref (outbox);
1507 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1511 on_account_inserted (TnyAccountStore *account_store,
1512 TnyAccount *account,
1515 ModestFolderViewPrivate *priv;
1516 GtkTreeModel *model, *filter_model;
1518 /* Ignore transport account insertions, we're not showing them
1519 in the folder view */
1520 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1523 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1526 /* If we're adding a new account, and there is no previous
1527 one, we need to select the visible server account */
1528 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1529 !priv->visible_account_id)
1530 modest_widget_memory_restore (modest_runtime_get_conf(),
1531 G_OBJECT (user_data),
1532 MODEST_CONF_FOLDER_VIEW_KEY);
1536 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1537 &filter_model, NULL, &model))
1540 /* Insert the account in the model */
1541 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1543 /* When the model is a list store (plain representation) the
1544 outbox is not a child of any account so we have to manually
1545 delete it because removing the local folders account won't
1546 delete it (because tny_folder_get_account() is not defined
1547 for a merge folder */
1548 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1549 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1551 priv->outbox_deleted_handler =
1552 g_signal_connect (account,
1554 G_CALLBACK (on_outbox_deleted_cb),
1558 /* Refilter the model */
1559 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1564 same_account_selected (ModestFolderView *self,
1565 TnyAccount *account)
1567 ModestFolderViewPrivate *priv;
1568 gboolean same_account = FALSE;
1570 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1572 if (priv->cur_folder_store) {
1573 TnyAccount *selected_folder_account = NULL;
1575 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1576 selected_folder_account =
1577 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1579 selected_folder_account =
1580 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1583 if (selected_folder_account == account)
1584 same_account = TRUE;
1586 g_object_unref (selected_folder_account);
1588 return same_account;
1592 on_account_changed (TnyAccountStore *account_store,
1593 TnyAccount *tny_account,
1596 ModestFolderView *self;
1597 ModestFolderViewPrivate *priv;
1598 GtkTreeModel *model, *filter_model;
1599 GtkTreeSelection *sel;
1600 gboolean same_account;
1602 /* Ignore transport account insertions, we're not showing them
1603 in the folder view */
1604 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1607 self = MODEST_FOLDER_VIEW (user_data);
1608 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1610 /* Get the inner model */
1611 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1612 &filter_model, NULL, &model))
1615 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1617 /* Invalidate the cur_folder_store only if the selected folder
1618 belongs to the account that is being removed */
1619 same_account = same_account_selected (self, tny_account);
1621 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1622 gtk_tree_selection_unselect_all (sel);
1625 /* Remove the account from the model */
1626 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1628 /* Insert the account in the model */
1629 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1631 /* Refilter the model */
1632 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1637 on_account_removed (TnyAccountStore *account_store,
1638 TnyAccount *account,
1641 ModestFolderView *self = NULL;
1642 ModestFolderViewPrivate *priv;
1643 GtkTreeModel *model, *filter_model;
1644 GtkTreeSelection *sel = NULL;
1645 gboolean same_account = FALSE;
1647 /* Ignore transport account removals, we're not showing them
1648 in the folder view */
1649 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1652 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1653 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1657 self = MODEST_FOLDER_VIEW (user_data);
1658 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1660 /* Invalidate the cur_folder_store only if the selected folder
1661 belongs to the account that is being removed */
1662 same_account = same_account_selected (self, account);
1664 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1665 gtk_tree_selection_unselect_all (sel);
1668 /* Invalidate row to select only if the folder to select
1669 belongs to the account that is being removed*/
1670 if (priv->folder_to_select) {
1671 TnyAccount *folder_to_select_account = NULL;
1673 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1674 if (folder_to_select_account == account) {
1675 modest_folder_view_disable_next_folder_selection (self);
1676 g_object_unref (priv->folder_to_select);
1677 priv->folder_to_select = NULL;
1679 g_object_unref (folder_to_select_account);
1682 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1683 &filter_model, NULL, &model))
1686 /* Disconnect the signal handler */
1687 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1688 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1689 if (g_signal_handler_is_connected (account,
1690 priv->outbox_deleted_handler))
1691 g_signal_handler_disconnect (account,
1692 priv->outbox_deleted_handler);
1695 /* Remove the account from the model */
1696 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1698 /* If the removed account is the currently viewed one then
1699 clear the configuration value. The new visible account will be the default account */
1700 if (priv->visible_account_id &&
1701 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1703 /* Clear the current visible account_id */
1704 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1705 modest_folder_view_set_mailbox (self, NULL);
1707 /* Call the restore method, this will set the new visible account */
1708 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1709 MODEST_CONF_FOLDER_VIEW_KEY);
1712 /* Refilter the model */
1713 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1718 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1720 GtkTreeViewColumn *col;
1722 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1724 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1726 g_printerr ("modest: failed get column for title\n");
1730 gtk_tree_view_column_set_title (col, title);
1731 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1736 modest_folder_view_on_map (ModestFolderView *self,
1737 GdkEventExpose *event,
1740 ModestFolderViewPrivate *priv;
1742 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1744 /* This won't happen often */
1745 if (G_UNLIKELY (priv->reselect)) {
1746 /* Select the first inbox or the local account if not found */
1748 /* TODO: this could cause a lock at startup, so we
1749 comment it for the moment. We know that this will
1750 be a bug, because the INBOX is not selected, but we
1751 need to rewrite some parts of Modest to avoid the
1752 deathlock situation */
1753 /* TODO: check if this is still the case */
1754 priv->reselect = FALSE;
1755 /* Notify the display name observers */
1756 g_signal_emit (G_OBJECT(self),
1757 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1761 if (priv->reexpand) {
1762 expand_root_items (self);
1763 priv->reexpand = FALSE;
1770 modest_folder_view_new (TnyFolderStoreQuery *query)
1772 return modest_folder_view_new_full (query, TRUE);
1776 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1779 ModestFolderViewPrivate *priv;
1780 GtkTreeSelection *sel;
1782 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1783 #ifdef MODEST_TOOLKIT_HILDON2
1784 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1787 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1790 priv->query = g_object_ref (query);
1792 priv->do_refresh = do_refresh;
1794 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1795 priv->changed_signal = g_signal_connect (sel, "changed",
1796 G_CALLBACK (on_selection_changed), self);
1798 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1800 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1802 /* Hide headers by default */
1803 gtk_tree_view_set_headers_visible ((GtkTreeView *)self, FALSE);
1805 return GTK_WIDGET(self);
1808 /* this feels dirty; any other way to expand all the root items? */
1810 expand_root_items (ModestFolderView *self)
1813 GtkTreeModel *model;
1816 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1817 path = gtk_tree_path_new_first ();
1819 /* all folders should have child items, so.. */
1821 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1822 gtk_tree_path_next (path);
1823 } while (gtk_tree_model_get_iter (model, &iter, path));
1825 gtk_tree_path_free (path);
1829 is_parent_of (TnyFolder *a, TnyFolder *b)
1832 gboolean retval = FALSE;
1834 a_id = tny_folder_get_id (a);
1836 gchar *string_to_match;
1839 string_to_match = g_strconcat (a_id, "/", NULL);
1840 b_id = tny_folder_get_id (b);
1841 retval = g_str_has_prefix (b_id, string_to_match);
1842 g_free (string_to_match);
1848 typedef struct _ForeachFolderInfo {
1851 } ForeachFolderInfo;
1854 foreach_folder_with_id (GtkTreeModel *model,
1859 ForeachFolderInfo *info;
1862 info = (ForeachFolderInfo *) data;
1863 gtk_tree_model_get (model, iter,
1864 INSTANCE_COLUMN, &instance,
1867 if (TNY_IS_FOLDER (instance)) {
1870 id = tny_folder_get_id (TNY_FOLDER (instance));
1872 collate = g_utf8_collate_key (id, -1);
1873 info->found = !strcmp (info->needle, collate);
1879 g_object_unref (instance);
1887 has_folder_with_id (ModestFolderView *self, const gchar *id)
1889 GtkTreeModel *model;
1890 ForeachFolderInfo info = {NULL, FALSE};
1892 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1893 info.needle = g_utf8_collate_key (id, -1);
1895 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1896 g_free (info.needle);
1902 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1905 gboolean retval = FALSE;
1907 a_id = tny_folder_get_id (a);
1910 b_id = tny_folder_get_id (b);
1913 const gchar *last_bar;
1914 gchar *string_to_match;
1915 last_bar = g_strrstr (b_id, "/");
1920 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1921 retval = has_folder_with_id (self, string_to_match);
1922 g_free (string_to_match);
1930 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1932 ModestFolderViewPrivate *priv;
1933 TnyIterator *iterator;
1934 gboolean retval = TRUE;
1936 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1937 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1939 for (iterator = tny_list_create_iterator (priv->list_to_move);
1940 retval && !tny_iterator_is_done (iterator);
1941 tny_iterator_next (iterator)) {
1943 instance = tny_iterator_get_current (iterator);
1944 if (instance == (GObject *) folder) {
1946 } else if (TNY_IS_FOLDER (instance)) {
1947 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1949 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1952 g_object_unref (instance);
1954 g_object_unref (iterator);
1961 * We use this function to implement the
1962 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1963 * account in this case, and the local folders.
1966 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1968 ModestFolderViewPrivate *priv;
1969 gboolean retval = TRUE;
1970 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1971 GObject *instance = NULL;
1972 const gchar *id = NULL;
1974 gboolean found = FALSE;
1975 gboolean cleared = FALSE;
1976 ModestTnyFolderRules rules = 0;
1979 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1980 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1982 gtk_tree_model_get (model, iter,
1983 NAME_COLUMN, &fname,
1985 INSTANCE_COLUMN, &instance,
1988 /* Do not show if there is no instance, this could indeed
1989 happen when the model is being modified while it's being
1990 drawn. This could occur for example when moving folders
1997 if (TNY_IS_ACCOUNT (instance)) {
1998 TnyAccount *acc = TNY_ACCOUNT (instance);
1999 const gchar *account_id = tny_account_get_id (acc);
2001 /* If it isn't a special folder,
2002 * don't show it unless it is the visible account: */
2003 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2004 !modest_tny_account_is_virtual_local_folders (acc) &&
2005 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2007 /* Show only the visible account id */
2008 if (priv->visible_account_id) {
2009 if (strcmp (account_id, priv->visible_account_id))
2016 /* Never show these to the user. They are merged into one folder
2017 * in the local-folders account instead: */
2018 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2021 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2022 /* Only show special folders for current account if needed */
2023 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2024 TnyAccount *account;
2026 account = tny_folder_get_account (TNY_FOLDER (instance));
2028 if (TNY_IS_ACCOUNT (account)) {
2029 const gchar *account_id = tny_account_get_id (account);
2031 if (!modest_tny_account_is_virtual_local_folders (account) &&
2032 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2033 /* Show only the visible account id */
2034 if (priv->visible_account_id) {
2035 if (strcmp (account_id, priv->visible_account_id)) {
2037 } else if (priv->mailbox) {
2038 /* Filter mailboxes */
2039 if (!g_str_has_prefix (fname, priv->mailbox)) {
2041 } else if (!strcmp (fname, priv->mailbox)) {
2042 /* Hide mailbox parent */
2048 g_object_unref (account);
2055 /* Check hiding (if necessary) */
2056 cleared = modest_email_clipboard_cleared (priv->clipboard);
2057 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2058 id = tny_folder_get_id (TNY_FOLDER(instance));
2059 if (priv->hidding_ids != NULL)
2060 for (i=0; i < priv->n_selected && !found; i++)
2061 if (priv->hidding_ids[i] != NULL && id != NULL)
2062 found = (!strcmp (priv->hidding_ids[i], id));
2067 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2068 folder as no message can be move there according to UI specs */
2069 if (retval && !priv->show_non_move) {
2070 if (priv->list_to_move &&
2071 tny_list_get_length (priv->list_to_move) > 0 &&
2072 TNY_IS_FOLDER (instance)) {
2073 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2075 if (retval && TNY_IS_FOLDER (instance) &&
2076 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2078 case TNY_FOLDER_TYPE_OUTBOX:
2079 case TNY_FOLDER_TYPE_SENT:
2080 case TNY_FOLDER_TYPE_DRAFTS:
2083 case TNY_FOLDER_TYPE_UNKNOWN:
2084 case TNY_FOLDER_TYPE_NORMAL:
2085 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2086 if (type == TNY_FOLDER_TYPE_INVALID)
2087 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2089 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2090 type == TNY_FOLDER_TYPE_SENT
2091 || type == TNY_FOLDER_TYPE_DRAFTS)
2098 if (retval && TNY_IS_ACCOUNT (instance) &&
2099 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2100 ModestProtocolType protocol_type;
2102 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2103 retval = !modest_protocol_registry_protocol_type_has_tag
2104 (modest_runtime_get_protocol_registry (),
2106 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2110 /* apply special filters */
2111 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2112 if (TNY_IS_ACCOUNT (instance))
2116 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2117 if (TNY_IS_FOLDER (instance))
2121 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2122 if (TNY_IS_ACCOUNT (instance)) {
2123 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2125 } else if (TNY_IS_FOLDER (instance)) {
2126 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2131 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2132 if (TNY_IS_ACCOUNT (instance)) {
2133 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2135 } else if (TNY_IS_FOLDER (instance)) {
2136 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2141 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2142 /* A mailbox is a fake folder with an @ in the middle of the name */
2143 if (!TNY_IS_FOLDER (instance) ||
2144 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2147 const gchar *folder_name;
2148 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2149 if (!folder_name || strchr (folder_name, '@') == NULL)
2155 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2156 if (TNY_IS_FOLDER (instance)) {
2157 /* Check folder rules */
2158 ModestTnyFolderRules rules;
2160 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2161 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2162 } else if (TNY_IS_ACCOUNT (instance)) {
2163 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2171 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2172 if (TNY_IS_FOLDER (instance)) {
2173 TnyFolderType guess_type;
2175 if (TNY_FOLDER_TYPE_NORMAL) {
2176 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2182 case TNY_FOLDER_TYPE_OUTBOX:
2183 case TNY_FOLDER_TYPE_SENT:
2184 case TNY_FOLDER_TYPE_DRAFTS:
2185 case TNY_FOLDER_TYPE_ARCHIVE:
2186 case TNY_FOLDER_TYPE_INBOX:
2189 case TNY_FOLDER_TYPE_UNKNOWN:
2190 case TNY_FOLDER_TYPE_NORMAL:
2196 } else if (TNY_IS_ACCOUNT (instance)) {
2201 if (retval && TNY_IS_FOLDER (instance)) {
2202 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2205 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2206 if (TNY_IS_FOLDER (instance)) {
2207 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2208 } else if (TNY_IS_ACCOUNT (instance)) {
2213 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2214 if (TNY_IS_FOLDER (instance)) {
2215 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2216 } else if (TNY_IS_ACCOUNT (instance)) {
2221 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2222 if (TNY_IS_FOLDER (instance)) {
2223 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2224 } else if (TNY_IS_ACCOUNT (instance)) {
2230 g_object_unref (instance);
2238 modest_folder_view_update_model (ModestFolderView *self,
2239 TnyAccountStore *account_store)
2241 ModestFolderViewPrivate *priv;
2242 GtkTreeModel *model;
2243 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2245 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2246 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2249 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2251 /* Notify that there is no folder selected */
2252 g_signal_emit (G_OBJECT(self),
2253 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2255 if (priv->cur_folder_store) {
2256 g_object_unref (priv->cur_folder_store);
2257 priv->cur_folder_store = NULL;
2260 /* FIXME: the local accounts are not shown when the query
2261 selects only the subscribed folders */
2262 TnyGtkFolderListStoreFlags flags;
2263 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2264 if (priv->do_refresh)
2265 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2267 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2268 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2270 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2271 MODEST_FOLDER_PATH_SEPARATOR);
2273 /* When the model is a list store (plain representation) the
2274 outbox is not a child of any account so we have to manually
2275 delete it because removing the local folders account won't
2276 delete it (because tny_folder_get_account() is not defined
2277 for a merge folder */
2278 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2279 TnyAccount *account;
2280 ModestTnyAccountStore *acc_store;
2282 acc_store = modest_runtime_get_account_store ();
2283 account = modest_tny_account_store_get_local_folders_account (acc_store);
2285 if (g_signal_handler_is_connected (account,
2286 priv->outbox_deleted_handler))
2287 g_signal_handler_disconnect (account,
2288 priv->outbox_deleted_handler);
2290 priv->outbox_deleted_handler =
2291 g_signal_connect (account,
2293 G_CALLBACK (on_outbox_deleted_cb),
2295 g_object_unref (account);
2298 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2299 /* Get the accounts */
2300 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2302 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2304 if (priv->visible_account_id) {
2305 TnyAccount *account;
2307 /* Add local folders account */
2308 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2311 tny_list_append (TNY_LIST (model), (GObject *) account);
2312 g_object_unref (account);
2315 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2318 tny_list_append (TNY_LIST (model), (GObject *) account);
2319 g_object_unref (account);
2322 /* Add visible account */
2323 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2324 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2325 priv->visible_account_id);
2327 tny_list_append (TNY_LIST (model), (GObject *) account);
2328 g_object_unref (account);
2330 g_warning ("You need to set an account first");
2331 g_object_unref (model);
2335 g_warning ("You need to set an account first");
2336 g_object_unref (model);
2341 sortable = gtk_tree_model_sort_new_with_model (model);
2342 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2344 GTK_SORT_ASCENDING);
2345 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2347 cmp_rows, NULL, NULL);
2349 /* Create filter model */
2350 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2351 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2356 GtkTreeModel *old_tny_model = NULL;
2357 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2358 if (priv->signal_handlers > 0) {
2359 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2360 G_OBJECT (old_tny_model),
2361 "activity-changed");
2366 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2368 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2371 G_CALLBACK (on_activity_changed),
2374 g_object_unref (model);
2375 g_object_unref (filter_model);
2376 g_object_unref (sortable);
2378 /* Force a reselection of the INBOX next time the widget is shown */
2379 priv->reselect = TRUE;
2386 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2388 GtkTreeModel *model = NULL;
2389 TnyFolderStore *folder = NULL;
2391 ModestFolderView *tree_view = NULL;
2392 ModestFolderViewPrivate *priv = NULL;
2393 gboolean selected = FALSE;
2395 g_return_if_fail (sel);
2396 g_return_if_fail (user_data);
2398 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2400 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2402 tree_view = MODEST_FOLDER_VIEW (user_data);
2405 gtk_tree_model_get (model, &iter,
2406 INSTANCE_COLUMN, &folder,
2409 /* If the folder is the same do not notify */
2410 if (folder && priv->cur_folder_store == folder) {
2411 g_object_unref (folder);
2416 /* Current folder was unselected */
2417 if (priv->cur_folder_store) {
2418 /* We must do this firstly because a libtinymail-camel
2419 implementation detail. If we issue the signal
2420 before doing the sync_async, then that signal could
2421 cause (and it actually does it) a free of the
2422 summary of the folder (because the main window will
2423 clear the headers view */
2425 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2426 priv->cur_folder_store, FALSE);
2428 g_object_unref (priv->cur_folder_store);
2429 priv->cur_folder_store = NULL;
2432 /* New current references */
2433 priv->cur_folder_store = folder;
2435 /* New folder has been selected. Do not notify if there is
2436 nothing new selected */
2438 g_signal_emit (G_OBJECT(tree_view),
2439 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2440 0, priv->cur_folder_store, TRUE);
2445 on_row_activated (GtkTreeView *treeview,
2446 GtkTreePath *treepath,
2447 GtkTreeViewColumn *column,
2450 GtkTreeModel *model = NULL;
2451 TnyFolderStore *folder = NULL;
2453 ModestFolderView *self = NULL;
2454 ModestFolderViewPrivate *priv = NULL;
2456 g_return_if_fail (treeview);
2457 g_return_if_fail (user_data);
2459 self = MODEST_FOLDER_VIEW (user_data);
2460 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2462 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2464 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2467 gtk_tree_model_get (model, &iter,
2468 INSTANCE_COLUMN, &folder,
2471 g_signal_emit (G_OBJECT(self),
2472 signals[FOLDER_ACTIVATED_SIGNAL],
2475 #ifdef MODEST_TOOLKIT_HILDON2
2476 HildonUIMode ui_mode;
2477 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2478 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2479 if (priv->cur_folder_store)
2480 g_object_unref (priv->cur_folder_store);
2481 priv->cur_folder_store = g_object_ref (folder);
2485 g_object_unref (folder);
2489 modest_folder_view_get_selected (ModestFolderView *self)
2491 ModestFolderViewPrivate *priv;
2493 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2495 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2496 if (priv->cur_folder_store)
2497 g_object_ref (priv->cur_folder_store);
2499 return priv->cur_folder_store;
2503 get_cmp_rows_type_pos (GObject *folder)
2505 /* Remote accounts -> Local account -> MMC account .*/
2508 if (TNY_IS_ACCOUNT (folder) &&
2509 modest_tny_account_is_virtual_local_folders (
2510 TNY_ACCOUNT (folder))) {
2512 } else if (TNY_IS_ACCOUNT (folder)) {
2513 TnyAccount *account = TNY_ACCOUNT (folder);
2514 const gchar *account_id = tny_account_get_id (account);
2515 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2521 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2522 return -1; /* Should never happen */
2527 inbox_is_special (TnyFolderStore *folder_store)
2529 gboolean is_special = TRUE;
2531 if (TNY_IS_FOLDER (folder_store)) {
2535 gchar *last_inbox_bar;
2537 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2538 downcase = g_utf8_strdown (id, -1);
2539 last_bar = g_strrstr (downcase, "/");
2541 last_inbox_bar = g_strrstr (downcase, "inbox/");
2542 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2553 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2555 TnyAccount *account;
2556 gboolean is_special;
2557 /* Inbox, Outbox, Drafts, Sent, User */
2560 if (!TNY_IS_FOLDER (folder_store))
2563 case TNY_FOLDER_TYPE_INBOX:
2565 account = tny_folder_get_account (folder_store);
2566 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2568 /* In inbox case we need to know if the inbox is really the top
2569 * inbox of the account, or if it's a submailbox inbox. To do
2570 * this we'll apply an heuristic rule: Find last "/" and check
2571 * if it's preceeded by another Inbox */
2572 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2573 g_object_unref (account);
2574 return is_special?0:4;
2577 case TNY_FOLDER_TYPE_OUTBOX:
2578 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2580 case TNY_FOLDER_TYPE_DRAFTS:
2582 account = tny_folder_get_account (folder_store);
2583 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2584 g_object_unref (account);
2585 return is_special?1:4;
2588 case TNY_FOLDER_TYPE_SENT:
2590 account = tny_folder_get_account (folder_store);
2591 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2592 g_object_unref (account);
2593 return is_special?3:4;
2602 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2604 const gchar *a1_name, *a2_name;
2606 a1_name = tny_account_get_name (a1);
2607 a2_name = tny_account_get_name (a2);
2609 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2613 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2615 TnyAccount *a1 = NULL, *a2 = NULL;
2618 if (TNY_IS_ACCOUNT (s1)) {
2619 a1 = TNY_ACCOUNT (g_object_ref (s1));
2620 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2621 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2624 if (TNY_IS_ACCOUNT (s2)) {
2625 a2 = TNY_ACCOUNT (g_object_ref (s2));
2626 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2627 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2644 /* First we sort with the type of account */
2645 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2649 cmp = compare_account_names (a1, a2);
2653 g_object_unref (a1);
2655 g_object_unref (a2);
2661 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2663 gint is_account1, is_account2;
2665 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2666 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2668 return is_account2 - is_account1;
2672 compare_folders (const gchar *name1, const gchar *name2)
2674 const gchar *separator1, *separator2;
2675 const gchar *next1, *next2;
2679 if (name1 == NULL || name1[0] == '\0')
2681 if (name2 == NULL || name2[0] == '\0')
2684 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2686 top1 = g_strndup (name1, separator1 - name1);
2688 top1 = g_strdup (name1);
2691 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2693 top2 = g_strndup (name2, separator2 - name2);
2695 top2 = g_strdup (name2);
2699 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2706 if (separator1 == NULL && separator2 == NULL)
2709 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2710 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2712 return compare_folders (next1, next2);
2717 * This function orders the mail accounts according to these rules:
2718 * 1st - remote accounts
2719 * 2nd - local account
2723 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2727 gchar *name1 = NULL;
2728 gchar *name2 = NULL;
2729 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2730 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2731 GObject *folder1 = NULL;
2732 GObject *folder2 = NULL;
2734 gtk_tree_model_get (tree_model, iter1,
2735 NAME_COLUMN, &name1,
2737 INSTANCE_COLUMN, &folder1,
2739 gtk_tree_model_get (tree_model, iter2,
2740 NAME_COLUMN, &name2,
2741 TYPE_COLUMN, &type2,
2742 INSTANCE_COLUMN, &folder2,
2745 /* Return if we get no folder. This could happen when folder
2746 operations are happening. The model is updated after the
2747 folder copy/move actually occurs, so there could be
2748 situations where the model to be drawn is not correct */
2749 if (!folder1 || !folder2)
2752 /* Sort by type. First the special folders, then the archives */
2753 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2757 /* Now we sort using the account of each folder */
2758 if (TNY_IS_FOLDER_STORE (folder1) &&
2759 TNY_IS_FOLDER_STORE (folder2)) {
2760 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2764 /* Each group is preceeded by its account */
2765 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2770 /* Pure sort by name */
2771 cmp = compare_folders (name1, name2);
2774 g_object_unref(G_OBJECT(folder1));
2776 g_object_unref(G_OBJECT(folder2));
2786 * We listen to the changes in the local folder account name key,
2787 * because we want to show the right name in the view. The local
2788 * folder account name corresponds to the device name in the Maemo
2789 * version. We do this because we do not want to query gconf on each
2790 * tree view refresh. It's better to cache it and change whenever
2794 on_configuration_key_changed (ModestConf* conf,
2796 ModestConfEvent event,
2797 ModestConfNotificationId id,
2798 ModestFolderView *self)
2800 ModestFolderViewPrivate *priv;
2803 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2804 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2806 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2807 g_free (priv->local_account_name);
2809 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2810 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2812 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2813 MODEST_CONF_DEVICE_NAME, NULL);
2815 /* Force a redraw */
2816 #if GTK_CHECK_VERSION(2, 8, 0)
2817 GtkTreeViewColumn * tree_column;
2819 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2821 gtk_tree_view_column_queue_resize (tree_column);
2823 gtk_widget_queue_draw (GTK_WIDGET (self));
2829 modest_folder_view_set_style (ModestFolderView *self,
2830 ModestFolderViewStyle style)
2832 ModestFolderViewPrivate *priv;
2834 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2835 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2836 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2838 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2841 priv->style = style;
2845 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2846 const gchar *account_id)
2848 ModestFolderViewPrivate *priv;
2849 GtkTreeModel *model;
2851 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2853 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2855 /* This will be used by the filter_row callback,
2856 * to decided which rows to show: */
2857 if (priv->visible_account_id) {
2858 g_free (priv->visible_account_id);
2859 priv->visible_account_id = NULL;
2862 priv->visible_account_id = g_strdup (account_id);
2865 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2866 if (GTK_IS_TREE_MODEL_FILTER (model))
2867 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2869 modest_folder_view_update_model(self,
2870 (TnyAccountStore *) modest_runtime_get_account_store());
2872 /* Save settings to gconf */
2873 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2874 MODEST_CONF_FOLDER_VIEW_KEY);
2876 /* Notify observers */
2877 g_signal_emit (G_OBJECT(self),
2878 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
2883 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2885 ModestFolderViewPrivate *priv;
2887 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2889 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2891 return (const gchar *) priv->visible_account_id;
2896 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2901 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2902 TnyFolder* a_folder;
2905 gtk_tree_model_get (model, iter,
2906 INSTANCE_COLUMN, &a_folder,
2912 if (folder == a_folder) {
2913 g_object_unref (a_folder);
2914 *folder_iter = *iter;
2917 g_object_unref (a_folder);
2919 if (gtk_tree_model_iter_children (model, &child, iter)) {
2920 if (find_folder_iter (model, &child, folder_iter, folder))
2924 } while (gtk_tree_model_iter_next (model, iter));
2931 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2933 ModestFolderViewPrivate *priv;
2935 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2937 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2939 if (priv->folder_to_select)
2940 g_object_unref(priv->folder_to_select);
2942 priv->folder_to_select = NULL;
2946 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2947 gboolean after_change)
2949 GtkTreeModel *model;
2950 GtkTreeIter iter, folder_iter;
2951 GtkTreeSelection *sel;
2952 ModestFolderViewPrivate *priv = NULL;
2954 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2955 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2957 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2960 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2961 gtk_tree_selection_unselect_all (sel);
2963 if (priv->folder_to_select)
2964 g_object_unref(priv->folder_to_select);
2965 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2969 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2974 /* Refilter the model, before selecting the folder */
2975 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2977 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2978 g_warning ("%s: model is empty", __FUNCTION__);
2982 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2985 path = gtk_tree_model_get_path (model, &folder_iter);
2986 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2988 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2989 gtk_tree_selection_select_iter (sel, &folder_iter);
2990 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2992 gtk_tree_path_free (path);
3000 modest_folder_view_copy_selection (ModestFolderView *self)
3002 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3004 /* Copy selection */
3005 _clipboard_set_selected_data (self, FALSE);
3009 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3011 ModestFolderViewPrivate *priv = NULL;
3012 GtkTreeModel *model = NULL;
3013 const gchar **hidding = NULL;
3014 guint i, n_selected;
3016 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3017 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3019 /* Copy selection */
3020 if (!_clipboard_set_selected_data (folder_view, TRUE))
3023 /* Get hidding ids */
3024 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3026 /* Clear hidding array created by previous cut operation */
3027 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3029 /* Copy hidding array */
3030 priv->n_selected = n_selected;
3031 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3032 for (i=0; i < n_selected; i++)
3033 priv->hidding_ids[i] = g_strdup(hidding[i]);
3035 /* Hide cut folders */
3036 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3037 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3041 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3042 ModestFolderView *folder_view_dst)
3044 GtkTreeModel *filter_model = NULL;
3045 GtkTreeModel *model = NULL;
3046 GtkTreeModel *new_filter_model = NULL;
3047 GtkTreeModel *old_tny_model = NULL;
3048 GtkTreeModel *new_tny_model = NULL;
3049 ModestFolderViewPrivate *dst_priv;
3051 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3052 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3054 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3055 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3056 new_tny_model = NULL;
3059 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3060 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3061 G_OBJECT (old_tny_model),
3062 "activity-changed");
3064 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3065 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3067 /* Build new filter model */
3068 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3069 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3076 /* Set copied model */
3077 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3078 if (new_tny_model) {
3079 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3080 G_OBJECT (new_tny_model),
3082 G_CALLBACK (on_activity_changed),
3087 g_object_unref (new_filter_model);
3091 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3094 GtkTreeModel *model = NULL;
3095 ModestFolderViewPrivate* priv;
3097 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3099 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3100 priv->show_non_move = show;
3101 /* modest_folder_view_update_model(folder_view, */
3102 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3104 /* Hide special folders */
3105 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3106 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3107 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3112 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3115 ModestFolderViewPrivate* priv;
3117 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3119 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3120 priv->show_message_count = show;
3122 g_object_set (G_OBJECT (priv->messages_renderer),
3123 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3127 /* Returns FALSE if it did not selected anything */
3129 _clipboard_set_selected_data (ModestFolderView *folder_view,
3132 ModestFolderViewPrivate *priv = NULL;
3133 TnyFolderStore *folder = NULL;
3134 gboolean retval = FALSE;
3136 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3137 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3139 /* Set selected data on clipboard */
3140 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3141 folder = modest_folder_view_get_selected (folder_view);
3143 /* Do not allow to select an account */
3144 if (TNY_IS_FOLDER (folder)) {
3145 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3150 g_object_unref (folder);
3156 _clear_hidding_filter (ModestFolderView *folder_view)
3158 ModestFolderViewPrivate *priv;
3161 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3162 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3164 if (priv->hidding_ids != NULL) {
3165 for (i=0; i < priv->n_selected; i++)
3166 g_free (priv->hidding_ids[i]);
3167 g_free(priv->hidding_ids);
3173 on_display_name_changed (ModestAccountMgr *mgr,
3174 const gchar *account,
3177 ModestFolderView *self;
3179 self = MODEST_FOLDER_VIEW (user_data);
3181 /* Force a redraw */
3182 #if GTK_CHECK_VERSION(2, 8, 0)
3183 GtkTreeViewColumn * tree_column;
3185 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3187 gtk_tree_view_column_queue_resize (tree_column);
3189 gtk_widget_queue_draw (GTK_WIDGET (self));
3194 modest_folder_view_set_cell_style (ModestFolderView *self,
3195 ModestFolderViewCellStyle cell_style)
3197 ModestFolderViewPrivate *priv = NULL;
3199 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3200 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3202 priv->cell_style = cell_style;
3204 g_object_set (G_OBJECT (priv->messages_renderer),
3205 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3208 gtk_widget_queue_draw (GTK_WIDGET (self));
3212 update_style (ModestFolderView *self)
3214 ModestFolderViewPrivate *priv;
3215 GdkColor style_color, style_active_color;
3216 PangoAttrList *attr_list;
3218 PangoAttribute *attr;
3220 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3221 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3225 attr_list = pango_attr_list_new ();
3227 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3228 gdk_color_parse (MODEST_SECONDARY_COLOR, &style_color);
3230 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3231 pango_attr_list_insert (attr_list, attr);
3234 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3236 "SmallSystemFont", NULL,
3239 attr = pango_attr_font_desc_new (pango_font_description_copy
3240 (style->font_desc));
3241 pango_attr_list_insert (attr_list, attr);
3243 g_object_set (G_OBJECT (priv->messages_renderer),
3244 "foreground-gdk", &style_color,
3245 "foreground-set", TRUE,
3246 "attributes", attr_list,
3248 pango_attr_list_unref (attr_list);
3251 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3252 priv->active_color = style_active_color;
3254 gdk_color_parse ("000", &(priv->active_color));
3259 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3261 if (strcmp ("style", spec->name) == 0) {
3262 update_style (MODEST_FOLDER_VIEW (obj));
3263 gtk_widget_queue_draw (GTK_WIDGET (obj));
3268 modest_folder_view_set_filter (ModestFolderView *self,
3269 ModestFolderViewFilter filter)
3271 ModestFolderViewPrivate *priv;
3272 GtkTreeModel *filter_model;
3274 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3275 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3277 priv->filter |= filter;
3279 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3280 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3281 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3286 modest_folder_view_unset_filter (ModestFolderView *self,
3287 ModestFolderViewFilter filter)
3289 ModestFolderViewPrivate *priv;
3290 GtkTreeModel *filter_model;
3292 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3293 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3295 priv->filter &= ~filter;
3297 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3298 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3299 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3304 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3305 ModestTnyFolderRules rules)
3307 GtkTreeModel *filter_model;
3309 gboolean fulfil = FALSE;
3311 if (!get_inner_models (self, &filter_model, NULL, NULL))
3314 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3318 TnyFolderStore *folder;
3320 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3322 if (TNY_IS_FOLDER (folder)) {
3323 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3324 /* Folder rules are negative: non_writable, non_deletable... */
3325 if (!(folder_rules & rules))
3328 g_object_unref (folder);
3331 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3337 modest_folder_view_set_list_to_move (ModestFolderView *self,
3340 ModestFolderViewPrivate *priv;
3342 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3343 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3345 if (priv->list_to_move)
3346 g_object_unref (priv->list_to_move);
3349 g_object_ref (list);
3351 priv->list_to_move = list;
3355 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3357 ModestFolderViewPrivate *priv;
3359 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3360 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3363 g_free (priv->mailbox);
3365 priv->mailbox = g_strdup (mailbox);
3367 /* Notify observers */
3368 g_signal_emit (G_OBJECT(self),
3369 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3370 priv->visible_account_id);
3374 modest_folder_view_get_mailbox (ModestFolderView *self)
3376 ModestFolderViewPrivate *priv;
3378 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3379 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3381 return (const gchar *) priv->mailbox;
3385 modest_folder_view_get_activity (ModestFolderView *self)
3387 ModestFolderViewPrivate *priv;
3388 GtkTreeModel *inner_model;
3390 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3391 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3393 if (!get_inner_models (self, NULL, NULL, &inner_model))
3396 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3397 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3404 on_activity_changed (TnyGtkFolderListStore *store,
3406 ModestFolderView *folder_view)
3408 ModestFolderViewPrivate *priv;
3410 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3411 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3412 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3414 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3419 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3421 GtkTreeModel *model;
3427 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3428 ret_value = TNY_LIST (model);
3429 g_object_ref (ret_value);