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);
1268 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1272 modest_folder_view_init (ModestFolderView *obj)
1274 ModestFolderViewPrivate *priv;
1277 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1279 priv->timer_expander = 0;
1280 priv->account_store = NULL;
1282 priv->do_refresh = TRUE;
1283 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1284 priv->cur_folder_store = NULL;
1285 priv->visible_account_id = NULL;
1286 priv->mailbox = NULL;
1287 priv->folder_to_select = NULL;
1288 priv->outbox_deleted_handler = 0;
1289 priv->reexpand = TRUE;
1290 priv->signal_handlers = 0;
1292 /* Initialize the local account name */
1293 conf = modest_runtime_get_conf();
1294 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1296 /* Init email clipboard */
1297 priv->clipboard = modest_runtime_get_email_clipboard ();
1298 priv->hidding_ids = NULL;
1299 priv->n_selected = 0;
1300 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1301 priv->reselect = FALSE;
1302 priv->show_non_move = TRUE;
1303 priv->list_to_move = NULL;
1304 priv->show_message_count = TRUE;
1306 /* Build treeview */
1307 add_columns (GTK_WIDGET (obj));
1309 /* Connect signals */
1310 g_signal_connect (G_OBJECT (obj),
1312 G_CALLBACK (on_key_pressed), NULL);
1314 priv->display_name_changed_signal =
1315 g_signal_connect (modest_runtime_get_account_mgr (),
1316 "display_name_changed",
1317 G_CALLBACK (on_display_name_changed),
1321 * Track changes in the local account name (in the device it
1322 * will be the device name)
1324 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1326 G_CALLBACK(on_configuration_key_changed),
1329 gdk_color_parse ("000", &priv->active_color);
1332 g_signal_connect (G_OBJECT (obj), "notify::style",
1333 G_CALLBACK (on_notify_style), (gpointer) obj);
1337 tny_account_store_view_init (gpointer g, gpointer iface_data)
1339 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1341 klass->set_account_store = modest_folder_view_set_account_store;
1345 modest_folder_view_dispose (GObject *obj)
1347 ModestFolderViewPrivate *priv;
1349 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1351 if (priv->signal_handlers) {
1352 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1353 priv->signal_handlers = NULL;
1356 /* Free external references */
1357 if (priv->account_store) {
1358 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1359 priv->account_inserted_signal);
1360 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1361 priv->account_removed_signal);
1362 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1363 priv->account_changed_signal);
1364 g_object_unref (G_OBJECT(priv->account_store));
1365 priv->account_store = NULL;
1369 g_object_unref (G_OBJECT (priv->query));
1373 if (priv->folder_to_select) {
1374 g_object_unref (G_OBJECT(priv->folder_to_select));
1375 priv->folder_to_select = NULL;
1378 if (priv->cur_folder_store) {
1379 g_object_unref (priv->cur_folder_store);
1380 priv->cur_folder_store = NULL;
1383 if (priv->list_to_move) {
1384 g_object_unref (priv->list_to_move);
1385 priv->list_to_move = NULL;
1388 G_OBJECT_CLASS(parent_class)->dispose (obj);
1392 modest_folder_view_finalize (GObject *obj)
1394 ModestFolderViewPrivate *priv;
1395 GtkTreeSelection *sel;
1396 TnyAccount *local_account;
1398 g_return_if_fail (obj);
1400 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1402 if (priv->timer_expander != 0) {
1403 g_source_remove (priv->timer_expander);
1404 priv->timer_expander = 0;
1407 local_account = (TnyAccount *)
1408 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1409 if (local_account) {
1410 if (g_signal_handler_is_connected (local_account,
1411 priv->outbox_deleted_handler))
1412 g_signal_handler_disconnect (local_account,
1413 priv->outbox_deleted_handler);
1414 g_object_unref (local_account);
1417 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1418 priv->display_name_changed_signal)) {
1419 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1420 priv->display_name_changed_signal);
1421 priv->display_name_changed_signal = 0;
1424 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1426 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1428 g_free (priv->local_account_name);
1429 g_free (priv->visible_account_id);
1430 g_free (priv->mailbox);
1432 if (priv->conf_key_signal) {
1433 g_signal_handler_disconnect (modest_runtime_get_conf (),
1434 priv->conf_key_signal);
1435 priv->conf_key_signal = 0;
1438 /* Clear hidding array created by cut operation */
1439 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1441 gdk_color_parse ("000", &priv->active_color);
1443 G_OBJECT_CLASS(parent_class)->finalize (obj);
1448 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1450 ModestFolderViewPrivate *priv;
1453 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1454 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1456 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1457 device = tny_account_store_get_device (account_store);
1459 if (G_UNLIKELY (priv->account_store)) {
1461 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1462 priv->account_inserted_signal))
1463 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1464 priv->account_inserted_signal);
1465 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1466 priv->account_removed_signal))
1467 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1468 priv->account_removed_signal);
1469 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1470 priv->account_changed_signal))
1471 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1472 priv->account_changed_signal);
1473 g_object_unref (G_OBJECT (priv->account_store));
1476 priv->account_store = g_object_ref (G_OBJECT (account_store));
1478 priv->account_removed_signal =
1479 g_signal_connect (G_OBJECT(account_store), "account_removed",
1480 G_CALLBACK (on_account_removed), self);
1482 priv->account_inserted_signal =
1483 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1484 G_CALLBACK (on_account_inserted), self);
1486 priv->account_changed_signal =
1487 g_signal_connect (G_OBJECT(account_store), "account_changed",
1488 G_CALLBACK (on_account_changed), self);
1490 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1491 priv->reselect = FALSE;
1493 g_object_unref (G_OBJECT (device));
1497 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1500 ModestFolderView *self;
1501 GtkTreeModel *model, *filter_model;
1504 self = MODEST_FOLDER_VIEW (user_data);
1506 if (!get_inner_models (self, &filter_model, NULL, &model))
1509 /* Remove outbox from model */
1510 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1511 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1512 g_object_unref (outbox);
1515 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1519 on_account_inserted (TnyAccountStore *account_store,
1520 TnyAccount *account,
1523 ModestFolderViewPrivate *priv;
1524 GtkTreeModel *model, *filter_model;
1526 /* Ignore transport account insertions, we're not showing them
1527 in the folder view */
1528 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1531 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1534 /* If we're adding a new account, and there is no previous
1535 one, we need to select the visible server account */
1536 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1537 !priv->visible_account_id)
1538 modest_widget_memory_restore (modest_runtime_get_conf(),
1539 G_OBJECT (user_data),
1540 MODEST_CONF_FOLDER_VIEW_KEY);
1544 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1545 &filter_model, NULL, &model))
1548 /* Insert the account in the model */
1549 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1551 /* When the model is a list store (plain representation) the
1552 outbox is not a child of any account so we have to manually
1553 delete it because removing the local folders account won't
1554 delete it (because tny_folder_get_account() is not defined
1555 for a merge folder */
1556 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1557 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1559 priv->outbox_deleted_handler =
1560 g_signal_connect (account,
1562 G_CALLBACK (on_outbox_deleted_cb),
1566 /* Refilter the model */
1567 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1572 same_account_selected (ModestFolderView *self,
1573 TnyAccount *account)
1575 ModestFolderViewPrivate *priv;
1576 gboolean same_account = FALSE;
1578 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1580 if (priv->cur_folder_store) {
1581 TnyAccount *selected_folder_account = NULL;
1583 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1584 selected_folder_account =
1585 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1587 selected_folder_account =
1588 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1591 if (selected_folder_account == account)
1592 same_account = TRUE;
1594 g_object_unref (selected_folder_account);
1596 return same_account;
1600 on_account_changed (TnyAccountStore *account_store,
1601 TnyAccount *tny_account,
1604 ModestFolderView *self;
1605 ModestFolderViewPrivate *priv;
1606 GtkTreeModel *model, *filter_model;
1607 GtkTreeSelection *sel;
1608 gboolean same_account;
1610 /* Ignore transport account insertions, we're not showing them
1611 in the folder view */
1612 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1615 self = MODEST_FOLDER_VIEW (user_data);
1616 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1618 /* Get the inner model */
1619 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1620 &filter_model, NULL, &model))
1623 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1625 /* Invalidate the cur_folder_store only if the selected folder
1626 belongs to the account that is being removed */
1627 same_account = same_account_selected (self, tny_account);
1629 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1630 gtk_tree_selection_unselect_all (sel);
1633 /* Remove the account from the model */
1634 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1636 /* Insert the account in the model */
1637 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1639 /* Refilter the model */
1640 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1645 on_account_removed (TnyAccountStore *account_store,
1646 TnyAccount *account,
1649 ModestFolderView *self = NULL;
1650 ModestFolderViewPrivate *priv;
1651 GtkTreeModel *model, *filter_model;
1652 GtkTreeSelection *sel = NULL;
1653 gboolean same_account = FALSE;
1655 /* Ignore transport account removals, we're not showing them
1656 in the folder view */
1657 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1660 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1661 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1665 self = MODEST_FOLDER_VIEW (user_data);
1666 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1668 /* Invalidate the cur_folder_store only if the selected folder
1669 belongs to the account that is being removed */
1670 same_account = same_account_selected (self, account);
1672 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1673 gtk_tree_selection_unselect_all (sel);
1676 /* Invalidate row to select only if the folder to select
1677 belongs to the account that is being removed*/
1678 if (priv->folder_to_select) {
1679 TnyAccount *folder_to_select_account = NULL;
1681 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1682 if (folder_to_select_account == account) {
1683 modest_folder_view_disable_next_folder_selection (self);
1684 g_object_unref (priv->folder_to_select);
1685 priv->folder_to_select = NULL;
1687 g_object_unref (folder_to_select_account);
1690 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1691 &filter_model, NULL, &model))
1694 /* Disconnect the signal handler */
1695 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1696 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1697 if (g_signal_handler_is_connected (account,
1698 priv->outbox_deleted_handler))
1699 g_signal_handler_disconnect (account,
1700 priv->outbox_deleted_handler);
1703 /* Remove the account from the model */
1704 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1706 /* If the removed account is the currently viewed one then
1707 clear the configuration value. The new visible account will be the default account */
1708 if (priv->visible_account_id &&
1709 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1711 /* Clear the current visible account_id */
1712 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1713 modest_folder_view_set_mailbox (self, NULL);
1715 /* Call the restore method, this will set the new visible account */
1716 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1717 MODEST_CONF_FOLDER_VIEW_KEY);
1720 /* Refilter the model */
1721 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1726 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1728 GtkTreeViewColumn *col;
1730 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1732 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1734 g_printerr ("modest: failed get column for title\n");
1738 gtk_tree_view_column_set_title (col, title);
1739 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1744 modest_folder_view_on_map (ModestFolderView *self,
1745 GdkEventExpose *event,
1748 ModestFolderViewPrivate *priv;
1750 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1752 /* This won't happen often */
1753 if (G_UNLIKELY (priv->reselect)) {
1754 /* Select the first inbox or the local account if not found */
1756 /* TODO: this could cause a lock at startup, so we
1757 comment it for the moment. We know that this will
1758 be a bug, because the INBOX is not selected, but we
1759 need to rewrite some parts of Modest to avoid the
1760 deathlock situation */
1761 /* TODO: check if this is still the case */
1762 priv->reselect = FALSE;
1763 /* Notify the display name observers */
1764 g_signal_emit (G_OBJECT(self),
1765 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1769 if (priv->reexpand) {
1770 expand_root_items (self);
1771 priv->reexpand = FALSE;
1778 modest_folder_view_new (TnyFolderStoreQuery *query)
1780 return modest_folder_view_new_full (query, TRUE);
1784 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1787 ModestFolderViewPrivate *priv;
1788 GtkTreeSelection *sel;
1790 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1791 #ifdef MODEST_TOOLKIT_HILDON2
1792 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1795 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1798 priv->query = g_object_ref (query);
1800 priv->do_refresh = do_refresh;
1802 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1803 priv->changed_signal = g_signal_connect (sel, "changed",
1804 G_CALLBACK (on_selection_changed), self);
1806 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1808 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1810 /* Hide headers by default */
1811 gtk_tree_view_set_headers_visible ((GtkTreeView *)self, FALSE);
1813 return GTK_WIDGET(self);
1816 /* this feels dirty; any other way to expand all the root items? */
1818 expand_root_items (ModestFolderView *self)
1821 GtkTreeModel *model;
1824 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1825 path = gtk_tree_path_new_first ();
1827 /* all folders should have child items, so.. */
1829 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1830 gtk_tree_path_next (path);
1831 } while (gtk_tree_model_get_iter (model, &iter, path));
1833 gtk_tree_path_free (path);
1837 is_parent_of (TnyFolder *a, TnyFolder *b)
1840 gboolean retval = FALSE;
1842 a_id = tny_folder_get_id (a);
1844 gchar *string_to_match;
1847 string_to_match = g_strconcat (a_id, "/", NULL);
1848 b_id = tny_folder_get_id (b);
1849 retval = g_str_has_prefix (b_id, string_to_match);
1850 g_free (string_to_match);
1856 typedef struct _ForeachFolderInfo {
1859 } ForeachFolderInfo;
1862 foreach_folder_with_id (GtkTreeModel *model,
1867 ForeachFolderInfo *info;
1870 info = (ForeachFolderInfo *) data;
1871 gtk_tree_model_get (model, iter,
1872 INSTANCE_COLUMN, &instance,
1875 if (TNY_IS_FOLDER (instance)) {
1878 id = tny_folder_get_id (TNY_FOLDER (instance));
1880 collate = g_utf8_collate_key (id, -1);
1881 info->found = !strcmp (info->needle, collate);
1887 g_object_unref (instance);
1895 has_folder_with_id (ModestFolderView *self, const gchar *id)
1897 GtkTreeModel *model;
1898 ForeachFolderInfo info = {NULL, FALSE};
1900 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1901 info.needle = g_utf8_collate_key (id, -1);
1903 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1904 g_free (info.needle);
1910 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1913 gboolean retval = FALSE;
1915 a_id = tny_folder_get_id (a);
1918 b_id = tny_folder_get_id (b);
1921 const gchar *last_bar;
1922 gchar *string_to_match;
1923 last_bar = g_strrstr (b_id, "/");
1928 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1929 retval = has_folder_with_id (self, string_to_match);
1930 g_free (string_to_match);
1938 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1940 ModestFolderViewPrivate *priv;
1941 TnyIterator *iterator;
1942 gboolean retval = TRUE;
1944 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1945 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1947 for (iterator = tny_list_create_iterator (priv->list_to_move);
1948 retval && !tny_iterator_is_done (iterator);
1949 tny_iterator_next (iterator)) {
1951 instance = tny_iterator_get_current (iterator);
1952 if (instance == (GObject *) folder) {
1954 } else if (TNY_IS_FOLDER (instance)) {
1955 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1957 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1960 g_object_unref (instance);
1962 g_object_unref (iterator);
1969 * We use this function to implement the
1970 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1971 * account in this case, and the local folders.
1974 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1976 ModestFolderViewPrivate *priv;
1977 gboolean retval = TRUE;
1978 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1979 GObject *instance = NULL;
1980 const gchar *id = NULL;
1982 gboolean found = FALSE;
1983 gboolean cleared = FALSE;
1984 ModestTnyFolderRules rules = 0;
1987 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1988 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1990 gtk_tree_model_get (model, iter,
1991 NAME_COLUMN, &fname,
1993 INSTANCE_COLUMN, &instance,
1996 /* Do not show if there is no instance, this could indeed
1997 happen when the model is being modified while it's being
1998 drawn. This could occur for example when moving folders
2005 if (TNY_IS_ACCOUNT (instance)) {
2006 TnyAccount *acc = TNY_ACCOUNT (instance);
2007 const gchar *account_id = tny_account_get_id (acc);
2009 /* If it isn't a special folder,
2010 * don't show it unless it is the visible account: */
2011 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2012 !modest_tny_account_is_virtual_local_folders (acc) &&
2013 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2015 /* Show only the visible account id */
2016 if (priv->visible_account_id) {
2017 if (strcmp (account_id, priv->visible_account_id))
2024 /* Never show these to the user. They are merged into one folder
2025 * in the local-folders account instead: */
2026 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2029 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2030 /* Only show special folders for current account if needed */
2031 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2032 TnyAccount *account;
2034 account = tny_folder_get_account (TNY_FOLDER (instance));
2036 if (TNY_IS_ACCOUNT (account)) {
2037 const gchar *account_id = tny_account_get_id (account);
2039 if (!modest_tny_account_is_virtual_local_folders (account) &&
2040 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2041 /* Show only the visible account id */
2042 if (priv->visible_account_id) {
2043 if (strcmp (account_id, priv->visible_account_id)) {
2045 } else if (priv->mailbox) {
2046 /* Filter mailboxes */
2047 if (!g_str_has_prefix (fname, priv->mailbox)) {
2049 } else if (!strcmp (fname, priv->mailbox)) {
2050 /* Hide mailbox parent */
2056 g_object_unref (account);
2063 /* Check hiding (if necessary) */
2064 cleared = modest_email_clipboard_cleared (priv->clipboard);
2065 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2066 id = tny_folder_get_id (TNY_FOLDER(instance));
2067 if (priv->hidding_ids != NULL)
2068 for (i=0; i < priv->n_selected && !found; i++)
2069 if (priv->hidding_ids[i] != NULL && id != NULL)
2070 found = (!strcmp (priv->hidding_ids[i], id));
2075 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2076 folder as no message can be move there according to UI specs */
2077 if (retval && !priv->show_non_move) {
2078 if (priv->list_to_move &&
2079 tny_list_get_length (priv->list_to_move) > 0 &&
2080 TNY_IS_FOLDER (instance)) {
2081 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2083 if (retval && TNY_IS_FOLDER (instance) &&
2084 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2086 case TNY_FOLDER_TYPE_OUTBOX:
2087 case TNY_FOLDER_TYPE_SENT:
2088 case TNY_FOLDER_TYPE_DRAFTS:
2091 case TNY_FOLDER_TYPE_UNKNOWN:
2092 case TNY_FOLDER_TYPE_NORMAL:
2093 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2094 if (type == TNY_FOLDER_TYPE_INVALID)
2095 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2097 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2098 type == TNY_FOLDER_TYPE_SENT
2099 || type == TNY_FOLDER_TYPE_DRAFTS)
2106 if (retval && TNY_IS_ACCOUNT (instance) &&
2107 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2108 ModestProtocolType protocol_type;
2110 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2111 retval = !modest_protocol_registry_protocol_type_has_tag
2112 (modest_runtime_get_protocol_registry (),
2114 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2118 /* apply special filters */
2119 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2120 if (TNY_IS_ACCOUNT (instance))
2124 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2125 if (TNY_IS_FOLDER (instance))
2129 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2130 if (TNY_IS_ACCOUNT (instance)) {
2131 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2133 } else if (TNY_IS_FOLDER (instance)) {
2134 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2139 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2140 if (TNY_IS_ACCOUNT (instance)) {
2141 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2143 } else if (TNY_IS_FOLDER (instance)) {
2144 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2149 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2150 /* A mailbox is a fake folder with an @ in the middle of the name */
2151 if (!TNY_IS_FOLDER (instance) ||
2152 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2155 const gchar *folder_name;
2156 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2157 if (!folder_name || strchr (folder_name, '@') == NULL)
2163 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2164 if (TNY_IS_FOLDER (instance)) {
2165 /* Check folder rules */
2166 ModestTnyFolderRules rules;
2168 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2169 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2170 } else if (TNY_IS_ACCOUNT (instance)) {
2171 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2179 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2180 if (TNY_IS_FOLDER (instance)) {
2181 TnyFolderType guess_type;
2183 if (TNY_FOLDER_TYPE_NORMAL) {
2184 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2190 case TNY_FOLDER_TYPE_OUTBOX:
2191 case TNY_FOLDER_TYPE_SENT:
2192 case TNY_FOLDER_TYPE_DRAFTS:
2193 case TNY_FOLDER_TYPE_ARCHIVE:
2194 case TNY_FOLDER_TYPE_INBOX:
2197 case TNY_FOLDER_TYPE_UNKNOWN:
2198 case TNY_FOLDER_TYPE_NORMAL:
2204 } else if (TNY_IS_ACCOUNT (instance)) {
2209 if (retval && TNY_IS_FOLDER (instance)) {
2210 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2213 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2214 if (TNY_IS_FOLDER (instance)) {
2215 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2216 } else if (TNY_IS_ACCOUNT (instance)) {
2221 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2222 if (TNY_IS_FOLDER (instance)) {
2223 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2224 } else if (TNY_IS_ACCOUNT (instance)) {
2229 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2230 if (TNY_IS_FOLDER (instance)) {
2231 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2232 } else if (TNY_IS_ACCOUNT (instance)) {
2238 g_object_unref (instance);
2246 modest_folder_view_update_model (ModestFolderView *self,
2247 TnyAccountStore *account_store)
2249 ModestFolderViewPrivate *priv;
2250 GtkTreeModel *model;
2251 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2253 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2254 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2257 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2259 /* Notify that there is no folder selected */
2260 g_signal_emit (G_OBJECT(self),
2261 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2263 if (priv->cur_folder_store) {
2264 g_object_unref (priv->cur_folder_store);
2265 priv->cur_folder_store = NULL;
2268 /* FIXME: the local accounts are not shown when the query
2269 selects only the subscribed folders */
2270 TnyGtkFolderListStoreFlags flags;
2271 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2272 if (priv->do_refresh)
2273 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2275 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2276 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2278 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2279 MODEST_FOLDER_PATH_SEPARATOR);
2281 /* When the model is a list store (plain representation) the
2282 outbox is not a child of any account so we have to manually
2283 delete it because removing the local folders account won't
2284 delete it (because tny_folder_get_account() is not defined
2285 for a merge folder */
2286 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2287 TnyAccount *account;
2288 ModestTnyAccountStore *acc_store;
2290 acc_store = modest_runtime_get_account_store ();
2291 account = modest_tny_account_store_get_local_folders_account (acc_store);
2293 if (g_signal_handler_is_connected (account,
2294 priv->outbox_deleted_handler))
2295 g_signal_handler_disconnect (account,
2296 priv->outbox_deleted_handler);
2298 priv->outbox_deleted_handler =
2299 g_signal_connect (account,
2301 G_CALLBACK (on_outbox_deleted_cb),
2303 g_object_unref (account);
2306 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2307 /* Get the accounts */
2308 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2310 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2312 if (priv->visible_account_id) {
2313 TnyAccount *account;
2315 /* Add local folders account */
2316 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2319 tny_list_append (TNY_LIST (model), (GObject *) account);
2320 g_object_unref (account);
2323 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2326 tny_list_append (TNY_LIST (model), (GObject *) account);
2327 g_object_unref (account);
2330 /* Add visible account */
2331 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2332 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2333 priv->visible_account_id);
2335 tny_list_append (TNY_LIST (model), (GObject *) account);
2336 g_object_unref (account);
2338 g_warning ("You need to set an account first");
2339 g_object_unref (model);
2343 g_warning ("You need to set an account first");
2344 g_object_unref (model);
2349 sortable = gtk_tree_model_sort_new_with_model (model);
2350 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2352 GTK_SORT_ASCENDING);
2353 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2355 cmp_rows, NULL, NULL);
2357 /* Create filter model */
2358 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2359 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2364 GtkTreeModel *old_tny_model = NULL;
2365 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2366 if (priv->signal_handlers > 0) {
2367 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2368 G_OBJECT (old_tny_model),
2369 "activity-changed");
2374 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2376 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2379 G_CALLBACK (on_activity_changed),
2382 g_object_unref (model);
2383 g_object_unref (filter_model);
2384 g_object_unref (sortable);
2386 /* Force a reselection of the INBOX next time the widget is shown */
2387 priv->reselect = TRUE;
2394 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2396 GtkTreeModel *model = NULL;
2397 TnyFolderStore *folder = NULL;
2399 ModestFolderView *tree_view = NULL;
2400 ModestFolderViewPrivate *priv = NULL;
2401 gboolean selected = FALSE;
2403 g_return_if_fail (sel);
2404 g_return_if_fail (user_data);
2406 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2408 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2410 tree_view = MODEST_FOLDER_VIEW (user_data);
2413 gtk_tree_model_get (model, &iter,
2414 INSTANCE_COLUMN, &folder,
2417 /* If the folder is the same do not notify */
2418 if (folder && priv->cur_folder_store == folder) {
2419 g_object_unref (folder);
2424 /* Current folder was unselected */
2425 if (priv->cur_folder_store) {
2426 /* We must do this firstly because a libtinymail-camel
2427 implementation detail. If we issue the signal
2428 before doing the sync_async, then that signal could
2429 cause (and it actually does it) a free of the
2430 summary of the folder (because the main window will
2431 clear the headers view */
2433 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2434 priv->cur_folder_store, FALSE);
2436 g_object_unref (priv->cur_folder_store);
2437 priv->cur_folder_store = NULL;
2440 /* New current references */
2441 priv->cur_folder_store = folder;
2443 /* New folder has been selected. Do not notify if there is
2444 nothing new selected */
2446 g_signal_emit (G_OBJECT(tree_view),
2447 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2448 0, priv->cur_folder_store, TRUE);
2453 on_row_activated (GtkTreeView *treeview,
2454 GtkTreePath *treepath,
2455 GtkTreeViewColumn *column,
2458 GtkTreeModel *model = NULL;
2459 TnyFolderStore *folder = NULL;
2461 ModestFolderView *self = NULL;
2462 ModestFolderViewPrivate *priv = NULL;
2464 g_return_if_fail (treeview);
2465 g_return_if_fail (user_data);
2467 self = MODEST_FOLDER_VIEW (user_data);
2468 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2470 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2472 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2475 gtk_tree_model_get (model, &iter,
2476 INSTANCE_COLUMN, &folder,
2479 g_signal_emit (G_OBJECT(self),
2480 signals[FOLDER_ACTIVATED_SIGNAL],
2483 #ifdef MODEST_TOOLKIT_HILDON2
2484 HildonUIMode ui_mode;
2485 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2486 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2487 if (priv->cur_folder_store)
2488 g_object_unref (priv->cur_folder_store);
2489 priv->cur_folder_store = g_object_ref (folder);
2493 g_object_unref (folder);
2497 modest_folder_view_get_selected (ModestFolderView *self)
2499 ModestFolderViewPrivate *priv;
2501 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2503 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2504 if (priv->cur_folder_store)
2505 g_object_ref (priv->cur_folder_store);
2507 return priv->cur_folder_store;
2511 get_cmp_rows_type_pos (GObject *folder)
2513 /* Remote accounts -> Local account -> MMC account .*/
2516 if (TNY_IS_ACCOUNT (folder) &&
2517 modest_tny_account_is_virtual_local_folders (
2518 TNY_ACCOUNT (folder))) {
2520 } else if (TNY_IS_ACCOUNT (folder)) {
2521 TnyAccount *account = TNY_ACCOUNT (folder);
2522 const gchar *account_id = tny_account_get_id (account);
2523 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2529 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2530 return -1; /* Should never happen */
2535 inbox_is_special (TnyFolderStore *folder_store)
2537 gboolean is_special = TRUE;
2539 if (TNY_IS_FOLDER (folder_store)) {
2543 gchar *last_inbox_bar;
2545 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2546 downcase = g_utf8_strdown (id, -1);
2547 last_bar = g_strrstr (downcase, "/");
2549 last_inbox_bar = g_strrstr (downcase, "inbox/");
2550 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2561 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2563 TnyAccount *account;
2564 gboolean is_special;
2565 /* Inbox, Outbox, Drafts, Sent, User */
2568 if (!TNY_IS_FOLDER (folder_store))
2571 case TNY_FOLDER_TYPE_INBOX:
2573 account = tny_folder_get_account (folder_store);
2574 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2576 /* In inbox case we need to know if the inbox is really the top
2577 * inbox of the account, or if it's a submailbox inbox. To do
2578 * this we'll apply an heuristic rule: Find last "/" and check
2579 * if it's preceeded by another Inbox */
2580 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2581 g_object_unref (account);
2582 return is_special?0:4;
2585 case TNY_FOLDER_TYPE_OUTBOX:
2586 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2588 case TNY_FOLDER_TYPE_DRAFTS:
2590 account = tny_folder_get_account (folder_store);
2591 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2592 g_object_unref (account);
2593 return is_special?1:4;
2596 case TNY_FOLDER_TYPE_SENT:
2598 account = tny_folder_get_account (folder_store);
2599 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2600 g_object_unref (account);
2601 return is_special?3:4;
2610 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2612 const gchar *a1_name, *a2_name;
2614 a1_name = tny_account_get_name (a1);
2615 a2_name = tny_account_get_name (a2);
2617 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2621 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2623 TnyAccount *a1 = NULL, *a2 = NULL;
2626 if (TNY_IS_ACCOUNT (s1)) {
2627 a1 = TNY_ACCOUNT (g_object_ref (s1));
2628 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2629 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2632 if (TNY_IS_ACCOUNT (s2)) {
2633 a2 = TNY_ACCOUNT (g_object_ref (s2));
2634 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2635 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2652 /* First we sort with the type of account */
2653 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2657 cmp = compare_account_names (a1, a2);
2661 g_object_unref (a1);
2663 g_object_unref (a2);
2669 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2671 gint is_account1, is_account2;
2673 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2674 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2676 return is_account2 - is_account1;
2680 compare_folders (const gchar *name1, const gchar *name2)
2682 const gchar *separator1, *separator2;
2683 const gchar *next1, *next2;
2687 if (name1 == NULL || name1[0] == '\0')
2689 if (name2 == NULL || name2[0] == '\0')
2692 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2694 top1 = g_strndup (name1, separator1 - name1);
2696 top1 = g_strdup (name1);
2699 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2701 top2 = g_strndup (name2, separator2 - name2);
2703 top2 = g_strdup (name2);
2707 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2714 if (separator1 == NULL && separator2 == NULL)
2717 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2718 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2720 return compare_folders (next1, next2);
2725 * This function orders the mail accounts according to these rules:
2726 * 1st - remote accounts
2727 * 2nd - local account
2731 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2735 gchar *name1 = NULL;
2736 gchar *name2 = NULL;
2737 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2738 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2739 GObject *folder1 = NULL;
2740 GObject *folder2 = NULL;
2742 gtk_tree_model_get (tree_model, iter1,
2743 NAME_COLUMN, &name1,
2745 INSTANCE_COLUMN, &folder1,
2747 gtk_tree_model_get (tree_model, iter2,
2748 NAME_COLUMN, &name2,
2749 TYPE_COLUMN, &type2,
2750 INSTANCE_COLUMN, &folder2,
2753 /* Return if we get no folder. This could happen when folder
2754 operations are happening. The model is updated after the
2755 folder copy/move actually occurs, so there could be
2756 situations where the model to be drawn is not correct */
2757 if (!folder1 || !folder2)
2760 /* Sort by type. First the special folders, then the archives */
2761 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2765 /* Now we sort using the account of each folder */
2766 if (TNY_IS_FOLDER_STORE (folder1) &&
2767 TNY_IS_FOLDER_STORE (folder2)) {
2768 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2772 /* Each group is preceeded by its account */
2773 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2778 /* Pure sort by name */
2779 cmp = compare_folders (name1, name2);
2782 g_object_unref(G_OBJECT(folder1));
2784 g_object_unref(G_OBJECT(folder2));
2794 * This function manages the navigation through the folders using the
2795 * keyboard or the hardware keys in the device
2798 on_key_pressed (GtkWidget *self,
2802 GtkTreeSelection *selection;
2804 GtkTreeModel *model;
2805 gboolean retval = FALSE;
2807 /* Up and Down are automatically managed by the treeview */
2808 if (event->keyval == GDK_Return) {
2809 /* Expand/Collapse the selected row */
2810 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2811 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2814 path = gtk_tree_model_get_path (model, &iter);
2816 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2817 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2819 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2820 gtk_tree_path_free (path);
2822 /* No further processing */
2830 * We listen to the changes in the local folder account name key,
2831 * because we want to show the right name in the view. The local
2832 * folder account name corresponds to the device name in the Maemo
2833 * version. We do this because we do not want to query gconf on each
2834 * tree view refresh. It's better to cache it and change whenever
2838 on_configuration_key_changed (ModestConf* conf,
2840 ModestConfEvent event,
2841 ModestConfNotificationId id,
2842 ModestFolderView *self)
2844 ModestFolderViewPrivate *priv;
2847 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2848 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2850 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2851 g_free (priv->local_account_name);
2853 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2854 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2856 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2857 MODEST_CONF_DEVICE_NAME, NULL);
2859 /* Force a redraw */
2860 #if GTK_CHECK_VERSION(2, 8, 0)
2861 GtkTreeViewColumn * tree_column;
2863 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2865 gtk_tree_view_column_queue_resize (tree_column);
2867 gtk_widget_queue_draw (GTK_WIDGET (self));
2873 modest_folder_view_set_style (ModestFolderView *self,
2874 ModestFolderViewStyle style)
2876 ModestFolderViewPrivate *priv;
2878 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2879 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2880 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2882 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2885 priv->style = style;
2889 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2890 const gchar *account_id)
2892 ModestFolderViewPrivate *priv;
2893 GtkTreeModel *model;
2895 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2897 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2899 /* This will be used by the filter_row callback,
2900 * to decided which rows to show: */
2901 if (priv->visible_account_id) {
2902 g_free (priv->visible_account_id);
2903 priv->visible_account_id = NULL;
2906 priv->visible_account_id = g_strdup (account_id);
2909 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2910 if (GTK_IS_TREE_MODEL_FILTER (model))
2911 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2913 modest_folder_view_update_model(self,
2914 (TnyAccountStore *) modest_runtime_get_account_store());
2916 /* Save settings to gconf */
2917 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2918 MODEST_CONF_FOLDER_VIEW_KEY);
2920 /* Notify observers */
2921 g_signal_emit (G_OBJECT(self),
2922 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
2927 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2929 ModestFolderViewPrivate *priv;
2931 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2933 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2935 return (const gchar *) priv->visible_account_id;
2940 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2945 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2946 TnyFolder* a_folder;
2949 gtk_tree_model_get (model, iter,
2950 INSTANCE_COLUMN, &a_folder,
2956 if (folder == a_folder) {
2957 g_object_unref (a_folder);
2958 *folder_iter = *iter;
2961 g_object_unref (a_folder);
2963 if (gtk_tree_model_iter_children (model, &child, iter)) {
2964 if (find_folder_iter (model, &child, folder_iter, folder))
2968 } while (gtk_tree_model_iter_next (model, iter));
2975 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2977 ModestFolderViewPrivate *priv;
2979 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2981 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2983 if (priv->folder_to_select)
2984 g_object_unref(priv->folder_to_select);
2986 priv->folder_to_select = NULL;
2990 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2991 gboolean after_change)
2993 GtkTreeModel *model;
2994 GtkTreeIter iter, folder_iter;
2995 GtkTreeSelection *sel;
2996 ModestFolderViewPrivate *priv = NULL;
2998 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2999 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3001 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3004 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3005 gtk_tree_selection_unselect_all (sel);
3007 if (priv->folder_to_select)
3008 g_object_unref(priv->folder_to_select);
3009 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3013 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3018 /* Refilter the model, before selecting the folder */
3019 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3021 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3022 g_warning ("%s: model is empty", __FUNCTION__);
3026 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3029 path = gtk_tree_model_get_path (model, &folder_iter);
3030 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3032 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3033 gtk_tree_selection_select_iter (sel, &folder_iter);
3034 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3036 gtk_tree_path_free (path);
3044 modest_folder_view_copy_selection (ModestFolderView *self)
3046 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3048 /* Copy selection */
3049 _clipboard_set_selected_data (self, FALSE);
3053 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3055 ModestFolderViewPrivate *priv = NULL;
3056 GtkTreeModel *model = NULL;
3057 const gchar **hidding = NULL;
3058 guint i, n_selected;
3060 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3061 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3063 /* Copy selection */
3064 if (!_clipboard_set_selected_data (folder_view, TRUE))
3067 /* Get hidding ids */
3068 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3070 /* Clear hidding array created by previous cut operation */
3071 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3073 /* Copy hidding array */
3074 priv->n_selected = n_selected;
3075 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3076 for (i=0; i < n_selected; i++)
3077 priv->hidding_ids[i] = g_strdup(hidding[i]);
3079 /* Hide cut folders */
3080 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3081 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3085 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3086 ModestFolderView *folder_view_dst)
3088 GtkTreeModel *filter_model = NULL;
3089 GtkTreeModel *model = NULL;
3090 GtkTreeModel *new_filter_model = NULL;
3091 GtkTreeModel *old_tny_model = NULL;
3092 GtkTreeModel *new_tny_model = NULL;
3093 ModestFolderViewPrivate *dst_priv;
3095 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3096 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3098 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3099 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3100 new_tny_model = NULL;
3103 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3104 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3105 G_OBJECT (old_tny_model),
3106 "activity-changed");
3108 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3109 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3111 /* Build new filter model */
3112 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3113 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3120 /* Set copied model */
3121 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3122 if (new_tny_model) {
3123 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3124 G_OBJECT (new_tny_model),
3126 G_CALLBACK (on_activity_changed),
3131 g_object_unref (new_filter_model);
3135 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3138 GtkTreeModel *model = NULL;
3139 ModestFolderViewPrivate* priv;
3141 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3143 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3144 priv->show_non_move = show;
3145 /* modest_folder_view_update_model(folder_view, */
3146 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3148 /* Hide special folders */
3149 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3150 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3151 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3156 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3159 ModestFolderViewPrivate* priv;
3161 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3163 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3164 priv->show_message_count = show;
3166 g_object_set (G_OBJECT (priv->messages_renderer),
3167 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3171 /* Returns FALSE if it did not selected anything */
3173 _clipboard_set_selected_data (ModestFolderView *folder_view,
3176 ModestFolderViewPrivate *priv = NULL;
3177 TnyFolderStore *folder = NULL;
3178 gboolean retval = FALSE;
3180 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3181 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3183 /* Set selected data on clipboard */
3184 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3185 folder = modest_folder_view_get_selected (folder_view);
3187 /* Do not allow to select an account */
3188 if (TNY_IS_FOLDER (folder)) {
3189 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3194 g_object_unref (folder);
3200 _clear_hidding_filter (ModestFolderView *folder_view)
3202 ModestFolderViewPrivate *priv;
3205 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3206 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3208 if (priv->hidding_ids != NULL) {
3209 for (i=0; i < priv->n_selected; i++)
3210 g_free (priv->hidding_ids[i]);
3211 g_free(priv->hidding_ids);
3217 on_display_name_changed (ModestAccountMgr *mgr,
3218 const gchar *account,
3221 ModestFolderView *self;
3223 self = MODEST_FOLDER_VIEW (user_data);
3225 /* Force a redraw */
3226 #if GTK_CHECK_VERSION(2, 8, 0)
3227 GtkTreeViewColumn * tree_column;
3229 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3231 gtk_tree_view_column_queue_resize (tree_column);
3233 gtk_widget_queue_draw (GTK_WIDGET (self));
3238 modest_folder_view_set_cell_style (ModestFolderView *self,
3239 ModestFolderViewCellStyle cell_style)
3241 ModestFolderViewPrivate *priv = NULL;
3243 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3244 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3246 priv->cell_style = cell_style;
3248 g_object_set (G_OBJECT (priv->messages_renderer),
3249 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3252 gtk_widget_queue_draw (GTK_WIDGET (self));
3256 update_style (ModestFolderView *self)
3258 ModestFolderViewPrivate *priv;
3259 GdkColor style_color, style_active_color;
3260 PangoAttrList *attr_list;
3262 PangoAttribute *attr;
3264 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3265 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3269 attr_list = pango_attr_list_new ();
3271 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3272 gdk_color_parse ("grey", &style_color);
3274 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3275 pango_attr_list_insert (attr_list, attr);
3278 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3280 "SmallSystemFont", NULL,
3283 attr = pango_attr_font_desc_new (pango_font_description_copy
3284 (style->font_desc));
3285 pango_attr_list_insert (attr_list, attr);
3287 g_object_set (G_OBJECT (priv->messages_renderer),
3288 "foreground-gdk", &style_color,
3289 "foreground-set", TRUE,
3290 "attributes", attr_list,
3292 pango_attr_list_unref (attr_list);
3295 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3296 priv->active_color = style_active_color;
3298 gdk_color_parse ("000", &(priv->active_color));
3303 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3305 if (strcmp ("style", spec->name) == 0) {
3306 update_style (MODEST_FOLDER_VIEW (obj));
3307 gtk_widget_queue_draw (GTK_WIDGET (obj));
3312 modest_folder_view_set_filter (ModestFolderView *self,
3313 ModestFolderViewFilter filter)
3315 ModestFolderViewPrivate *priv;
3316 GtkTreeModel *filter_model;
3318 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3319 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3321 priv->filter |= filter;
3323 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3324 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3325 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3330 modest_folder_view_unset_filter (ModestFolderView *self,
3331 ModestFolderViewFilter filter)
3333 ModestFolderViewPrivate *priv;
3334 GtkTreeModel *filter_model;
3336 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3337 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3339 priv->filter &= ~filter;
3341 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3342 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3343 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3348 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3349 ModestTnyFolderRules rules)
3351 GtkTreeModel *filter_model;
3353 gboolean fulfil = FALSE;
3355 if (!get_inner_models (self, &filter_model, NULL, NULL))
3358 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3362 TnyFolderStore *folder;
3364 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3366 if (TNY_IS_FOLDER (folder)) {
3367 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3368 /* Folder rules are negative: non_writable, non_deletable... */
3369 if (!(folder_rules & rules))
3372 g_object_unref (folder);
3375 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3381 modest_folder_view_set_list_to_move (ModestFolderView *self,
3384 ModestFolderViewPrivate *priv;
3386 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3387 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3389 if (priv->list_to_move)
3390 g_object_unref (priv->list_to_move);
3393 g_object_ref (list);
3395 priv->list_to_move = list;
3399 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3401 ModestFolderViewPrivate *priv;
3403 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3404 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3407 g_free (priv->mailbox);
3409 priv->mailbox = g_strdup (mailbox);
3411 /* Notify observers */
3412 g_signal_emit (G_OBJECT(self),
3413 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3414 priv->visible_account_id);
3418 modest_folder_view_get_mailbox (ModestFolderView *self)
3420 ModestFolderViewPrivate *priv;
3422 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3423 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3425 return (const gchar *) priv->mailbox;
3429 modest_folder_view_get_activity (ModestFolderView *self)
3431 ModestFolderViewPrivate *priv;
3432 GtkTreeModel *inner_model;
3434 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3435 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3437 if (!get_inner_models (self, NULL, NULL, &inner_model))
3440 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3441 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3448 on_activity_changed (TnyGtkFolderListStore *store,
3450 ModestFolderView *folder_view)
3452 ModestFolderViewPrivate *priv;
3454 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3455 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3456 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3458 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3463 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3465 GtkTreeModel *model;
3471 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3472 ret_value = TNY_LIST (model);
3473 g_object_ref (ret_value);