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 replace_special_folder_prefix (gchar **item_name)
586 const gchar *separator;
589 if (item_name == NULL || *item_name == NULL || **item_name == '\0')
591 separator = g_strstr_len (*item_name, -1, MODEST_FOLDER_PATH_SEPARATOR);
592 if (separator == NULL)
595 prefix = g_strndup (*item_name, separator - *item_name);
598 if (prefix && *prefix != '\0') {
599 TnyFolderType folder_type;
603 downcase = g_utf8_strdown (prefix, -1);
607 if (strcmp (downcase, "inbox") == 0) {
608 folder_type = TNY_FOLDER_TYPE_INBOX;
609 new_name = g_strconcat (_("mcen_me_folder_inbox"), separator, NULL);
611 *item_name = new_name;
613 folder_type = modest_local_folder_info_get_type (prefix);
614 switch (folder_type) {
615 case TNY_FOLDER_TYPE_INBOX:
616 case TNY_FOLDER_TYPE_ARCHIVE:
617 new_name = g_strconcat (modest_local_folder_info_get_type_display_name (folder_type), separator, NULL);
619 *item_name = new_name;
630 text_cell_data (GtkTreeViewColumn *column,
631 GtkCellRenderer *renderer,
632 GtkTreeModel *tree_model,
636 ModestFolderViewPrivate *priv;
637 GObject *rendobj = (GObject *) renderer;
639 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
640 GObject *instance = NULL;
641 gboolean use_markup = FALSE;
643 gtk_tree_model_get (tree_model, iter,
646 INSTANCE_COLUMN, &instance,
648 if (!fname || !instance)
651 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
652 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
654 gchar *item_name = NULL;
655 gint item_weight = 400;
657 if (type != TNY_FOLDER_TYPE_ROOT) {
662 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
663 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
666 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
667 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
669 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
672 /* Sometimes an special folder is reported by the server as
673 NORMAL, like some versions of Dovecot */
674 if (type == TNY_FOLDER_TYPE_NORMAL ||
675 type == TNY_FOLDER_TYPE_UNKNOWN) {
676 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
680 /* note: we cannot reliably get the counts from the
681 * tree model, we need to use explicit calls on
682 * tny_folder for some reason. Select the number to
683 * show: the unread or unsent messages. in case of
684 * outbox/drafts, show all */
685 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
686 (type == TNY_FOLDER_TYPE_OUTBOX) ||
687 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
688 number = tny_folder_get_all_count (TNY_FOLDER(instance));
691 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
695 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
696 item_name = g_strdup (fname);
703 /* Use bold font style if there are unread or unset messages */
705 if (priv->show_message_count) {
706 item_name = g_strdup_printf ("%s (%d)", fname, number);
708 item_name = g_strdup (fname);
712 item_name = g_strdup (fname);
717 } else if (TNY_IS_ACCOUNT (instance)) {
718 /* If it's a server account */
719 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
720 item_name = g_strdup (priv->local_account_name);
722 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
723 /* fname is only correct when the items are first
724 * added to the model, not when the account is
725 * changed later, so get the name from the account
727 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
730 item_name = g_strdup (fname);
736 if (type == TNY_FOLDER_TYPE_INBOX &&
737 g_str_has_suffix (fname, "Inbox")) {
739 item_name = g_strdup (_("mcen_me_folder_inbox"));
743 item_name = g_strdup ("unknown");
745 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
746 gboolean multiaccount;
748 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
749 /* Convert item_name to markup */
750 format_compact_style (&item_name, instance, priv->mailbox,
752 multiaccount, &use_markup);
754 replace_special_folder_prefix (&item_name);
757 if (item_name && item_weight) {
758 /* Set the name in the treeview cell: */
759 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
760 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
761 g_object_set (rendobj,
764 "foreground-set", TRUE,
765 "foreground-gdk", &(priv->active_color),
768 g_object_set (rendobj,
770 "foreground-set", FALSE,
772 "weight", item_weight,
776 /* Notify display name observers */
777 /* TODO: What listens for this signal, and how can it use only the new name? */
778 if (((GObject *) priv->cur_folder_store) == instance) {
779 g_signal_emit (G_OBJECT(self),
780 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
787 /* If it is a Memory card account, make sure that we have the correct name.
788 * This function will be trigerred again when the name has been retrieved: */
789 if (TNY_IS_STORE_ACCOUNT (instance) &&
790 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
792 /* Get the account name asynchronously: */
793 GetMmcAccountNameData *callback_data =
794 g_slice_new0(GetMmcAccountNameData);
795 callback_data->self = self;
797 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
799 callback_data->previous_name = g_strdup (name);
801 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
802 on_get_mmc_account_name, callback_data);
806 g_object_unref (G_OBJECT (instance));
812 messages_cell_data (GtkTreeViewColumn *column,
813 GtkCellRenderer *renderer,
814 GtkTreeModel *tree_model,
818 ModestFolderView *self;
819 ModestFolderViewPrivate *priv;
820 GObject *rendobj = (GObject *) renderer;
821 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
822 GObject *instance = NULL;
823 gchar *item_name = NULL;
825 gtk_tree_model_get (tree_model, iter,
827 INSTANCE_COLUMN, &instance,
832 self = MODEST_FOLDER_VIEW (data);
833 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
836 if (type != TNY_FOLDER_TYPE_ROOT) {
841 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
842 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
845 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
847 /* Sometimes an special folder is reported by the server as
848 NORMAL, like some versions of Dovecot */
849 if (type == TNY_FOLDER_TYPE_NORMAL ||
850 type == TNY_FOLDER_TYPE_UNKNOWN) {
851 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
855 /* note: we cannot reliably get the counts from the tree model, we need
856 * to use explicit calls on tny_folder for some reason.
858 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
859 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
860 (type == TNY_FOLDER_TYPE_OUTBOX) ||
861 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
862 number = tny_folder_get_all_count (TNY_FOLDER(instance));
865 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
869 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
871 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
872 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
878 item_name = g_strdup ("");
881 /* Set the name in the treeview cell: */
882 g_object_set (rendobj,"text", item_name, NULL);
890 g_object_unref (G_OBJECT (instance));
896 GdkPixbuf *pixbuf_open;
897 GdkPixbuf *pixbuf_close;
901 static inline GdkPixbuf *
902 get_composite_pixbuf (const gchar *icon_name,
904 GdkPixbuf *base_pixbuf)
906 GdkPixbuf *emblem, *retval = NULL;
908 emblem = modest_platform_get_icon (icon_name, size);
910 retval = gdk_pixbuf_copy (base_pixbuf);
911 gdk_pixbuf_composite (emblem, retval, 0, 0,
912 MIN (gdk_pixbuf_get_width (emblem),
913 gdk_pixbuf_get_width (retval)),
914 MIN (gdk_pixbuf_get_height (emblem),
915 gdk_pixbuf_get_height (retval)),
916 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
917 g_object_unref (emblem);
922 static inline ThreePixbufs *
923 get_composite_icons (const gchar *icon_code,
925 GdkPixbuf **pixbuf_open,
926 GdkPixbuf **pixbuf_close)
928 ThreePixbufs *retval;
930 if (pixbuf && !*pixbuf) {
932 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
934 *pixbuf = gdk_pixbuf_copy (icon);
940 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
941 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
945 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
946 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
950 retval = g_slice_new0 (ThreePixbufs);
951 if (pixbuf && *pixbuf)
952 retval->pixbuf = g_object_ref (*pixbuf);
954 retval->pixbuf = NULL;
955 if (pixbuf_open && *pixbuf_open)
956 retval->pixbuf_open = g_object_ref (*pixbuf_open);
958 retval->pixbuf_open = NULL;
959 if (pixbuf_close && *pixbuf_close)
960 retval->pixbuf_close = g_object_ref (*pixbuf_close);
962 retval->pixbuf_close = NULL;
967 static inline ThreePixbufs *
968 get_account_protocol_pixbufs (ModestFolderView *folder_view,
969 ModestProtocolType protocol_type,
972 ModestProtocol *protocol;
973 const GdkPixbuf *pixbuf = NULL;
974 ModestFolderViewPrivate *priv;
976 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
978 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
981 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
982 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
983 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
984 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
985 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
986 object, FOLDER_ICON_SIZE);
990 ThreePixbufs *retval;
991 retval = g_slice_new0 (ThreePixbufs);
992 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
993 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
994 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
1001 static inline ThreePixbufs*
1002 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
1004 TnyAccount *account = NULL;
1005 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
1006 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
1007 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
1008 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
1009 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
1011 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1012 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1013 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1014 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1015 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1017 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1018 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1019 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1020 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1021 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1023 ThreePixbufs *retval = NULL;
1025 if (TNY_IS_ACCOUNT (instance)) {
1026 account = g_object_ref (instance);
1027 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1028 account = tny_folder_get_account (TNY_FOLDER (instance));
1032 ModestProtocolType account_store_protocol;
1034 account_store_protocol = modest_tny_account_get_protocol_type (account);
1035 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1036 g_object_unref (account);
1042 /* Sometimes an special folder is reported by the server as
1043 NORMAL, like some versions of Dovecot */
1044 if (type == TNY_FOLDER_TYPE_NORMAL ||
1045 type == TNY_FOLDER_TYPE_UNKNOWN) {
1046 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1049 /* It's not enough with check the folder type. We need to
1050 ensure that we're not giving a special folder icon to a
1051 normal folder with the same name than a special folder */
1052 if (TNY_IS_FOLDER (instance) &&
1053 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1054 type = TNY_FOLDER_TYPE_NORMAL;
1056 /* Remote folders should not be treated as special folders */
1057 if (TNY_IS_FOLDER_STORE (instance) &&
1058 !TNY_IS_ACCOUNT (instance) &&
1059 type != TNY_FOLDER_TYPE_INBOX &&
1060 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1061 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1064 &anorm_pixbuf_close);
1069 case TNY_FOLDER_TYPE_INVALID:
1070 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1073 case TNY_FOLDER_TYPE_ROOT:
1074 if (TNY_IS_ACCOUNT (instance)) {
1076 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1077 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1080 &avirt_pixbuf_close);
1082 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1084 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1085 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1088 &ammc_pixbuf_close);
1090 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1093 &anorm_pixbuf_close);
1098 case TNY_FOLDER_TYPE_INBOX:
1099 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1102 &inbox_pixbuf_close);
1104 case TNY_FOLDER_TYPE_OUTBOX:
1105 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1107 &outbox_pixbuf_open,
1108 &outbox_pixbuf_close);
1110 case TNY_FOLDER_TYPE_JUNK:
1111 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1114 &junk_pixbuf_close);
1116 case TNY_FOLDER_TYPE_SENT:
1117 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1120 &sent_pixbuf_close);
1122 case TNY_FOLDER_TYPE_TRASH:
1123 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1126 &trash_pixbuf_close);
1128 case TNY_FOLDER_TYPE_DRAFTS:
1129 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1132 &draft_pixbuf_close);
1134 case TNY_FOLDER_TYPE_ARCHIVE:
1135 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1140 case TNY_FOLDER_TYPE_NORMAL:
1142 /* Memory card folders could have an special icon */
1143 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1144 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1149 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1151 &normal_pixbuf_open,
1152 &normal_pixbuf_close);
1161 free_pixbufs (ThreePixbufs *pixbufs)
1163 if (pixbufs->pixbuf)
1164 g_object_unref (pixbufs->pixbuf);
1165 if (pixbufs->pixbuf_open)
1166 g_object_unref (pixbufs->pixbuf_open);
1167 if (pixbufs->pixbuf_close)
1168 g_object_unref (pixbufs->pixbuf_close);
1169 g_slice_free (ThreePixbufs, pixbufs);
1173 icon_cell_data (GtkTreeViewColumn *column,
1174 GtkCellRenderer *renderer,
1175 GtkTreeModel *tree_model,
1179 GObject *rendobj = NULL, *instance = NULL;
1180 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1181 gboolean has_children;
1182 ThreePixbufs *pixbufs;
1183 ModestFolderView *folder_view = (ModestFolderView *) data;
1185 rendobj = (GObject *) renderer;
1187 gtk_tree_model_get (tree_model, iter,
1189 INSTANCE_COLUMN, &instance,
1195 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1196 pixbufs = get_folder_icons (folder_view, type, instance);
1197 g_object_unref (instance);
1200 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1203 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1204 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1207 free_pixbufs (pixbufs);
1211 add_columns (GtkWidget *treeview)
1213 GtkTreeViewColumn *column;
1214 GtkCellRenderer *renderer;
1215 GtkTreeSelection *sel;
1216 ModestFolderViewPrivate *priv;
1218 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1221 column = gtk_tree_view_column_new ();
1223 /* Set icon and text render function */
1224 renderer = gtk_cell_renderer_pixbuf_new();
1225 g_object_set (renderer,
1226 "xpad", MODEST_MARGIN_DEFAULT,
1227 "ypad", MODEST_MARGIN_DEFAULT,
1229 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1230 gtk_tree_view_column_set_cell_data_func(column, renderer,
1231 icon_cell_data, treeview, NULL);
1233 renderer = gtk_cell_renderer_text_new();
1234 g_object_set (renderer,
1235 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1236 "ypad", MODEST_MARGIN_DEFAULT,
1237 "xpad", MODEST_MARGIN_DEFAULT,
1238 "ellipsize-set", TRUE, NULL);
1239 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1240 gtk_tree_view_column_set_cell_data_func(column, renderer,
1241 text_cell_data, treeview, NULL);
1243 priv->messages_renderer = gtk_cell_renderer_text_new ();
1244 g_object_set (priv->messages_renderer,
1246 "ypad", MODEST_MARGIN_DEFAULT,
1247 "xpad", MODEST_MARGIN_DOUBLE,
1248 "alignment", PANGO_ALIGN_RIGHT,
1252 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1253 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1254 messages_cell_data, treeview, NULL);
1256 /* Set selection mode */
1257 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1258 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1260 /* Set treeview appearance */
1261 gtk_tree_view_column_set_spacing (column, 2);
1262 gtk_tree_view_column_set_resizable (column, TRUE);
1263 gtk_tree_view_column_set_fixed_width (column, TRUE);
1264 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1265 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1266 gtk_tree_view_set_rules_hint ((GtkTreeView *) treeview, TRUE);
1269 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1273 modest_folder_view_init (ModestFolderView *obj)
1275 ModestFolderViewPrivate *priv;
1278 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1280 priv->timer_expander = 0;
1281 priv->account_store = NULL;
1283 priv->do_refresh = TRUE;
1284 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1285 priv->cur_folder_store = NULL;
1286 priv->visible_account_id = NULL;
1287 priv->mailbox = NULL;
1288 priv->folder_to_select = NULL;
1289 priv->outbox_deleted_handler = 0;
1290 priv->reexpand = TRUE;
1291 priv->signal_handlers = 0;
1293 /* Initialize the local account name */
1294 conf = modest_runtime_get_conf();
1295 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1297 /* Init email clipboard */
1298 priv->clipboard = modest_runtime_get_email_clipboard ();
1299 priv->hidding_ids = NULL;
1300 priv->n_selected = 0;
1301 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1302 priv->reselect = FALSE;
1303 priv->show_non_move = TRUE;
1304 priv->list_to_move = NULL;
1305 priv->show_message_count = TRUE;
1307 /* Build treeview */
1308 add_columns (GTK_WIDGET (obj));
1310 /* Connect signals */
1311 g_signal_connect (G_OBJECT (obj),
1313 G_CALLBACK (on_key_pressed), NULL);
1315 priv->display_name_changed_signal =
1316 g_signal_connect (modest_runtime_get_account_mgr (),
1317 "display_name_changed",
1318 G_CALLBACK (on_display_name_changed),
1322 * Track changes in the local account name (in the device it
1323 * will be the device name)
1325 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1327 G_CALLBACK(on_configuration_key_changed),
1330 gdk_color_parse ("000", &priv->active_color);
1333 g_signal_connect (G_OBJECT (obj), "notify::style",
1334 G_CALLBACK (on_notify_style), (gpointer) obj);
1338 tny_account_store_view_init (gpointer g, gpointer iface_data)
1340 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1342 klass->set_account_store = modest_folder_view_set_account_store;
1346 modest_folder_view_dispose (GObject *obj)
1348 ModestFolderViewPrivate *priv;
1350 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1352 if (priv->signal_handlers) {
1353 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1354 priv->signal_handlers = NULL;
1357 /* Free external references */
1358 if (priv->account_store) {
1359 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1360 priv->account_inserted_signal);
1361 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1362 priv->account_removed_signal);
1363 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1364 priv->account_changed_signal);
1365 g_object_unref (G_OBJECT(priv->account_store));
1366 priv->account_store = NULL;
1370 g_object_unref (G_OBJECT (priv->query));
1374 if (priv->folder_to_select) {
1375 g_object_unref (G_OBJECT(priv->folder_to_select));
1376 priv->folder_to_select = NULL;
1379 if (priv->cur_folder_store) {
1380 g_object_unref (priv->cur_folder_store);
1381 priv->cur_folder_store = NULL;
1384 if (priv->list_to_move) {
1385 g_object_unref (priv->list_to_move);
1386 priv->list_to_move = NULL;
1389 G_OBJECT_CLASS(parent_class)->dispose (obj);
1393 modest_folder_view_finalize (GObject *obj)
1395 ModestFolderViewPrivate *priv;
1396 GtkTreeSelection *sel;
1397 TnyAccount *local_account;
1399 g_return_if_fail (obj);
1401 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1403 if (priv->timer_expander != 0) {
1404 g_source_remove (priv->timer_expander);
1405 priv->timer_expander = 0;
1408 local_account = (TnyAccount *)
1409 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1410 if (local_account) {
1411 if (g_signal_handler_is_connected (local_account,
1412 priv->outbox_deleted_handler))
1413 g_signal_handler_disconnect (local_account,
1414 priv->outbox_deleted_handler);
1415 g_object_unref (local_account);
1418 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1419 priv->display_name_changed_signal)) {
1420 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1421 priv->display_name_changed_signal);
1422 priv->display_name_changed_signal = 0;
1425 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1427 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1429 g_free (priv->local_account_name);
1430 g_free (priv->visible_account_id);
1431 g_free (priv->mailbox);
1433 if (priv->conf_key_signal) {
1434 g_signal_handler_disconnect (modest_runtime_get_conf (),
1435 priv->conf_key_signal);
1436 priv->conf_key_signal = 0;
1439 /* Clear hidding array created by cut operation */
1440 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1442 gdk_color_parse ("000", &priv->active_color);
1444 G_OBJECT_CLASS(parent_class)->finalize (obj);
1449 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1451 ModestFolderViewPrivate *priv;
1454 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1455 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1457 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1458 device = tny_account_store_get_device (account_store);
1460 if (G_UNLIKELY (priv->account_store)) {
1462 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1463 priv->account_inserted_signal))
1464 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1465 priv->account_inserted_signal);
1466 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1467 priv->account_removed_signal))
1468 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1469 priv->account_removed_signal);
1470 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1471 priv->account_changed_signal))
1472 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1473 priv->account_changed_signal);
1474 g_object_unref (G_OBJECT (priv->account_store));
1477 priv->account_store = g_object_ref (G_OBJECT (account_store));
1479 priv->account_removed_signal =
1480 g_signal_connect (G_OBJECT(account_store), "account_removed",
1481 G_CALLBACK (on_account_removed), self);
1483 priv->account_inserted_signal =
1484 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1485 G_CALLBACK (on_account_inserted), self);
1487 priv->account_changed_signal =
1488 g_signal_connect (G_OBJECT(account_store), "account_changed",
1489 G_CALLBACK (on_account_changed), self);
1491 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1492 priv->reselect = FALSE;
1494 g_object_unref (G_OBJECT (device));
1498 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1501 ModestFolderView *self;
1502 GtkTreeModel *model, *filter_model;
1505 self = MODEST_FOLDER_VIEW (user_data);
1507 if (!get_inner_models (self, &filter_model, NULL, &model))
1510 /* Remove outbox from model */
1511 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1512 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1513 g_object_unref (outbox);
1516 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1520 on_account_inserted (TnyAccountStore *account_store,
1521 TnyAccount *account,
1524 ModestFolderViewPrivate *priv;
1525 GtkTreeModel *model, *filter_model;
1527 /* Ignore transport account insertions, we're not showing them
1528 in the folder view */
1529 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1532 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1535 /* If we're adding a new account, and there is no previous
1536 one, we need to select the visible server account */
1537 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1538 !priv->visible_account_id)
1539 modest_widget_memory_restore (modest_runtime_get_conf(),
1540 G_OBJECT (user_data),
1541 MODEST_CONF_FOLDER_VIEW_KEY);
1545 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1546 &filter_model, NULL, &model))
1549 /* Insert the account in the model */
1550 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1552 /* When the model is a list store (plain representation) the
1553 outbox is not a child of any account so we have to manually
1554 delete it because removing the local folders account won't
1555 delete it (because tny_folder_get_account() is not defined
1556 for a merge folder */
1557 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1558 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1560 priv->outbox_deleted_handler =
1561 g_signal_connect (account,
1563 G_CALLBACK (on_outbox_deleted_cb),
1567 /* Refilter the model */
1568 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1573 same_account_selected (ModestFolderView *self,
1574 TnyAccount *account)
1576 ModestFolderViewPrivate *priv;
1577 gboolean same_account = FALSE;
1579 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1581 if (priv->cur_folder_store) {
1582 TnyAccount *selected_folder_account = NULL;
1584 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1585 selected_folder_account =
1586 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1588 selected_folder_account =
1589 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1592 if (selected_folder_account == account)
1593 same_account = TRUE;
1595 g_object_unref (selected_folder_account);
1597 return same_account;
1601 on_account_changed (TnyAccountStore *account_store,
1602 TnyAccount *tny_account,
1605 ModestFolderView *self;
1606 ModestFolderViewPrivate *priv;
1607 GtkTreeModel *model, *filter_model;
1608 GtkTreeSelection *sel;
1609 gboolean same_account;
1611 /* Ignore transport account insertions, we're not showing them
1612 in the folder view */
1613 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1616 self = MODEST_FOLDER_VIEW (user_data);
1617 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1619 /* Get the inner model */
1620 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1621 &filter_model, NULL, &model))
1624 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1626 /* Invalidate the cur_folder_store only if the selected folder
1627 belongs to the account that is being removed */
1628 same_account = same_account_selected (self, tny_account);
1630 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1631 gtk_tree_selection_unselect_all (sel);
1634 /* Remove the account from the model */
1635 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1637 /* Insert the account in the model */
1638 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1640 /* Refilter the model */
1641 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1646 on_account_removed (TnyAccountStore *account_store,
1647 TnyAccount *account,
1650 ModestFolderView *self = NULL;
1651 ModestFolderViewPrivate *priv;
1652 GtkTreeModel *model, *filter_model;
1653 GtkTreeSelection *sel = NULL;
1654 gboolean same_account = FALSE;
1656 /* Ignore transport account removals, we're not showing them
1657 in the folder view */
1658 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1661 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1662 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1666 self = MODEST_FOLDER_VIEW (user_data);
1667 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1669 /* Invalidate the cur_folder_store only if the selected folder
1670 belongs to the account that is being removed */
1671 same_account = same_account_selected (self, account);
1673 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1674 gtk_tree_selection_unselect_all (sel);
1677 /* Invalidate row to select only if the folder to select
1678 belongs to the account that is being removed*/
1679 if (priv->folder_to_select) {
1680 TnyAccount *folder_to_select_account = NULL;
1682 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1683 if (folder_to_select_account == account) {
1684 modest_folder_view_disable_next_folder_selection (self);
1685 g_object_unref (priv->folder_to_select);
1686 priv->folder_to_select = NULL;
1688 g_object_unref (folder_to_select_account);
1691 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1692 &filter_model, NULL, &model))
1695 /* Disconnect the signal handler */
1696 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1697 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1698 if (g_signal_handler_is_connected (account,
1699 priv->outbox_deleted_handler))
1700 g_signal_handler_disconnect (account,
1701 priv->outbox_deleted_handler);
1704 /* Remove the account from the model */
1705 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1707 /* If the removed account is the currently viewed one then
1708 clear the configuration value. The new visible account will be the default account */
1709 if (priv->visible_account_id &&
1710 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1712 /* Clear the current visible account_id */
1713 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1714 modest_folder_view_set_mailbox (self, NULL);
1716 /* Call the restore method, this will set the new visible account */
1717 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1718 MODEST_CONF_FOLDER_VIEW_KEY);
1721 /* Refilter the model */
1722 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1727 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1729 GtkTreeViewColumn *col;
1731 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1733 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1735 g_printerr ("modest: failed get column for title\n");
1739 gtk_tree_view_column_set_title (col, title);
1740 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1745 modest_folder_view_on_map (ModestFolderView *self,
1746 GdkEventExpose *event,
1749 ModestFolderViewPrivate *priv;
1751 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1753 /* This won't happen often */
1754 if (G_UNLIKELY (priv->reselect)) {
1755 /* Select the first inbox or the local account if not found */
1757 /* TODO: this could cause a lock at startup, so we
1758 comment it for the moment. We know that this will
1759 be a bug, because the INBOX is not selected, but we
1760 need to rewrite some parts of Modest to avoid the
1761 deathlock situation */
1762 /* TODO: check if this is still the case */
1763 priv->reselect = FALSE;
1764 /* Notify the display name observers */
1765 g_signal_emit (G_OBJECT(self),
1766 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1770 if (priv->reexpand) {
1771 expand_root_items (self);
1772 priv->reexpand = FALSE;
1779 modest_folder_view_new (TnyFolderStoreQuery *query)
1781 return modest_folder_view_new_full (query, TRUE);
1785 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1788 ModestFolderViewPrivate *priv;
1789 GtkTreeSelection *sel;
1791 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1792 #ifdef MODEST_TOOLKIT_HILDON2
1793 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1796 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1799 priv->query = g_object_ref (query);
1801 priv->do_refresh = do_refresh;
1803 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1804 priv->changed_signal = g_signal_connect (sel, "changed",
1805 G_CALLBACK (on_selection_changed), self);
1807 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1809 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1811 /* Hide headers by default */
1812 gtk_tree_view_set_headers_visible ((GtkTreeView *)self, FALSE);
1814 return GTK_WIDGET(self);
1817 /* this feels dirty; any other way to expand all the root items? */
1819 expand_root_items (ModestFolderView *self)
1822 GtkTreeModel *model;
1825 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1826 path = gtk_tree_path_new_first ();
1828 /* all folders should have child items, so.. */
1830 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1831 gtk_tree_path_next (path);
1832 } while (gtk_tree_model_get_iter (model, &iter, path));
1834 gtk_tree_path_free (path);
1838 is_parent_of (TnyFolder *a, TnyFolder *b)
1841 gboolean retval = FALSE;
1843 a_id = tny_folder_get_id (a);
1845 gchar *string_to_match;
1848 string_to_match = g_strconcat (a_id, "/", NULL);
1849 b_id = tny_folder_get_id (b);
1850 retval = g_str_has_prefix (b_id, string_to_match);
1851 g_free (string_to_match);
1857 typedef struct _ForeachFolderInfo {
1860 } ForeachFolderInfo;
1863 foreach_folder_with_id (GtkTreeModel *model,
1868 ForeachFolderInfo *info;
1871 info = (ForeachFolderInfo *) data;
1872 gtk_tree_model_get (model, iter,
1873 INSTANCE_COLUMN, &instance,
1876 if (TNY_IS_FOLDER (instance)) {
1879 id = tny_folder_get_id (TNY_FOLDER (instance));
1881 collate = g_utf8_collate_key (id, -1);
1882 info->found = !strcmp (info->needle, collate);
1888 g_object_unref (instance);
1896 has_folder_with_id (ModestFolderView *self, const gchar *id)
1898 GtkTreeModel *model;
1899 ForeachFolderInfo info = {NULL, FALSE};
1901 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1902 info.needle = g_utf8_collate_key (id, -1);
1904 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1905 g_free (info.needle);
1911 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1914 gboolean retval = FALSE;
1916 a_id = tny_folder_get_id (a);
1919 b_id = tny_folder_get_id (b);
1922 const gchar *last_bar;
1923 gchar *string_to_match;
1924 last_bar = g_strrstr (b_id, "/");
1929 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1930 retval = has_folder_with_id (self, string_to_match);
1931 g_free (string_to_match);
1939 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1941 ModestFolderViewPrivate *priv;
1942 TnyIterator *iterator;
1943 gboolean retval = TRUE;
1945 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1946 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1948 for (iterator = tny_list_create_iterator (priv->list_to_move);
1949 retval && !tny_iterator_is_done (iterator);
1950 tny_iterator_next (iterator)) {
1952 instance = tny_iterator_get_current (iterator);
1953 if (instance == (GObject *) folder) {
1955 } else if (TNY_IS_FOLDER (instance)) {
1956 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1958 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1961 g_object_unref (instance);
1963 g_object_unref (iterator);
1970 * We use this function to implement the
1971 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1972 * account in this case, and the local folders.
1975 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1977 ModestFolderViewPrivate *priv;
1978 gboolean retval = TRUE;
1979 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1980 GObject *instance = NULL;
1981 const gchar *id = NULL;
1983 gboolean found = FALSE;
1984 gboolean cleared = FALSE;
1985 ModestTnyFolderRules rules = 0;
1988 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1989 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1991 gtk_tree_model_get (model, iter,
1992 NAME_COLUMN, &fname,
1994 INSTANCE_COLUMN, &instance,
1997 /* Do not show if there is no instance, this could indeed
1998 happen when the model is being modified while it's being
1999 drawn. This could occur for example when moving folders
2006 if (TNY_IS_ACCOUNT (instance)) {
2007 TnyAccount *acc = TNY_ACCOUNT (instance);
2008 const gchar *account_id = tny_account_get_id (acc);
2010 /* If it isn't a special folder,
2011 * don't show it unless it is the visible account: */
2012 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2013 !modest_tny_account_is_virtual_local_folders (acc) &&
2014 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2016 /* Show only the visible account id */
2017 if (priv->visible_account_id) {
2018 if (strcmp (account_id, priv->visible_account_id))
2025 /* Never show these to the user. They are merged into one folder
2026 * in the local-folders account instead: */
2027 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2030 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2031 /* Only show special folders for current account if needed */
2032 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2033 TnyAccount *account;
2035 account = tny_folder_get_account (TNY_FOLDER (instance));
2037 if (TNY_IS_ACCOUNT (account)) {
2038 const gchar *account_id = tny_account_get_id (account);
2040 if (!modest_tny_account_is_virtual_local_folders (account) &&
2041 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2042 /* Show only the visible account id */
2043 if (priv->visible_account_id) {
2044 if (strcmp (account_id, priv->visible_account_id)) {
2046 } else if (priv->mailbox) {
2047 /* Filter mailboxes */
2048 if (!g_str_has_prefix (fname, priv->mailbox)) {
2050 } else if (!strcmp (fname, priv->mailbox)) {
2051 /* Hide mailbox parent */
2057 g_object_unref (account);
2064 /* Check hiding (if necessary) */
2065 cleared = modest_email_clipboard_cleared (priv->clipboard);
2066 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2067 id = tny_folder_get_id (TNY_FOLDER(instance));
2068 if (priv->hidding_ids != NULL)
2069 for (i=0; i < priv->n_selected && !found; i++)
2070 if (priv->hidding_ids[i] != NULL && id != NULL)
2071 found = (!strcmp (priv->hidding_ids[i], id));
2076 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2077 folder as no message can be move there according to UI specs */
2078 if (retval && !priv->show_non_move) {
2079 if (priv->list_to_move &&
2080 tny_list_get_length (priv->list_to_move) > 0 &&
2081 TNY_IS_FOLDER (instance)) {
2082 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2084 if (retval && TNY_IS_FOLDER (instance) &&
2085 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2087 case TNY_FOLDER_TYPE_OUTBOX:
2088 case TNY_FOLDER_TYPE_SENT:
2089 case TNY_FOLDER_TYPE_DRAFTS:
2092 case TNY_FOLDER_TYPE_UNKNOWN:
2093 case TNY_FOLDER_TYPE_NORMAL:
2094 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2095 if (type == TNY_FOLDER_TYPE_INVALID)
2096 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2098 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2099 type == TNY_FOLDER_TYPE_SENT
2100 || type == TNY_FOLDER_TYPE_DRAFTS)
2107 if (retval && TNY_IS_ACCOUNT (instance) &&
2108 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2109 ModestProtocolType protocol_type;
2111 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2112 retval = !modest_protocol_registry_protocol_type_has_tag
2113 (modest_runtime_get_protocol_registry (),
2115 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2119 /* apply special filters */
2120 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2121 if (TNY_IS_ACCOUNT (instance))
2125 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2126 if (TNY_IS_FOLDER (instance))
2130 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2131 if (TNY_IS_ACCOUNT (instance)) {
2132 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2134 } else if (TNY_IS_FOLDER (instance)) {
2135 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2140 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2141 if (TNY_IS_ACCOUNT (instance)) {
2142 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2144 } else if (TNY_IS_FOLDER (instance)) {
2145 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2150 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2151 /* A mailbox is a fake folder with an @ in the middle of the name */
2152 if (!TNY_IS_FOLDER (instance) ||
2153 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2156 const gchar *folder_name;
2157 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2158 if (!folder_name || strchr (folder_name, '@') == NULL)
2164 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2165 if (TNY_IS_FOLDER (instance)) {
2166 /* Check folder rules */
2167 ModestTnyFolderRules rules;
2169 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2170 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2171 } else if (TNY_IS_ACCOUNT (instance)) {
2172 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2180 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2181 if (TNY_IS_FOLDER (instance)) {
2182 TnyFolderType guess_type;
2184 if (TNY_FOLDER_TYPE_NORMAL) {
2185 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2191 case TNY_FOLDER_TYPE_OUTBOX:
2192 case TNY_FOLDER_TYPE_SENT:
2193 case TNY_FOLDER_TYPE_DRAFTS:
2194 case TNY_FOLDER_TYPE_ARCHIVE:
2195 case TNY_FOLDER_TYPE_INBOX:
2198 case TNY_FOLDER_TYPE_UNKNOWN:
2199 case TNY_FOLDER_TYPE_NORMAL:
2205 } else if (TNY_IS_ACCOUNT (instance)) {
2210 if (retval && TNY_IS_FOLDER (instance)) {
2211 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2214 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2215 if (TNY_IS_FOLDER (instance)) {
2216 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2217 } else if (TNY_IS_ACCOUNT (instance)) {
2222 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2223 if (TNY_IS_FOLDER (instance)) {
2224 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2225 } else if (TNY_IS_ACCOUNT (instance)) {
2230 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2231 if (TNY_IS_FOLDER (instance)) {
2232 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2233 } else if (TNY_IS_ACCOUNT (instance)) {
2239 g_object_unref (instance);
2247 modest_folder_view_update_model (ModestFolderView *self,
2248 TnyAccountStore *account_store)
2250 ModestFolderViewPrivate *priv;
2251 GtkTreeModel *model;
2252 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2254 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2255 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2258 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2260 /* Notify that there is no folder selected */
2261 g_signal_emit (G_OBJECT(self),
2262 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2264 if (priv->cur_folder_store) {
2265 g_object_unref (priv->cur_folder_store);
2266 priv->cur_folder_store = NULL;
2269 /* FIXME: the local accounts are not shown when the query
2270 selects only the subscribed folders */
2271 TnyGtkFolderListStoreFlags flags;
2272 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2273 if (priv->do_refresh)
2274 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2276 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2277 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2279 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2280 MODEST_FOLDER_PATH_SEPARATOR);
2282 /* When the model is a list store (plain representation) the
2283 outbox is not a child of any account so we have to manually
2284 delete it because removing the local folders account won't
2285 delete it (because tny_folder_get_account() is not defined
2286 for a merge folder */
2287 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2288 TnyAccount *account;
2289 ModestTnyAccountStore *acc_store;
2291 acc_store = modest_runtime_get_account_store ();
2292 account = modest_tny_account_store_get_local_folders_account (acc_store);
2294 if (g_signal_handler_is_connected (account,
2295 priv->outbox_deleted_handler))
2296 g_signal_handler_disconnect (account,
2297 priv->outbox_deleted_handler);
2299 priv->outbox_deleted_handler =
2300 g_signal_connect (account,
2302 G_CALLBACK (on_outbox_deleted_cb),
2304 g_object_unref (account);
2307 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2308 /* Get the accounts */
2309 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2311 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2313 if (priv->visible_account_id) {
2314 TnyAccount *account;
2316 /* Add local folders account */
2317 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2320 tny_list_append (TNY_LIST (model), (GObject *) account);
2321 g_object_unref (account);
2324 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2327 tny_list_append (TNY_LIST (model), (GObject *) account);
2328 g_object_unref (account);
2331 /* Add visible account */
2332 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2333 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2334 priv->visible_account_id);
2336 tny_list_append (TNY_LIST (model), (GObject *) account);
2337 g_object_unref (account);
2339 g_warning ("You need to set an account first");
2340 g_object_unref (model);
2344 g_warning ("You need to set an account first");
2345 g_object_unref (model);
2350 sortable = gtk_tree_model_sort_new_with_model (model);
2351 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2353 GTK_SORT_ASCENDING);
2354 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2356 cmp_rows, NULL, NULL);
2358 /* Create filter model */
2359 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2360 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2365 GtkTreeModel *old_tny_model = NULL;
2366 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2367 if (priv->signal_handlers > 0) {
2368 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2369 G_OBJECT (old_tny_model),
2370 "activity-changed");
2375 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2377 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2380 G_CALLBACK (on_activity_changed),
2383 g_object_unref (model);
2384 g_object_unref (filter_model);
2385 g_object_unref (sortable);
2387 /* Force a reselection of the INBOX next time the widget is shown */
2388 priv->reselect = TRUE;
2395 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2397 GtkTreeModel *model = NULL;
2398 TnyFolderStore *folder = NULL;
2400 ModestFolderView *tree_view = NULL;
2401 ModestFolderViewPrivate *priv = NULL;
2402 gboolean selected = FALSE;
2404 g_return_if_fail (sel);
2405 g_return_if_fail (user_data);
2407 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2409 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2411 tree_view = MODEST_FOLDER_VIEW (user_data);
2414 gtk_tree_model_get (model, &iter,
2415 INSTANCE_COLUMN, &folder,
2418 /* If the folder is the same do not notify */
2419 if (folder && priv->cur_folder_store == folder) {
2420 g_object_unref (folder);
2425 /* Current folder was unselected */
2426 if (priv->cur_folder_store) {
2427 /* We must do this firstly because a libtinymail-camel
2428 implementation detail. If we issue the signal
2429 before doing the sync_async, then that signal could
2430 cause (and it actually does it) a free of the
2431 summary of the folder (because the main window will
2432 clear the headers view */
2434 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2435 priv->cur_folder_store, FALSE);
2437 g_object_unref (priv->cur_folder_store);
2438 priv->cur_folder_store = NULL;
2441 /* New current references */
2442 priv->cur_folder_store = folder;
2444 /* New folder has been selected. Do not notify if there is
2445 nothing new selected */
2447 g_signal_emit (G_OBJECT(tree_view),
2448 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2449 0, priv->cur_folder_store, TRUE);
2454 on_row_activated (GtkTreeView *treeview,
2455 GtkTreePath *treepath,
2456 GtkTreeViewColumn *column,
2459 GtkTreeModel *model = NULL;
2460 TnyFolderStore *folder = NULL;
2462 ModestFolderView *self = NULL;
2463 ModestFolderViewPrivate *priv = NULL;
2465 g_return_if_fail (treeview);
2466 g_return_if_fail (user_data);
2468 self = MODEST_FOLDER_VIEW (user_data);
2469 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2471 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2473 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2476 gtk_tree_model_get (model, &iter,
2477 INSTANCE_COLUMN, &folder,
2480 g_signal_emit (G_OBJECT(self),
2481 signals[FOLDER_ACTIVATED_SIGNAL],
2484 #ifdef MODEST_TOOLKIT_HILDON2
2485 HildonUIMode ui_mode;
2486 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2487 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2488 if (priv->cur_folder_store)
2489 g_object_unref (priv->cur_folder_store);
2490 priv->cur_folder_store = g_object_ref (folder);
2494 g_object_unref (folder);
2498 modest_folder_view_get_selected (ModestFolderView *self)
2500 ModestFolderViewPrivate *priv;
2502 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2504 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2505 if (priv->cur_folder_store)
2506 g_object_ref (priv->cur_folder_store);
2508 return priv->cur_folder_store;
2512 get_cmp_rows_type_pos (GObject *folder)
2514 /* Remote accounts -> Local account -> MMC account .*/
2517 if (TNY_IS_ACCOUNT (folder) &&
2518 modest_tny_account_is_virtual_local_folders (
2519 TNY_ACCOUNT (folder))) {
2521 } else if (TNY_IS_ACCOUNT (folder)) {
2522 TnyAccount *account = TNY_ACCOUNT (folder);
2523 const gchar *account_id = tny_account_get_id (account);
2524 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2530 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2531 return -1; /* Should never happen */
2536 inbox_is_special (TnyFolderStore *folder_store)
2538 gboolean is_special = TRUE;
2540 if (TNY_IS_FOLDER (folder_store)) {
2544 gchar *last_inbox_bar;
2546 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2547 downcase = g_utf8_strdown (id, -1);
2548 last_bar = g_strrstr (downcase, "/");
2550 last_inbox_bar = g_strrstr (downcase, "inbox/");
2551 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2562 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2564 TnyAccount *account;
2565 gboolean is_special;
2566 /* Inbox, Outbox, Drafts, Sent, User */
2569 if (!TNY_IS_FOLDER (folder_store))
2572 case TNY_FOLDER_TYPE_INBOX:
2574 account = tny_folder_get_account (folder_store);
2575 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2577 /* In inbox case we need to know if the inbox is really the top
2578 * inbox of the account, or if it's a submailbox inbox. To do
2579 * this we'll apply an heuristic rule: Find last "/" and check
2580 * if it's preceeded by another Inbox */
2581 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2582 g_object_unref (account);
2583 return is_special?0:4;
2586 case TNY_FOLDER_TYPE_OUTBOX:
2587 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2589 case TNY_FOLDER_TYPE_DRAFTS:
2591 account = tny_folder_get_account (folder_store);
2592 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2593 g_object_unref (account);
2594 return is_special?1:4;
2597 case TNY_FOLDER_TYPE_SENT:
2599 account = tny_folder_get_account (folder_store);
2600 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2601 g_object_unref (account);
2602 return is_special?3:4;
2611 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2613 const gchar *a1_name, *a2_name;
2615 a1_name = tny_account_get_name (a1);
2616 a2_name = tny_account_get_name (a2);
2618 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2622 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2624 TnyAccount *a1 = NULL, *a2 = NULL;
2627 if (TNY_IS_ACCOUNT (s1)) {
2628 a1 = TNY_ACCOUNT (g_object_ref (s1));
2629 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2630 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2633 if (TNY_IS_ACCOUNT (s2)) {
2634 a2 = TNY_ACCOUNT (g_object_ref (s2));
2635 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2636 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2653 /* First we sort with the type of account */
2654 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2658 cmp = compare_account_names (a1, a2);
2662 g_object_unref (a1);
2664 g_object_unref (a2);
2670 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2672 gint is_account1, is_account2;
2674 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2675 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2677 return is_account2 - is_account1;
2681 compare_folders (const gchar *name1, const gchar *name2)
2683 const gchar *separator1, *separator2;
2684 const gchar *next1, *next2;
2688 if (name1 == NULL || name1[0] == '\0')
2690 if (name2 == NULL || name2[0] == '\0')
2693 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2695 top1 = g_strndup (name1, separator1 - name1);
2697 top1 = g_strdup (name1);
2700 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2702 top2 = g_strndup (name2, separator2 - name2);
2704 top2 = g_strdup (name2);
2708 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2715 if (separator1 == NULL && separator2 == NULL)
2718 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2719 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2721 return compare_folders (next1, next2);
2726 * This function orders the mail accounts according to these rules:
2727 * 1st - remote accounts
2728 * 2nd - local account
2732 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2736 gchar *name1 = NULL;
2737 gchar *name2 = NULL;
2738 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2739 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2740 GObject *folder1 = NULL;
2741 GObject *folder2 = NULL;
2743 gtk_tree_model_get (tree_model, iter1,
2744 NAME_COLUMN, &name1,
2746 INSTANCE_COLUMN, &folder1,
2748 gtk_tree_model_get (tree_model, iter2,
2749 NAME_COLUMN, &name2,
2750 TYPE_COLUMN, &type2,
2751 INSTANCE_COLUMN, &folder2,
2754 /* Return if we get no folder. This could happen when folder
2755 operations are happening. The model is updated after the
2756 folder copy/move actually occurs, so there could be
2757 situations where the model to be drawn is not correct */
2758 if (!folder1 || !folder2)
2761 /* Sort by type. First the special folders, then the archives */
2762 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2766 /* Now we sort using the account of each folder */
2767 if (TNY_IS_FOLDER_STORE (folder1) &&
2768 TNY_IS_FOLDER_STORE (folder2)) {
2769 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2773 /* Each group is preceeded by its account */
2774 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2779 /* Pure sort by name */
2780 cmp = compare_folders (name1, name2);
2783 g_object_unref(G_OBJECT(folder1));
2785 g_object_unref(G_OBJECT(folder2));
2795 * This function manages the navigation through the folders using the
2796 * keyboard or the hardware keys in the device
2799 on_key_pressed (GtkWidget *self,
2803 GtkTreeSelection *selection;
2805 GtkTreeModel *model;
2806 gboolean retval = FALSE;
2808 /* Up and Down are automatically managed by the treeview */
2809 if (event->keyval == GDK_Return) {
2810 /* Expand/Collapse the selected row */
2811 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2812 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2815 path = gtk_tree_model_get_path (model, &iter);
2817 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2818 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2820 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2821 gtk_tree_path_free (path);
2823 /* No further processing */
2831 * We listen to the changes in the local folder account name key,
2832 * because we want to show the right name in the view. The local
2833 * folder account name corresponds to the device name in the Maemo
2834 * version. We do this because we do not want to query gconf on each
2835 * tree view refresh. It's better to cache it and change whenever
2839 on_configuration_key_changed (ModestConf* conf,
2841 ModestConfEvent event,
2842 ModestConfNotificationId id,
2843 ModestFolderView *self)
2845 ModestFolderViewPrivate *priv;
2848 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2849 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2851 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2852 g_free (priv->local_account_name);
2854 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2855 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2857 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2858 MODEST_CONF_DEVICE_NAME, NULL);
2860 /* Force a redraw */
2861 #if GTK_CHECK_VERSION(2, 8, 0)
2862 GtkTreeViewColumn * tree_column;
2864 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2866 gtk_tree_view_column_queue_resize (tree_column);
2868 gtk_widget_queue_draw (GTK_WIDGET (self));
2874 modest_folder_view_set_style (ModestFolderView *self,
2875 ModestFolderViewStyle style)
2877 ModestFolderViewPrivate *priv;
2879 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2880 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2881 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2883 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2886 priv->style = style;
2890 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2891 const gchar *account_id)
2893 ModestFolderViewPrivate *priv;
2894 GtkTreeModel *model;
2896 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2898 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2900 /* This will be used by the filter_row callback,
2901 * to decided which rows to show: */
2902 if (priv->visible_account_id) {
2903 g_free (priv->visible_account_id);
2904 priv->visible_account_id = NULL;
2907 priv->visible_account_id = g_strdup (account_id);
2910 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2911 if (GTK_IS_TREE_MODEL_FILTER (model))
2912 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2914 modest_folder_view_update_model(self,
2915 (TnyAccountStore *) modest_runtime_get_account_store());
2917 /* Save settings to gconf */
2918 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2919 MODEST_CONF_FOLDER_VIEW_KEY);
2921 /* Notify observers */
2922 g_signal_emit (G_OBJECT(self),
2923 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
2928 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2930 ModestFolderViewPrivate *priv;
2932 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2934 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2936 return (const gchar *) priv->visible_account_id;
2941 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2946 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2947 TnyFolder* a_folder;
2950 gtk_tree_model_get (model, iter,
2951 INSTANCE_COLUMN, &a_folder,
2957 if (folder == a_folder) {
2958 g_object_unref (a_folder);
2959 *folder_iter = *iter;
2962 g_object_unref (a_folder);
2964 if (gtk_tree_model_iter_children (model, &child, iter)) {
2965 if (find_folder_iter (model, &child, folder_iter, folder))
2969 } while (gtk_tree_model_iter_next (model, iter));
2976 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2978 ModestFolderViewPrivate *priv;
2980 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2982 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2984 if (priv->folder_to_select)
2985 g_object_unref(priv->folder_to_select);
2987 priv->folder_to_select = NULL;
2991 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2992 gboolean after_change)
2994 GtkTreeModel *model;
2995 GtkTreeIter iter, folder_iter;
2996 GtkTreeSelection *sel;
2997 ModestFolderViewPrivate *priv = NULL;
2999 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3000 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3002 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3005 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3006 gtk_tree_selection_unselect_all (sel);
3008 if (priv->folder_to_select)
3009 g_object_unref(priv->folder_to_select);
3010 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3014 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3019 /* Refilter the model, before selecting the folder */
3020 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3022 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3023 g_warning ("%s: model is empty", __FUNCTION__);
3027 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3030 path = gtk_tree_model_get_path (model, &folder_iter);
3031 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3033 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3034 gtk_tree_selection_select_iter (sel, &folder_iter);
3035 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3037 gtk_tree_path_free (path);
3045 modest_folder_view_copy_selection (ModestFolderView *self)
3047 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3049 /* Copy selection */
3050 _clipboard_set_selected_data (self, FALSE);
3054 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3056 ModestFolderViewPrivate *priv = NULL;
3057 GtkTreeModel *model = NULL;
3058 const gchar **hidding = NULL;
3059 guint i, n_selected;
3061 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3062 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3064 /* Copy selection */
3065 if (!_clipboard_set_selected_data (folder_view, TRUE))
3068 /* Get hidding ids */
3069 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3071 /* Clear hidding array created by previous cut operation */
3072 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3074 /* Copy hidding array */
3075 priv->n_selected = n_selected;
3076 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3077 for (i=0; i < n_selected; i++)
3078 priv->hidding_ids[i] = g_strdup(hidding[i]);
3080 /* Hide cut folders */
3081 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3082 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3086 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3087 ModestFolderView *folder_view_dst)
3089 GtkTreeModel *filter_model = NULL;
3090 GtkTreeModel *model = NULL;
3091 GtkTreeModel *new_filter_model = NULL;
3092 GtkTreeModel *old_tny_model = NULL;
3093 GtkTreeModel *new_tny_model = NULL;
3094 ModestFolderViewPrivate *dst_priv;
3096 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3097 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3099 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3100 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3101 new_tny_model = NULL;
3104 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3105 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3106 G_OBJECT (old_tny_model),
3107 "activity-changed");
3109 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3110 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3112 /* Build new filter model */
3113 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3114 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3121 /* Set copied model */
3122 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3123 if (new_tny_model) {
3124 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3125 G_OBJECT (new_tny_model),
3127 G_CALLBACK (on_activity_changed),
3132 g_object_unref (new_filter_model);
3136 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3139 GtkTreeModel *model = NULL;
3140 ModestFolderViewPrivate* priv;
3142 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3144 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3145 priv->show_non_move = show;
3146 /* modest_folder_view_update_model(folder_view, */
3147 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3149 /* Hide special folders */
3150 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3151 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3152 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3157 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3160 ModestFolderViewPrivate* priv;
3162 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3164 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3165 priv->show_message_count = show;
3167 g_object_set (G_OBJECT (priv->messages_renderer),
3168 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3172 /* Returns FALSE if it did not selected anything */
3174 _clipboard_set_selected_data (ModestFolderView *folder_view,
3177 ModestFolderViewPrivate *priv = NULL;
3178 TnyFolderStore *folder = NULL;
3179 gboolean retval = FALSE;
3181 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3182 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3184 /* Set selected data on clipboard */
3185 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3186 folder = modest_folder_view_get_selected (folder_view);
3188 /* Do not allow to select an account */
3189 if (TNY_IS_FOLDER (folder)) {
3190 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3195 g_object_unref (folder);
3201 _clear_hidding_filter (ModestFolderView *folder_view)
3203 ModestFolderViewPrivate *priv;
3206 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3207 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3209 if (priv->hidding_ids != NULL) {
3210 for (i=0; i < priv->n_selected; i++)
3211 g_free (priv->hidding_ids[i]);
3212 g_free(priv->hidding_ids);
3218 on_display_name_changed (ModestAccountMgr *mgr,
3219 const gchar *account,
3222 ModestFolderView *self;
3224 self = MODEST_FOLDER_VIEW (user_data);
3226 /* Force a redraw */
3227 #if GTK_CHECK_VERSION(2, 8, 0)
3228 GtkTreeViewColumn * tree_column;
3230 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3232 gtk_tree_view_column_queue_resize (tree_column);
3234 gtk_widget_queue_draw (GTK_WIDGET (self));
3239 modest_folder_view_set_cell_style (ModestFolderView *self,
3240 ModestFolderViewCellStyle cell_style)
3242 ModestFolderViewPrivate *priv = NULL;
3244 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3245 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3247 priv->cell_style = cell_style;
3249 g_object_set (G_OBJECT (priv->messages_renderer),
3250 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3253 gtk_widget_queue_draw (GTK_WIDGET (self));
3257 update_style (ModestFolderView *self)
3259 ModestFolderViewPrivate *priv;
3260 GdkColor style_color, style_active_color;
3261 PangoAttrList *attr_list;
3263 PangoAttribute *attr;
3265 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3266 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3270 attr_list = pango_attr_list_new ();
3272 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3273 gdk_color_parse ("grey", &style_color);
3275 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3276 pango_attr_list_insert (attr_list, attr);
3279 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3281 "SmallSystemFont", NULL,
3284 attr = pango_attr_font_desc_new (pango_font_description_copy
3285 (style->font_desc));
3286 pango_attr_list_insert (attr_list, attr);
3288 g_object_set (G_OBJECT (priv->messages_renderer),
3289 "foreground-gdk", &style_color,
3290 "foreground-set", TRUE,
3291 "attributes", attr_list,
3293 pango_attr_list_unref (attr_list);
3296 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3297 priv->active_color = style_active_color;
3299 gdk_color_parse ("000", &(priv->active_color));
3304 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3306 if (strcmp ("style", spec->name) == 0) {
3307 update_style (MODEST_FOLDER_VIEW (obj));
3308 gtk_widget_queue_draw (GTK_WIDGET (obj));
3313 modest_folder_view_set_filter (ModestFolderView *self,
3314 ModestFolderViewFilter filter)
3316 ModestFolderViewPrivate *priv;
3317 GtkTreeModel *filter_model;
3319 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3320 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3322 priv->filter |= filter;
3324 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3325 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3326 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3331 modest_folder_view_unset_filter (ModestFolderView *self,
3332 ModestFolderViewFilter filter)
3334 ModestFolderViewPrivate *priv;
3335 GtkTreeModel *filter_model;
3337 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3338 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3340 priv->filter &= ~filter;
3342 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3343 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3344 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3349 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3350 ModestTnyFolderRules rules)
3352 GtkTreeModel *filter_model;
3354 gboolean fulfil = FALSE;
3356 if (!get_inner_models (self, &filter_model, NULL, NULL))
3359 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3363 TnyFolderStore *folder;
3365 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3367 if (TNY_IS_FOLDER (folder)) {
3368 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3369 /* Folder rules are negative: non_writable, non_deletable... */
3370 if (!(folder_rules & rules))
3373 g_object_unref (folder);
3376 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3382 modest_folder_view_set_list_to_move (ModestFolderView *self,
3385 ModestFolderViewPrivate *priv;
3387 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3388 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3390 if (priv->list_to_move)
3391 g_object_unref (priv->list_to_move);
3394 g_object_ref (list);
3396 priv->list_to_move = list;
3400 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3402 ModestFolderViewPrivate *priv;
3404 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3405 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3408 g_free (priv->mailbox);
3410 priv->mailbox = g_strdup (mailbox);
3412 /* Notify observers */
3413 g_signal_emit (G_OBJECT(self),
3414 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3415 priv->visible_account_id);
3419 modest_folder_view_get_mailbox (ModestFolderView *self)
3421 ModestFolderViewPrivate *priv;
3423 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3424 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3426 return (const gchar *) priv->mailbox;
3430 modest_folder_view_get_activity (ModestFolderView *self)
3432 ModestFolderViewPrivate *priv;
3433 GtkTreeModel *inner_model;
3435 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3436 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3438 if (!get_inner_models (self, NULL, NULL, &inner_model))
3441 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3442 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3449 on_activity_changed (TnyGtkFolderListStore *store,
3451 ModestFolderView *folder_view)
3453 ModestFolderViewPrivate *priv;
3455 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3456 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3457 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3459 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3464 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3466 GtkTreeModel *model;
3472 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3473 ret_value = TNY_LIST (model);
3474 g_object_ref (ret_value);