1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
65 #include <modest-account-protocol.h>
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
77 /* Column names depending on we use list store or tree store */
78 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
79 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
80 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
81 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
82 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
84 /* 'private'/'protected' functions */
85 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
86 static void modest_folder_view_init (ModestFolderView *obj);
87 static void modest_folder_view_finalize (GObject *obj);
88 static void modest_folder_view_dispose (GObject *obj);
90 static void tny_account_store_view_init (gpointer g,
93 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
94 TnyAccountStore *account_store);
96 static void on_selection_changed (GtkTreeSelection *sel,
99 static void on_row_activated (GtkTreeView *treeview,
101 GtkTreeViewColumn *column,
104 static void on_account_removed (TnyAccountStore *self,
108 static void on_account_inserted (TnyAccountStore *self,
112 static void on_account_changed (TnyAccountStore *self,
116 static gint cmp_rows (GtkTreeModel *tree_model,
121 static gboolean filter_row (GtkTreeModel *model,
125 static gboolean on_key_pressed (GtkWidget *self,
129 static void on_configuration_key_changed (ModestConf* conf,
131 ModestConfEvent event,
132 ModestConfNotificationId notification_id,
133 ModestFolderView *self);
135 static void expand_root_items (ModestFolderView *self);
137 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
140 static void _clear_hidding_filter (ModestFolderView *folder_view);
142 static void on_display_name_changed (ModestAccountMgr *self,
143 const gchar *account,
145 static void update_style (ModestFolderView *self);
146 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
147 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
148 static gboolean inbox_is_special (TnyFolderStore *folder_store);
150 static gboolean get_inner_models (ModestFolderView *self,
151 GtkTreeModel **filter_model,
152 GtkTreeModel **sort_model,
153 GtkTreeModel **tny_model);
154 static void on_activity_changed (TnyGtkFolderListStore *store,
156 ModestFolderView *folder_view);
159 FOLDER_SELECTION_CHANGED_SIGNAL,
160 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
161 FOLDER_ACTIVATED_SIGNAL,
162 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
163 ACTIVITY_CHANGED_SIGNAL,
167 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
168 struct _ModestFolderViewPrivate {
169 TnyAccountStore *account_store;
170 TnyFolderStore *cur_folder_store;
172 TnyFolder *folder_to_select; /* folder to select after the next update */
174 gulong changed_signal;
175 gulong account_inserted_signal;
176 gulong account_removed_signal;
177 gulong account_changed_signal;
178 gulong conf_key_signal;
179 gulong display_name_changed_signal;
181 /* not unref this object, its a singlenton */
182 ModestEmailClipboard *clipboard;
184 /* Filter tree model */
187 ModestFolderViewFilter filter;
189 TnyFolderStoreQuery *query;
191 guint timer_expander;
193 gchar *local_account_name;
194 gchar *visible_account_id;
196 ModestFolderViewStyle style;
197 ModestFolderViewCellStyle cell_style;
198 gboolean show_message_count;
200 gboolean reselect; /* we use this to force a reselection of the INBOX */
201 gboolean show_non_move;
202 TnyList *list_to_move;
203 gboolean reexpand; /* next time we expose, we'll expand all root folders */
205 GtkCellRenderer *messages_renderer;
207 gulong outbox_deleted_handler;
209 GSList *signal_handlers;
210 GdkColor active_color;
212 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
213 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
214 MODEST_TYPE_FOLDER_VIEW, \
215 ModestFolderViewPrivate))
217 static GObjectClass *parent_class = NULL;
219 static guint signals[LAST_SIGNAL] = {0};
222 modest_folder_view_get_type (void)
224 static GType my_type = 0;
226 static const GTypeInfo my_info = {
227 sizeof(ModestFolderViewClass),
228 NULL, /* base init */
229 NULL, /* base finalize */
230 (GClassInitFunc) modest_folder_view_class_init,
231 NULL, /* class finalize */
232 NULL, /* class data */
233 sizeof(ModestFolderView),
235 (GInstanceInitFunc) modest_folder_view_init,
239 static const GInterfaceInfo tny_account_store_view_info = {
240 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
241 NULL, /* interface_finalize */
242 NULL /* interface_data */
246 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
250 g_type_add_interface_static (my_type,
251 TNY_TYPE_ACCOUNT_STORE_VIEW,
252 &tny_account_store_view_info);
258 modest_folder_view_class_init (ModestFolderViewClass *klass)
260 GObjectClass *gobject_class;
261 GtkTreeViewClass *treeview_class;
262 gobject_class = (GObjectClass*) klass;
263 treeview_class = (GtkTreeViewClass*) klass;
265 parent_class = g_type_class_peek_parent (klass);
266 gobject_class->finalize = modest_folder_view_finalize;
267 gobject_class->dispose = modest_folder_view_dispose;
269 g_type_class_add_private (gobject_class,
270 sizeof(ModestFolderViewPrivate));
272 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
273 g_signal_new ("folder_selection_changed",
274 G_TYPE_FROM_CLASS (gobject_class),
276 G_STRUCT_OFFSET (ModestFolderViewClass,
277 folder_selection_changed),
279 modest_marshal_VOID__POINTER_BOOLEAN,
280 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
283 * This signal is emitted whenever the currently selected
284 * folder display name is computed. Note that the name could
285 * be different to the folder name, because we could append
286 * the unread messages count to the folder name to build the
287 * folder display name
289 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
290 g_signal_new ("folder-display-name-changed",
291 G_TYPE_FROM_CLASS (gobject_class),
293 G_STRUCT_OFFSET (ModestFolderViewClass,
294 folder_display_name_changed),
296 g_cclosure_marshal_VOID__STRING,
297 G_TYPE_NONE, 1, G_TYPE_STRING);
299 signals[FOLDER_ACTIVATED_SIGNAL] =
300 g_signal_new ("folder_activated",
301 G_TYPE_FROM_CLASS (gobject_class),
303 G_STRUCT_OFFSET (ModestFolderViewClass,
306 g_cclosure_marshal_VOID__POINTER,
307 G_TYPE_NONE, 1, G_TYPE_POINTER);
310 * Emitted whenever the visible account changes
312 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
313 g_signal_new ("visible-account-changed",
314 G_TYPE_FROM_CLASS (gobject_class),
316 G_STRUCT_OFFSET (ModestFolderViewClass,
317 visible_account_changed),
319 g_cclosure_marshal_VOID__STRING,
320 G_TYPE_NONE, 1, G_TYPE_STRING);
323 * Emitted when the underlying GtkListStore is updating data
325 signals[ACTIVITY_CHANGED_SIGNAL] =
326 g_signal_new ("activity-changed",
327 G_TYPE_FROM_CLASS (gobject_class),
329 G_STRUCT_OFFSET (ModestFolderViewClass,
332 g_cclosure_marshal_VOID__BOOLEAN,
333 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
335 treeview_class->select_cursor_parent = NULL;
337 #ifdef MODEST_TOOLKIT_HILDON2
338 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
344 /* Retrieves the filter, sort and tny models of the folder view. If
345 any of these does not exist then it returns FALSE */
347 get_inner_models (ModestFolderView *self,
348 GtkTreeModel **filter_model,
349 GtkTreeModel **sort_model,
350 GtkTreeModel **tny_model)
352 GtkTreeModel *s_model, *f_model, *t_model;
354 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
355 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
356 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
360 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
361 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
362 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
366 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
370 *filter_model = f_model;
372 *sort_model = s_model;
374 *tny_model = t_model;
379 /* Simplify checks for NULLs: */
381 strings_are_equal (const gchar *a, const gchar *b)
387 return (strcmp (a, b) == 0);
394 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
396 GObject *instance = NULL;
398 gtk_tree_model_get (model, iter,
399 INSTANCE_COLUMN, &instance,
403 return FALSE; /* keep walking */
405 if (!TNY_IS_ACCOUNT (instance)) {
406 g_object_unref (instance);
407 return FALSE; /* keep walking */
410 /* Check if this is the looked-for account: */
411 TnyAccount *this_account = TNY_ACCOUNT (instance);
412 TnyAccount *account = TNY_ACCOUNT (data);
414 const gchar *this_account_id = tny_account_get_id(this_account);
415 const gchar *account_id = tny_account_get_id(account);
416 g_object_unref (instance);
419 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
420 if (strings_are_equal(this_account_id, account_id)) {
421 /* Tell the model that the data has changed, so that
422 * it calls the cell_data_func callbacks again: */
423 /* TODO: This does not seem to actually cause the new string to be shown: */
424 gtk_tree_model_row_changed (model, path, iter);
426 return TRUE; /* stop walking */
429 return FALSE; /* keep walking */
434 ModestFolderView *self;
435 gchar *previous_name;
436 } GetMmcAccountNameData;
439 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
441 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
443 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
445 if (!strings_are_equal (
446 tny_account_get_name(TNY_ACCOUNT(account)),
447 data->previous_name)) {
449 /* Tell the model that the data has changed, so that
450 * it calls the cell_data_func callbacks again: */
451 ModestFolderView *self = data->self;
452 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
454 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
457 g_free (data->previous_name);
458 g_slice_free (GetMmcAccountNameData, data);
462 convert_parent_folders_to_dots (gchar **item_name)
465 gint n_inbox_parents = 0;
468 gchar *last_separator;
470 if (item_name == NULL)
473 path_start = *item_name;
474 for (c = *item_name; *c != '\0'; c++) {
475 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
477 if (c != path_start) {
478 compare = g_strndup (path_start, c - path_start);
479 compare = g_strstrip (compare);
480 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
490 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
491 if (last_separator != NULL) {
492 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
499 buffer = g_string_new ("");
500 for (i = 0; i < n_parents - n_inbox_parents; i++) {
501 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
503 buffer = g_string_append (buffer, last_separator);
505 *item_name = g_string_free (buffer, FALSE);
511 format_compact_style (gchar **item_name,
513 const gchar *mailbox,
515 gboolean multiaccount,
516 gboolean *use_markup)
520 TnyFolderType folder_type;
522 if (!TNY_IS_FOLDER (instance))
525 folder = (TnyFolder *) instance;
527 folder_type = tny_folder_get_folder_type (folder);
528 is_special = (get_cmp_pos (folder_type, folder)!= 4);
531 /* Remove mailbox prefix if any */
532 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
533 if (g_str_has_prefix (*item_name, prefix)) {
534 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
536 *item_name = new_item_name;
540 if (!is_special || multiaccount) {
541 TnyAccount *account = tny_folder_get_account (folder);
542 const gchar *folder_name;
543 gboolean concat_folder_name = FALSE;
546 /* Should not happen */
550 /* convert parent folders to dots */
551 convert_parent_folders_to_dots (item_name);
553 folder_name = tny_folder_get_name (folder);
554 if (g_str_has_suffix (*item_name, folder_name)) {
555 gchar *offset = g_strrstr (*item_name, folder_name);
557 concat_folder_name = TRUE;
560 buffer = g_string_new ("");
562 buffer = g_string_append (buffer, *item_name);
563 if (concat_folder_name) {
564 if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
565 buffer = g_string_append (buffer, folder_name);
566 /* TODO: append a sensitive string to the remote drafts to
567 * be able to know it's the remote one */
568 /* buffer = g_string_append (buffer, " (TODO:remote)"); */
570 buffer = g_string_append (buffer, folder_name);
574 g_object_unref (account);
576 *item_name = g_string_free (buffer, FALSE);
584 text_cell_data (GtkTreeViewColumn *column,
585 GtkCellRenderer *renderer,
586 GtkTreeModel *tree_model,
590 ModestFolderViewPrivate *priv;
591 GObject *rendobj = (GObject *) renderer;
593 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
594 GObject *instance = NULL;
595 gboolean use_markup = FALSE;
597 gtk_tree_model_get (tree_model, iter,
600 INSTANCE_COLUMN, &instance,
602 if (!fname || !instance)
605 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
606 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
608 gchar *item_name = NULL;
609 gint item_weight = 400;
611 if (type != TNY_FOLDER_TYPE_ROOT) {
616 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
617 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
620 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
621 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
623 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
626 /* Sometimes an special folder is reported by the server as
627 NORMAL, like some versions of Dovecot */
628 if (type == TNY_FOLDER_TYPE_NORMAL ||
629 type == TNY_FOLDER_TYPE_UNKNOWN) {
630 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
634 /* note: we cannot reliably get the counts from the
635 * tree model, we need to use explicit calls on
636 * tny_folder for some reason. Select the number to
637 * show: the unread or unsent messages. in case of
638 * outbox/drafts, show all */
639 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
640 (type == TNY_FOLDER_TYPE_OUTBOX) ||
641 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
642 number = tny_folder_get_all_count (TNY_FOLDER(instance));
645 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
649 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
650 item_name = g_strdup (fname);
657 /* Use bold font style if there are unread or unset messages */
659 if (priv->show_message_count) {
660 item_name = g_strdup_printf ("%s (%d)", fname, number);
662 item_name = g_strdup (fname);
666 item_name = g_strdup (fname);
671 } else if (TNY_IS_ACCOUNT (instance)) {
672 /* If it's a server account */
673 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
674 item_name = g_strdup (priv->local_account_name);
676 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
677 /* fname is only correct when the items are first
678 * added to the model, not when the account is
679 * changed later, so get the name from the account
681 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
684 item_name = g_strdup (fname);
690 if (type == TNY_FOLDER_TYPE_INBOX &&
691 g_str_has_suffix (fname, "Inbox")) {
693 item_name = g_strdup (_("mcen_me_folder_inbox"));
697 item_name = g_strdup ("unknown");
699 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
700 gboolean multiaccount;
702 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
703 /* Convert item_name to markup */
704 format_compact_style (&item_name, instance, priv->mailbox,
706 multiaccount, &use_markup);
709 if (item_name && item_weight) {
710 /* Set the name in the treeview cell: */
711 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
712 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
713 g_object_set (rendobj,
716 "foreground-set", TRUE,
717 "foreground-gdk", &(priv->active_color),
720 g_object_set (rendobj,
722 "foreground-set", FALSE,
724 "weight", item_weight,
728 /* Notify display name observers */
729 /* TODO: What listens for this signal, and how can it use only the new name? */
730 if (((GObject *) priv->cur_folder_store) == instance) {
731 g_signal_emit (G_OBJECT(self),
732 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
739 /* If it is a Memory card account, make sure that we have the correct name.
740 * This function will be trigerred again when the name has been retrieved: */
741 if (TNY_IS_STORE_ACCOUNT (instance) &&
742 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
744 /* Get the account name asynchronously: */
745 GetMmcAccountNameData *callback_data =
746 g_slice_new0(GetMmcAccountNameData);
747 callback_data->self = self;
749 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
751 callback_data->previous_name = g_strdup (name);
753 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
754 on_get_mmc_account_name, callback_data);
758 g_object_unref (G_OBJECT (instance));
764 messages_cell_data (GtkTreeViewColumn *column,
765 GtkCellRenderer *renderer,
766 GtkTreeModel *tree_model,
770 ModestFolderView *self;
771 ModestFolderViewPrivate *priv;
772 GObject *rendobj = (GObject *) renderer;
773 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
774 GObject *instance = NULL;
775 gchar *item_name = NULL;
777 gtk_tree_model_get (tree_model, iter,
779 INSTANCE_COLUMN, &instance,
784 self = MODEST_FOLDER_VIEW (data);
785 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
788 if (type != TNY_FOLDER_TYPE_ROOT) {
793 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
794 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
797 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
799 /* Sometimes an special folder is reported by the server as
800 NORMAL, like some versions of Dovecot */
801 if (type == TNY_FOLDER_TYPE_NORMAL ||
802 type == TNY_FOLDER_TYPE_UNKNOWN) {
803 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
807 /* note: we cannot reliably get the counts from the tree model, we need
808 * to use explicit calls on tny_folder for some reason.
810 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
811 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
812 (type == TNY_FOLDER_TYPE_OUTBOX) ||
813 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
814 number = tny_folder_get_all_count (TNY_FOLDER(instance));
817 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
821 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
823 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : N_("mcen_va_new_message"),
824 (drafts) ? "mcen_ti_messages" : N_("mcen_va_new_messages"),
830 item_name = g_strdup ("");
833 /* Set the name in the treeview cell: */
834 g_object_set (rendobj,"text", item_name, NULL);
842 g_object_unref (G_OBJECT (instance));
848 GdkPixbuf *pixbuf_open;
849 GdkPixbuf *pixbuf_close;
853 static inline GdkPixbuf *
854 get_composite_pixbuf (const gchar *icon_name,
856 GdkPixbuf *base_pixbuf)
858 GdkPixbuf *emblem, *retval = NULL;
860 emblem = modest_platform_get_icon (icon_name, size);
862 retval = gdk_pixbuf_copy (base_pixbuf);
863 gdk_pixbuf_composite (emblem, retval, 0, 0,
864 MIN (gdk_pixbuf_get_width (emblem),
865 gdk_pixbuf_get_width (retval)),
866 MIN (gdk_pixbuf_get_height (emblem),
867 gdk_pixbuf_get_height (retval)),
868 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
869 g_object_unref (emblem);
874 static inline ThreePixbufs *
875 get_composite_icons (const gchar *icon_code,
877 GdkPixbuf **pixbuf_open,
878 GdkPixbuf **pixbuf_close)
880 ThreePixbufs *retval;
882 if (pixbuf && !*pixbuf) {
884 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
886 *pixbuf = gdk_pixbuf_copy (icon);
892 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
893 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
897 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
898 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
902 retval = g_slice_new0 (ThreePixbufs);
903 if (pixbuf && *pixbuf)
904 retval->pixbuf = g_object_ref (*pixbuf);
906 retval->pixbuf = NULL;
907 if (pixbuf_open && *pixbuf_open)
908 retval->pixbuf_open = g_object_ref (*pixbuf_open);
910 retval->pixbuf_open = NULL;
911 if (pixbuf_close && *pixbuf_close)
912 retval->pixbuf_close = g_object_ref (*pixbuf_close);
914 retval->pixbuf_close = NULL;
919 static inline ThreePixbufs *
920 get_account_protocol_pixbufs (ModestFolderView *folder_view,
921 ModestProtocolType protocol_type,
924 ModestProtocol *protocol;
925 const GdkPixbuf *pixbuf = NULL;
926 ModestFolderViewPrivate *priv;
928 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
930 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
933 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
934 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
935 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
936 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
937 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
938 object, FOLDER_ICON_SIZE);
942 ThreePixbufs *retval;
943 retval = g_slice_new0 (ThreePixbufs);
944 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
945 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
946 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
953 static inline ThreePixbufs*
954 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
956 TnyAccount *account = NULL;
957 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
958 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
959 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
960 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
961 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
963 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
964 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
965 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
966 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
967 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
969 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
970 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
971 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
972 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
973 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
975 ThreePixbufs *retval = NULL;
977 if (TNY_IS_ACCOUNT (instance)) {
978 account = g_object_ref (instance);
979 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
980 account = tny_folder_get_account (TNY_FOLDER (instance));
984 ModestProtocolType account_store_protocol;
986 account_store_protocol = modest_tny_account_get_protocol_type (account);
987 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
988 g_object_unref (account);
994 /* Sometimes an special folder is reported by the server as
995 NORMAL, like some versions of Dovecot */
996 if (type == TNY_FOLDER_TYPE_NORMAL ||
997 type == TNY_FOLDER_TYPE_UNKNOWN) {
998 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1001 /* It's not enough with check the folder type. We need to
1002 ensure that we're not giving a special folder icon to a
1003 normal folder with the same name than a special folder */
1004 if (TNY_IS_FOLDER (instance) &&
1005 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1006 type = TNY_FOLDER_TYPE_NORMAL;
1008 /* Remote folders should not be treated as special folders */
1009 if (TNY_IS_FOLDER_STORE (instance) &&
1010 !TNY_IS_ACCOUNT (instance) &&
1011 type != TNY_FOLDER_TYPE_INBOX &&
1012 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1013 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1016 &anorm_pixbuf_close);
1021 case TNY_FOLDER_TYPE_INVALID:
1022 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1025 case TNY_FOLDER_TYPE_ROOT:
1026 if (TNY_IS_ACCOUNT (instance)) {
1028 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1029 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1032 &avirt_pixbuf_close);
1034 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1036 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1037 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1040 &ammc_pixbuf_close);
1042 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1045 &anorm_pixbuf_close);
1050 case TNY_FOLDER_TYPE_INBOX:
1051 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1054 &inbox_pixbuf_close);
1056 case TNY_FOLDER_TYPE_OUTBOX:
1057 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1059 &outbox_pixbuf_open,
1060 &outbox_pixbuf_close);
1062 case TNY_FOLDER_TYPE_JUNK:
1063 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1066 &junk_pixbuf_close);
1068 case TNY_FOLDER_TYPE_SENT:
1069 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1072 &sent_pixbuf_close);
1074 case TNY_FOLDER_TYPE_TRASH:
1075 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1078 &trash_pixbuf_close);
1080 case TNY_FOLDER_TYPE_DRAFTS:
1081 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1084 &draft_pixbuf_close);
1086 case TNY_FOLDER_TYPE_ARCHIVE:
1087 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1092 case TNY_FOLDER_TYPE_NORMAL:
1094 /* Memory card folders could have an special icon */
1095 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1096 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1101 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1103 &normal_pixbuf_open,
1104 &normal_pixbuf_close);
1113 free_pixbufs (ThreePixbufs *pixbufs)
1115 if (pixbufs->pixbuf)
1116 g_object_unref (pixbufs->pixbuf);
1117 if (pixbufs->pixbuf_open)
1118 g_object_unref (pixbufs->pixbuf_open);
1119 if (pixbufs->pixbuf_close)
1120 g_object_unref (pixbufs->pixbuf_close);
1121 g_slice_free (ThreePixbufs, pixbufs);
1125 icon_cell_data (GtkTreeViewColumn *column,
1126 GtkCellRenderer *renderer,
1127 GtkTreeModel *tree_model,
1131 GObject *rendobj = NULL, *instance = NULL;
1132 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1133 gboolean has_children;
1134 ThreePixbufs *pixbufs;
1135 ModestFolderView *folder_view = (ModestFolderView *) data;
1137 rendobj = (GObject *) renderer;
1139 gtk_tree_model_get (tree_model, iter,
1141 INSTANCE_COLUMN, &instance,
1147 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1148 pixbufs = get_folder_icons (folder_view, type, instance);
1149 g_object_unref (instance);
1152 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1155 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1156 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1159 free_pixbufs (pixbufs);
1163 add_columns (GtkWidget *treeview)
1165 GtkTreeViewColumn *column;
1166 GtkCellRenderer *renderer;
1167 GtkTreeSelection *sel;
1168 ModestFolderViewPrivate *priv;
1170 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1173 column = gtk_tree_view_column_new ();
1175 /* Set icon and text render function */
1176 renderer = gtk_cell_renderer_pixbuf_new();
1177 g_object_set (renderer,
1178 "xpad", MODEST_MARGIN_DEFAULT,
1179 "ypad", MODEST_MARGIN_DEFAULT,
1181 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1182 gtk_tree_view_column_set_cell_data_func(column, renderer,
1183 icon_cell_data, treeview, NULL);
1185 renderer = gtk_cell_renderer_text_new();
1186 g_object_set (renderer,
1187 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1188 "ypad", MODEST_MARGIN_DEFAULT,
1189 "xpad", MODEST_MARGIN_DEFAULT,
1190 "ellipsize-set", TRUE, NULL);
1191 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1192 gtk_tree_view_column_set_cell_data_func(column, renderer,
1193 text_cell_data, treeview, NULL);
1195 priv->messages_renderer = gtk_cell_renderer_text_new ();
1196 g_object_set (priv->messages_renderer,
1198 "ypad", MODEST_MARGIN_DEFAULT,
1199 "xpad", MODEST_MARGIN_DOUBLE,
1200 "alignment", PANGO_ALIGN_RIGHT,
1204 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1205 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1206 messages_cell_data, treeview, NULL);
1208 /* Set selection mode */
1209 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1210 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1212 /* Set treeview appearance */
1213 gtk_tree_view_column_set_spacing (column, 2);
1214 gtk_tree_view_column_set_resizable (column, TRUE);
1215 gtk_tree_view_column_set_fixed_width (column, TRUE);
1216 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1217 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1220 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1224 modest_folder_view_init (ModestFolderView *obj)
1226 ModestFolderViewPrivate *priv;
1229 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1231 priv->timer_expander = 0;
1232 priv->account_store = NULL;
1234 priv->do_refresh = TRUE;
1235 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1236 priv->cur_folder_store = NULL;
1237 priv->visible_account_id = NULL;
1238 priv->mailbox = NULL;
1239 priv->folder_to_select = NULL;
1240 priv->outbox_deleted_handler = 0;
1241 priv->reexpand = TRUE;
1242 priv->signal_handlers = 0;
1244 /* Initialize the local account name */
1245 conf = modest_runtime_get_conf();
1246 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1248 /* Init email clipboard */
1249 priv->clipboard = modest_runtime_get_email_clipboard ();
1250 priv->hidding_ids = NULL;
1251 priv->n_selected = 0;
1252 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1253 priv->reselect = FALSE;
1254 priv->show_non_move = TRUE;
1255 priv->list_to_move = NULL;
1256 priv->show_message_count = TRUE;
1258 /* Build treeview */
1259 add_columns (GTK_WIDGET (obj));
1261 /* Connect signals */
1262 g_signal_connect (G_OBJECT (obj),
1264 G_CALLBACK (on_key_pressed), NULL);
1266 priv->display_name_changed_signal =
1267 g_signal_connect (modest_runtime_get_account_mgr (),
1268 "display_name_changed",
1269 G_CALLBACK (on_display_name_changed),
1273 * Track changes in the local account name (in the device it
1274 * will be the device name)
1276 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1278 G_CALLBACK(on_configuration_key_changed),
1281 gdk_color_parse ("000", &priv->active_color);
1284 g_signal_connect (G_OBJECT (obj), "notify::style",
1285 G_CALLBACK (on_notify_style), (gpointer) obj);
1289 tny_account_store_view_init (gpointer g, gpointer iface_data)
1291 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1293 klass->set_account_store = modest_folder_view_set_account_store;
1297 modest_folder_view_dispose (GObject *obj)
1299 ModestFolderViewPrivate *priv;
1301 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1303 if (priv->signal_handlers) {
1304 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1305 priv->signal_handlers = NULL;
1308 /* Free external references */
1309 if (priv->account_store) {
1310 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1311 priv->account_inserted_signal);
1312 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1313 priv->account_removed_signal);
1314 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1315 priv->account_changed_signal);
1316 g_object_unref (G_OBJECT(priv->account_store));
1317 priv->account_store = NULL;
1321 g_object_unref (G_OBJECT (priv->query));
1325 if (priv->folder_to_select) {
1326 g_object_unref (G_OBJECT(priv->folder_to_select));
1327 priv->folder_to_select = NULL;
1330 if (priv->cur_folder_store) {
1331 g_object_unref (priv->cur_folder_store);
1332 priv->cur_folder_store = NULL;
1335 if (priv->list_to_move) {
1336 g_object_unref (priv->list_to_move);
1337 priv->list_to_move = NULL;
1340 G_OBJECT_CLASS(parent_class)->dispose (obj);
1344 modest_folder_view_finalize (GObject *obj)
1346 ModestFolderViewPrivate *priv;
1347 GtkTreeSelection *sel;
1348 TnyAccount *local_account;
1350 g_return_if_fail (obj);
1352 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1354 if (priv->timer_expander != 0) {
1355 g_source_remove (priv->timer_expander);
1356 priv->timer_expander = 0;
1359 local_account = (TnyAccount *)
1360 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1361 if (local_account) {
1362 if (g_signal_handler_is_connected (local_account,
1363 priv->outbox_deleted_handler))
1364 g_signal_handler_disconnect (local_account,
1365 priv->outbox_deleted_handler);
1366 g_object_unref (local_account);
1369 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1370 priv->display_name_changed_signal)) {
1371 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1372 priv->display_name_changed_signal);
1373 priv->display_name_changed_signal = 0;
1376 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1378 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1380 g_free (priv->local_account_name);
1381 g_free (priv->visible_account_id);
1382 g_free (priv->mailbox);
1384 if (priv->conf_key_signal) {
1385 g_signal_handler_disconnect (modest_runtime_get_conf (),
1386 priv->conf_key_signal);
1387 priv->conf_key_signal = 0;
1390 /* Clear hidding array created by cut operation */
1391 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1393 gdk_color_parse ("000", &priv->active_color);
1395 G_OBJECT_CLASS(parent_class)->finalize (obj);
1400 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1402 ModestFolderViewPrivate *priv;
1405 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1406 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1408 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1409 device = tny_account_store_get_device (account_store);
1411 if (G_UNLIKELY (priv->account_store)) {
1413 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1414 priv->account_inserted_signal))
1415 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1416 priv->account_inserted_signal);
1417 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1418 priv->account_removed_signal))
1419 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1420 priv->account_removed_signal);
1421 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1422 priv->account_changed_signal))
1423 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1424 priv->account_changed_signal);
1425 g_object_unref (G_OBJECT (priv->account_store));
1428 priv->account_store = g_object_ref (G_OBJECT (account_store));
1430 priv->account_removed_signal =
1431 g_signal_connect (G_OBJECT(account_store), "account_removed",
1432 G_CALLBACK (on_account_removed), self);
1434 priv->account_inserted_signal =
1435 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1436 G_CALLBACK (on_account_inserted), self);
1438 priv->account_changed_signal =
1439 g_signal_connect (G_OBJECT(account_store), "account_changed",
1440 G_CALLBACK (on_account_changed), self);
1442 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1443 priv->reselect = FALSE;
1445 g_object_unref (G_OBJECT (device));
1449 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1452 ModestFolderView *self;
1453 GtkTreeModel *model, *filter_model;
1456 self = MODEST_FOLDER_VIEW (user_data);
1458 if (!get_inner_models (self, &filter_model, NULL, &model))
1461 /* Remove outbox from model */
1462 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1463 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1464 g_object_unref (outbox);
1467 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1471 on_account_inserted (TnyAccountStore *account_store,
1472 TnyAccount *account,
1475 ModestFolderViewPrivate *priv;
1476 GtkTreeModel *model, *filter_model;
1478 /* Ignore transport account insertions, we're not showing them
1479 in the folder view */
1480 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1483 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1486 /* If we're adding a new account, and there is no previous
1487 one, we need to select the visible server account */
1488 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1489 !priv->visible_account_id)
1490 modest_widget_memory_restore (modest_runtime_get_conf(),
1491 G_OBJECT (user_data),
1492 MODEST_CONF_FOLDER_VIEW_KEY);
1496 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1497 &filter_model, NULL, &model))
1500 /* Insert the account in the model */
1501 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1503 /* When the model is a list store (plain representation) the
1504 outbox is not a child of any account so we have to manually
1505 delete it because removing the local folders account won't
1506 delete it (because tny_folder_get_account() is not defined
1507 for a merge folder */
1508 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1509 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1511 priv->outbox_deleted_handler =
1512 g_signal_connect (account,
1514 G_CALLBACK (on_outbox_deleted_cb),
1518 /* Refilter the model */
1519 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1524 same_account_selected (ModestFolderView *self,
1525 TnyAccount *account)
1527 ModestFolderViewPrivate *priv;
1528 gboolean same_account = FALSE;
1530 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1532 if (priv->cur_folder_store) {
1533 TnyAccount *selected_folder_account = NULL;
1535 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1536 selected_folder_account =
1537 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1539 selected_folder_account =
1540 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1543 if (selected_folder_account == account)
1544 same_account = TRUE;
1546 g_object_unref (selected_folder_account);
1548 return same_account;
1552 on_account_changed (TnyAccountStore *account_store,
1553 TnyAccount *tny_account,
1556 ModestFolderView *self;
1557 ModestFolderViewPrivate *priv;
1558 GtkTreeModel *model, *filter_model;
1559 GtkTreeSelection *sel;
1560 gboolean same_account;
1562 /* Ignore transport account insertions, we're not showing them
1563 in the folder view */
1564 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1567 self = MODEST_FOLDER_VIEW (user_data);
1568 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1570 /* Get the inner model */
1571 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1572 &filter_model, NULL, &model))
1575 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1577 /* Invalidate the cur_folder_store only if the selected folder
1578 belongs to the account that is being removed */
1579 same_account = same_account_selected (self, tny_account);
1581 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1582 gtk_tree_selection_unselect_all (sel);
1585 /* Remove the account from the model */
1586 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1588 /* Insert the account in the model */
1589 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1591 /* Refilter the model */
1592 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1597 on_account_removed (TnyAccountStore *account_store,
1598 TnyAccount *account,
1601 ModestFolderView *self = NULL;
1602 ModestFolderViewPrivate *priv;
1603 GtkTreeModel *model, *filter_model;
1604 GtkTreeSelection *sel = NULL;
1605 gboolean same_account = FALSE;
1607 /* Ignore transport account removals, we're not showing them
1608 in the folder view */
1609 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1612 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1613 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1617 self = MODEST_FOLDER_VIEW (user_data);
1618 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1620 /* Invalidate the cur_folder_store only if the selected folder
1621 belongs to the account that is being removed */
1622 same_account = same_account_selected (self, account);
1624 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1625 gtk_tree_selection_unselect_all (sel);
1628 /* Invalidate row to select only if the folder to select
1629 belongs to the account that is being removed*/
1630 if (priv->folder_to_select) {
1631 TnyAccount *folder_to_select_account = NULL;
1633 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1634 if (folder_to_select_account == account) {
1635 modest_folder_view_disable_next_folder_selection (self);
1636 g_object_unref (priv->folder_to_select);
1637 priv->folder_to_select = NULL;
1639 g_object_unref (folder_to_select_account);
1642 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1643 &filter_model, NULL, &model))
1646 /* Disconnect the signal handler */
1647 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1648 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1649 if (g_signal_handler_is_connected (account,
1650 priv->outbox_deleted_handler))
1651 g_signal_handler_disconnect (account,
1652 priv->outbox_deleted_handler);
1655 /* Remove the account from the model */
1656 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1658 /* If the removed account is the currently viewed one then
1659 clear the configuration value. The new visible account will be the default account */
1660 if (priv->visible_account_id &&
1661 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1663 /* Clear the current visible account_id */
1664 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1665 modest_folder_view_set_mailbox (self, NULL);
1667 /* Call the restore method, this will set the new visible account */
1668 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1669 MODEST_CONF_FOLDER_VIEW_KEY);
1672 /* Refilter the model */
1673 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1678 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1680 GtkTreeViewColumn *col;
1682 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1684 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1686 g_printerr ("modest: failed get column for title\n");
1690 gtk_tree_view_column_set_title (col, title);
1691 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1696 modest_folder_view_on_map (ModestFolderView *self,
1697 GdkEventExpose *event,
1700 ModestFolderViewPrivate *priv;
1702 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1704 /* This won't happen often */
1705 if (G_UNLIKELY (priv->reselect)) {
1706 /* Select the first inbox or the local account if not found */
1708 /* TODO: this could cause a lock at startup, so we
1709 comment it for the moment. We know that this will
1710 be a bug, because the INBOX is not selected, but we
1711 need to rewrite some parts of Modest to avoid the
1712 deathlock situation */
1713 /* TODO: check if this is still the case */
1714 priv->reselect = FALSE;
1715 /* Notify the display name observers */
1716 g_signal_emit (G_OBJECT(self),
1717 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1721 if (priv->reexpand) {
1722 expand_root_items (self);
1723 priv->reexpand = FALSE;
1730 modest_folder_view_new (TnyFolderStoreQuery *query)
1732 return modest_folder_view_new_full (query, TRUE);
1736 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1739 ModestFolderViewPrivate *priv;
1740 GtkTreeSelection *sel;
1742 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1743 #ifdef MODEST_TOOLKIT_HILDON2
1744 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1747 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1750 priv->query = g_object_ref (query);
1752 priv->do_refresh = do_refresh;
1754 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1755 priv->changed_signal = g_signal_connect (sel, "changed",
1756 G_CALLBACK (on_selection_changed), self);
1758 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1760 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1762 /* Hide headers by default */
1763 gtk_tree_view_set_headers_visible ((GtkTreeView *)self, FALSE);
1765 return GTK_WIDGET(self);
1768 /* this feels dirty; any other way to expand all the root items? */
1770 expand_root_items (ModestFolderView *self)
1773 GtkTreeModel *model;
1776 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1777 path = gtk_tree_path_new_first ();
1779 /* all folders should have child items, so.. */
1781 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1782 gtk_tree_path_next (path);
1783 } while (gtk_tree_model_get_iter (model, &iter, path));
1785 gtk_tree_path_free (path);
1789 is_parent_of (TnyFolder *a, TnyFolder *b)
1792 gboolean retval = FALSE;
1794 a_id = tny_folder_get_id (a);
1796 gchar *string_to_match;
1799 string_to_match = g_strconcat (a_id, "/", NULL);
1800 b_id = tny_folder_get_id (b);
1801 retval = g_str_has_prefix (b_id, string_to_match);
1802 g_free (string_to_match);
1808 typedef struct _ForeachFolderInfo {
1811 } ForeachFolderInfo;
1814 foreach_folder_with_id (GtkTreeModel *model,
1819 ForeachFolderInfo *info;
1822 info = (ForeachFolderInfo *) data;
1823 gtk_tree_model_get (model, iter,
1824 INSTANCE_COLUMN, &instance,
1827 if (TNY_IS_FOLDER (instance)) {
1830 id = tny_folder_get_id (TNY_FOLDER (instance));
1832 collate = g_utf8_collate_key (id, -1);
1833 info->found = !strcmp (info->needle, collate);
1839 g_object_unref (instance);
1847 has_folder_with_id (ModestFolderView *self, const gchar *id)
1849 GtkTreeModel *model;
1850 ForeachFolderInfo info = {NULL, FALSE};
1852 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1853 info.needle = g_utf8_collate_key (id, -1);
1855 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1856 g_free (info.needle);
1862 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1865 gboolean retval = FALSE;
1867 a_id = tny_folder_get_id (a);
1870 b_id = tny_folder_get_id (b);
1873 const gchar *last_bar;
1874 gchar *string_to_match;
1875 last_bar = g_strrstr (b_id, "/");
1880 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1881 retval = has_folder_with_id (self, string_to_match);
1882 g_free (string_to_match);
1890 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1892 ModestFolderViewPrivate *priv;
1893 TnyIterator *iterator;
1894 gboolean retval = TRUE;
1896 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1897 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1899 for (iterator = tny_list_create_iterator (priv->list_to_move);
1900 retval && !tny_iterator_is_done (iterator);
1901 tny_iterator_next (iterator)) {
1903 instance = tny_iterator_get_current (iterator);
1904 if (instance == (GObject *) folder) {
1906 } else if (TNY_IS_FOLDER (instance)) {
1907 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1909 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1912 g_object_unref (instance);
1914 g_object_unref (iterator);
1921 * We use this function to implement the
1922 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1923 * account in this case, and the local folders.
1926 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1928 ModestFolderViewPrivate *priv;
1929 gboolean retval = TRUE;
1930 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1931 GObject *instance = NULL;
1932 const gchar *id = NULL;
1934 gboolean found = FALSE;
1935 gboolean cleared = FALSE;
1936 ModestTnyFolderRules rules = 0;
1939 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1940 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1942 gtk_tree_model_get (model, iter,
1943 NAME_COLUMN, &fname,
1945 INSTANCE_COLUMN, &instance,
1948 /* Do not show if there is no instance, this could indeed
1949 happen when the model is being modified while it's being
1950 drawn. This could occur for example when moving folders
1957 if (TNY_IS_ACCOUNT (instance)) {
1958 TnyAccount *acc = TNY_ACCOUNT (instance);
1959 const gchar *account_id = tny_account_get_id (acc);
1961 /* If it isn't a special folder,
1962 * don't show it unless it is the visible account: */
1963 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1964 !modest_tny_account_is_virtual_local_folders (acc) &&
1965 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1967 /* Show only the visible account id */
1968 if (priv->visible_account_id) {
1969 if (strcmp (account_id, priv->visible_account_id))
1976 /* Never show these to the user. They are merged into one folder
1977 * in the local-folders account instead: */
1978 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1981 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1982 /* Only show special folders for current account if needed */
1983 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1984 TnyAccount *account;
1986 account = tny_folder_get_account (TNY_FOLDER (instance));
1988 if (TNY_IS_ACCOUNT (account)) {
1989 const gchar *account_id = tny_account_get_id (account);
1991 if (!modest_tny_account_is_virtual_local_folders (account) &&
1992 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1993 /* Show only the visible account id */
1994 if (priv->visible_account_id) {
1995 if (strcmp (account_id, priv->visible_account_id)) {
1997 } else if (priv->mailbox) {
1998 /* Filter mailboxes */
1999 if (!g_str_has_prefix (fname, priv->mailbox)) {
2001 } else if (!strcmp (fname, priv->mailbox)) {
2002 /* Hide mailbox parent */
2008 g_object_unref (account);
2015 /* Check hiding (if necessary) */
2016 cleared = modest_email_clipboard_cleared (priv->clipboard);
2017 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2018 id = tny_folder_get_id (TNY_FOLDER(instance));
2019 if (priv->hidding_ids != NULL)
2020 for (i=0; i < priv->n_selected && !found; i++)
2021 if (priv->hidding_ids[i] != NULL && id != NULL)
2022 found = (!strcmp (priv->hidding_ids[i], id));
2027 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2028 folder as no message can be move there according to UI specs */
2029 if (retval && !priv->show_non_move) {
2030 if (priv->list_to_move &&
2031 tny_list_get_length (priv->list_to_move) > 0 &&
2032 TNY_IS_FOLDER (instance)) {
2033 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2035 if (retval && TNY_IS_FOLDER (instance) &&
2036 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2038 case TNY_FOLDER_TYPE_OUTBOX:
2039 case TNY_FOLDER_TYPE_SENT:
2040 case TNY_FOLDER_TYPE_DRAFTS:
2043 case TNY_FOLDER_TYPE_UNKNOWN:
2044 case TNY_FOLDER_TYPE_NORMAL:
2045 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2046 if (type == TNY_FOLDER_TYPE_INVALID)
2047 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2049 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2050 type == TNY_FOLDER_TYPE_SENT
2051 || type == TNY_FOLDER_TYPE_DRAFTS)
2058 if (retval && TNY_IS_ACCOUNT (instance) &&
2059 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2060 ModestProtocolType protocol_type;
2062 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2063 retval = !modest_protocol_registry_protocol_type_has_tag
2064 (modest_runtime_get_protocol_registry (),
2066 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2070 /* apply special filters */
2071 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2072 if (TNY_IS_ACCOUNT (instance))
2076 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2077 if (TNY_IS_FOLDER (instance))
2081 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2082 if (TNY_IS_ACCOUNT (instance)) {
2083 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2085 } else if (TNY_IS_FOLDER (instance)) {
2086 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2091 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2092 if (TNY_IS_ACCOUNT (instance)) {
2093 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2095 } else if (TNY_IS_FOLDER (instance)) {
2096 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2101 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2102 /* A mailbox is a fake folder with an @ in the middle of the name */
2103 if (!TNY_IS_FOLDER (instance) ||
2104 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2107 const gchar *folder_name;
2108 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2109 if (!folder_name || strchr (folder_name, '@') == NULL)
2115 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2116 if (TNY_IS_FOLDER (instance)) {
2117 /* Check folder rules */
2118 ModestTnyFolderRules rules;
2120 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2121 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2122 } else if (TNY_IS_ACCOUNT (instance)) {
2123 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2131 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2132 if (TNY_IS_FOLDER (instance)) {
2133 TnyFolderType guess_type;
2135 if (TNY_FOLDER_TYPE_NORMAL) {
2136 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2142 case TNY_FOLDER_TYPE_OUTBOX:
2143 case TNY_FOLDER_TYPE_SENT:
2144 case TNY_FOLDER_TYPE_DRAFTS:
2145 case TNY_FOLDER_TYPE_ARCHIVE:
2146 case TNY_FOLDER_TYPE_INBOX:
2149 case TNY_FOLDER_TYPE_UNKNOWN:
2150 case TNY_FOLDER_TYPE_NORMAL:
2156 } else if (TNY_IS_ACCOUNT (instance)) {
2161 if (retval && TNY_IS_FOLDER (instance)) {
2162 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2165 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2166 if (TNY_IS_FOLDER (instance)) {
2167 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2168 } else if (TNY_IS_ACCOUNT (instance)) {
2173 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2174 if (TNY_IS_FOLDER (instance)) {
2175 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2176 } else if (TNY_IS_ACCOUNT (instance)) {
2181 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2182 if (TNY_IS_FOLDER (instance)) {
2183 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2184 } else if (TNY_IS_ACCOUNT (instance)) {
2190 g_object_unref (instance);
2198 modest_folder_view_update_model (ModestFolderView *self,
2199 TnyAccountStore *account_store)
2201 ModestFolderViewPrivate *priv;
2202 GtkTreeModel *model;
2203 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2205 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2206 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2209 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2211 /* Notify that there is no folder selected */
2212 g_signal_emit (G_OBJECT(self),
2213 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2215 if (priv->cur_folder_store) {
2216 g_object_unref (priv->cur_folder_store);
2217 priv->cur_folder_store = NULL;
2220 /* FIXME: the local accounts are not shown when the query
2221 selects only the subscribed folders */
2222 TnyGtkFolderListStoreFlags flags;
2223 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2224 if (priv->do_refresh)
2225 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2227 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2228 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2230 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2231 MODEST_FOLDER_PATH_SEPARATOR);
2233 /* When the model is a list store (plain representation) the
2234 outbox is not a child of any account so we have to manually
2235 delete it because removing the local folders account won't
2236 delete it (because tny_folder_get_account() is not defined
2237 for a merge folder */
2238 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2239 TnyAccount *account;
2240 ModestTnyAccountStore *acc_store;
2242 acc_store = modest_runtime_get_account_store ();
2243 account = modest_tny_account_store_get_local_folders_account (acc_store);
2245 if (g_signal_handler_is_connected (account,
2246 priv->outbox_deleted_handler))
2247 g_signal_handler_disconnect (account,
2248 priv->outbox_deleted_handler);
2250 priv->outbox_deleted_handler =
2251 g_signal_connect (account,
2253 G_CALLBACK (on_outbox_deleted_cb),
2255 g_object_unref (account);
2258 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2259 /* Get the accounts */
2260 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2262 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2264 if (priv->visible_account_id) {
2265 TnyAccount *account;
2267 /* Add local folders account */
2268 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2271 tny_list_append (TNY_LIST (model), (GObject *) account);
2272 g_object_unref (account);
2275 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2278 tny_list_append (TNY_LIST (model), (GObject *) account);
2279 g_object_unref (account);
2282 /* Add visible account */
2283 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2284 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2285 priv->visible_account_id);
2287 tny_list_append (TNY_LIST (model), (GObject *) account);
2288 g_object_unref (account);
2290 g_warning ("You need to set an account first");
2291 g_object_unref (model);
2295 g_warning ("You need to set an account first");
2296 g_object_unref (model);
2301 sortable = gtk_tree_model_sort_new_with_model (model);
2302 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2304 GTK_SORT_ASCENDING);
2305 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2307 cmp_rows, NULL, NULL);
2309 /* Create filter model */
2310 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2311 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2316 GtkTreeModel *old_tny_model = NULL;
2317 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2318 if (priv->signal_handlers > 0) {
2319 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2320 G_OBJECT (old_tny_model),
2321 "activity-changed");
2326 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2328 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2331 G_CALLBACK (on_activity_changed),
2334 g_object_unref (model);
2335 g_object_unref (filter_model);
2336 g_object_unref (sortable);
2338 /* Force a reselection of the INBOX next time the widget is shown */
2339 priv->reselect = TRUE;
2346 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2348 GtkTreeModel *model = NULL;
2349 TnyFolderStore *folder = NULL;
2351 ModestFolderView *tree_view = NULL;
2352 ModestFolderViewPrivate *priv = NULL;
2353 gboolean selected = FALSE;
2355 g_return_if_fail (sel);
2356 g_return_if_fail (user_data);
2358 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2360 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2362 tree_view = MODEST_FOLDER_VIEW (user_data);
2365 gtk_tree_model_get (model, &iter,
2366 INSTANCE_COLUMN, &folder,
2369 /* If the folder is the same do not notify */
2370 if (folder && priv->cur_folder_store == folder) {
2371 g_object_unref (folder);
2376 /* Current folder was unselected */
2377 if (priv->cur_folder_store) {
2378 /* We must do this firstly because a libtinymail-camel
2379 implementation detail. If we issue the signal
2380 before doing the sync_async, then that signal could
2381 cause (and it actually does it) a free of the
2382 summary of the folder (because the main window will
2383 clear the headers view */
2385 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2386 priv->cur_folder_store, FALSE);
2388 g_object_unref (priv->cur_folder_store);
2389 priv->cur_folder_store = NULL;
2392 /* New current references */
2393 priv->cur_folder_store = folder;
2395 /* New folder has been selected. Do not notify if there is
2396 nothing new selected */
2398 g_signal_emit (G_OBJECT(tree_view),
2399 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2400 0, priv->cur_folder_store, TRUE);
2405 on_row_activated (GtkTreeView *treeview,
2406 GtkTreePath *treepath,
2407 GtkTreeViewColumn *column,
2410 GtkTreeModel *model = NULL;
2411 TnyFolderStore *folder = NULL;
2413 ModestFolderView *self = NULL;
2414 ModestFolderViewPrivate *priv = NULL;
2416 g_return_if_fail (treeview);
2417 g_return_if_fail (user_data);
2419 self = MODEST_FOLDER_VIEW (user_data);
2420 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2422 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2424 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2427 gtk_tree_model_get (model, &iter,
2428 INSTANCE_COLUMN, &folder,
2431 g_signal_emit (G_OBJECT(self),
2432 signals[FOLDER_ACTIVATED_SIGNAL],
2435 #ifdef MODEST_TOOLKIT_HILDON2
2436 HildonUIMode ui_mode;
2437 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2438 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2439 if (priv->cur_folder_store)
2440 g_object_unref (priv->cur_folder_store);
2441 priv->cur_folder_store = g_object_ref (folder);
2445 g_object_unref (folder);
2449 modest_folder_view_get_selected (ModestFolderView *self)
2451 ModestFolderViewPrivate *priv;
2453 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2455 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2456 if (priv->cur_folder_store)
2457 g_object_ref (priv->cur_folder_store);
2459 return priv->cur_folder_store;
2463 get_cmp_rows_type_pos (GObject *folder)
2465 /* Remote accounts -> Local account -> MMC account .*/
2468 if (TNY_IS_ACCOUNT (folder) &&
2469 modest_tny_account_is_virtual_local_folders (
2470 TNY_ACCOUNT (folder))) {
2472 } else if (TNY_IS_ACCOUNT (folder)) {
2473 TnyAccount *account = TNY_ACCOUNT (folder);
2474 const gchar *account_id = tny_account_get_id (account);
2475 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2481 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2482 return -1; /* Should never happen */
2487 inbox_is_special (TnyFolderStore *folder_store)
2489 gboolean is_special = TRUE;
2491 if (TNY_IS_FOLDER (folder_store)) {
2495 gchar *last_inbox_bar;
2497 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2498 downcase = g_utf8_strdown (id, -1);
2499 last_bar = g_strrstr (downcase, "/");
2501 last_inbox_bar = g_strrstr (downcase, "inbox/");
2502 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2513 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2515 TnyAccount *account;
2516 gboolean is_special;
2517 /* Inbox, Outbox, Drafts, Sent, User */
2520 if (!TNY_IS_FOLDER (folder_store))
2523 case TNY_FOLDER_TYPE_INBOX:
2525 account = tny_folder_get_account (folder_store);
2526 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2528 /* In inbox case we need to know if the inbox is really the top
2529 * inbox of the account, or if it's a submailbox inbox. To do
2530 * this we'll apply an heuristic rule: Find last "/" and check
2531 * if it's preceeded by another Inbox */
2532 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2533 g_object_unref (account);
2534 return is_special?0:4;
2537 case TNY_FOLDER_TYPE_OUTBOX:
2538 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2540 case TNY_FOLDER_TYPE_DRAFTS:
2542 account = tny_folder_get_account (folder_store);
2543 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2544 g_object_unref (account);
2545 return is_special?1:4;
2548 case TNY_FOLDER_TYPE_SENT:
2550 account = tny_folder_get_account (folder_store);
2551 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2552 g_object_unref (account);
2553 return is_special?3:4;
2562 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2564 const gchar *a1_name, *a2_name;
2566 a1_name = tny_account_get_name (a1);
2567 a2_name = tny_account_get_name (a2);
2569 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2573 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2575 TnyAccount *a1 = NULL, *a2 = NULL;
2578 if (TNY_IS_ACCOUNT (s1)) {
2579 a1 = TNY_ACCOUNT (g_object_ref (s1));
2580 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2581 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2584 if (TNY_IS_ACCOUNT (s2)) {
2585 a2 = TNY_ACCOUNT (g_object_ref (s2));
2586 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2587 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2604 /* First we sort with the type of account */
2605 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2609 cmp = compare_account_names (a1, a2);
2613 g_object_unref (a1);
2615 g_object_unref (a2);
2621 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2623 gint is_account1, is_account2;
2625 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2626 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2628 return is_account2 - is_account1;
2632 compare_folders (const gchar *name1, const gchar *name2)
2634 const gchar *separator1, *separator2;
2635 const gchar *next1, *next2;
2639 if (name1 == NULL || name1[0] == '\0')
2641 if (name2 == NULL || name2[0] == '\0')
2644 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2646 top1 = g_strndup (name1, separator1 - name1);
2648 top1 = g_strdup (name1);
2651 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2653 top2 = g_strndup (name2, separator2 - name2);
2655 top2 = g_strdup (name2);
2659 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2666 if (separator1 == NULL && separator2 == NULL)
2669 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2670 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2672 return compare_folders (next1, next2);
2677 * This function orders the mail accounts according to these rules:
2678 * 1st - remote accounts
2679 * 2nd - local account
2683 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2687 gchar *name1 = NULL;
2688 gchar *name2 = NULL;
2689 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2690 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2691 GObject *folder1 = NULL;
2692 GObject *folder2 = NULL;
2694 gtk_tree_model_get (tree_model, iter1,
2695 NAME_COLUMN, &name1,
2697 INSTANCE_COLUMN, &folder1,
2699 gtk_tree_model_get (tree_model, iter2,
2700 NAME_COLUMN, &name2,
2701 TYPE_COLUMN, &type2,
2702 INSTANCE_COLUMN, &folder2,
2705 /* Return if we get no folder. This could happen when folder
2706 operations are happening. The model is updated after the
2707 folder copy/move actually occurs, so there could be
2708 situations where the model to be drawn is not correct */
2709 if (!folder1 || !folder2)
2712 /* Sort by type. First the special folders, then the archives */
2713 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2717 /* Now we sort using the account of each folder */
2718 if (TNY_IS_FOLDER_STORE (folder1) &&
2719 TNY_IS_FOLDER_STORE (folder2)) {
2720 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2724 /* Each group is preceeded by its account */
2725 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2730 /* Pure sort by name */
2731 cmp = compare_folders (name1, name2);
2734 g_object_unref(G_OBJECT(folder1));
2736 g_object_unref(G_OBJECT(folder2));
2746 * This function manages the navigation through the folders using the
2747 * keyboard or the hardware keys in the device
2750 on_key_pressed (GtkWidget *self,
2754 GtkTreeSelection *selection;
2756 GtkTreeModel *model;
2757 gboolean retval = FALSE;
2759 /* Up and Down are automatically managed by the treeview */
2760 if (event->keyval == GDK_Return) {
2761 /* Expand/Collapse the selected row */
2762 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2763 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2766 path = gtk_tree_model_get_path (model, &iter);
2768 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2769 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2771 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2772 gtk_tree_path_free (path);
2774 /* No further processing */
2782 * We listen to the changes in the local folder account name key,
2783 * because we want to show the right name in the view. The local
2784 * folder account name corresponds to the device name in the Maemo
2785 * version. We do this because we do not want to query gconf on each
2786 * tree view refresh. It's better to cache it and change whenever
2790 on_configuration_key_changed (ModestConf* conf,
2792 ModestConfEvent event,
2793 ModestConfNotificationId id,
2794 ModestFolderView *self)
2796 ModestFolderViewPrivate *priv;
2799 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2800 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2802 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2803 g_free (priv->local_account_name);
2805 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2806 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2808 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2809 MODEST_CONF_DEVICE_NAME, NULL);
2811 /* Force a redraw */
2812 #if GTK_CHECK_VERSION(2, 8, 0)
2813 GtkTreeViewColumn * tree_column;
2815 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2817 gtk_tree_view_column_queue_resize (tree_column);
2819 gtk_widget_queue_draw (GTK_WIDGET (self));
2825 modest_folder_view_set_style (ModestFolderView *self,
2826 ModestFolderViewStyle style)
2828 ModestFolderViewPrivate *priv;
2830 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2831 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2832 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2834 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2837 priv->style = style;
2841 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2842 const gchar *account_id)
2844 ModestFolderViewPrivate *priv;
2845 GtkTreeModel *model;
2847 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2849 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2851 /* This will be used by the filter_row callback,
2852 * to decided which rows to show: */
2853 if (priv->visible_account_id) {
2854 g_free (priv->visible_account_id);
2855 priv->visible_account_id = NULL;
2858 priv->visible_account_id = g_strdup (account_id);
2861 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2862 if (GTK_IS_TREE_MODEL_FILTER (model))
2863 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2865 modest_folder_view_update_model(self,
2866 (TnyAccountStore *) modest_runtime_get_account_store());
2868 /* Save settings to gconf */
2869 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2870 MODEST_CONF_FOLDER_VIEW_KEY);
2872 /* Notify observers */
2873 g_signal_emit (G_OBJECT(self),
2874 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
2879 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2881 ModestFolderViewPrivate *priv;
2883 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2885 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2887 return (const gchar *) priv->visible_account_id;
2892 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2897 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2898 TnyFolder* a_folder;
2901 gtk_tree_model_get (model, iter,
2902 INSTANCE_COLUMN, &a_folder,
2908 if (folder == a_folder) {
2909 g_object_unref (a_folder);
2910 *folder_iter = *iter;
2913 g_object_unref (a_folder);
2915 if (gtk_tree_model_iter_children (model, &child, iter)) {
2916 if (find_folder_iter (model, &child, folder_iter, folder))
2920 } while (gtk_tree_model_iter_next (model, iter));
2927 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2929 ModestFolderViewPrivate *priv;
2931 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2933 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2935 if (priv->folder_to_select)
2936 g_object_unref(priv->folder_to_select);
2938 priv->folder_to_select = NULL;
2942 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2943 gboolean after_change)
2945 GtkTreeModel *model;
2946 GtkTreeIter iter, folder_iter;
2947 GtkTreeSelection *sel;
2948 ModestFolderViewPrivate *priv = NULL;
2950 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2951 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2953 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2956 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2957 gtk_tree_selection_unselect_all (sel);
2959 if (priv->folder_to_select)
2960 g_object_unref(priv->folder_to_select);
2961 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2965 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2970 /* Refilter the model, before selecting the folder */
2971 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2973 if (!gtk_tree_model_get_iter_first (model, &iter)) {
2974 g_warning ("%s: model is empty", __FUNCTION__);
2978 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2981 path = gtk_tree_model_get_path (model, &folder_iter);
2982 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2984 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2985 gtk_tree_selection_select_iter (sel, &folder_iter);
2986 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2988 gtk_tree_path_free (path);
2996 modest_folder_view_copy_selection (ModestFolderView *self)
2998 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3000 /* Copy selection */
3001 _clipboard_set_selected_data (self, FALSE);
3005 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3007 ModestFolderViewPrivate *priv = NULL;
3008 GtkTreeModel *model = NULL;
3009 const gchar **hidding = NULL;
3010 guint i, n_selected;
3012 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3013 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3015 /* Copy selection */
3016 if (!_clipboard_set_selected_data (folder_view, TRUE))
3019 /* Get hidding ids */
3020 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3022 /* Clear hidding array created by previous cut operation */
3023 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3025 /* Copy hidding array */
3026 priv->n_selected = n_selected;
3027 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3028 for (i=0; i < n_selected; i++)
3029 priv->hidding_ids[i] = g_strdup(hidding[i]);
3031 /* Hide cut folders */
3032 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3033 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3037 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3038 ModestFolderView *folder_view_dst)
3040 GtkTreeModel *filter_model = NULL;
3041 GtkTreeModel *model = NULL;
3042 GtkTreeModel *new_filter_model = NULL;
3043 GtkTreeModel *old_tny_model = NULL;
3044 GtkTreeModel *new_tny_model = NULL;
3045 ModestFolderViewPrivate *dst_priv;
3047 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3048 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3050 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3051 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3052 new_tny_model = NULL;
3055 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3056 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3057 G_OBJECT (old_tny_model),
3058 "activity-changed");
3060 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3061 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3063 /* Build new filter model */
3064 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3065 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3072 /* Set copied model */
3073 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3074 if (new_tny_model) {
3075 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3076 G_OBJECT (new_tny_model),
3078 G_CALLBACK (on_activity_changed),
3083 g_object_unref (new_filter_model);
3087 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3090 GtkTreeModel *model = NULL;
3091 ModestFolderViewPrivate* priv;
3093 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3095 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3096 priv->show_non_move = show;
3097 /* modest_folder_view_update_model(folder_view, */
3098 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3100 /* Hide special folders */
3101 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3102 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3103 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3108 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3111 ModestFolderViewPrivate* priv;
3113 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3115 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3116 priv->show_message_count = show;
3118 g_object_set (G_OBJECT (priv->messages_renderer),
3119 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3123 /* Returns FALSE if it did not selected anything */
3125 _clipboard_set_selected_data (ModestFolderView *folder_view,
3128 ModestFolderViewPrivate *priv = NULL;
3129 TnyFolderStore *folder = NULL;
3130 gboolean retval = FALSE;
3132 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3133 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3135 /* Set selected data on clipboard */
3136 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3137 folder = modest_folder_view_get_selected (folder_view);
3139 /* Do not allow to select an account */
3140 if (TNY_IS_FOLDER (folder)) {
3141 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3146 g_object_unref (folder);
3152 _clear_hidding_filter (ModestFolderView *folder_view)
3154 ModestFolderViewPrivate *priv;
3157 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3158 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3160 if (priv->hidding_ids != NULL) {
3161 for (i=0; i < priv->n_selected; i++)
3162 g_free (priv->hidding_ids[i]);
3163 g_free(priv->hidding_ids);
3169 on_display_name_changed (ModestAccountMgr *mgr,
3170 const gchar *account,
3173 ModestFolderView *self;
3175 self = MODEST_FOLDER_VIEW (user_data);
3177 /* Force a redraw */
3178 #if GTK_CHECK_VERSION(2, 8, 0)
3179 GtkTreeViewColumn * tree_column;
3181 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3183 gtk_tree_view_column_queue_resize (tree_column);
3185 gtk_widget_queue_draw (GTK_WIDGET (self));
3190 modest_folder_view_set_cell_style (ModestFolderView *self,
3191 ModestFolderViewCellStyle cell_style)
3193 ModestFolderViewPrivate *priv = NULL;
3195 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3196 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3198 priv->cell_style = cell_style;
3200 g_object_set (G_OBJECT (priv->messages_renderer),
3201 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3204 gtk_widget_queue_draw (GTK_WIDGET (self));
3208 update_style (ModestFolderView *self)
3210 ModestFolderViewPrivate *priv;
3211 GdkColor style_color, style_active_color;
3212 PangoAttrList *attr_list;
3214 PangoAttribute *attr;
3216 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3217 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3221 attr_list = pango_attr_list_new ();
3223 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3224 gdk_color_parse ("grey", &style_color);
3226 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3227 pango_attr_list_insert (attr_list, attr);
3230 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3232 "SmallSystemFont", NULL,
3235 attr = pango_attr_font_desc_new (pango_font_description_copy
3236 (style->font_desc));
3237 pango_attr_list_insert (attr_list, attr);
3239 g_object_set (G_OBJECT (priv->messages_renderer),
3240 "foreground-gdk", &style_color,
3241 "foreground-set", TRUE,
3242 "attributes", attr_list,
3244 pango_attr_list_unref (attr_list);
3247 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3248 priv->active_color = style_active_color;
3250 gdk_color_parse ("000", &(priv->active_color));
3255 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3257 if (strcmp ("style", spec->name) == 0) {
3258 update_style (MODEST_FOLDER_VIEW (obj));
3259 gtk_widget_queue_draw (GTK_WIDGET (obj));
3264 modest_folder_view_set_filter (ModestFolderView *self,
3265 ModestFolderViewFilter filter)
3267 ModestFolderViewPrivate *priv;
3268 GtkTreeModel *filter_model;
3270 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3271 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3273 priv->filter |= filter;
3275 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3276 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3277 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3282 modest_folder_view_unset_filter (ModestFolderView *self,
3283 ModestFolderViewFilter filter)
3285 ModestFolderViewPrivate *priv;
3286 GtkTreeModel *filter_model;
3288 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3289 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3291 priv->filter &= ~filter;
3293 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3294 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3295 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3300 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3301 ModestTnyFolderRules rules)
3303 GtkTreeModel *filter_model;
3305 gboolean fulfil = FALSE;
3307 if (!get_inner_models (self, &filter_model, NULL, NULL))
3310 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3314 TnyFolderStore *folder;
3316 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3318 if (TNY_IS_FOLDER (folder)) {
3319 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3320 /* Folder rules are negative: non_writable, non_deletable... */
3321 if (!(folder_rules & rules))
3324 g_object_unref (folder);
3327 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3333 modest_folder_view_set_list_to_move (ModestFolderView *self,
3336 ModestFolderViewPrivate *priv;
3338 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3339 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3341 if (priv->list_to_move)
3342 g_object_unref (priv->list_to_move);
3345 g_object_ref (list);
3347 priv->list_to_move = list;
3351 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3353 ModestFolderViewPrivate *priv;
3355 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3356 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3359 g_free (priv->mailbox);
3361 priv->mailbox = g_strdup (mailbox);
3363 /* Notify observers */
3364 g_signal_emit (G_OBJECT(self),
3365 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3366 priv->visible_account_id);
3370 modest_folder_view_get_mailbox (ModestFolderView *self)
3372 ModestFolderViewPrivate *priv;
3374 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3375 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3377 return (const gchar *) priv->mailbox;
3381 modest_folder_view_get_activity (ModestFolderView *self)
3383 ModestFolderViewPrivate *priv;
3384 GtkTreeModel *inner_model;
3386 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3387 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3388 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
3390 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3391 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3398 on_activity_changed (TnyGtkFolderListStore *store,
3400 ModestFolderView *folder_view)
3402 ModestFolderViewPrivate *priv;
3404 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3405 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3406 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3408 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3413 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3415 GtkTreeModel *model;
3421 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3422 ret_value = TNY_LIST (model);
3423 g_object_ref (ret_value);