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 gboolean on_key_pressed (GtkWidget *self,
129 static void on_configuration_key_changed (ModestConf* conf,
131 ModestConfEvent event,
132 ModestConfNotificationId notification_id,
133 ModestFolderView *self);
135 static void expand_root_items (ModestFolderView *self);
137 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
140 static void _clear_hidding_filter (ModestFolderView *folder_view);
142 static void on_display_name_changed (ModestAccountMgr *self,
143 const gchar *account,
145 static void update_style (ModestFolderView *self);
146 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
147 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
148 static gboolean inbox_is_special (TnyFolderStore *folder_store);
150 static gboolean get_inner_models (ModestFolderView *self,
151 GtkTreeModel **filter_model,
152 GtkTreeModel **sort_model,
153 GtkTreeModel **tny_model);
154 static void on_activity_changed (TnyGtkFolderListStore *store,
156 ModestFolderView *folder_view);
159 FOLDER_SELECTION_CHANGED_SIGNAL,
160 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
161 FOLDER_ACTIVATED_SIGNAL,
162 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
163 ACTIVITY_CHANGED_SIGNAL,
167 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
168 struct _ModestFolderViewPrivate {
169 TnyAccountStore *account_store;
170 TnyFolderStore *cur_folder_store;
172 TnyFolder *folder_to_select; /* folder to select after the next update */
174 gulong changed_signal;
175 gulong account_inserted_signal;
176 gulong account_removed_signal;
177 gulong account_changed_signal;
178 gulong conf_key_signal;
179 gulong display_name_changed_signal;
181 /* not unref this object, its a singlenton */
182 ModestEmailClipboard *clipboard;
184 /* Filter tree model */
187 ModestFolderViewFilter filter;
189 TnyFolderStoreQuery *query;
191 guint timer_expander;
193 gchar *local_account_name;
194 gchar *visible_account_id;
196 ModestFolderViewStyle style;
197 ModestFolderViewCellStyle cell_style;
198 gboolean show_message_count;
200 gboolean reselect; /* we use this to force a reselection of the INBOX */
201 gboolean show_non_move;
202 TnyList *list_to_move;
203 gboolean reexpand; /* next time we expose, we'll expand all root folders */
205 GtkCellRenderer *messages_renderer;
207 gulong outbox_deleted_handler;
209 GSList *signal_handlers;
210 GdkColor active_color;
212 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
213 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
214 MODEST_TYPE_FOLDER_VIEW, \
215 ModestFolderViewPrivate))
217 static GObjectClass *parent_class = NULL;
219 static guint signals[LAST_SIGNAL] = {0};
222 modest_folder_view_get_type (void)
224 static GType my_type = 0;
226 static const GTypeInfo my_info = {
227 sizeof(ModestFolderViewClass),
228 NULL, /* base init */
229 NULL, /* base finalize */
230 (GClassInitFunc) modest_folder_view_class_init,
231 NULL, /* class finalize */
232 NULL, /* class data */
233 sizeof(ModestFolderView),
235 (GInstanceInitFunc) modest_folder_view_init,
239 static const GInterfaceInfo tny_account_store_view_info = {
240 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
241 NULL, /* interface_finalize */
242 NULL /* interface_data */
246 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
250 g_type_add_interface_static (my_type,
251 TNY_TYPE_ACCOUNT_STORE_VIEW,
252 &tny_account_store_view_info);
258 modest_folder_view_class_init (ModestFolderViewClass *klass)
260 GObjectClass *gobject_class;
261 GtkTreeViewClass *treeview_class;
262 gobject_class = (GObjectClass*) klass;
263 treeview_class = (GtkTreeViewClass*) klass;
265 parent_class = g_type_class_peek_parent (klass);
266 gobject_class->finalize = modest_folder_view_finalize;
267 gobject_class->dispose = modest_folder_view_dispose;
269 g_type_class_add_private (gobject_class,
270 sizeof(ModestFolderViewPrivate));
272 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
273 g_signal_new ("folder_selection_changed",
274 G_TYPE_FROM_CLASS (gobject_class),
276 G_STRUCT_OFFSET (ModestFolderViewClass,
277 folder_selection_changed),
279 modest_marshal_VOID__POINTER_BOOLEAN,
280 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
283 * This signal is emitted whenever the currently selected
284 * folder display name is computed. Note that the name could
285 * be different to the folder name, because we could append
286 * the unread messages count to the folder name to build the
287 * folder display name
289 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
290 g_signal_new ("folder-display-name-changed",
291 G_TYPE_FROM_CLASS (gobject_class),
293 G_STRUCT_OFFSET (ModestFolderViewClass,
294 folder_display_name_changed),
296 g_cclosure_marshal_VOID__STRING,
297 G_TYPE_NONE, 1, G_TYPE_STRING);
299 signals[FOLDER_ACTIVATED_SIGNAL] =
300 g_signal_new ("folder_activated",
301 G_TYPE_FROM_CLASS (gobject_class),
303 G_STRUCT_OFFSET (ModestFolderViewClass,
306 g_cclosure_marshal_VOID__POINTER,
307 G_TYPE_NONE, 1, G_TYPE_POINTER);
310 * Emitted whenever the visible account changes
312 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
313 g_signal_new ("visible-account-changed",
314 G_TYPE_FROM_CLASS (gobject_class),
316 G_STRUCT_OFFSET (ModestFolderViewClass,
317 visible_account_changed),
319 g_cclosure_marshal_VOID__STRING,
320 G_TYPE_NONE, 1, G_TYPE_STRING);
323 * Emitted when the underlying GtkListStore is updating data
325 signals[ACTIVITY_CHANGED_SIGNAL] =
326 g_signal_new ("activity-changed",
327 G_TYPE_FROM_CLASS (gobject_class),
329 G_STRUCT_OFFSET (ModestFolderViewClass,
332 g_cclosure_marshal_VOID__BOOLEAN,
333 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
335 treeview_class->select_cursor_parent = NULL;
337 #ifdef MODEST_TOOLKIT_HILDON2
338 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
344 /* Retrieves the filter, sort and tny models of the folder view. If
345 any of these does not exist then it returns FALSE */
347 get_inner_models (ModestFolderView *self,
348 GtkTreeModel **filter_model,
349 GtkTreeModel **sort_model,
350 GtkTreeModel **tny_model)
352 GtkTreeModel *s_model, *f_model, *t_model;
354 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
355 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
356 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
360 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
361 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
362 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
366 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
370 *filter_model = f_model;
372 *sort_model = s_model;
374 *tny_model = t_model;
379 /* Simplify checks for NULLs: */
381 strings_are_equal (const gchar *a, const gchar *b)
387 return (strcmp (a, b) == 0);
394 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
396 GObject *instance = NULL;
398 gtk_tree_model_get (model, iter,
399 INSTANCE_COLUMN, &instance,
403 return FALSE; /* keep walking */
405 if (!TNY_IS_ACCOUNT (instance)) {
406 g_object_unref (instance);
407 return FALSE; /* keep walking */
410 /* Check if this is the looked-for account: */
411 TnyAccount *this_account = TNY_ACCOUNT (instance);
412 TnyAccount *account = TNY_ACCOUNT (data);
414 const gchar *this_account_id = tny_account_get_id(this_account);
415 const gchar *account_id = tny_account_get_id(account);
416 g_object_unref (instance);
419 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
420 if (strings_are_equal(this_account_id, account_id)) {
421 /* Tell the model that the data has changed, so that
422 * it calls the cell_data_func callbacks again: */
423 /* TODO: This does not seem to actually cause the new string to be shown: */
424 gtk_tree_model_row_changed (model, path, iter);
426 return TRUE; /* stop walking */
429 return FALSE; /* keep walking */
434 ModestFolderView *self;
435 gchar *previous_name;
436 } GetMmcAccountNameData;
439 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
441 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
443 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
445 if (!strings_are_equal (
446 tny_account_get_name(TNY_ACCOUNT(account)),
447 data->previous_name)) {
449 /* Tell the model that the data has changed, so that
450 * it calls the cell_data_func callbacks again: */
451 ModestFolderView *self = data->self;
452 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
454 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
457 g_free (data->previous_name);
458 g_slice_free (GetMmcAccountNameData, data);
462 convert_parent_folders_to_dots (gchar **item_name)
465 gint n_inbox_parents = 0;
468 gchar *last_separator;
470 if (item_name == NULL)
473 path_start = *item_name;
474 for (c = *item_name; *c != '\0'; c++) {
475 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
477 if (c != path_start) {
478 compare = g_strndup (path_start, c - path_start);
479 compare = g_strstrip (compare);
480 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
490 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
491 if (last_separator != NULL) {
492 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
499 buffer = g_string_new ("");
500 for (i = 0; i < n_parents - n_inbox_parents; i++) {
501 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
503 buffer = g_string_append (buffer, last_separator);
505 *item_name = g_string_free (buffer, FALSE);
511 format_compact_style (gchar **item_name,
513 const gchar *mailbox,
515 gboolean multiaccount,
516 gboolean *use_markup)
520 TnyFolderType folder_type;
522 if (!TNY_IS_FOLDER (instance))
525 folder = (TnyFolder *) instance;
527 folder_type = tny_folder_get_folder_type (folder);
528 is_special = (get_cmp_pos (folder_type, folder)!= 4);
531 /* Remove mailbox prefix if any */
532 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
533 if (g_str_has_prefix (*item_name, prefix)) {
534 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
536 *item_name = new_item_name;
540 if (!is_special || multiaccount) {
541 TnyAccount *account = tny_folder_get_account (folder);
542 const gchar *folder_name;
543 gboolean concat_folder_name = FALSE;
546 /* Should not happen */
550 /* convert parent folders to dots */
551 convert_parent_folders_to_dots (item_name);
553 folder_name = tny_folder_get_name (folder);
554 if (g_str_has_suffix (*item_name, folder_name)) {
555 gchar *offset = g_strrstr (*item_name, folder_name);
557 concat_folder_name = TRUE;
560 buffer = g_string_new ("");
562 buffer = g_string_append (buffer, *item_name);
563 if (concat_folder_name) {
564 if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
565 buffer = g_string_append (buffer, folder_name);
566 /* TODO: append a sensitive string to the remote drafts to
567 * be able to know it's the remote one */
568 /* buffer = g_string_append (buffer, " (TODO:remote)"); */
570 buffer = g_string_append (buffer, folder_name);
574 g_object_unref (account);
576 *item_name = g_string_free (buffer, FALSE);
584 text_cell_data (GtkTreeViewColumn *column,
585 GtkCellRenderer *renderer,
586 GtkTreeModel *tree_model,
590 ModestFolderViewPrivate *priv;
591 GObject *rendobj = (GObject *) renderer;
593 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
594 GObject *instance = NULL;
595 gboolean use_markup = FALSE;
597 gtk_tree_model_get (tree_model, iter,
600 INSTANCE_COLUMN, &instance,
602 if (!fname || !instance)
605 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
606 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
608 gchar *item_name = NULL;
609 gint item_weight = 400;
611 if (type != TNY_FOLDER_TYPE_ROOT) {
616 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
617 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
620 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
621 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
623 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
626 /* Sometimes an special folder is reported by the server as
627 NORMAL, like some versions of Dovecot */
628 if (type == TNY_FOLDER_TYPE_NORMAL ||
629 type == TNY_FOLDER_TYPE_UNKNOWN) {
630 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
634 /* note: we cannot reliably get the counts from the
635 * tree model, we need to use explicit calls on
636 * tny_folder for some reason. Select the number to
637 * show: the unread or unsent messages. in case of
638 * outbox/drafts, show all */
639 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
640 (type == TNY_FOLDER_TYPE_OUTBOX) ||
641 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
642 number = tny_folder_get_all_count (TNY_FOLDER(instance));
645 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
649 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
650 item_name = g_strdup (fname);
657 /* Use bold font style if there are unread or unset messages */
659 if (priv->show_message_count) {
660 item_name = g_strdup_printf ("%s (%d)", fname, number);
662 item_name = g_strdup (fname);
666 item_name = g_strdup (fname);
671 } else if (TNY_IS_ACCOUNT (instance)) {
672 /* If it's a server account */
673 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
674 item_name = g_strdup (priv->local_account_name);
676 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
677 /* fname is only correct when the items are first
678 * added to the model, not when the account is
679 * changed later, so get the name from the account
681 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
684 item_name = g_strdup (fname);
690 if (type == TNY_FOLDER_TYPE_INBOX &&
691 g_str_has_suffix (fname, "Inbox")) {
693 item_name = g_strdup (_("mcen_me_folder_inbox"));
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: */
711 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
712 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
713 g_object_set (rendobj,
716 "foreground-set", TRUE,
717 "foreground-gdk", &(priv->active_color),
720 g_object_set (rendobj,
722 "foreground-set", FALSE,
724 "weight", item_weight,
728 /* Notify display name observers */
729 /* TODO: What listens for this signal, and how can it use only the new name? */
730 if (((GObject *) priv->cur_folder_store) == instance) {
731 g_signal_emit (G_OBJECT(self),
732 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
739 /* If it is a Memory card account, make sure that we have the correct name.
740 * This function will be trigerred again when the name has been retrieved: */
741 if (TNY_IS_STORE_ACCOUNT (instance) &&
742 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
744 /* Get the account name asynchronously: */
745 GetMmcAccountNameData *callback_data =
746 g_slice_new0(GetMmcAccountNameData);
747 callback_data->self = self;
749 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
751 callback_data->previous_name = g_strdup (name);
753 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
754 on_get_mmc_account_name, callback_data);
758 g_object_unref (G_OBJECT (instance));
764 messages_cell_data (GtkTreeViewColumn *column,
765 GtkCellRenderer *renderer,
766 GtkTreeModel *tree_model,
770 ModestFolderView *self;
771 ModestFolderViewPrivate *priv;
772 GObject *rendobj = (GObject *) renderer;
773 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
774 GObject *instance = NULL;
775 gchar *item_name = NULL;
777 gtk_tree_model_get (tree_model, iter,
779 INSTANCE_COLUMN, &instance,
784 self = MODEST_FOLDER_VIEW (data);
785 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
788 if (type != TNY_FOLDER_TYPE_ROOT) {
793 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
794 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
797 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
799 /* Sometimes an special folder is reported by the server as
800 NORMAL, like some versions of Dovecot */
801 if (type == TNY_FOLDER_TYPE_NORMAL ||
802 type == TNY_FOLDER_TYPE_UNKNOWN) {
803 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
807 /* note: we cannot reliably get the counts from the tree model, we need
808 * to use explicit calls on tny_folder for some reason.
810 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
811 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
812 (type == TNY_FOLDER_TYPE_OUTBOX) ||
813 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
814 number = tny_folder_get_all_count (TNY_FOLDER(instance));
817 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
821 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
823 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
824 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
830 item_name = g_strdup ("");
833 /* Set the name in the treeview cell: */
834 g_object_set (rendobj,"text", item_name, NULL);
842 g_object_unref (G_OBJECT (instance));
848 GdkPixbuf *pixbuf_open;
849 GdkPixbuf *pixbuf_close;
853 static inline GdkPixbuf *
854 get_composite_pixbuf (const gchar *icon_name,
856 GdkPixbuf *base_pixbuf)
858 GdkPixbuf *emblem, *retval = NULL;
860 emblem = modest_platform_get_icon (icon_name, size);
862 retval = gdk_pixbuf_copy (base_pixbuf);
863 gdk_pixbuf_composite (emblem, retval, 0, 0,
864 MIN (gdk_pixbuf_get_width (emblem),
865 gdk_pixbuf_get_width (retval)),
866 MIN (gdk_pixbuf_get_height (emblem),
867 gdk_pixbuf_get_height (retval)),
868 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
869 g_object_unref (emblem);
874 static inline ThreePixbufs *
875 get_composite_icons (const gchar *icon_code,
877 GdkPixbuf **pixbuf_open,
878 GdkPixbuf **pixbuf_close)
880 ThreePixbufs *retval;
882 if (pixbuf && !*pixbuf) {
884 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
886 *pixbuf = gdk_pixbuf_copy (icon);
892 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
893 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
897 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
898 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
902 retval = g_slice_new0 (ThreePixbufs);
903 if (pixbuf && *pixbuf)
904 retval->pixbuf = g_object_ref (*pixbuf);
906 retval->pixbuf = NULL;
907 if (pixbuf_open && *pixbuf_open)
908 retval->pixbuf_open = g_object_ref (*pixbuf_open);
910 retval->pixbuf_open = NULL;
911 if (pixbuf_close && *pixbuf_close)
912 retval->pixbuf_close = g_object_ref (*pixbuf_close);
914 retval->pixbuf_close = NULL;
919 static inline ThreePixbufs *
920 get_account_protocol_pixbufs (ModestFolderView *folder_view,
921 ModestProtocolType protocol_type,
924 ModestProtocol *protocol;
925 const GdkPixbuf *pixbuf = NULL;
926 ModestFolderViewPrivate *priv;
928 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
930 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
933 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
934 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
935 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
936 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
937 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
938 object, FOLDER_ICON_SIZE);
942 ThreePixbufs *retval;
943 retval = g_slice_new0 (ThreePixbufs);
944 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
945 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
946 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
953 static inline ThreePixbufs*
954 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
956 TnyAccount *account = NULL;
957 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
958 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
959 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
960 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
961 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
963 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
964 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
965 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
966 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
967 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
969 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
970 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
971 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
972 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
973 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
975 ThreePixbufs *retval = NULL;
977 if (TNY_IS_ACCOUNT (instance)) {
978 account = g_object_ref (instance);
979 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
980 account = tny_folder_get_account (TNY_FOLDER (instance));
984 ModestProtocolType account_store_protocol;
986 account_store_protocol = modest_tny_account_get_protocol_type (account);
987 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
988 g_object_unref (account);
994 /* Sometimes an special folder is reported by the server as
995 NORMAL, like some versions of Dovecot */
996 if (type == TNY_FOLDER_TYPE_NORMAL ||
997 type == TNY_FOLDER_TYPE_UNKNOWN) {
998 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1001 /* It's not enough with check the folder type. We need to
1002 ensure that we're not giving a special folder icon to a
1003 normal folder with the same name than a special folder */
1004 if (TNY_IS_FOLDER (instance) &&
1005 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1006 type = TNY_FOLDER_TYPE_NORMAL;
1008 /* Remote folders should not be treated as special folders */
1009 if (TNY_IS_FOLDER_STORE (instance) &&
1010 !TNY_IS_ACCOUNT (instance) &&
1011 type != TNY_FOLDER_TYPE_INBOX &&
1012 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1013 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1016 &anorm_pixbuf_close);
1021 case TNY_FOLDER_TYPE_INVALID:
1022 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1025 case TNY_FOLDER_TYPE_ROOT:
1026 if (TNY_IS_ACCOUNT (instance)) {
1028 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1029 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1032 &avirt_pixbuf_close);
1034 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1036 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1037 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1040 &ammc_pixbuf_close);
1042 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1045 &anorm_pixbuf_close);
1050 case TNY_FOLDER_TYPE_INBOX:
1051 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1054 &inbox_pixbuf_close);
1056 case TNY_FOLDER_TYPE_OUTBOX:
1057 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1059 &outbox_pixbuf_open,
1060 &outbox_pixbuf_close);
1062 case TNY_FOLDER_TYPE_JUNK:
1063 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1066 &junk_pixbuf_close);
1068 case TNY_FOLDER_TYPE_SENT:
1069 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1072 &sent_pixbuf_close);
1074 case TNY_FOLDER_TYPE_TRASH:
1075 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1078 &trash_pixbuf_close);
1080 case TNY_FOLDER_TYPE_DRAFTS:
1081 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1084 &draft_pixbuf_close);
1086 case TNY_FOLDER_TYPE_ARCHIVE:
1087 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1092 case TNY_FOLDER_TYPE_NORMAL:
1094 /* Memory card folders could have an special icon */
1095 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1096 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1101 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1103 &normal_pixbuf_open,
1104 &normal_pixbuf_close);
1113 free_pixbufs (ThreePixbufs *pixbufs)
1115 if (pixbufs->pixbuf)
1116 g_object_unref (pixbufs->pixbuf);
1117 if (pixbufs->pixbuf_open)
1118 g_object_unref (pixbufs->pixbuf_open);
1119 if (pixbufs->pixbuf_close)
1120 g_object_unref (pixbufs->pixbuf_close);
1121 g_slice_free (ThreePixbufs, pixbufs);
1125 icon_cell_data (GtkTreeViewColumn *column,
1126 GtkCellRenderer *renderer,
1127 GtkTreeModel *tree_model,
1131 GObject *rendobj = NULL, *instance = NULL;
1132 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1133 gboolean has_children;
1134 ThreePixbufs *pixbufs;
1135 ModestFolderView *folder_view = (ModestFolderView *) data;
1137 rendobj = (GObject *) renderer;
1139 gtk_tree_model_get (tree_model, iter,
1141 INSTANCE_COLUMN, &instance,
1147 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1148 pixbufs = get_folder_icons (folder_view, type, instance);
1149 g_object_unref (instance);
1152 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1155 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1156 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1159 free_pixbufs (pixbufs);
1163 add_columns (GtkWidget *treeview)
1165 GtkTreeViewColumn *column;
1166 GtkCellRenderer *renderer;
1167 GtkTreeSelection *sel;
1168 ModestFolderViewPrivate *priv;
1170 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1173 column = gtk_tree_view_column_new ();
1175 /* Set icon and text render function */
1176 renderer = gtk_cell_renderer_pixbuf_new();
1177 g_object_set (renderer,
1178 "xpad", MODEST_MARGIN_DEFAULT,
1179 "ypad", MODEST_MARGIN_DEFAULT,
1181 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1182 gtk_tree_view_column_set_cell_data_func(column, renderer,
1183 icon_cell_data, treeview, NULL);
1185 renderer = gtk_cell_renderer_text_new();
1186 g_object_set (renderer,
1187 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1188 "ypad", MODEST_MARGIN_DEFAULT,
1189 "xpad", MODEST_MARGIN_DEFAULT,
1190 "ellipsize-set", TRUE, NULL);
1191 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1192 gtk_tree_view_column_set_cell_data_func(column, renderer,
1193 text_cell_data, treeview, NULL);
1195 priv->messages_renderer = gtk_cell_renderer_text_new ();
1196 g_object_set (priv->messages_renderer,
1198 "ypad", MODEST_MARGIN_DEFAULT,
1199 "xpad", MODEST_MARGIN_DOUBLE,
1200 "alignment", PANGO_ALIGN_RIGHT,
1204 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1205 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1206 messages_cell_data, treeview, NULL);
1208 /* Set selection mode */
1209 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1210 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1212 /* Set treeview appearance */
1213 gtk_tree_view_column_set_spacing (column, 2);
1214 gtk_tree_view_column_set_resizable (column, TRUE);
1215 gtk_tree_view_column_set_fixed_width (column, TRUE);
1216 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1217 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1220 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1224 modest_folder_view_init (ModestFolderView *obj)
1226 ModestFolderViewPrivate *priv;
1229 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1231 priv->timer_expander = 0;
1232 priv->account_store = NULL;
1234 priv->do_refresh = TRUE;
1235 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1236 priv->cur_folder_store = NULL;
1237 priv->visible_account_id = NULL;
1238 priv->mailbox = NULL;
1239 priv->folder_to_select = NULL;
1240 priv->outbox_deleted_handler = 0;
1241 priv->reexpand = TRUE;
1242 priv->signal_handlers = 0;
1244 /* Initialize the local account name */
1245 conf = modest_runtime_get_conf();
1246 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1248 /* Init email clipboard */
1249 priv->clipboard = modest_runtime_get_email_clipboard ();
1250 priv->hidding_ids = NULL;
1251 priv->n_selected = 0;
1252 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1253 priv->reselect = FALSE;
1254 priv->show_non_move = TRUE;
1255 priv->list_to_move = NULL;
1256 priv->show_message_count = TRUE;
1258 /* Build treeview */
1259 add_columns (GTK_WIDGET (obj));
1261 /* Connect signals */
1262 g_signal_connect (G_OBJECT (obj),
1264 G_CALLBACK (on_key_pressed), NULL);
1266 priv->display_name_changed_signal =
1267 g_signal_connect (modest_runtime_get_account_mgr (),
1268 "display_name_changed",
1269 G_CALLBACK (on_display_name_changed),
1273 * Track changes in the local account name (in the device it
1274 * will be the device name)
1276 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1278 G_CALLBACK(on_configuration_key_changed),
1281 gdk_color_parse ("000", &priv->active_color);
1284 g_signal_connect (G_OBJECT (obj), "notify::style",
1285 G_CALLBACK (on_notify_style), (gpointer) obj);
1289 tny_account_store_view_init (gpointer g, gpointer iface_data)
1291 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1293 klass->set_account_store = modest_folder_view_set_account_store;
1297 modest_folder_view_dispose (GObject *obj)
1299 ModestFolderViewPrivate *priv;
1301 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1303 if (priv->signal_handlers) {
1304 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1305 priv->signal_handlers = NULL;
1308 /* Free external references */
1309 if (priv->account_store) {
1310 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1311 priv->account_inserted_signal);
1312 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1313 priv->account_removed_signal);
1314 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1315 priv->account_changed_signal);
1316 g_object_unref (G_OBJECT(priv->account_store));
1317 priv->account_store = NULL;
1321 g_object_unref (G_OBJECT (priv->query));
1325 if (priv->folder_to_select) {
1326 g_object_unref (G_OBJECT(priv->folder_to_select));
1327 priv->folder_to_select = NULL;
1330 if (priv->cur_folder_store) {
1331 g_object_unref (priv->cur_folder_store);
1332 priv->cur_folder_store = NULL;
1335 if (priv->list_to_move) {
1336 g_object_unref (priv->list_to_move);
1337 priv->list_to_move = NULL;
1340 G_OBJECT_CLASS(parent_class)->dispose (obj);
1344 modest_folder_view_finalize (GObject *obj)
1346 ModestFolderViewPrivate *priv;
1347 GtkTreeSelection *sel;
1348 TnyAccount *local_account;
1350 g_return_if_fail (obj);
1352 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1354 if (priv->timer_expander != 0) {
1355 g_source_remove (priv->timer_expander);
1356 priv->timer_expander = 0;
1359 local_account = (TnyAccount *)
1360 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1361 if (local_account) {
1362 if (g_signal_handler_is_connected (local_account,
1363 priv->outbox_deleted_handler))
1364 g_signal_handler_disconnect (local_account,
1365 priv->outbox_deleted_handler);
1366 g_object_unref (local_account);
1369 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1370 priv->display_name_changed_signal)) {
1371 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1372 priv->display_name_changed_signal);
1373 priv->display_name_changed_signal = 0;
1376 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1378 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1380 g_free (priv->local_account_name);
1381 g_free (priv->visible_account_id);
1382 g_free (priv->mailbox);
1384 if (priv->conf_key_signal) {
1385 g_signal_handler_disconnect (modest_runtime_get_conf (),
1386 priv->conf_key_signal);
1387 priv->conf_key_signal = 0;
1390 /* Clear hidding array created by cut operation */
1391 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1393 gdk_color_parse ("000", &priv->active_color);
1395 G_OBJECT_CLASS(parent_class)->finalize (obj);
1400 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1402 ModestFolderViewPrivate *priv;
1405 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1406 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1408 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1409 device = tny_account_store_get_device (account_store);
1411 if (G_UNLIKELY (priv->account_store)) {
1413 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1414 priv->account_inserted_signal))
1415 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1416 priv->account_inserted_signal);
1417 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1418 priv->account_removed_signal))
1419 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1420 priv->account_removed_signal);
1421 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1422 priv->account_changed_signal))
1423 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1424 priv->account_changed_signal);
1425 g_object_unref (G_OBJECT (priv->account_store));
1428 priv->account_store = g_object_ref (G_OBJECT (account_store));
1430 priv->account_removed_signal =
1431 g_signal_connect (G_OBJECT(account_store), "account_removed",
1432 G_CALLBACK (on_account_removed), self);
1434 priv->account_inserted_signal =
1435 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1436 G_CALLBACK (on_account_inserted), self);
1438 priv->account_changed_signal =
1439 g_signal_connect (G_OBJECT(account_store), "account_changed",
1440 G_CALLBACK (on_account_changed), self);
1442 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1443 priv->reselect = FALSE;
1445 g_object_unref (G_OBJECT (device));
1449 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1452 ModestFolderView *self;
1453 GtkTreeModel *model, *filter_model;
1456 self = MODEST_FOLDER_VIEW (user_data);
1458 if (!get_inner_models (self, &filter_model, NULL, &model))
1461 /* Remove outbox from model */
1462 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1463 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1464 g_object_unref (outbox);
1467 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1471 on_account_inserted (TnyAccountStore *account_store,
1472 TnyAccount *account,
1475 ModestFolderViewPrivate *priv;
1476 GtkTreeModel *model, *filter_model;
1478 /* Ignore transport account insertions, we're not showing them
1479 in the folder view */
1480 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1483 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1486 /* If we're adding a new account, and there is no previous
1487 one, we need to select the visible server account */
1488 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1489 !priv->visible_account_id)
1490 modest_widget_memory_restore (modest_runtime_get_conf(),
1491 G_OBJECT (user_data),
1492 MODEST_CONF_FOLDER_VIEW_KEY);
1496 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1497 &filter_model, NULL, &model))
1500 /* Insert the account in the model */
1501 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1503 /* When the model is a list store (plain representation) the
1504 outbox is not a child of any account so we have to manually
1505 delete it because removing the local folders account won't
1506 delete it (because tny_folder_get_account() is not defined
1507 for a merge folder */
1508 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1509 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1511 priv->outbox_deleted_handler =
1512 g_signal_connect (account,
1514 G_CALLBACK (on_outbox_deleted_cb),
1518 /* Refilter the model */
1519 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1524 same_account_selected (ModestFolderView *self,
1525 TnyAccount *account)
1527 ModestFolderViewPrivate *priv;
1528 gboolean same_account = FALSE;
1530 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1532 if (priv->cur_folder_store) {
1533 TnyAccount *selected_folder_account = NULL;
1535 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1536 selected_folder_account =
1537 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1539 selected_folder_account =
1540 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1543 if (selected_folder_account == account)
1544 same_account = TRUE;
1546 g_object_unref (selected_folder_account);
1548 return same_account;
1552 on_account_changed (TnyAccountStore *account_store,
1553 TnyAccount *tny_account,
1556 ModestFolderView *self;
1557 ModestFolderViewPrivate *priv;
1558 GtkTreeModel *model, *filter_model;
1559 GtkTreeSelection *sel;
1560 gboolean same_account;
1562 /* Ignore transport account insertions, we're not showing them
1563 in the folder view */
1564 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1567 self = MODEST_FOLDER_VIEW (user_data);
1568 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1570 /* Get the inner model */
1571 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1572 &filter_model, NULL, &model))
1575 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1577 /* Invalidate the cur_folder_store only if the selected folder
1578 belongs to the account that is being removed */
1579 same_account = same_account_selected (self, tny_account);
1581 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1582 gtk_tree_selection_unselect_all (sel);
1585 /* Remove the account from the model */
1586 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1588 /* Insert the account in the model */
1589 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1591 /* Refilter the model */
1592 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1597 on_account_removed (TnyAccountStore *account_store,
1598 TnyAccount *account,
1601 ModestFolderView *self = NULL;
1602 ModestFolderViewPrivate *priv;
1603 GtkTreeModel *model, *filter_model;
1604 GtkTreeSelection *sel = NULL;
1605 gboolean same_account = FALSE;
1607 /* Ignore transport account removals, we're not showing them
1608 in the folder view */
1609 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1612 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1613 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1617 self = MODEST_FOLDER_VIEW (user_data);
1618 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1620 /* Invalidate the cur_folder_store only if the selected folder
1621 belongs to the account that is being removed */
1622 same_account = same_account_selected (self, account);
1624 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1625 gtk_tree_selection_unselect_all (sel);
1628 /* Invalidate row to select only if the folder to select
1629 belongs to the account that is being removed*/
1630 if (priv->folder_to_select) {
1631 TnyAccount *folder_to_select_account = NULL;
1633 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1634 if (folder_to_select_account == account) {
1635 modest_folder_view_disable_next_folder_selection (self);
1636 g_object_unref (priv->folder_to_select);
1637 priv->folder_to_select = NULL;
1639 g_object_unref (folder_to_select_account);
1642 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1643 &filter_model, NULL, &model))
1646 /* Disconnect the signal handler */
1647 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1648 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1649 if (g_signal_handler_is_connected (account,
1650 priv->outbox_deleted_handler))
1651 g_signal_handler_disconnect (account,
1652 priv->outbox_deleted_handler);
1655 /* Remove the account from the model */
1656 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1658 /* If the removed account is the currently viewed one then
1659 clear the configuration value. The new visible account will be the default account */
1660 if (priv->visible_account_id &&
1661 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1663 /* Clear the current visible account_id */
1664 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1665 modest_folder_view_set_mailbox (self, NULL);
1667 /* Call the restore method, this will set the new visible account */
1668 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1669 MODEST_CONF_FOLDER_VIEW_KEY);
1672 /* Refilter the model */
1673 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1678 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1680 GtkTreeViewColumn *col;
1682 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1684 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1686 g_printerr ("modest: failed get column for title\n");
1690 gtk_tree_view_column_set_title (col, title);
1691 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1696 modest_folder_view_on_map (ModestFolderView *self,
1697 GdkEventExpose *event,
1700 ModestFolderViewPrivate *priv;
1702 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1704 /* This won't happen often */
1705 if (G_UNLIKELY (priv->reselect)) {
1706 /* Select the first inbox or the local account if not found */
1708 /* TODO: this could cause a lock at startup, so we
1709 comment it for the moment. We know that this will
1710 be a bug, because the INBOX is not selected, but we
1711 need to rewrite some parts of Modest to avoid the
1712 deathlock situation */
1713 /* TODO: check if this is still the case */
1714 priv->reselect = FALSE;
1715 /* Notify the display name observers */
1716 g_signal_emit (G_OBJECT(self),
1717 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1721 if (priv->reexpand) {
1722 expand_root_items (self);
1723 priv->reexpand = FALSE;
1730 modest_folder_view_new (TnyFolderStoreQuery *query)
1732 return modest_folder_view_new_full (query, TRUE);
1736 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1739 ModestFolderViewPrivate *priv;
1740 GtkTreeSelection *sel;
1742 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1743 #ifdef MODEST_TOOLKIT_HILDON2
1744 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1747 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1750 priv->query = g_object_ref (query);
1752 priv->do_refresh = do_refresh;
1754 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1755 priv->changed_signal = g_signal_connect (sel, "changed",
1756 G_CALLBACK (on_selection_changed), self);
1758 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1760 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1762 return GTK_WIDGET(self);
1765 /* this feels dirty; any other way to expand all the root items? */
1767 expand_root_items (ModestFolderView *self)
1770 GtkTreeModel *model;
1773 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1774 path = gtk_tree_path_new_first ();
1776 /* all folders should have child items, so.. */
1778 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1779 gtk_tree_path_next (path);
1780 } while (gtk_tree_model_get_iter (model, &iter, path));
1782 gtk_tree_path_free (path);
1786 is_parent_of (TnyFolder *a, TnyFolder *b)
1789 gboolean retval = FALSE;
1791 a_id = tny_folder_get_id (a);
1793 gchar *string_to_match;
1796 string_to_match = g_strconcat (a_id, "/", NULL);
1797 b_id = tny_folder_get_id (b);
1798 retval = g_str_has_prefix (b_id, string_to_match);
1799 g_free (string_to_match);
1805 typedef struct _ForeachFolderInfo {
1808 } ForeachFolderInfo;
1811 foreach_folder_with_id (GtkTreeModel *model,
1816 ForeachFolderInfo *info;
1819 info = (ForeachFolderInfo *) data;
1820 gtk_tree_model_get (model, iter,
1821 INSTANCE_COLUMN, &instance,
1824 if (TNY_IS_FOLDER (instance)) {
1827 id = tny_folder_get_id (TNY_FOLDER (instance));
1829 collate = g_utf8_collate_key (id, -1);
1830 info->found = !strcmp (info->needle, collate);
1836 g_object_unref (instance);
1844 has_folder_with_id (ModestFolderView *self, const gchar *id)
1846 GtkTreeModel *model;
1847 ForeachFolderInfo info = {NULL, FALSE};
1849 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1850 info.needle = g_utf8_collate_key (id, -1);
1852 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1853 g_free (info.needle);
1859 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1862 gboolean retval = FALSE;
1864 a_id = tny_folder_get_id (a);
1867 b_id = tny_folder_get_id (b);
1870 const gchar *last_bar;
1871 gchar *string_to_match;
1872 last_bar = g_strrstr (b_id, "/");
1877 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1878 retval = has_folder_with_id (self, string_to_match);
1879 g_free (string_to_match);
1887 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1889 ModestFolderViewPrivate *priv;
1890 TnyIterator *iterator;
1891 gboolean retval = TRUE;
1893 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1894 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1896 for (iterator = tny_list_create_iterator (priv->list_to_move);
1897 retval && !tny_iterator_is_done (iterator);
1898 tny_iterator_next (iterator)) {
1900 instance = tny_iterator_get_current (iterator);
1901 if (instance == (GObject *) folder) {
1903 } else if (TNY_IS_FOLDER (instance)) {
1904 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1906 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1909 g_object_unref (instance);
1911 g_object_unref (iterator);
1918 * We use this function to implement the
1919 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1920 * account in this case, and the local folders.
1923 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1925 ModestFolderViewPrivate *priv;
1926 gboolean retval = TRUE;
1927 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1928 GObject *instance = NULL;
1929 const gchar *id = NULL;
1931 gboolean found = FALSE;
1932 gboolean cleared = FALSE;
1933 ModestTnyFolderRules rules = 0;
1936 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1937 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1939 gtk_tree_model_get (model, iter,
1940 NAME_COLUMN, &fname,
1942 INSTANCE_COLUMN, &instance,
1945 /* Do not show if there is no instance, this could indeed
1946 happen when the model is being modified while it's being
1947 drawn. This could occur for example when moving folders
1954 if (TNY_IS_ACCOUNT (instance)) {
1955 TnyAccount *acc = TNY_ACCOUNT (instance);
1956 const gchar *account_id = tny_account_get_id (acc);
1958 /* If it isn't a special folder,
1959 * don't show it unless it is the visible account: */
1960 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1961 !modest_tny_account_is_virtual_local_folders (acc) &&
1962 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1964 /* Show only the visible account id */
1965 if (priv->visible_account_id) {
1966 if (strcmp (account_id, priv->visible_account_id))
1973 /* Never show these to the user. They are merged into one folder
1974 * in the local-folders account instead: */
1975 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1978 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1979 /* Only show special folders for current account if needed */
1980 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1981 TnyAccount *account;
1983 account = tny_folder_get_account (TNY_FOLDER (instance));
1985 if (TNY_IS_ACCOUNT (account)) {
1986 const gchar *account_id = tny_account_get_id (account);
1988 if (!modest_tny_account_is_virtual_local_folders (account) &&
1989 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1990 /* Show only the visible account id */
1991 if (priv->visible_account_id) {
1992 if (strcmp (account_id, priv->visible_account_id)) {
1994 } else if (priv->mailbox) {
1995 /* Filter mailboxes */
1996 if (!g_str_has_prefix (fname, priv->mailbox)) {
1998 } else if (!strcmp (fname, priv->mailbox)) {
1999 /* Hide mailbox parent */
2005 g_object_unref (account);
2012 /* Check hiding (if necessary) */
2013 cleared = modest_email_clipboard_cleared (priv->clipboard);
2014 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2015 id = tny_folder_get_id (TNY_FOLDER(instance));
2016 if (priv->hidding_ids != NULL)
2017 for (i=0; i < priv->n_selected && !found; i++)
2018 if (priv->hidding_ids[i] != NULL && id != NULL)
2019 found = (!strcmp (priv->hidding_ids[i], id));
2024 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2025 folder as no message can be move there according to UI specs */
2026 if (retval && !priv->show_non_move) {
2027 if (priv->list_to_move &&
2028 tny_list_get_length (priv->list_to_move) > 0 &&
2029 TNY_IS_FOLDER (instance)) {
2030 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2032 if (retval && TNY_IS_FOLDER (instance) &&
2033 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2035 case TNY_FOLDER_TYPE_OUTBOX:
2036 case TNY_FOLDER_TYPE_SENT:
2037 case TNY_FOLDER_TYPE_DRAFTS:
2040 case TNY_FOLDER_TYPE_UNKNOWN:
2041 case TNY_FOLDER_TYPE_NORMAL:
2042 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2043 if (type == TNY_FOLDER_TYPE_INVALID)
2044 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2046 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2047 type == TNY_FOLDER_TYPE_SENT
2048 || type == TNY_FOLDER_TYPE_DRAFTS)
2055 if (retval && TNY_IS_ACCOUNT (instance) &&
2056 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2057 ModestProtocolType protocol_type;
2059 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2060 retval = !modest_protocol_registry_protocol_type_has_tag
2061 (modest_runtime_get_protocol_registry (),
2063 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2067 /* apply special filters */
2068 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2069 if (TNY_IS_ACCOUNT (instance))
2073 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2074 if (TNY_IS_FOLDER (instance))
2078 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2079 if (TNY_IS_ACCOUNT (instance)) {
2080 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2082 } else if (TNY_IS_FOLDER (instance)) {
2083 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2088 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2089 if (TNY_IS_ACCOUNT (instance)) {
2090 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2092 } else if (TNY_IS_FOLDER (instance)) {
2093 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2098 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2099 /* A mailbox is a fake folder with an @ in the middle of the name */
2100 if (!TNY_IS_FOLDER (instance) ||
2101 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2104 const gchar *folder_name;
2105 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2106 if (!folder_name || strchr (folder_name, '@') == NULL)
2112 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2113 if (TNY_IS_FOLDER (instance)) {
2114 /* Check folder rules */
2115 ModestTnyFolderRules rules;
2117 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2118 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2119 } else if (TNY_IS_ACCOUNT (instance)) {
2120 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2128 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2129 if (TNY_IS_FOLDER (instance)) {
2130 TnyFolderType guess_type;
2132 if (TNY_FOLDER_TYPE_NORMAL) {
2133 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2139 case TNY_FOLDER_TYPE_OUTBOX:
2140 case TNY_FOLDER_TYPE_SENT:
2141 case TNY_FOLDER_TYPE_DRAFTS:
2142 case TNY_FOLDER_TYPE_ARCHIVE:
2143 case TNY_FOLDER_TYPE_INBOX:
2146 case TNY_FOLDER_TYPE_UNKNOWN:
2147 case TNY_FOLDER_TYPE_NORMAL:
2153 } else if (TNY_IS_ACCOUNT (instance)) {
2158 if (retval && TNY_IS_FOLDER (instance)) {
2159 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2162 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2163 if (TNY_IS_FOLDER (instance)) {
2164 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2165 } else if (TNY_IS_ACCOUNT (instance)) {
2170 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2171 if (TNY_IS_FOLDER (instance)) {
2172 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2173 } else if (TNY_IS_ACCOUNT (instance)) {
2178 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2179 if (TNY_IS_FOLDER (instance)) {
2180 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2181 } else if (TNY_IS_ACCOUNT (instance)) {
2187 g_object_unref (instance);
2195 modest_folder_view_update_model (ModestFolderView *self,
2196 TnyAccountStore *account_store)
2198 ModestFolderViewPrivate *priv;
2199 GtkTreeModel *model;
2200 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2202 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2203 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2206 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2208 /* Notify that there is no folder selected */
2209 g_signal_emit (G_OBJECT(self),
2210 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2212 if (priv->cur_folder_store) {
2213 g_object_unref (priv->cur_folder_store);
2214 priv->cur_folder_store = NULL;
2217 /* FIXME: the local accounts are not shown when the query
2218 selects only the subscribed folders */
2219 TnyGtkFolderListStoreFlags flags;
2220 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2221 if (priv->do_refresh)
2222 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2224 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2225 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2227 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2228 MODEST_FOLDER_PATH_SEPARATOR);
2230 /* When the model is a list store (plain representation) the
2231 outbox is not a child of any account so we have to manually
2232 delete it because removing the local folders account won't
2233 delete it (because tny_folder_get_account() is not defined
2234 for a merge folder */
2235 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2236 TnyAccount *account;
2237 ModestTnyAccountStore *acc_store;
2239 acc_store = modest_runtime_get_account_store ();
2240 account = modest_tny_account_store_get_local_folders_account (acc_store);
2242 if (g_signal_handler_is_connected (account,
2243 priv->outbox_deleted_handler))
2244 g_signal_handler_disconnect (account,
2245 priv->outbox_deleted_handler);
2247 priv->outbox_deleted_handler =
2248 g_signal_connect (account,
2250 G_CALLBACK (on_outbox_deleted_cb),
2252 g_object_unref (account);
2255 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2256 /* Get the accounts */
2257 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2259 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2261 if (priv->visible_account_id) {
2262 TnyAccount *account;
2264 /* Add local folders account */
2265 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2268 tny_list_append (TNY_LIST (model), (GObject *) account);
2269 g_object_unref (account);
2272 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2275 tny_list_append (TNY_LIST (model), (GObject *) account);
2276 g_object_unref (account);
2279 /* Add visible account */
2280 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2281 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2282 priv->visible_account_id);
2284 tny_list_append (TNY_LIST (model), (GObject *) account);
2285 g_object_unref (account);
2287 g_warning ("You need to set an account first");
2288 g_object_unref (model);
2292 g_warning ("You need to set an account first");
2293 g_object_unref (model);
2298 sortable = gtk_tree_model_sort_new_with_model (model);
2299 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2301 GTK_SORT_ASCENDING);
2302 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2304 cmp_rows, NULL, NULL);
2306 /* Create filter model */
2307 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2308 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2313 GtkTreeModel *old_tny_model = NULL;
2314 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2315 if (priv->signal_handlers > 0) {
2316 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2317 G_OBJECT (old_tny_model),
2318 "activity-changed");
2323 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2325 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2328 G_CALLBACK (on_activity_changed),
2331 g_object_unref (model);
2332 g_object_unref (filter_model);
2333 g_object_unref (sortable);
2335 /* Force a reselection of the INBOX next time the widget is shown */
2336 priv->reselect = TRUE;
2343 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2345 GtkTreeModel *model = NULL;
2346 TnyFolderStore *folder = NULL;
2348 ModestFolderView *tree_view = NULL;
2349 ModestFolderViewPrivate *priv = NULL;
2350 gboolean selected = FALSE;
2352 g_return_if_fail (sel);
2353 g_return_if_fail (user_data);
2355 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2357 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2359 tree_view = MODEST_FOLDER_VIEW (user_data);
2362 gtk_tree_model_get (model, &iter,
2363 INSTANCE_COLUMN, &folder,
2366 /* If the folder is the same do not notify */
2367 if (folder && priv->cur_folder_store == folder) {
2368 g_object_unref (folder);
2373 /* Current folder was unselected */
2374 if (priv->cur_folder_store) {
2375 /* We must do this firstly because a libtinymail-camel
2376 implementation detail. If we issue the signal
2377 before doing the sync_async, then that signal could
2378 cause (and it actually does it) a free of the
2379 summary of the folder (because the main window will
2380 clear the headers view */
2382 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2383 priv->cur_folder_store, FALSE);
2385 g_object_unref (priv->cur_folder_store);
2386 priv->cur_folder_store = NULL;
2389 /* New current references */
2390 priv->cur_folder_store = folder;
2392 /* New folder has been selected. Do not notify if there is
2393 nothing new selected */
2395 g_signal_emit (G_OBJECT(tree_view),
2396 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2397 0, priv->cur_folder_store, TRUE);
2402 on_row_activated (GtkTreeView *treeview,
2403 GtkTreePath *treepath,
2404 GtkTreeViewColumn *column,
2407 GtkTreeModel *model = NULL;
2408 TnyFolderStore *folder = NULL;
2410 ModestFolderView *self = NULL;
2411 ModestFolderViewPrivate *priv = NULL;
2413 g_return_if_fail (treeview);
2414 g_return_if_fail (user_data);
2416 self = MODEST_FOLDER_VIEW (user_data);
2417 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2419 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2421 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2424 gtk_tree_model_get (model, &iter,
2425 INSTANCE_COLUMN, &folder,
2428 g_signal_emit (G_OBJECT(self),
2429 signals[FOLDER_ACTIVATED_SIGNAL],
2432 #ifdef MODEST_TOOLKIT_HILDON2
2433 HildonUIMode ui_mode;
2434 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2435 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2436 if (priv->cur_folder_store)
2437 g_object_unref (priv->cur_folder_store);
2438 priv->cur_folder_store = g_object_ref (folder);
2442 g_object_unref (folder);
2446 modest_folder_view_get_selected (ModestFolderView *self)
2448 ModestFolderViewPrivate *priv;
2450 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2452 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2453 if (priv->cur_folder_store)
2454 g_object_ref (priv->cur_folder_store);
2456 return priv->cur_folder_store;
2460 get_cmp_rows_type_pos (GObject *folder)
2462 /* Remote accounts -> Local account -> MMC account .*/
2465 if (TNY_IS_ACCOUNT (folder) &&
2466 modest_tny_account_is_virtual_local_folders (
2467 TNY_ACCOUNT (folder))) {
2469 } else if (TNY_IS_ACCOUNT (folder)) {
2470 TnyAccount *account = TNY_ACCOUNT (folder);
2471 const gchar *account_id = tny_account_get_id (account);
2472 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2478 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2479 return -1; /* Should never happen */
2484 inbox_is_special (TnyFolderStore *folder_store)
2486 gboolean is_special = TRUE;
2488 if (TNY_IS_FOLDER (folder_store)) {
2492 gchar *last_inbox_bar;
2494 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2495 downcase = g_utf8_strdown (id, -1);
2496 last_bar = g_strrstr (downcase, "/");
2498 last_inbox_bar = g_strrstr (downcase, "inbox/");
2499 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2510 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2512 TnyAccount *account;
2513 gboolean is_special;
2514 /* Inbox, Outbox, Drafts, Sent, User */
2517 if (!TNY_IS_FOLDER (folder_store))
2520 case TNY_FOLDER_TYPE_INBOX:
2522 account = tny_folder_get_account (folder_store);
2523 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2525 /* In inbox case we need to know if the inbox is really the top
2526 * inbox of the account, or if it's a submailbox inbox. To do
2527 * this we'll apply an heuristic rule: Find last "/" and check
2528 * if it's preceeded by another Inbox */
2529 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2530 g_object_unref (account);
2531 return is_special?0:4;
2534 case TNY_FOLDER_TYPE_OUTBOX:
2535 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2537 case TNY_FOLDER_TYPE_DRAFTS:
2539 account = tny_folder_get_account (folder_store);
2540 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2541 g_object_unref (account);
2542 return is_special?1:4;
2545 case TNY_FOLDER_TYPE_SENT:
2547 account = tny_folder_get_account (folder_store);
2548 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2549 g_object_unref (account);
2550 return is_special?3:4;
2559 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2561 const gchar *a1_name, *a2_name;
2563 a1_name = tny_account_get_name (a1);
2564 a2_name = tny_account_get_name (a2);
2566 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2570 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2572 TnyAccount *a1 = NULL, *a2 = NULL;
2575 if (TNY_IS_ACCOUNT (s1)) {
2576 a1 = TNY_ACCOUNT (g_object_ref (s1));
2577 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2578 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2581 if (TNY_IS_ACCOUNT (s2)) {
2582 a2 = TNY_ACCOUNT (g_object_ref (s2));
2583 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2584 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2601 /* First we sort with the type of account */
2602 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2606 cmp = compare_account_names (a1, a2);
2610 g_object_unref (a1);
2612 g_object_unref (a2);
2618 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2620 gint is_account1, is_account2;
2622 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2623 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2625 return is_account2 - is_account1;
2629 compare_folders (const gchar *name1, const gchar *name2)
2631 const gchar *separator1, *separator2;
2632 const gchar *next1, *next2;
2636 if (name1 == NULL || name1[0] == '\0')
2638 if (name2 == NULL || name2[0] == '\0')
2641 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2643 top1 = g_strndup (name1, separator1 - name1);
2645 top1 = g_strdup (name1);
2648 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2650 top2 = g_strndup (name2, separator2 - name2);
2652 top2 = g_strdup (name2);
2656 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2663 if (separator1 == NULL && separator2 == NULL)
2666 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2667 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2669 return compare_folders (next1, next2);
2674 * This function orders the mail accounts according to these rules:
2675 * 1st - remote accounts
2676 * 2nd - local account
2680 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2684 gchar *name1 = NULL;
2685 gchar *name2 = NULL;
2686 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2687 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2688 GObject *folder1 = NULL;
2689 GObject *folder2 = NULL;
2691 gtk_tree_model_get (tree_model, iter1,
2692 NAME_COLUMN, &name1,
2694 INSTANCE_COLUMN, &folder1,
2696 gtk_tree_model_get (tree_model, iter2,
2697 NAME_COLUMN, &name2,
2698 TYPE_COLUMN, &type2,
2699 INSTANCE_COLUMN, &folder2,
2702 /* Return if we get no folder. This could happen when folder
2703 operations are happening. The model is updated after the
2704 folder copy/move actually occurs, so there could be
2705 situations where the model to be drawn is not correct */
2706 if (!folder1 || !folder2)
2709 /* Sort by type. First the special folders, then the archives */
2710 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2714 /* Now we sort using the account of each folder */
2715 if (TNY_IS_FOLDER_STORE (folder1) &&
2716 TNY_IS_FOLDER_STORE (folder2)) {
2717 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2721 /* Each group is preceeded by its account */
2722 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2727 /* Pure sort by name */
2728 cmp = compare_folders (name1, name2);
2731 g_object_unref(G_OBJECT(folder1));
2733 g_object_unref(G_OBJECT(folder2));
2743 * This function manages the navigation through the folders using the
2744 * keyboard or the hardware keys in the device
2747 on_key_pressed (GtkWidget *self,
2751 GtkTreeSelection *selection;
2753 GtkTreeModel *model;
2754 gboolean retval = FALSE;
2756 /* Up and Down are automatically managed by the treeview */
2757 if (event->keyval == GDK_Return) {
2758 /* Expand/Collapse the selected row */
2759 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2760 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2763 path = gtk_tree_model_get_path (model, &iter);
2765 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2766 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2768 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2769 gtk_tree_path_free (path);
2771 /* No further processing */
2779 * We listen to the changes in the local folder account name key,
2780 * because we want to show the right name in the view. The local
2781 * folder account name corresponds to the device name in the Maemo
2782 * version. We do this because we do not want to query gconf on each
2783 * tree view refresh. It's better to cache it and change whenever
2787 on_configuration_key_changed (ModestConf* conf,
2789 ModestConfEvent event,
2790 ModestConfNotificationId id,
2791 ModestFolderView *self)
2793 ModestFolderViewPrivate *priv;
2796 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2797 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2799 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2800 g_free (priv->local_account_name);
2802 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2803 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2805 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2806 MODEST_CONF_DEVICE_NAME, NULL);
2808 /* Force a redraw */
2809 #if GTK_CHECK_VERSION(2, 8, 0)
2810 GtkTreeViewColumn * tree_column;
2812 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2814 gtk_tree_view_column_queue_resize (tree_column);
2816 gtk_widget_queue_draw (GTK_WIDGET (self));
2822 modest_folder_view_set_style (ModestFolderView *self,
2823 ModestFolderViewStyle style)
2825 ModestFolderViewPrivate *priv;
2827 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2828 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2829 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2831 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2834 priv->style = style;
2838 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2839 const gchar *account_id)
2841 ModestFolderViewPrivate *priv;
2842 GtkTreeModel *model;
2844 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2846 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2848 /* This will be used by the filter_row callback,
2849 * to decided which rows to show: */
2850 if (priv->visible_account_id) {
2851 g_free (priv->visible_account_id);
2852 priv->visible_account_id = NULL;
2855 priv->visible_account_id = g_strdup (account_id);
2858 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2859 if (GTK_IS_TREE_MODEL_FILTER (model))
2860 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2862 modest_folder_view_update_model(self,
2863 (TnyAccountStore *) modest_runtime_get_account_store());
2865 /* Save settings to gconf */
2866 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2867 MODEST_CONF_FOLDER_VIEW_KEY);
2869 /* Notify observers */
2870 g_signal_emit (G_OBJECT(self),
2871 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
2876 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2878 ModestFolderViewPrivate *priv;
2880 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2882 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2884 return (const gchar *) priv->visible_account_id;
2888 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2892 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2894 gtk_tree_model_get (model, iter,
2898 gboolean result = FALSE;
2899 if (type == TNY_FOLDER_TYPE_INBOX) {
2903 *inbox_iter = *iter;
2907 if (gtk_tree_model_iter_children (model, &child, iter)) {
2908 if (find_inbox_iter (model, &child, inbox_iter))
2912 } while (gtk_tree_model_iter_next (model, iter));
2919 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2924 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2925 TnyFolder* a_folder;
2928 gtk_tree_model_get (model, iter,
2929 INSTANCE_COLUMN, &a_folder,
2935 if (folder == a_folder) {
2936 g_object_unref (a_folder);
2937 *folder_iter = *iter;
2940 g_object_unref (a_folder);
2942 if (gtk_tree_model_iter_children (model, &child, iter)) {
2943 if (find_folder_iter (model, &child, folder_iter, folder))
2947 } while (gtk_tree_model_iter_next (model, iter));
2954 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2956 ModestFolderViewPrivate *priv;
2958 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2960 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2962 if (priv->folder_to_select)
2963 g_object_unref(priv->folder_to_select);
2965 priv->folder_to_select = NULL;
2969 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2970 gboolean after_change)
2972 GtkTreeModel *model;
2973 GtkTreeIter iter, folder_iter;
2974 GtkTreeSelection *sel;
2975 ModestFolderViewPrivate *priv = NULL;
2977 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2978 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2980 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2983 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2984 gtk_tree_selection_unselect_all (sel);
2986 if (priv->folder_to_select)
2987 g_object_unref(priv->folder_to_select);
2988 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2992 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2997 /* Refilter the model, before selecting the folder */
2998 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3000 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3001 g_warning ("%s: model is empty", __FUNCTION__);
3005 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3008 path = gtk_tree_model_get_path (model, &folder_iter);
3009 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3011 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3012 gtk_tree_selection_select_iter (sel, &folder_iter);
3013 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3015 gtk_tree_path_free (path);
3023 modest_folder_view_copy_selection (ModestFolderView *self)
3025 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3027 /* Copy selection */
3028 _clipboard_set_selected_data (self, FALSE);
3032 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3034 ModestFolderViewPrivate *priv = NULL;
3035 GtkTreeModel *model = NULL;
3036 const gchar **hidding = NULL;
3037 guint i, n_selected;
3039 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3040 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3042 /* Copy selection */
3043 if (!_clipboard_set_selected_data (folder_view, TRUE))
3046 /* Get hidding ids */
3047 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3049 /* Clear hidding array created by previous cut operation */
3050 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3052 /* Copy hidding array */
3053 priv->n_selected = n_selected;
3054 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3055 for (i=0; i < n_selected; i++)
3056 priv->hidding_ids[i] = g_strdup(hidding[i]);
3058 /* Hide cut folders */
3059 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3060 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3064 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3065 ModestFolderView *folder_view_dst)
3067 GtkTreeModel *filter_model = NULL;
3068 GtkTreeModel *model = NULL;
3069 GtkTreeModel *new_filter_model = NULL;
3070 GtkTreeModel *old_tny_model = NULL;
3071 GtkTreeModel *new_tny_model = NULL;
3072 ModestFolderViewPrivate *dst_priv;
3074 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3075 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3077 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3078 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3079 new_tny_model = NULL;
3082 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3083 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3084 G_OBJECT (old_tny_model),
3085 "activity-changed");
3087 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3088 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3090 /* Build new filter model */
3091 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3092 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3099 /* Set copied model */
3100 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3101 if (new_tny_model) {
3102 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3103 G_OBJECT (new_tny_model),
3105 G_CALLBACK (on_activity_changed),
3110 g_object_unref (new_filter_model);
3114 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3117 GtkTreeModel *model = NULL;
3118 ModestFolderViewPrivate* priv;
3120 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3122 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3123 priv->show_non_move = show;
3124 /* modest_folder_view_update_model(folder_view, */
3125 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3127 /* Hide special folders */
3128 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3129 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3130 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3135 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3138 ModestFolderViewPrivate* priv;
3140 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3142 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3143 priv->show_message_count = show;
3145 g_object_set (G_OBJECT (priv->messages_renderer),
3146 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3150 /* Returns FALSE if it did not selected anything */
3152 _clipboard_set_selected_data (ModestFolderView *folder_view,
3155 ModestFolderViewPrivate *priv = NULL;
3156 TnyFolderStore *folder = NULL;
3157 gboolean retval = FALSE;
3159 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3160 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3162 /* Set selected data on clipboard */
3163 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3164 folder = modest_folder_view_get_selected (folder_view);
3166 /* Do not allow to select an account */
3167 if (TNY_IS_FOLDER (folder)) {
3168 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3173 g_object_unref (folder);
3179 _clear_hidding_filter (ModestFolderView *folder_view)
3181 ModestFolderViewPrivate *priv;
3184 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3185 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3187 if (priv->hidding_ids != NULL) {
3188 for (i=0; i < priv->n_selected; i++)
3189 g_free (priv->hidding_ids[i]);
3190 g_free(priv->hidding_ids);
3196 on_display_name_changed (ModestAccountMgr *mgr,
3197 const gchar *account,
3200 ModestFolderView *self;
3202 self = MODEST_FOLDER_VIEW (user_data);
3204 /* Force a redraw */
3205 #if GTK_CHECK_VERSION(2, 8, 0)
3206 GtkTreeViewColumn * tree_column;
3208 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3210 gtk_tree_view_column_queue_resize (tree_column);
3212 gtk_widget_queue_draw (GTK_WIDGET (self));
3217 modest_folder_view_set_cell_style (ModestFolderView *self,
3218 ModestFolderViewCellStyle cell_style)
3220 ModestFolderViewPrivate *priv = NULL;
3222 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3223 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3225 priv->cell_style = cell_style;
3227 g_object_set (G_OBJECT (priv->messages_renderer),
3228 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3231 gtk_widget_queue_draw (GTK_WIDGET (self));
3235 update_style (ModestFolderView *self)
3237 ModestFolderViewPrivate *priv;
3238 GdkColor style_color, style_active_color;
3239 PangoAttrList *attr_list;
3241 PangoAttribute *attr;
3243 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3244 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3248 attr_list = pango_attr_list_new ();
3250 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3251 gdk_color_parse ("grey", &style_color);
3253 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3254 pango_attr_list_insert (attr_list, attr);
3257 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3259 "SmallSystemFont", NULL,
3262 attr = pango_attr_font_desc_new (pango_font_description_copy
3263 (style->font_desc));
3264 pango_attr_list_insert (attr_list, attr);
3266 g_object_set (G_OBJECT (priv->messages_renderer),
3267 "foreground-gdk", &style_color,
3268 "foreground-set", TRUE,
3269 "attributes", attr_list,
3271 pango_attr_list_unref (attr_list);
3274 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3275 priv->active_color = style_active_color;
3277 gdk_color_parse ("000", &(priv->active_color));
3282 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3284 if (strcmp ("style", spec->name) == 0) {
3285 update_style (MODEST_FOLDER_VIEW (obj));
3286 gtk_widget_queue_draw (GTK_WIDGET (obj));
3291 modest_folder_view_set_filter (ModestFolderView *self,
3292 ModestFolderViewFilter filter)
3294 ModestFolderViewPrivate *priv;
3295 GtkTreeModel *filter_model;
3297 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3298 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3300 priv->filter |= filter;
3302 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3303 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3304 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3309 modest_folder_view_unset_filter (ModestFolderView *self,
3310 ModestFolderViewFilter filter)
3312 ModestFolderViewPrivate *priv;
3313 GtkTreeModel *filter_model;
3315 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3316 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3318 priv->filter &= ~filter;
3320 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3321 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3322 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3327 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3328 ModestTnyFolderRules rules)
3330 GtkTreeModel *filter_model;
3332 gboolean fulfil = FALSE;
3334 if (!get_inner_models (self, &filter_model, NULL, NULL))
3337 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3341 TnyFolderStore *folder;
3343 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3345 if (TNY_IS_FOLDER (folder)) {
3346 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3347 /* Folder rules are negative: non_writable, non_deletable... */
3348 if (!(folder_rules & rules))
3351 g_object_unref (folder);
3354 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3360 modest_folder_view_set_list_to_move (ModestFolderView *self,
3363 ModestFolderViewPrivate *priv;
3365 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3366 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3368 if (priv->list_to_move)
3369 g_object_unref (priv->list_to_move);
3372 g_object_ref (list);
3374 priv->list_to_move = list;
3378 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3380 ModestFolderViewPrivate *priv;
3382 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3383 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3386 g_free (priv->mailbox);
3388 priv->mailbox = g_strdup (mailbox);
3390 /* Notify observers */
3391 g_signal_emit (G_OBJECT(self),
3392 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3393 priv->visible_account_id);
3397 modest_folder_view_get_mailbox (ModestFolderView *self)
3399 ModestFolderViewPrivate *priv;
3401 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3402 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3404 return (const gchar *) priv->mailbox;
3408 modest_folder_view_get_activity (ModestFolderView *self)
3410 ModestFolderViewPrivate *priv;
3411 GtkTreeModel *inner_model;
3413 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3414 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3415 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
3417 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3418 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3425 on_activity_changed (TnyGtkFolderListStore *store,
3427 ModestFolderView *folder_view)
3429 ModestFolderViewPrivate *priv;
3431 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3432 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3433 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3435 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3440 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3442 GtkTreeModel *model;
3448 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3449 ret_value = TNY_LIST (model);
3450 g_object_ref (ret_value);