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 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
98 static void modest_folder_view_init (ModestFolderView *obj);
99 static void modest_folder_view_finalize (GObject *obj);
101 static void tny_account_store_view_init (gpointer g,
102 gpointer iface_data);
104 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
105 TnyAccountStore *account_store);
107 static void on_selection_changed (GtkTreeSelection *sel,
110 static void on_row_activated (GtkTreeView *treeview,
112 GtkTreeViewColumn *column,
115 static void on_account_removed (TnyAccountStore *self,
119 static void on_account_inserted (TnyAccountStore *self,
123 static void on_account_changed (TnyAccountStore *self,
127 static gint cmp_rows (GtkTreeModel *tree_model,
132 static gboolean filter_row (GtkTreeModel *model,
136 static gboolean on_key_pressed (GtkWidget *self,
140 static void on_configuration_key_changed (ModestConf* conf,
142 ModestConfEvent event,
143 ModestConfNotificationId notification_id,
144 ModestFolderView *self);
147 static void on_drag_data_get (GtkWidget *widget,
148 GdkDragContext *context,
149 GtkSelectionData *selection_data,
154 static void on_drag_data_received (GtkWidget *widget,
155 GdkDragContext *context,
158 GtkSelectionData *selection_data,
163 static gboolean on_drag_motion (GtkWidget *widget,
164 GdkDragContext *context,
170 static void expand_root_items (ModestFolderView *self);
172 static gint expand_row_timeout (gpointer data);
174 static void setup_drag_and_drop (GtkTreeView *self);
176 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
179 static void _clear_hidding_filter (ModestFolderView *folder_view);
181 #ifndef MODEST_TOOLKIT_HILDON2
182 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
185 ModestFolderView *self);
188 static void on_display_name_changed (ModestAccountMgr *self,
189 const gchar *account,
191 static void update_style (ModestFolderView *self);
192 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
193 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
194 static gboolean inbox_is_special (TnyFolderStore *folder_store);
196 static gboolean get_inner_models (ModestFolderView *self,
197 GtkTreeModel **filter_model,
198 GtkTreeModel **sort_model,
199 GtkTreeModel **tny_model);
202 FOLDER_SELECTION_CHANGED_SIGNAL,
203 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
204 FOLDER_ACTIVATED_SIGNAL,
205 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
209 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
210 struct _ModestFolderViewPrivate {
211 TnyAccountStore *account_store;
212 TnyFolderStore *cur_folder_store;
214 TnyFolder *folder_to_select; /* folder to select after the next update */
216 gulong changed_signal;
217 gulong account_inserted_signal;
218 gulong account_removed_signal;
219 gulong account_changed_signal;
220 gulong conf_key_signal;
221 gulong display_name_changed_signal;
223 /* not unref this object, its a singlenton */
224 ModestEmailClipboard *clipboard;
226 /* Filter tree model */
229 ModestFolderViewFilter filter;
231 TnyFolderStoreQuery *query;
232 guint timer_expander;
234 gchar *local_account_name;
235 gchar *visible_account_id;
237 ModestFolderViewStyle style;
238 ModestFolderViewCellStyle cell_style;
239 gboolean show_message_count;
241 gboolean reselect; /* we use this to force a reselection of the INBOX */
242 gboolean show_non_move;
243 TnyList *list_to_move;
244 gboolean reexpand; /* next time we expose, we'll expand all root folders */
246 GtkCellRenderer *messages_renderer;
248 gulong outbox_deleted_handler;
250 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
251 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
252 MODEST_TYPE_FOLDER_VIEW, \
253 ModestFolderViewPrivate))
255 static GObjectClass *parent_class = NULL;
257 static guint signals[LAST_SIGNAL] = {0};
260 modest_folder_view_get_type (void)
262 static GType my_type = 0;
264 static const GTypeInfo my_info = {
265 sizeof(ModestFolderViewClass),
266 NULL, /* base init */
267 NULL, /* base finalize */
268 (GClassInitFunc) modest_folder_view_class_init,
269 NULL, /* class finalize */
270 NULL, /* class data */
271 sizeof(ModestFolderView),
273 (GInstanceInitFunc) modest_folder_view_init,
277 static const GInterfaceInfo tny_account_store_view_info = {
278 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
279 NULL, /* interface_finalize */
280 NULL /* interface_data */
284 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
288 g_type_add_interface_static (my_type,
289 TNY_TYPE_ACCOUNT_STORE_VIEW,
290 &tny_account_store_view_info);
296 modest_folder_view_class_init (ModestFolderViewClass *klass)
298 GObjectClass *gobject_class;
299 GtkTreeViewClass *treeview_class;
300 gobject_class = (GObjectClass*) klass;
301 treeview_class = (GtkTreeViewClass*) klass;
303 parent_class = g_type_class_peek_parent (klass);
304 gobject_class->finalize = modest_folder_view_finalize;
306 g_type_class_add_private (gobject_class,
307 sizeof(ModestFolderViewPrivate));
309 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
310 g_signal_new ("folder_selection_changed",
311 G_TYPE_FROM_CLASS (gobject_class),
313 G_STRUCT_OFFSET (ModestFolderViewClass,
314 folder_selection_changed),
316 modest_marshal_VOID__POINTER_BOOLEAN,
317 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
320 * This signal is emitted whenever the currently selected
321 * folder display name is computed. Note that the name could
322 * be different to the folder name, because we could append
323 * the unread messages count to the folder name to build the
324 * folder display name
326 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
327 g_signal_new ("folder-display-name-changed",
328 G_TYPE_FROM_CLASS (gobject_class),
330 G_STRUCT_OFFSET (ModestFolderViewClass,
331 folder_display_name_changed),
333 g_cclosure_marshal_VOID__STRING,
334 G_TYPE_NONE, 1, G_TYPE_STRING);
336 signals[FOLDER_ACTIVATED_SIGNAL] =
337 g_signal_new ("folder_activated",
338 G_TYPE_FROM_CLASS (gobject_class),
340 G_STRUCT_OFFSET (ModestFolderViewClass,
343 g_cclosure_marshal_VOID__POINTER,
344 G_TYPE_NONE, 1, G_TYPE_POINTER);
347 * Emitted whenever the visible account changes
349 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
350 g_signal_new ("visible-account-changed",
351 G_TYPE_FROM_CLASS (gobject_class),
353 G_STRUCT_OFFSET (ModestFolderViewClass,
354 visible_account_changed),
356 g_cclosure_marshal_VOID__STRING,
357 G_TYPE_NONE, 1, G_TYPE_STRING);
359 treeview_class->select_cursor_parent = NULL;
361 #ifdef MODEST_TOOLKIT_HILDON2
362 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
368 /* Retrieves the filter, sort and tny models of the folder view. If
369 any of these does not exist then it returns FALSE */
371 get_inner_models (ModestFolderView *self,
372 GtkTreeModel **filter_model,
373 GtkTreeModel **sort_model,
374 GtkTreeModel **tny_model)
376 GtkTreeModel *s_model, *f_model, *t_model;
378 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
379 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
380 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
384 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
385 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
386 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
390 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
394 *filter_model = f_model;
396 *sort_model = s_model;
398 *tny_model = t_model;
403 /* Simplify checks for NULLs: */
405 strings_are_equal (const gchar *a, const gchar *b)
411 return (strcmp (a, b) == 0);
418 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
420 GObject *instance = NULL;
422 gtk_tree_model_get (model, iter,
423 INSTANCE_COLUMN, &instance,
427 return FALSE; /* keep walking */
429 if (!TNY_IS_ACCOUNT (instance)) {
430 g_object_unref (instance);
431 return FALSE; /* keep walking */
434 /* Check if this is the looked-for account: */
435 TnyAccount *this_account = TNY_ACCOUNT (instance);
436 TnyAccount *account = TNY_ACCOUNT (data);
438 const gchar *this_account_id = tny_account_get_id(this_account);
439 const gchar *account_id = tny_account_get_id(account);
440 g_object_unref (instance);
443 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
444 if (strings_are_equal(this_account_id, account_id)) {
445 /* Tell the model that the data has changed, so that
446 * it calls the cell_data_func callbacks again: */
447 /* TODO: This does not seem to actually cause the new string to be shown: */
448 gtk_tree_model_row_changed (model, path, iter);
450 return TRUE; /* stop walking */
453 return FALSE; /* keep walking */
458 ModestFolderView *self;
459 gchar *previous_name;
460 } GetMmcAccountNameData;
463 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
465 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
467 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
469 if (!strings_are_equal (
470 tny_account_get_name(TNY_ACCOUNT(account)),
471 data->previous_name)) {
473 /* Tell the model that the data has changed, so that
474 * it calls the cell_data_func callbacks again: */
475 ModestFolderView *self = data->self;
476 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
478 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
481 g_free (data->previous_name);
482 g_slice_free (GetMmcAccountNameData, data);
486 convert_parent_folders_to_dots (gchar **item_name)
490 gchar *last_separator;
492 if (item_name == NULL)
495 for (c = *item_name; *c != '\0'; c++) {
496 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
501 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
502 if (last_separator != NULL) {
503 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
510 buffer = g_string_new ("");
511 for (i = 0; i < n_parents; i++) {
512 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
514 buffer = g_string_append (buffer, last_separator);
516 *item_name = g_string_free (buffer, FALSE);
522 format_compact_style (gchar **item_name,
524 const gchar *mailbox,
526 gboolean multiaccount,
527 gboolean *use_markup)
531 TnyFolderType folder_type;
533 if (!TNY_IS_FOLDER (instance))
536 folder = (TnyFolder *) instance;
538 folder_type = tny_folder_get_folder_type (folder);
539 is_special = (get_cmp_pos (folder_type, folder)!= 4);
542 /* Remove mailbox prefix if any */
543 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
544 if (g_str_has_prefix (*item_name, prefix)) {
545 gchar *new_item_name;
547 new_item_name = g_strdup (*item_name + strlen (prefix));
548 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
549 g_free (new_item_name);
550 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
553 *item_name = new_item_name;
555 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
558 *item_name = g_strdup (_("mcen_me_folder_inbox"));
561 if (!is_special || multiaccount) {
562 TnyAccount *account = tny_folder_get_account (folder);
563 const gchar *folder_name;
564 gboolean concat_folder_name = FALSE;
567 /* Should not happen */
571 /* convert parent folders to dots */
572 convert_parent_folders_to_dots (item_name);
574 folder_name = tny_folder_get_name (folder);
575 if (g_str_has_suffix (*item_name, folder_name)) {
576 gchar *offset = g_strrstr (*item_name, folder_name);
578 concat_folder_name = TRUE;
581 buffer = g_string_new ("");
583 buffer = g_string_append (buffer, *item_name);
584 if (concat_folder_name) {
585 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
586 buffer = g_string_append (buffer, folder_name);
587 if (bold) buffer = g_string_append (buffer, "</span>");
590 g_object_unref (account);
592 *item_name = g_string_free (buffer, FALSE);
600 text_cell_data (GtkTreeViewColumn *column,
601 GtkCellRenderer *renderer,
602 GtkTreeModel *tree_model,
606 ModestFolderViewPrivate *priv;
607 GObject *rendobj = (GObject *) renderer;
609 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
610 GObject *instance = NULL;
611 gboolean use_markup = FALSE;
613 gtk_tree_model_get (tree_model, iter,
616 INSTANCE_COLUMN, &instance,
618 if (!fname || !instance)
621 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
622 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
624 gchar *item_name = NULL;
625 gint item_weight = 400;
627 if (type != TNY_FOLDER_TYPE_ROOT) {
631 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
632 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
633 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
634 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
636 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
639 /* Sometimes an special folder is reported by the server as
640 NORMAL, like some versions of Dovecot */
641 if (type == TNY_FOLDER_TYPE_NORMAL ||
642 type == TNY_FOLDER_TYPE_UNKNOWN) {
643 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
647 /* note: we cannot reliably get the counts from the
648 * tree model, we need to use explicit calls on
649 * tny_folder for some reason. Select the number to
650 * show: the unread or unsent messages. in case of
651 * outbox/drafts, show all */
652 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
653 (type == TNY_FOLDER_TYPE_OUTBOX) ||
654 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
655 number = tny_folder_get_all_count (TNY_FOLDER(instance));
658 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
662 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
663 item_name = g_strdup (fname);
670 /* Use bold font style if there are unread or unset messages */
672 if (priv->show_message_count) {
673 item_name = g_strdup_printf ("%s (%d)", fname, number);
675 item_name = g_strdup (fname);
679 item_name = g_strdup (fname);
684 } else if (TNY_IS_ACCOUNT (instance)) {
685 /* If it's a server account */
686 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
687 item_name = g_strdup (priv->local_account_name);
689 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
690 /* fname is only correct when the items are first
691 * added to the model, not when the account is
692 * changed later, so get the name from the account
694 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
697 item_name = g_strdup (fname);
703 item_name = g_strdup ("unknown");
705 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
706 gboolean multiaccount;
708 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
709 /* Convert item_name to markup */
710 format_compact_style (&item_name, instance, priv->mailbox,
712 multiaccount, &use_markup);
715 if (item_name && item_weight) {
716 /* Set the name in the treeview cell: */
718 g_object_set (rendobj, "markup", item_name, NULL);
720 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
722 /* Notify display name observers */
723 /* TODO: What listens for this signal, and how can it use only the new name? */
724 if (((GObject *) priv->cur_folder_store) == instance) {
725 g_signal_emit (G_OBJECT(self),
726 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
733 /* If it is a Memory card account, make sure that we have the correct name.
734 * This function will be trigerred again when the name has been retrieved: */
735 if (TNY_IS_STORE_ACCOUNT (instance) &&
736 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
738 /* Get the account name asynchronously: */
739 GetMmcAccountNameData *callback_data =
740 g_slice_new0(GetMmcAccountNameData);
741 callback_data->self = self;
743 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
745 callback_data->previous_name = g_strdup (name);
747 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
748 on_get_mmc_account_name, callback_data);
752 g_object_unref (G_OBJECT (instance));
758 messages_cell_data (GtkTreeViewColumn *column,
759 GtkCellRenderer *renderer,
760 GtkTreeModel *tree_model,
764 ModestFolderView *self;
765 ModestFolderViewPrivate *priv;
766 GObject *rendobj = (GObject *) renderer;
767 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
768 GObject *instance = NULL;
769 gchar *item_name = NULL;
771 gtk_tree_model_get (tree_model, iter,
773 INSTANCE_COLUMN, &instance,
778 self = MODEST_FOLDER_VIEW (data);
779 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
782 if (type != TNY_FOLDER_TYPE_ROOT) {
786 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
787 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
788 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
790 /* Sometimes an special folder is reported by the server as
791 NORMAL, like some versions of Dovecot */
792 if (type == TNY_FOLDER_TYPE_NORMAL ||
793 type == TNY_FOLDER_TYPE_UNKNOWN) {
794 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
798 /* note: we cannot reliably get the counts from the tree model, we need
799 * to use explicit calls on tny_folder for some reason.
801 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
802 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
803 (type == TNY_FOLDER_TYPE_OUTBOX) ||
804 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
805 number = tny_folder_get_all_count (TNY_FOLDER(instance));
808 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
812 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
814 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
822 item_name = g_strdup ("");
825 /* Set the name in the treeview cell: */
826 g_object_set (rendobj,"text", item_name, NULL);
834 g_object_unref (G_OBJECT (instance));
840 GdkPixbuf *pixbuf_open;
841 GdkPixbuf *pixbuf_close;
845 static inline GdkPixbuf *
846 get_composite_pixbuf (const gchar *icon_name,
848 GdkPixbuf *base_pixbuf)
850 GdkPixbuf *emblem, *retval = NULL;
852 emblem = modest_platform_get_icon (icon_name, size);
854 retval = gdk_pixbuf_copy (base_pixbuf);
855 gdk_pixbuf_composite (emblem, retval, 0, 0,
856 MIN (gdk_pixbuf_get_width (emblem),
857 gdk_pixbuf_get_width (retval)),
858 MIN (gdk_pixbuf_get_height (emblem),
859 gdk_pixbuf_get_height (retval)),
860 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
861 g_object_unref (emblem);
866 static inline ThreePixbufs *
867 get_composite_icons (const gchar *icon_code,
869 GdkPixbuf **pixbuf_open,
870 GdkPixbuf **pixbuf_close)
872 ThreePixbufs *retval;
875 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
878 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
883 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
887 retval = g_slice_new0 (ThreePixbufs);
889 retval->pixbuf = g_object_ref (*pixbuf);
891 retval->pixbuf_open = g_object_ref (*pixbuf_open);
893 retval->pixbuf_close = g_object_ref (*pixbuf_close);
898 static inline ThreePixbufs *
899 get_account_protocol_pixbufs (ModestProtocolType protocol_type,
902 ModestProtocol *protocol;
903 const GdkPixbuf *pixbuf = NULL;
905 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
908 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
909 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
910 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
911 object, FOLDER_ICON_SIZE);
915 ThreePixbufs *retval;
916 retval = g_slice_new0 (ThreePixbufs);
917 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
918 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
919 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
926 static inline ThreePixbufs*
927 get_folder_icons (TnyFolderType type, GObject *instance)
929 TnyAccount *account = NULL;
930 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
931 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
932 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
933 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
934 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
936 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
937 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
938 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
939 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
940 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
942 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
943 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
944 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
945 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
946 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
948 ThreePixbufs *retval = NULL;
950 if (TNY_IS_ACCOUNT (instance)) {
951 account = g_object_ref (instance);
952 } else if (TNY_IS_FOLDER (instance)) {
953 account = tny_folder_get_account (TNY_FOLDER (instance));
957 ModestProtocolType account_store_protocol;
959 account_store_protocol = modest_tny_account_get_protocol_type (account);
960 retval = get_account_protocol_pixbufs (account_store_protocol, instance);
961 g_object_unref (account);
967 /* Sometimes an special folder is reported by the server as
968 NORMAL, like some versions of Dovecot */
969 if (type == TNY_FOLDER_TYPE_NORMAL ||
970 type == TNY_FOLDER_TYPE_UNKNOWN) {
971 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
974 /* It's not enough with check the folder type. We need to
975 ensure that we're not giving a special folder icon to a
976 normal folder with the same name than a special folder */
977 if (TNY_IS_FOLDER (instance) &&
978 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
979 type = TNY_FOLDER_TYPE_NORMAL;
981 /* Remote folders should not be treated as special folders */
982 if (TNY_IS_FOLDER_STORE (instance) &&
983 !TNY_IS_ACCOUNT (instance) &&
984 type != TNY_FOLDER_TYPE_INBOX &&
985 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
986 #ifdef MODEST_TOOLKIT_HILDON2
987 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
990 &anorm_pixbuf_close);
992 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
995 &normal_pixbuf_close);
1001 case TNY_FOLDER_TYPE_INVALID:
1002 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1005 case TNY_FOLDER_TYPE_ROOT:
1006 if (TNY_IS_ACCOUNT (instance)) {
1008 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1009 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1012 &avirt_pixbuf_close);
1014 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1016 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1017 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1020 &ammc_pixbuf_close);
1022 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1025 &anorm_pixbuf_close);
1030 case TNY_FOLDER_TYPE_INBOX:
1031 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1034 &inbox_pixbuf_close);
1036 case TNY_FOLDER_TYPE_OUTBOX:
1037 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1039 &outbox_pixbuf_open,
1040 &outbox_pixbuf_close);
1042 case TNY_FOLDER_TYPE_JUNK:
1043 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1046 &junk_pixbuf_close);
1048 case TNY_FOLDER_TYPE_SENT:
1049 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1052 &sent_pixbuf_close);
1054 case TNY_FOLDER_TYPE_TRASH:
1055 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1058 &trash_pixbuf_close);
1060 case TNY_FOLDER_TYPE_DRAFTS:
1061 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1064 &draft_pixbuf_close);
1066 case TNY_FOLDER_TYPE_ARCHIVE:
1067 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1072 case TNY_FOLDER_TYPE_NORMAL:
1074 /* Memory card folders could have an special icon */
1075 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1076 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1081 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1083 &normal_pixbuf_open,
1084 &normal_pixbuf_close);
1093 free_pixbufs (ThreePixbufs *pixbufs)
1095 if (pixbufs->pixbuf)
1096 g_object_unref (pixbufs->pixbuf);
1097 if (pixbufs->pixbuf_open)
1098 g_object_unref (pixbufs->pixbuf_open);
1099 if (pixbufs->pixbuf_close)
1100 g_object_unref (pixbufs->pixbuf_close);
1101 g_slice_free (ThreePixbufs, pixbufs);
1105 icon_cell_data (GtkTreeViewColumn *column,
1106 GtkCellRenderer *renderer,
1107 GtkTreeModel *tree_model,
1111 GObject *rendobj = NULL, *instance = NULL;
1112 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1113 gboolean has_children;
1114 ThreePixbufs *pixbufs;
1116 rendobj = (GObject *) renderer;
1118 gtk_tree_model_get (tree_model, iter,
1120 INSTANCE_COLUMN, &instance,
1126 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1127 pixbufs = get_folder_icons (type, instance);
1128 g_object_unref (instance);
1131 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1134 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1135 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1138 free_pixbufs (pixbufs);
1142 add_columns (GtkWidget *treeview)
1144 GtkTreeViewColumn *column;
1145 GtkCellRenderer *renderer;
1146 GtkTreeSelection *sel;
1147 ModestFolderViewPrivate *priv;
1149 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1152 column = gtk_tree_view_column_new ();
1154 /* Set icon and text render function */
1155 renderer = gtk_cell_renderer_pixbuf_new();
1156 #ifdef MODEST_TOOLKIT_HILDON2
1157 g_object_set (renderer,
1158 "xpad", MODEST_MARGIN_DEFAULT,
1159 "ypad", MODEST_MARGIN_DEFAULT,
1162 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1163 gtk_tree_view_column_set_cell_data_func(column, renderer,
1164 icon_cell_data, treeview, NULL);
1166 renderer = gtk_cell_renderer_text_new();
1167 g_object_set (renderer,
1168 #ifdef MODEST_TOOLKIT_HILDON2
1169 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1170 "ypad", MODEST_MARGIN_DEFAULT,
1171 "xpad", MODEST_MARGIN_DEFAULT,
1173 "ellipsize", PANGO_ELLIPSIZE_END,
1175 "ellipsize-set", TRUE, NULL);
1176 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1177 gtk_tree_view_column_set_cell_data_func(column, renderer,
1178 text_cell_data, treeview, NULL);
1180 priv->messages_renderer = gtk_cell_renderer_text_new ();
1181 g_object_set (priv->messages_renderer,
1182 #ifdef MODEST_TOOLKIT_HILDON2
1184 "ypad", MODEST_MARGIN_DEFAULT,
1185 "xpad", MODEST_MARGIN_DOUBLE,
1187 "scale", PANGO_SCALE_X_SMALL,
1190 "alignment", PANGO_ALIGN_RIGHT,
1194 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1195 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1196 messages_cell_data, treeview, NULL);
1198 /* Set selection mode */
1199 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1200 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1202 /* Set treeview appearance */
1203 gtk_tree_view_column_set_spacing (column, 2);
1204 gtk_tree_view_column_set_resizable (column, TRUE);
1205 gtk_tree_view_column_set_fixed_width (column, TRUE);
1206 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1207 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1210 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1214 modest_folder_view_init (ModestFolderView *obj)
1216 ModestFolderViewPrivate *priv;
1219 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1221 priv->timer_expander = 0;
1222 priv->account_store = NULL;
1224 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1225 priv->cur_folder_store = NULL;
1226 priv->visible_account_id = NULL;
1227 priv->mailbox = NULL;
1228 priv->folder_to_select = NULL;
1229 priv->outbox_deleted_handler = 0;
1230 priv->reexpand = TRUE;
1232 /* Initialize the local account name */
1233 conf = modest_runtime_get_conf();
1234 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1236 /* Init email clipboard */
1237 priv->clipboard = modest_runtime_get_email_clipboard ();
1238 priv->hidding_ids = NULL;
1239 priv->n_selected = 0;
1240 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1241 priv->reselect = FALSE;
1242 priv->show_non_move = TRUE;
1243 priv->list_to_move = NULL;
1244 priv->show_message_count = TRUE;
1246 /* Build treeview */
1247 add_columns (GTK_WIDGET (obj));
1249 /* Setup drag and drop */
1250 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1252 /* Connect signals */
1253 g_signal_connect (G_OBJECT (obj),
1255 G_CALLBACK (on_key_pressed), NULL);
1257 priv->display_name_changed_signal =
1258 g_signal_connect (modest_runtime_get_account_mgr (),
1259 "display_name_changed",
1260 G_CALLBACK (on_display_name_changed),
1264 * Track changes in the local account name (in the device it
1265 * will be the device name)
1267 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1269 G_CALLBACK(on_configuration_key_changed),
1273 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1279 tny_account_store_view_init (gpointer g, gpointer iface_data)
1281 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1283 klass->set_account_store = modest_folder_view_set_account_store;
1287 modest_folder_view_finalize (GObject *obj)
1289 ModestFolderViewPrivate *priv;
1290 GtkTreeSelection *sel;
1291 TnyAccount *local_account;
1293 g_return_if_fail (obj);
1295 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1297 if (priv->timer_expander != 0) {
1298 g_source_remove (priv->timer_expander);
1299 priv->timer_expander = 0;
1302 local_account = (TnyAccount *)
1303 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1304 if (local_account) {
1305 if (g_signal_handler_is_connected (local_account,
1306 priv->outbox_deleted_handler))
1307 g_signal_handler_disconnect (local_account,
1308 priv->outbox_deleted_handler);
1309 g_object_unref (local_account);
1312 if (priv->account_store) {
1313 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1314 priv->account_inserted_signal);
1315 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1316 priv->account_removed_signal);
1317 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1318 priv->account_changed_signal);
1319 g_object_unref (G_OBJECT(priv->account_store));
1320 priv->account_store = NULL;
1323 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1324 priv->display_name_changed_signal)) {
1325 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1326 priv->display_name_changed_signal);
1327 priv->display_name_changed_signal = 0;
1331 g_object_unref (G_OBJECT (priv->query));
1335 if (priv->folder_to_select) {
1336 g_object_unref (G_OBJECT(priv->folder_to_select));
1337 priv->folder_to_select = NULL;
1340 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1342 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1344 g_free (priv->local_account_name);
1345 g_free (priv->visible_account_id);
1346 g_free (priv->mailbox);
1348 if (priv->conf_key_signal) {
1349 g_signal_handler_disconnect (modest_runtime_get_conf (),
1350 priv->conf_key_signal);
1351 priv->conf_key_signal = 0;
1354 if (priv->cur_folder_store) {
1355 g_object_unref (priv->cur_folder_store);
1356 priv->cur_folder_store = NULL;
1359 if (priv->list_to_move) {
1360 g_object_unref (priv->list_to_move);
1361 priv->list_to_move = NULL;
1364 /* Clear hidding array created by cut operation */
1365 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1367 G_OBJECT_CLASS(parent_class)->finalize (obj);
1372 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1374 ModestFolderViewPrivate *priv;
1377 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1378 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1380 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1381 device = tny_account_store_get_device (account_store);
1383 if (G_UNLIKELY (priv->account_store)) {
1385 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1386 priv->account_inserted_signal))
1387 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1388 priv->account_inserted_signal);
1389 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1390 priv->account_removed_signal))
1391 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1392 priv->account_removed_signal);
1393 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1394 priv->account_changed_signal))
1395 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1396 priv->account_changed_signal);
1397 g_object_unref (G_OBJECT (priv->account_store));
1400 priv->account_store = g_object_ref (G_OBJECT (account_store));
1402 priv->account_removed_signal =
1403 g_signal_connect (G_OBJECT(account_store), "account_removed",
1404 G_CALLBACK (on_account_removed), self);
1406 priv->account_inserted_signal =
1407 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1408 G_CALLBACK (on_account_inserted), self);
1410 priv->account_changed_signal =
1411 g_signal_connect (G_OBJECT(account_store), "account_changed",
1412 G_CALLBACK (on_account_changed), self);
1414 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1415 priv->reselect = FALSE;
1416 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1418 g_object_unref (G_OBJECT (device));
1422 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1425 ModestFolderView *self;
1426 GtkTreeModel *model, *filter_model;
1429 self = MODEST_FOLDER_VIEW (user_data);
1431 if (!get_inner_models (self, &filter_model, NULL, &model))
1434 /* Remove outbox from model */
1435 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1436 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1437 g_object_unref (outbox);
1440 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1444 on_account_inserted (TnyAccountStore *account_store,
1445 TnyAccount *account,
1448 ModestFolderViewPrivate *priv;
1449 GtkTreeModel *model, *filter_model;
1451 /* Ignore transport account insertions, we're not showing them
1452 in the folder view */
1453 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1456 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1459 /* If we're adding a new account, and there is no previous
1460 one, we need to select the visible server account */
1461 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1462 !priv->visible_account_id)
1463 modest_widget_memory_restore (modest_runtime_get_conf(),
1464 G_OBJECT (user_data),
1465 MODEST_CONF_FOLDER_VIEW_KEY);
1469 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1470 &filter_model, NULL, &model))
1473 /* Insert the account in the model */
1474 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1476 /* When the model is a list store (plain representation) the
1477 outbox is not a child of any account so we have to manually
1478 delete it because removing the local folders account won't
1479 delete it (because tny_folder_get_account() is not defined
1480 for a merge folder */
1481 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1482 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1484 priv->outbox_deleted_handler =
1485 g_signal_connect (account,
1487 G_CALLBACK (on_outbox_deleted_cb),
1491 /* Refilter the model */
1492 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1497 same_account_selected (ModestFolderView *self,
1498 TnyAccount *account)
1500 ModestFolderViewPrivate *priv;
1501 gboolean same_account = FALSE;
1503 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1505 if (priv->cur_folder_store) {
1506 TnyAccount *selected_folder_account = NULL;
1508 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1509 selected_folder_account =
1510 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1512 selected_folder_account =
1513 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1516 if (selected_folder_account == account)
1517 same_account = TRUE;
1519 g_object_unref (selected_folder_account);
1521 return same_account;
1526 * Selects the first inbox or the local account in an idle
1529 on_idle_select_first_inbox_or_local (gpointer user_data)
1531 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1533 gdk_threads_enter ();
1534 modest_folder_view_select_first_inbox_or_local (self);
1535 gdk_threads_leave ();
1541 on_account_changed (TnyAccountStore *account_store,
1542 TnyAccount *tny_account,
1545 ModestFolderView *self;
1546 ModestFolderViewPrivate *priv;
1547 GtkTreeModel *model, *filter_model;
1548 GtkTreeSelection *sel;
1549 gboolean same_account;
1551 /* Ignore transport account insertions, we're not showing them
1552 in the folder view */
1553 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1556 self = MODEST_FOLDER_VIEW (user_data);
1557 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1559 /* Get the inner model */
1560 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1561 &filter_model, NULL, &model))
1564 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1566 /* Invalidate the cur_folder_store only if the selected folder
1567 belongs to the account that is being removed */
1568 same_account = same_account_selected (self, tny_account);
1570 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1571 gtk_tree_selection_unselect_all (sel);
1574 /* Remove the account from the model */
1575 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1577 /* Insert the account in the model */
1578 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1580 /* Refilter the model */
1581 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1583 /* Select the first INBOX if the currently selected folder
1584 belongs to the account that is being deleted */
1585 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1586 g_idle_add (on_idle_select_first_inbox_or_local, self);
1590 on_account_removed (TnyAccountStore *account_store,
1591 TnyAccount *account,
1594 ModestFolderView *self = NULL;
1595 ModestFolderViewPrivate *priv;
1596 GtkTreeModel *model, *filter_model;
1597 GtkTreeSelection *sel = NULL;
1598 gboolean same_account = FALSE;
1600 /* Ignore transport account removals, we're not showing them
1601 in the folder view */
1602 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1605 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1606 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1610 self = MODEST_FOLDER_VIEW (user_data);
1611 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1613 /* Invalidate the cur_folder_store only if the selected folder
1614 belongs to the account that is being removed */
1615 same_account = same_account_selected (self, account);
1617 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1618 gtk_tree_selection_unselect_all (sel);
1621 /* Invalidate row to select only if the folder to select
1622 belongs to the account that is being removed*/
1623 if (priv->folder_to_select) {
1624 TnyAccount *folder_to_select_account = NULL;
1626 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1627 if (folder_to_select_account == account) {
1628 modest_folder_view_disable_next_folder_selection (self);
1629 g_object_unref (priv->folder_to_select);
1630 priv->folder_to_select = NULL;
1632 g_object_unref (folder_to_select_account);
1635 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1636 &filter_model, NULL, &model))
1639 /* Disconnect the signal handler */
1640 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1641 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1642 if (g_signal_handler_is_connected (account,
1643 priv->outbox_deleted_handler))
1644 g_signal_handler_disconnect (account,
1645 priv->outbox_deleted_handler);
1648 /* Remove the account from the model */
1649 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1651 /* If the removed account is the currently viewed one then
1652 clear the configuration value. The new visible account will be the default account */
1653 if (priv->visible_account_id &&
1654 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1656 /* Clear the current visible account_id */
1657 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1658 modest_folder_view_set_mailbox (self, NULL);
1660 /* Call the restore method, this will set the new visible account */
1661 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1662 MODEST_CONF_FOLDER_VIEW_KEY);
1665 /* Refilter the model */
1666 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1668 /* Select the first INBOX if the currently selected folder
1669 belongs to the account that is being deleted */
1671 g_idle_add (on_idle_select_first_inbox_or_local, self);
1675 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1677 GtkTreeViewColumn *col;
1679 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1681 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1683 g_printerr ("modest: failed get column for title\n");
1687 gtk_tree_view_column_set_title (col, title);
1688 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1693 modest_folder_view_on_map (ModestFolderView *self,
1694 GdkEventExpose *event,
1697 ModestFolderViewPrivate *priv;
1699 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1701 /* This won't happen often */
1702 if (G_UNLIKELY (priv->reselect)) {
1703 /* Select the first inbox or the local account if not found */
1705 /* TODO: this could cause a lock at startup, so we
1706 comment it for the moment. We know that this will
1707 be a bug, because the INBOX is not selected, but we
1708 need to rewrite some parts of Modest to avoid the
1709 deathlock situation */
1710 /* TODO: check if this is still the case */
1711 priv->reselect = FALSE;
1712 modest_folder_view_select_first_inbox_or_local (self);
1713 /* Notify the display name observers */
1714 g_signal_emit (G_OBJECT(self),
1715 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1719 if (priv->reexpand) {
1720 expand_root_items (self);
1721 priv->reexpand = FALSE;
1728 modest_folder_view_new (TnyFolderStoreQuery *query)
1731 ModestFolderViewPrivate *priv;
1732 GtkTreeSelection *sel;
1734 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1735 #ifdef MODEST_TOOLKIT_HILDON2
1736 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1739 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1742 priv->query = g_object_ref (query);
1744 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1745 priv->changed_signal = g_signal_connect (sel, "changed",
1746 G_CALLBACK (on_selection_changed), self);
1748 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1750 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1752 return GTK_WIDGET(self);
1755 /* this feels dirty; any other way to expand all the root items? */
1757 expand_root_items (ModestFolderView *self)
1760 GtkTreeModel *model;
1763 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1764 path = gtk_tree_path_new_first ();
1766 /* all folders should have child items, so.. */
1768 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1769 gtk_tree_path_next (path);
1770 } while (gtk_tree_model_get_iter (model, &iter, path));
1772 gtk_tree_path_free (path);
1776 is_parent_of (TnyFolder *a, TnyFolder *b)
1779 gboolean retval = FALSE;
1781 a_id = tny_folder_get_id (a);
1783 gchar *string_to_match;
1786 string_to_match = g_strconcat (a_id, "/", NULL);
1787 b_id = tny_folder_get_id (b);
1788 retval = g_str_has_prefix (b_id, string_to_match);
1789 g_free (string_to_match);
1795 typedef struct _ForeachFolderInfo {
1798 } ForeachFolderInfo;
1801 foreach_folder_with_id (GtkTreeModel *model,
1806 ForeachFolderInfo *info;
1809 info = (ForeachFolderInfo *) data;
1810 gtk_tree_model_get (model, iter,
1811 INSTANCE_COLUMN, &instance,
1814 if (TNY_IS_FOLDER (instance)) {
1817 id = tny_folder_get_id (TNY_FOLDER (instance));
1819 collate = g_utf8_collate_key (id, -1);
1820 info->found = !strcmp (info->needle, collate);
1826 g_object_unref (instance);
1834 has_folder_with_id (ModestFolderView *self, const gchar *id)
1836 GtkTreeModel *model;
1837 ForeachFolderInfo info = {NULL, FALSE};
1839 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1840 info.needle = g_utf8_collate_key (id, -1);
1842 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1843 g_free (info.needle);
1849 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1852 gboolean retval = FALSE;
1854 a_id = tny_folder_get_id (a);
1857 b_id = tny_folder_get_id (b);
1860 const gchar *last_bar;
1861 gchar *string_to_match;
1862 last_bar = g_strrstr (b_id, "/");
1867 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1868 retval = has_folder_with_id (self, string_to_match);
1869 g_free (string_to_match);
1877 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1879 ModestFolderViewPrivate *priv;
1880 TnyIterator *iterator;
1881 gboolean retval = TRUE;
1883 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1884 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1886 for (iterator = tny_list_create_iterator (priv->list_to_move);
1887 retval && !tny_iterator_is_done (iterator);
1888 tny_iterator_next (iterator)) {
1890 instance = tny_iterator_get_current (iterator);
1891 if (instance == (GObject *) folder) {
1893 } else if (TNY_IS_FOLDER (instance)) {
1894 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1896 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1899 g_object_unref (instance);
1901 g_object_unref (iterator);
1908 * We use this function to implement the
1909 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1910 * account in this case, and the local folders.
1913 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1915 ModestFolderViewPrivate *priv;
1916 gboolean retval = TRUE;
1917 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1918 GObject *instance = NULL;
1919 const gchar *id = NULL;
1921 gboolean found = FALSE;
1922 gboolean cleared = FALSE;
1923 ModestTnyFolderRules rules = 0;
1926 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1927 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1929 gtk_tree_model_get (model, iter,
1930 NAME_COLUMN, &fname,
1932 INSTANCE_COLUMN, &instance,
1935 /* Do not show if there is no instance, this could indeed
1936 happen when the model is being modified while it's being
1937 drawn. This could occur for example when moving folders
1944 if (TNY_IS_ACCOUNT (instance)) {
1945 TnyAccount *acc = TNY_ACCOUNT (instance);
1946 const gchar *account_id = tny_account_get_id (acc);
1948 /* If it isn't a special folder,
1949 * don't show it unless it is the visible account: */
1950 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1951 !modest_tny_account_is_virtual_local_folders (acc) &&
1952 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1954 /* Show only the visible account id */
1955 if (priv->visible_account_id) {
1956 if (strcmp (account_id, priv->visible_account_id))
1963 /* Never show these to the user. They are merged into one folder
1964 * in the local-folders account instead: */
1965 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1968 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1969 /* Only show special folders for current account if needed */
1970 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1971 TnyAccount *account;
1973 account = tny_folder_get_account (TNY_FOLDER (instance));
1975 if (TNY_IS_ACCOUNT (account)) {
1976 const gchar *account_id = tny_account_get_id (account);
1978 if (!modest_tny_account_is_virtual_local_folders (account) &&
1979 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1980 /* Show only the visible account id */
1981 if (priv->visible_account_id) {
1982 if (strcmp (account_id, priv->visible_account_id)) {
1984 } else if (priv->mailbox) {
1985 /* Filter mailboxes */
1986 if (!g_str_has_prefix (fname, priv->mailbox)) {
1988 } else if (!strcmp (fname, priv->mailbox)) {
1989 /* Hide mailbox parent */
1995 g_object_unref (account);
2002 /* Check hiding (if necessary) */
2003 cleared = modest_email_clipboard_cleared (priv->clipboard);
2004 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2005 id = tny_folder_get_id (TNY_FOLDER(instance));
2006 if (priv->hidding_ids != NULL)
2007 for (i=0; i < priv->n_selected && !found; i++)
2008 if (priv->hidding_ids[i] != NULL && id != NULL)
2009 found = (!strcmp (priv->hidding_ids[i], id));
2014 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2015 folder as no message can be move there according to UI specs */
2016 if (retval && !priv->show_non_move) {
2017 if (priv->list_to_move &&
2018 tny_list_get_length (priv->list_to_move) > 0 &&
2019 TNY_IS_FOLDER (instance)) {
2020 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2022 if (retval && TNY_IS_FOLDER (instance) &&
2023 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2025 case TNY_FOLDER_TYPE_OUTBOX:
2026 case TNY_FOLDER_TYPE_SENT:
2027 case TNY_FOLDER_TYPE_DRAFTS:
2030 case TNY_FOLDER_TYPE_UNKNOWN:
2031 case TNY_FOLDER_TYPE_NORMAL:
2032 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2033 if (type == TNY_FOLDER_TYPE_INVALID)
2034 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2036 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2037 type == TNY_FOLDER_TYPE_SENT
2038 || type == TNY_FOLDER_TYPE_DRAFTS)
2045 if (retval && TNY_IS_ACCOUNT (instance) &&
2046 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2047 ModestProtocolType protocol_type;
2049 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2050 retval = !modest_protocol_registry_protocol_type_has_tag
2051 (modest_runtime_get_protocol_registry (),
2053 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2057 /* apply special filters */
2058 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2059 if (TNY_IS_ACCOUNT (instance))
2063 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2064 if (TNY_IS_FOLDER (instance))
2068 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2069 if (TNY_IS_ACCOUNT (instance)) {
2070 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2072 } else if (TNY_IS_FOLDER (instance)) {
2073 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2078 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2079 if (TNY_IS_ACCOUNT (instance)) {
2080 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2082 } else if (TNY_IS_FOLDER (instance)) {
2083 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2088 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2089 /* A mailbox is a fake folder with an @ in the middle of the name */
2090 if (!TNY_IS_FOLDER (instance) ||
2091 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2094 const gchar *folder_name;
2095 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2096 if (!folder_name || strchr (folder_name, '@') == NULL)
2102 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2103 if (TNY_IS_FOLDER (instance)) {
2104 /* Check folder rules */
2105 ModestTnyFolderRules rules;
2107 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2108 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2109 } else if (TNY_IS_ACCOUNT (instance)) {
2110 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2118 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2119 if (TNY_IS_FOLDER (instance)) {
2120 TnyFolderType guess_type;
2122 if (TNY_FOLDER_TYPE_NORMAL) {
2123 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2129 case TNY_FOLDER_TYPE_OUTBOX:
2130 case TNY_FOLDER_TYPE_SENT:
2131 case TNY_FOLDER_TYPE_DRAFTS:
2132 case TNY_FOLDER_TYPE_ARCHIVE:
2133 case TNY_FOLDER_TYPE_INBOX:
2136 case TNY_FOLDER_TYPE_UNKNOWN:
2137 case TNY_FOLDER_TYPE_NORMAL:
2143 } else if (TNY_IS_ACCOUNT (instance)) {
2148 if (retval && TNY_IS_FOLDER (instance)) {
2149 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2152 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2153 if (TNY_IS_FOLDER (instance)) {
2154 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2155 } else if (TNY_IS_ACCOUNT (instance)) {
2160 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2161 if (TNY_IS_FOLDER (instance)) {
2162 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2163 } else if (TNY_IS_ACCOUNT (instance)) {
2168 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2169 if (TNY_IS_FOLDER (instance)) {
2170 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2171 } else if (TNY_IS_ACCOUNT (instance)) {
2177 g_object_unref (instance);
2185 modest_folder_view_update_model (ModestFolderView *self,
2186 TnyAccountStore *account_store)
2188 ModestFolderViewPrivate *priv;
2189 GtkTreeModel *model /* , *old_model */;
2190 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2192 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2193 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2196 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2198 /* Notify that there is no folder selected */
2199 g_signal_emit (G_OBJECT(self),
2200 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2202 if (priv->cur_folder_store) {
2203 g_object_unref (priv->cur_folder_store);
2204 priv->cur_folder_store = NULL;
2207 /* FIXME: the local accounts are not shown when the query
2208 selects only the subscribed folders */
2209 #ifdef MODEST_TOOLKIT_HILDON2
2210 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2211 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2212 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2213 MODEST_FOLDER_PATH_SEPARATOR);
2215 model = tny_gtk_folder_store_tree_model_new (NULL);
2218 /* When the model is a list store (plain representation) the
2219 outbox is not a child of any account so we have to manually
2220 delete it because removing the local folders account won't
2221 delete it (because tny_folder_get_account() is not defined
2222 for a merge folder */
2223 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2224 TnyAccount *account;
2225 ModestTnyAccountStore *acc_store;
2227 acc_store = modest_runtime_get_account_store ();
2228 account = modest_tny_account_store_get_local_folders_account (acc_store);
2230 if (g_signal_handler_is_connected (account,
2231 priv->outbox_deleted_handler))
2232 g_signal_handler_disconnect (account,
2233 priv->outbox_deleted_handler);
2235 priv->outbox_deleted_handler =
2236 g_signal_connect (account,
2238 G_CALLBACK (on_outbox_deleted_cb),
2240 g_object_unref (account);
2243 /* Get the accounts: */
2244 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2246 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2248 sortable = gtk_tree_model_sort_new_with_model (model);
2249 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2251 GTK_SORT_ASCENDING);
2252 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2254 cmp_rows, NULL, NULL);
2256 /* Create filter model */
2257 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2258 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2264 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2265 #ifndef MODEST_TOOLKIT_HILDON2
2266 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2267 (GCallback) on_row_inserted_maybe_select_folder, self);
2270 g_object_unref (model);
2271 g_object_unref (filter_model);
2272 g_object_unref (sortable);
2274 /* Force a reselection of the INBOX next time the widget is shown */
2275 priv->reselect = TRUE;
2282 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2284 GtkTreeModel *model = NULL;
2285 TnyFolderStore *folder = NULL;
2287 ModestFolderView *tree_view = NULL;
2288 ModestFolderViewPrivate *priv = NULL;
2289 gboolean selected = FALSE;
2291 g_return_if_fail (sel);
2292 g_return_if_fail (user_data);
2294 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2296 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2298 tree_view = MODEST_FOLDER_VIEW (user_data);
2301 gtk_tree_model_get (model, &iter,
2302 INSTANCE_COLUMN, &folder,
2305 /* If the folder is the same do not notify */
2306 if (folder && priv->cur_folder_store == folder) {
2307 g_object_unref (folder);
2312 /* Current folder was unselected */
2313 if (priv->cur_folder_store) {
2314 /* We must do this firstly because a libtinymail-camel
2315 implementation detail. If we issue the signal
2316 before doing the sync_async, then that signal could
2317 cause (and it actually does it) a free of the
2318 summary of the folder (because the main window will
2319 clear the headers view */
2320 if (TNY_IS_FOLDER(priv->cur_folder_store))
2321 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2322 FALSE, NULL, NULL, NULL);
2324 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2325 priv->cur_folder_store, FALSE);
2327 g_object_unref (priv->cur_folder_store);
2328 priv->cur_folder_store = NULL;
2331 /* New current references */
2332 priv->cur_folder_store = folder;
2334 /* New folder has been selected. Do not notify if there is
2335 nothing new selected */
2337 g_signal_emit (G_OBJECT(tree_view),
2338 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2339 0, priv->cur_folder_store, TRUE);
2344 on_row_activated (GtkTreeView *treeview,
2345 GtkTreePath *treepath,
2346 GtkTreeViewColumn *column,
2349 GtkTreeModel *model = NULL;
2350 TnyFolderStore *folder = NULL;
2352 ModestFolderView *self = NULL;
2353 ModestFolderViewPrivate *priv = NULL;
2355 g_return_if_fail (treeview);
2356 g_return_if_fail (user_data);
2358 self = MODEST_FOLDER_VIEW (user_data);
2359 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2361 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2363 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2366 gtk_tree_model_get (model, &iter,
2367 INSTANCE_COLUMN, &folder,
2370 g_signal_emit (G_OBJECT(self),
2371 signals[FOLDER_ACTIVATED_SIGNAL],
2374 #ifdef MODEST_TOOLKIT_HILDON2
2375 HildonUIMode ui_mode;
2376 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2377 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2378 if (priv->cur_folder_store)
2379 g_object_unref (priv->cur_folder_store);
2380 priv->cur_folder_store = g_object_ref (folder);
2384 g_object_unref (folder);
2388 modest_folder_view_get_selected (ModestFolderView *self)
2390 ModestFolderViewPrivate *priv;
2392 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2394 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2395 if (priv->cur_folder_store)
2396 g_object_ref (priv->cur_folder_store);
2398 return priv->cur_folder_store;
2402 get_cmp_rows_type_pos (GObject *folder)
2404 /* Remote accounts -> Local account -> MMC account .*/
2407 if (TNY_IS_ACCOUNT (folder) &&
2408 modest_tny_account_is_virtual_local_folders (
2409 TNY_ACCOUNT (folder))) {
2411 } else if (TNY_IS_ACCOUNT (folder)) {
2412 TnyAccount *account = TNY_ACCOUNT (folder);
2413 const gchar *account_id = tny_account_get_id (account);
2414 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2420 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2421 return -1; /* Should never happen */
2426 inbox_is_special (TnyFolderStore *folder_store)
2428 gboolean is_special = TRUE;
2430 if (TNY_IS_FOLDER (folder_store)) {
2434 gchar *last_inbox_bar;
2436 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2437 downcase = g_utf8_strdown (id, -1);
2438 last_bar = g_strrstr (downcase, "/");
2440 last_inbox_bar = g_strrstr (downcase, "inbox/");
2441 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2452 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2454 TnyAccount *account;
2455 gboolean is_special;
2456 /* Inbox, Outbox, Drafts, Sent, User */
2459 if (!TNY_IS_FOLDER (folder_store))
2462 case TNY_FOLDER_TYPE_INBOX:
2464 account = tny_folder_get_account (folder_store);
2465 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2467 /* In inbox case we need to know if the inbox is really the top
2468 * inbox of the account, or if it's a submailbox inbox. To do
2469 * this we'll apply an heuristic rule: Find last "/" and check
2470 * if it's preceeded by another Inbox */
2471 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2472 g_object_unref (account);
2473 return is_special?0:4;
2476 case TNY_FOLDER_TYPE_OUTBOX:
2477 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2479 case TNY_FOLDER_TYPE_DRAFTS:
2481 account = tny_folder_get_account (folder_store);
2482 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2483 g_object_unref (account);
2484 return is_special?1:4;
2487 case TNY_FOLDER_TYPE_SENT:
2489 account = tny_folder_get_account (folder_store);
2490 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2491 g_object_unref (account);
2492 return is_special?3:4;
2501 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2503 const gchar *a1_name, *a2_name;
2505 a1_name = tny_account_get_name (a1);
2506 a2_name = tny_account_get_name (a2);
2508 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2512 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2514 TnyAccount *a1 = NULL, *a2 = NULL;
2517 if (TNY_IS_ACCOUNT (s1)) {
2518 a1 = TNY_ACCOUNT (g_object_ref (s1));
2519 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2520 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2523 if (TNY_IS_ACCOUNT (s2)) {
2524 a2 = TNY_ACCOUNT (g_object_ref (s2));
2525 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2526 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2543 /* First we sort with the type of account */
2544 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2548 cmp = compare_account_names (a1, a2);
2552 g_object_unref (a1);
2554 g_object_unref (a2);
2560 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2562 gint is_account1, is_account2;
2564 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2565 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2567 return is_account2 - is_account1;
2571 * This function orders the mail accounts according to these rules:
2572 * 1st - remote accounts
2573 * 2nd - local account
2577 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2581 gchar *name1 = NULL;
2582 gchar *name2 = NULL;
2583 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2584 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2585 GObject *folder1 = NULL;
2586 GObject *folder2 = NULL;
2588 gtk_tree_model_get (tree_model, iter1,
2589 NAME_COLUMN, &name1,
2591 INSTANCE_COLUMN, &folder1,
2593 gtk_tree_model_get (tree_model, iter2,
2594 NAME_COLUMN, &name2,
2595 TYPE_COLUMN, &type2,
2596 INSTANCE_COLUMN, &folder2,
2599 /* Return if we get no folder. This could happen when folder
2600 operations are happening. The model is updated after the
2601 folder copy/move actually occurs, so there could be
2602 situations where the model to be drawn is not correct */
2603 if (!folder1 || !folder2)
2606 /* Sort by type. First the special folders, then the archives */
2607 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2611 /* Now we sort using the account of each folder */
2612 if (TNY_IS_FOLDER_STORE (folder1) &&
2613 TNY_IS_FOLDER_STORE (folder2)) {
2614 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2618 /* Each group is preceeded by its account */
2619 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2624 /* Pure sort by name */
2625 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2628 g_object_unref(G_OBJECT(folder1));
2630 g_object_unref(G_OBJECT(folder2));
2638 /*****************************************************************************/
2639 /* DRAG and DROP stuff */
2640 /*****************************************************************************/
2642 * This function fills the #GtkSelectionData with the row and the
2643 * model that has been dragged. It's called when this widget is a
2644 * source for dnd after the event drop happened
2647 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2648 guint info, guint time, gpointer data)
2650 GtkTreeSelection *selection;
2651 GtkTreeModel *model;
2653 GtkTreePath *source_row;
2655 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2656 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2658 source_row = gtk_tree_model_get_path (model, &iter);
2659 gtk_tree_set_row_drag_data (selection_data,
2663 gtk_tree_path_free (source_row);
2667 typedef struct _DndHelper {
2668 ModestFolderView *folder_view;
2669 gboolean delete_source;
2670 GtkTreePath *source_row;
2674 dnd_helper_destroyer (DndHelper *helper)
2676 /* Free the helper */
2677 gtk_tree_path_free (helper->source_row);
2678 g_slice_free (DndHelper, helper);
2682 xfer_folder_cb (ModestMailOperation *mail_op,
2683 TnyFolder *new_folder,
2687 /* Select the folder */
2688 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2694 /* get the folder for the row the treepath refers to. */
2695 /* folder must be unref'd */
2696 static TnyFolderStore *
2697 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2700 TnyFolderStore *folder = NULL;
2702 if (gtk_tree_model_get_iter (model,&iter, path))
2703 gtk_tree_model_get (model, &iter,
2704 INSTANCE_COLUMN, &folder,
2711 * This function is used by drag_data_received_cb to manage drag and
2712 * drop of a header, i.e, and drag from the header view to the folder
2716 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2717 GtkTreeModel *dest_model,
2718 GtkTreePath *dest_row,
2719 GtkSelectionData *selection_data)
2721 TnyList *headers = NULL;
2722 TnyFolder *folder = NULL, *src_folder = NULL;
2723 TnyFolderType folder_type;
2724 GtkTreeIter source_iter, dest_iter;
2725 ModestWindowMgr *mgr = NULL;
2726 ModestWindow *main_win = NULL;
2727 gchar **uris, **tmp;
2729 /* Build the list of headers */
2730 mgr = modest_runtime_get_window_mgr ();
2731 headers = tny_simple_list_new ();
2732 uris = modest_dnd_selection_data_get_paths (selection_data);
2735 while (*tmp != NULL) {
2738 gboolean first = TRUE;
2741 path = gtk_tree_path_new_from_string (*tmp);
2742 gtk_tree_model_get_iter (source_model, &source_iter, path);
2743 gtk_tree_model_get (source_model, &source_iter,
2744 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2747 /* Do not enable d&d of headers already opened */
2748 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2749 tny_list_append (headers, G_OBJECT (header));
2751 if (G_UNLIKELY (first)) {
2752 src_folder = tny_header_get_folder (header);
2756 /* Free and go on */
2757 gtk_tree_path_free (path);
2758 g_object_unref (header);
2763 /* This could happen ig we perform a d&d very quickly over the
2764 same row that row could dissapear because message is
2766 if (!TNY_IS_FOLDER (src_folder))
2769 /* Get the target folder */
2770 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2771 gtk_tree_model_get (dest_model, &dest_iter,
2775 if (!folder || !TNY_IS_FOLDER(folder)) {
2776 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2780 folder_type = modest_tny_folder_guess_folder_type (folder);
2781 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2782 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2783 goto cleanup; /* cannot move messages there */
2786 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2787 /* g_warning ("folder not writable"); */
2788 goto cleanup; /* verboten! */
2791 /* Ask for confirmation to move */
2792 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2794 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2798 /* Transfer messages */
2799 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2804 if (G_IS_OBJECT (src_folder))
2805 g_object_unref (src_folder);
2806 if (G_IS_OBJECT(folder))
2807 g_object_unref (G_OBJECT (folder));
2808 if (G_IS_OBJECT(headers))
2809 g_object_unref (headers);
2813 TnyFolderStore *src_folder;
2814 TnyFolderStore *dst_folder;
2815 ModestFolderView *folder_view;
2820 dnd_folder_info_destroyer (DndFolderInfo *info)
2822 if (info->src_folder)
2823 g_object_unref (info->src_folder);
2824 if (info->dst_folder)
2825 g_object_unref (info->dst_folder);
2826 g_slice_free (DndFolderInfo, info);
2830 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2831 GtkWindow *parent_window,
2832 TnyAccount *account)
2835 modest_ui_actions_on_account_connection_error (parent_window, account);
2837 /* Free the helper & info */
2838 dnd_helper_destroyer (info->helper);
2839 dnd_folder_info_destroyer (info);
2843 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2845 GtkWindow *parent_window,
2846 TnyAccount *account,
2849 DndFolderInfo *info = NULL;
2850 ModestMailOperation *mail_op;
2852 info = (DndFolderInfo *) user_data;
2854 if (err || canceled) {
2855 dnd_on_connection_failed_destroyer (info, parent_window, account);
2859 /* Do the mail operation */
2860 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2861 modest_ui_actions_move_folder_error_handler,
2862 info->src_folder, NULL);
2864 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2867 /* Transfer the folder */
2868 modest_mail_operation_xfer_folder (mail_op,
2869 TNY_FOLDER (info->src_folder),
2871 info->helper->delete_source,
2873 info->helper->folder_view);
2876 g_object_unref (G_OBJECT (mail_op));
2877 dnd_helper_destroyer (info->helper);
2878 dnd_folder_info_destroyer (info);
2883 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2885 GtkWindow *parent_window,
2886 TnyAccount *account,
2889 DndFolderInfo *info = NULL;
2891 info = (DndFolderInfo *) user_data;
2893 if (err || canceled) {
2894 dnd_on_connection_failed_destroyer (info, parent_window, account);
2898 /* Connect to source folder and perform the copy/move */
2899 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2901 drag_and_drop_from_folder_view_src_folder_performer,
2906 * This function is used by drag_data_received_cb to manage drag and
2907 * drop of a folder, i.e, and drag from the folder view to the same
2911 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2912 GtkTreeModel *dest_model,
2913 GtkTreePath *dest_row,
2914 GtkSelectionData *selection_data,
2917 GtkTreeIter dest_iter, iter;
2918 TnyFolderStore *dest_folder = NULL;
2919 TnyFolderStore *folder = NULL;
2920 gboolean forbidden = FALSE;
2922 DndFolderInfo *info = NULL;
2924 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2926 g_warning ("%s: BUG: no main window", __FUNCTION__);
2927 dnd_helper_destroyer (helper);
2932 /* check the folder rules for the destination */
2933 folder = tree_path_to_folder (dest_model, dest_row);
2934 if (TNY_IS_FOLDER(folder)) {
2935 ModestTnyFolderRules rules =
2936 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2937 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2938 } else if (TNY_IS_FOLDER_STORE(folder)) {
2939 /* enable local root as destination for folders */
2940 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2941 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2944 g_object_unref (folder);
2947 /* check the folder rules for the source */
2948 folder = tree_path_to_folder (source_model, helper->source_row);
2949 if (TNY_IS_FOLDER(folder)) {
2950 ModestTnyFolderRules rules =
2951 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2952 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2955 g_object_unref (folder);
2959 /* Check if the drag is possible */
2960 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2962 modest_platform_run_information_dialog ((GtkWindow *) win,
2963 _("mail_in_ui_folder_move_target_error"),
2965 /* Restore the previous selection */
2966 folder = tree_path_to_folder (source_model, helper->source_row);
2968 if (TNY_IS_FOLDER (folder))
2969 modest_folder_view_select_folder (helper->folder_view,
2970 TNY_FOLDER (folder), FALSE);
2971 g_object_unref (folder);
2973 dnd_helper_destroyer (helper);
2978 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2979 gtk_tree_model_get (dest_model, &dest_iter,
2982 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2983 gtk_tree_model_get (source_model, &iter,
2987 /* Create the info for the performer */
2988 info = g_slice_new0 (DndFolderInfo);
2989 info->src_folder = g_object_ref (folder);
2990 info->dst_folder = g_object_ref (dest_folder);
2991 info->helper = helper;
2993 /* Connect to the destination folder and perform the copy/move */
2994 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2996 drag_and_drop_from_folder_view_dst_folder_performer,
3000 g_object_unref (dest_folder);
3001 g_object_unref (folder);
3005 * This function receives the data set by the "drag-data-get" signal
3006 * handler. This information comes within the #GtkSelectionData. This
3007 * function will manage both the drags of folders of the treeview and
3008 * drags of headers of the header view widget.
3011 on_drag_data_received (GtkWidget *widget,
3012 GdkDragContext *context,
3015 GtkSelectionData *selection_data,
3020 GtkWidget *source_widget;
3021 GtkTreeModel *dest_model, *source_model;
3022 GtkTreePath *source_row, *dest_row;
3023 GtkTreeViewDropPosition pos;
3024 gboolean delete_source = FALSE;
3025 gboolean success = FALSE;
3027 /* Do not allow further process */
3028 g_signal_stop_emission_by_name (widget, "drag-data-received");
3029 source_widget = gtk_drag_get_source_widget (context);
3031 /* Get the action */
3032 if (context->action == GDK_ACTION_MOVE) {
3033 delete_source = TRUE;
3035 /* Notify that there is no folder selected. We need to
3036 do this in order to update the headers view (and
3037 its monitors, because when moving, the old folder
3038 won't longer exist. We can not wait for the end of
3039 the operation, because the operation won't start if
3040 the folder is in use */
3041 if (source_widget == widget) {
3042 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3043 gtk_tree_selection_unselect_all (sel);
3047 /* Check if the get_data failed */
3048 if (selection_data == NULL || selection_data->length < 0)
3051 /* Select the destination model */
3052 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3054 /* Get the path to the destination row. Can not call
3055 gtk_tree_view_get_drag_dest_row() because the source row
3056 is not selected anymore */
3057 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3060 /* Only allow drops IN other rows */
3062 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3063 pos == GTK_TREE_VIEW_DROP_AFTER)
3067 /* Drags from the header view */
3068 if (source_widget != widget) {
3069 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3071 drag_and_drop_from_header_view (source_model,
3076 DndHelper *helper = NULL;
3078 /* Get the source model and row */
3079 gtk_tree_get_row_drag_data (selection_data,
3083 /* Create the helper */
3084 helper = g_slice_new0 (DndHelper);
3085 helper->delete_source = delete_source;
3086 helper->source_row = gtk_tree_path_copy (source_row);
3087 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3089 drag_and_drop_from_folder_view (source_model,
3095 gtk_tree_path_free (source_row);
3099 gtk_tree_path_free (dest_row);
3102 /* Finish the drag and drop */
3103 gtk_drag_finish (context, success, FALSE, time);
3107 * We define a "drag-drop" signal handler because we do not want to
3108 * use the default one, because the default one always calls
3109 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3110 * signal handler, because there we have all the information available
3111 * to know if the dnd was a success or not.
3114 drag_drop_cb (GtkWidget *widget,
3115 GdkDragContext *context,
3123 if (!context->targets)
3126 /* Check if we're dragging a folder row */
3127 target = gtk_drag_dest_find_target (widget, context, NULL);
3129 /* Request the data from the source. */
3130 gtk_drag_get_data(widget, context, target, time);
3136 * This function expands a node of a tree view if it's not expanded
3137 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3138 * does that, so that's why they're here.
3141 expand_row_timeout (gpointer data)
3143 GtkTreeView *tree_view = data;
3144 GtkTreePath *dest_path = NULL;
3145 GtkTreeViewDropPosition pos;
3146 gboolean result = FALSE;
3148 gdk_threads_enter ();
3150 gtk_tree_view_get_drag_dest_row (tree_view,
3155 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3156 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3157 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3158 gtk_tree_path_free (dest_path);
3162 gtk_tree_path_free (dest_path);
3167 gdk_threads_leave ();
3173 * This function is called whenever the pointer is moved over a widget
3174 * while dragging some data. It installs a timeout that will expand a
3175 * node of the treeview if not expanded yet. This function also calls
3176 * gdk_drag_status in order to set the suggested action that will be
3177 * used by the "drag-data-received" signal handler to know if we
3178 * should do a move or just a copy of the data.
3181 on_drag_motion (GtkWidget *widget,
3182 GdkDragContext *context,
3188 GtkTreeViewDropPosition pos;
3189 GtkTreePath *dest_row;
3190 GtkTreeModel *dest_model;
3191 ModestFolderViewPrivate *priv;
3192 GdkDragAction suggested_action;
3193 gboolean valid_location = FALSE;
3194 TnyFolderStore *folder = NULL;
3196 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3198 if (priv->timer_expander != 0) {
3199 g_source_remove (priv->timer_expander);
3200 priv->timer_expander = 0;
3203 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3208 /* Do not allow drops between folders */
3210 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3211 pos == GTK_TREE_VIEW_DROP_AFTER) {
3212 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3213 gdk_drag_status(context, 0, time);
3214 valid_location = FALSE;
3217 valid_location = TRUE;
3220 /* Check that the destination folder is writable */
3221 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3222 folder = tree_path_to_folder (dest_model, dest_row);
3223 if (folder && TNY_IS_FOLDER (folder)) {
3224 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3226 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3227 valid_location = FALSE;
3232 /* Expand the selected row after 1/2 second */
3233 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3234 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3236 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3238 /* Select the desired action. By default we pick MOVE */
3239 suggested_action = GDK_ACTION_MOVE;
3241 if (context->actions == GDK_ACTION_COPY)
3242 gdk_drag_status(context, GDK_ACTION_COPY, time);
3243 else if (context->actions == GDK_ACTION_MOVE)
3244 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3245 else if (context->actions & suggested_action)
3246 gdk_drag_status(context, suggested_action, time);
3248 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3252 g_object_unref (folder);
3254 gtk_tree_path_free (dest_row);
3256 g_signal_stop_emission_by_name (widget, "drag-motion");
3258 return valid_location;
3262 * This function sets the treeview as a source and a target for dnd
3263 * events. It also connects all the requirede signals.
3266 setup_drag_and_drop (GtkTreeView *self)
3268 /* Set up the folder view as a dnd destination. Set only the
3269 highlight flag, otherwise gtk will have a different
3271 #ifdef MODEST_TOOLKIT_HILDON2
3274 gtk_drag_dest_set (GTK_WIDGET (self),
3275 GTK_DEST_DEFAULT_HIGHLIGHT,
3276 folder_view_drag_types,
3277 G_N_ELEMENTS (folder_view_drag_types),
3278 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3280 g_signal_connect (G_OBJECT (self),
3281 "drag_data_received",
3282 G_CALLBACK (on_drag_data_received),
3286 /* Set up the treeview as a dnd source */
3287 gtk_drag_source_set (GTK_WIDGET (self),
3289 folder_view_drag_types,
3290 G_N_ELEMENTS (folder_view_drag_types),
3291 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3293 g_signal_connect (G_OBJECT (self),
3295 G_CALLBACK (on_drag_motion),
3298 g_signal_connect (G_OBJECT (self),
3300 G_CALLBACK (on_drag_data_get),
3303 g_signal_connect (G_OBJECT (self),
3305 G_CALLBACK (drag_drop_cb),
3310 * This function manages the navigation through the folders using the
3311 * keyboard or the hardware keys in the device
3314 on_key_pressed (GtkWidget *self,
3318 GtkTreeSelection *selection;
3320 GtkTreeModel *model;
3321 gboolean retval = FALSE;
3323 /* Up and Down are automatically managed by the treeview */
3324 if (event->keyval == GDK_Return) {
3325 /* Expand/Collapse the selected row */
3326 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3327 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3330 path = gtk_tree_model_get_path (model, &iter);
3332 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3333 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3335 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3336 gtk_tree_path_free (path);
3338 /* No further processing */
3346 * We listen to the changes in the local folder account name key,
3347 * because we want to show the right name in the view. The local
3348 * folder account name corresponds to the device name in the Maemo
3349 * version. We do this because we do not want to query gconf on each
3350 * tree view refresh. It's better to cache it and change whenever
3354 on_configuration_key_changed (ModestConf* conf,
3356 ModestConfEvent event,
3357 ModestConfNotificationId id,
3358 ModestFolderView *self)
3360 ModestFolderViewPrivate *priv;
3363 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3364 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3366 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3367 g_free (priv->local_account_name);
3369 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3370 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3372 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3373 MODEST_CONF_DEVICE_NAME, NULL);
3375 /* Force a redraw */
3376 #if GTK_CHECK_VERSION(2, 8, 0)
3377 GtkTreeViewColumn * tree_column;
3379 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3381 gtk_tree_view_column_queue_resize (tree_column);
3383 gtk_widget_queue_draw (GTK_WIDGET (self));
3389 modest_folder_view_set_style (ModestFolderView *self,
3390 ModestFolderViewStyle style)
3392 ModestFolderViewPrivate *priv;
3394 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3395 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3396 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3398 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3401 priv->style = style;
3405 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3406 const gchar *account_id)
3408 ModestFolderViewPrivate *priv;
3409 GtkTreeModel *model;
3411 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3413 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3415 /* This will be used by the filter_row callback,
3416 * to decided which rows to show: */
3417 if (priv->visible_account_id) {
3418 g_free (priv->visible_account_id);
3419 priv->visible_account_id = NULL;
3422 priv->visible_account_id = g_strdup (account_id);
3425 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3426 if (GTK_IS_TREE_MODEL_FILTER (model))
3427 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3429 /* Save settings to gconf */
3430 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3431 MODEST_CONF_FOLDER_VIEW_KEY);
3433 /* Notify observers */
3434 g_signal_emit (G_OBJECT(self),
3435 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3440 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3442 ModestFolderViewPrivate *priv;
3444 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3446 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3448 return (const gchar *) priv->visible_account_id;
3452 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3456 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3458 gtk_tree_model_get (model, iter,
3462 gboolean result = FALSE;
3463 if (type == TNY_FOLDER_TYPE_INBOX) {
3467 *inbox_iter = *iter;
3471 if (gtk_tree_model_iter_children (model, &child, iter)) {
3472 if (find_inbox_iter (model, &child, inbox_iter))
3476 } while (gtk_tree_model_iter_next (model, iter));
3485 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3487 #ifndef MODEST_TOOLKIT_HILDON2
3488 GtkTreeModel *model;
3489 GtkTreeIter iter, inbox_iter;
3490 GtkTreeSelection *sel;
3491 GtkTreePath *path = NULL;
3493 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3495 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3499 expand_root_items (self);
3500 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3502 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3503 g_warning ("%s: model is empty", __FUNCTION__);
3507 if (find_inbox_iter (model, &iter, &inbox_iter))
3508 path = gtk_tree_model_get_path (model, &inbox_iter);
3510 path = gtk_tree_path_new_first ();
3512 /* Select the row and free */
3513 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3514 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3515 gtk_tree_path_free (path);
3518 gtk_widget_grab_focus (GTK_WIDGET(self));
3525 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3530 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3531 TnyFolder* a_folder;
3534 gtk_tree_model_get (model, iter,
3535 INSTANCE_COLUMN, &a_folder,
3541 if (folder == a_folder) {
3542 g_object_unref (a_folder);
3543 *folder_iter = *iter;
3546 g_object_unref (a_folder);
3548 if (gtk_tree_model_iter_children (model, &child, iter)) {
3549 if (find_folder_iter (model, &child, folder_iter, folder))
3553 } while (gtk_tree_model_iter_next (model, iter));
3558 #ifndef MODEST_TOOLKIT_HILDON2
3560 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3563 ModestFolderView *self)
3565 ModestFolderViewPrivate *priv = NULL;
3566 GtkTreeSelection *sel;
3567 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3568 GObject *instance = NULL;
3570 if (!MODEST_IS_FOLDER_VIEW(self))
3573 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3575 priv->reexpand = TRUE;
3577 gtk_tree_model_get (tree_model, iter,
3579 INSTANCE_COLUMN, &instance,
3585 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3586 priv->folder_to_select = g_object_ref (instance);
3588 g_object_unref (instance);
3590 if (priv->folder_to_select) {
3592 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3595 path = gtk_tree_model_get_path (tree_model, iter);
3596 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3598 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3600 gtk_tree_selection_select_iter (sel, iter);
3601 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3603 gtk_tree_path_free (path);
3607 modest_folder_view_disable_next_folder_selection (self);
3609 /* Refilter the model */
3610 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3616 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3618 ModestFolderViewPrivate *priv;
3620 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3622 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3624 if (priv->folder_to_select)
3625 g_object_unref(priv->folder_to_select);
3627 priv->folder_to_select = NULL;
3631 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3632 gboolean after_change)
3634 GtkTreeModel *model;
3635 GtkTreeIter iter, folder_iter;
3636 GtkTreeSelection *sel;
3637 ModestFolderViewPrivate *priv = NULL;
3639 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3640 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3642 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3645 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3646 gtk_tree_selection_unselect_all (sel);
3648 if (priv->folder_to_select)
3649 g_object_unref(priv->folder_to_select);
3650 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3654 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3659 /* Refilter the model, before selecting the folder */
3660 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3662 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3663 g_warning ("%s: model is empty", __FUNCTION__);
3667 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3670 path = gtk_tree_model_get_path (model, &folder_iter);
3671 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3673 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3674 gtk_tree_selection_select_iter (sel, &folder_iter);
3675 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3677 gtk_tree_path_free (path);
3685 modest_folder_view_copy_selection (ModestFolderView *self)
3687 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3689 /* Copy selection */
3690 _clipboard_set_selected_data (self, FALSE);
3694 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3696 ModestFolderViewPrivate *priv = NULL;
3697 GtkTreeModel *model = NULL;
3698 const gchar **hidding = NULL;
3699 guint i, n_selected;
3701 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3702 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3704 /* Copy selection */
3705 if (!_clipboard_set_selected_data (folder_view, TRUE))
3708 /* Get hidding ids */
3709 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3711 /* Clear hidding array created by previous cut operation */
3712 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3714 /* Copy hidding array */
3715 priv->n_selected = n_selected;
3716 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3717 for (i=0; i < n_selected; i++)
3718 priv->hidding_ids[i] = g_strdup(hidding[i]);
3720 /* Hide cut folders */
3721 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3722 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3726 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3727 ModestFolderView *folder_view_dst)
3729 GtkTreeModel *filter_model = NULL;
3730 GtkTreeModel *model = NULL;
3731 GtkTreeModel *new_filter_model = NULL;
3733 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3734 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3737 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3738 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3740 /* Build new filter model */
3741 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3742 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3746 /* Set copied model */
3747 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3748 #ifndef MODEST_TOOLKIT_HILDON2
3749 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3750 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3754 g_object_unref (new_filter_model);
3758 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3761 GtkTreeModel *model = NULL;
3762 ModestFolderViewPrivate* priv;
3764 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3766 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3767 priv->show_non_move = show;
3768 /* modest_folder_view_update_model(folder_view, */
3769 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3771 /* Hide special folders */
3772 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3773 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3774 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3779 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3782 ModestFolderViewPrivate* priv;
3784 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3786 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3787 priv->show_message_count = show;
3789 g_object_set (G_OBJECT (priv->messages_renderer),
3790 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3794 /* Returns FALSE if it did not selected anything */
3796 _clipboard_set_selected_data (ModestFolderView *folder_view,
3799 ModestFolderViewPrivate *priv = NULL;
3800 TnyFolderStore *folder = NULL;
3801 gboolean retval = FALSE;
3803 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3804 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3806 /* Set selected data on clipboard */
3807 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3808 folder = modest_folder_view_get_selected (folder_view);
3810 /* Do not allow to select an account */
3811 if (TNY_IS_FOLDER (folder)) {
3812 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3817 g_object_unref (folder);
3823 _clear_hidding_filter (ModestFolderView *folder_view)
3825 ModestFolderViewPrivate *priv;
3828 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3829 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3831 if (priv->hidding_ids != NULL) {
3832 for (i=0; i < priv->n_selected; i++)
3833 g_free (priv->hidding_ids[i]);
3834 g_free(priv->hidding_ids);
3840 on_display_name_changed (ModestAccountMgr *mgr,
3841 const gchar *account,
3844 ModestFolderView *self;
3846 self = MODEST_FOLDER_VIEW (user_data);
3848 /* Force a redraw */
3849 #if GTK_CHECK_VERSION(2, 8, 0)
3850 GtkTreeViewColumn * tree_column;
3852 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3854 gtk_tree_view_column_queue_resize (tree_column);
3856 gtk_widget_queue_draw (GTK_WIDGET (self));
3861 modest_folder_view_set_cell_style (ModestFolderView *self,
3862 ModestFolderViewCellStyle cell_style)
3864 ModestFolderViewPrivate *priv = NULL;
3866 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3867 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3869 priv->cell_style = cell_style;
3871 g_object_set (G_OBJECT (priv->messages_renderer),
3872 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3875 gtk_widget_queue_draw (GTK_WIDGET (self));
3879 update_style (ModestFolderView *self)
3881 ModestFolderViewPrivate *priv;
3882 GdkColor style_color;
3883 PangoAttrList *attr_list;
3885 PangoAttribute *attr;
3887 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3888 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3892 attr_list = pango_attr_list_new ();
3893 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3894 gdk_color_parse ("grey", &style_color);
3896 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3897 pango_attr_list_insert (attr_list, attr);
3900 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3902 "SmallSystemFont", NULL,
3905 attr = pango_attr_font_desc_new (pango_font_description_copy
3906 (style->font_desc));
3907 pango_attr_list_insert (attr_list, attr);
3909 g_object_set (G_OBJECT (priv->messages_renderer),
3910 "foreground-gdk", &style_color,
3911 "foreground-set", TRUE,
3912 "attributes", attr_list,
3914 pango_attr_list_unref (attr_list);
3919 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3921 if (strcmp ("style", spec->name) == 0) {
3922 update_style (MODEST_FOLDER_VIEW (obj));
3923 gtk_widget_queue_draw (GTK_WIDGET (obj));
3928 modest_folder_view_set_filter (ModestFolderView *self,
3929 ModestFolderViewFilter filter)
3931 ModestFolderViewPrivate *priv;
3932 GtkTreeModel *filter_model;
3934 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3935 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3937 priv->filter |= filter;
3939 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3940 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3941 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3946 modest_folder_view_unset_filter (ModestFolderView *self,
3947 ModestFolderViewFilter filter)
3949 ModestFolderViewPrivate *priv;
3950 GtkTreeModel *filter_model;
3952 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3953 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3955 priv->filter &= ~filter;
3957 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3958 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3959 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3964 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3965 ModestTnyFolderRules rules)
3967 GtkTreeModel *filter_model;
3969 gboolean fulfil = FALSE;
3971 if (!get_inner_models (self, &filter_model, NULL, NULL))
3974 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3978 TnyFolderStore *folder;
3980 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3982 if (TNY_IS_FOLDER (folder)) {
3983 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3984 /* Folder rules are negative: non_writable, non_deletable... */
3985 if (!(folder_rules & rules))
3988 g_object_unref (folder);
3991 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3997 modest_folder_view_set_list_to_move (ModestFolderView *self,
4000 ModestFolderViewPrivate *priv;
4002 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4003 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4005 if (priv->list_to_move)
4006 g_object_unref (priv->list_to_move);
4009 g_object_ref (list);
4011 priv->list_to_move = list;
4015 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4017 ModestFolderViewPrivate *priv;
4019 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4020 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4023 g_free (priv->mailbox);
4025 priv->mailbox = g_strdup (mailbox);
4027 /* Notify observers */
4028 g_signal_emit (G_OBJECT(self),
4029 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4030 priv->visible_account_id);
4034 modest_folder_view_get_mailbox (ModestFolderView *self)
4036 ModestFolderViewPrivate *priv;
4038 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4039 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4041 return (const gchar *) priv->mailbox;