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 (ModestFolderView *folder_view,
900 ModestProtocolType protocol_type,
903 ModestProtocol *protocol;
904 const GdkPixbuf *pixbuf = NULL;
905 ModestFolderViewPrivate *priv;
907 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
909 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
912 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
913 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
914 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
915 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
916 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
917 object, FOLDER_ICON_SIZE);
921 ThreePixbufs *retval;
922 retval = g_slice_new0 (ThreePixbufs);
923 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
924 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
925 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
932 static inline ThreePixbufs*
933 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
935 TnyAccount *account = NULL;
936 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
937 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
938 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
939 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
940 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
942 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
943 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
944 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
945 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
946 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
948 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
949 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
950 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
951 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
952 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
954 ThreePixbufs *retval = NULL;
956 if (TNY_IS_ACCOUNT (instance)) {
957 account = g_object_ref (instance);
958 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
959 account = tny_folder_get_account (TNY_FOLDER (instance));
963 ModestProtocolType account_store_protocol;
965 account_store_protocol = modest_tny_account_get_protocol_type (account);
966 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
967 g_object_unref (account);
973 /* Sometimes an special folder is reported by the server as
974 NORMAL, like some versions of Dovecot */
975 if (type == TNY_FOLDER_TYPE_NORMAL ||
976 type == TNY_FOLDER_TYPE_UNKNOWN) {
977 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
980 /* It's not enough with check the folder type. We need to
981 ensure that we're not giving a special folder icon to a
982 normal folder with the same name than a special folder */
983 if (TNY_IS_FOLDER (instance) &&
984 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
985 type = TNY_FOLDER_TYPE_NORMAL;
987 /* Remote folders should not be treated as special folders */
988 if (TNY_IS_FOLDER_STORE (instance) &&
989 !TNY_IS_ACCOUNT (instance) &&
990 type != TNY_FOLDER_TYPE_INBOX &&
991 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
992 #ifdef MODEST_TOOLKIT_HILDON2
993 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
996 &anorm_pixbuf_close);
998 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1000 &normal_pixbuf_open,
1001 &normal_pixbuf_close);
1007 case TNY_FOLDER_TYPE_INVALID:
1008 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1011 case TNY_FOLDER_TYPE_ROOT:
1012 if (TNY_IS_ACCOUNT (instance)) {
1014 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1015 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1018 &avirt_pixbuf_close);
1020 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1022 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1023 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1026 &ammc_pixbuf_close);
1028 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1031 &anorm_pixbuf_close);
1036 case TNY_FOLDER_TYPE_INBOX:
1037 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1040 &inbox_pixbuf_close);
1042 case TNY_FOLDER_TYPE_OUTBOX:
1043 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1045 &outbox_pixbuf_open,
1046 &outbox_pixbuf_close);
1048 case TNY_FOLDER_TYPE_JUNK:
1049 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1052 &junk_pixbuf_close);
1054 case TNY_FOLDER_TYPE_SENT:
1055 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1058 &sent_pixbuf_close);
1060 case TNY_FOLDER_TYPE_TRASH:
1061 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1064 &trash_pixbuf_close);
1066 case TNY_FOLDER_TYPE_DRAFTS:
1067 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1070 &draft_pixbuf_close);
1072 case TNY_FOLDER_TYPE_ARCHIVE:
1073 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1078 case TNY_FOLDER_TYPE_NORMAL:
1080 /* Memory card folders could have an special icon */
1081 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1082 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1087 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1089 &normal_pixbuf_open,
1090 &normal_pixbuf_close);
1099 free_pixbufs (ThreePixbufs *pixbufs)
1101 if (pixbufs->pixbuf)
1102 g_object_unref (pixbufs->pixbuf);
1103 if (pixbufs->pixbuf_open)
1104 g_object_unref (pixbufs->pixbuf_open);
1105 if (pixbufs->pixbuf_close)
1106 g_object_unref (pixbufs->pixbuf_close);
1107 g_slice_free (ThreePixbufs, pixbufs);
1111 icon_cell_data (GtkTreeViewColumn *column,
1112 GtkCellRenderer *renderer,
1113 GtkTreeModel *tree_model,
1117 GObject *rendobj = NULL, *instance = NULL;
1118 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1119 gboolean has_children;
1120 ThreePixbufs *pixbufs;
1121 ModestFolderView *folder_view = (ModestFolderView *) data;
1123 rendobj = (GObject *) renderer;
1125 gtk_tree_model_get (tree_model, iter,
1127 INSTANCE_COLUMN, &instance,
1133 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1134 pixbufs = get_folder_icons (folder_view, type, instance);
1135 g_object_unref (instance);
1138 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1141 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1142 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1145 free_pixbufs (pixbufs);
1149 add_columns (GtkWidget *treeview)
1151 GtkTreeViewColumn *column;
1152 GtkCellRenderer *renderer;
1153 GtkTreeSelection *sel;
1154 ModestFolderViewPrivate *priv;
1156 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1159 column = gtk_tree_view_column_new ();
1161 /* Set icon and text render function */
1162 renderer = gtk_cell_renderer_pixbuf_new();
1163 #ifdef MODEST_TOOLKIT_HILDON2
1164 g_object_set (renderer,
1165 "xpad", MODEST_MARGIN_DEFAULT,
1166 "ypad", MODEST_MARGIN_DEFAULT,
1169 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1170 gtk_tree_view_column_set_cell_data_func(column, renderer,
1171 icon_cell_data, treeview, NULL);
1173 renderer = gtk_cell_renderer_text_new();
1174 g_object_set (renderer,
1175 #ifdef MODEST_TOOLKIT_HILDON2
1176 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1177 "ypad", MODEST_MARGIN_DEFAULT,
1178 "xpad", MODEST_MARGIN_DEFAULT,
1180 "ellipsize", PANGO_ELLIPSIZE_END,
1182 "ellipsize-set", TRUE, NULL);
1183 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1184 gtk_tree_view_column_set_cell_data_func(column, renderer,
1185 text_cell_data, treeview, NULL);
1187 priv->messages_renderer = gtk_cell_renderer_text_new ();
1188 g_object_set (priv->messages_renderer,
1189 #ifdef MODEST_TOOLKIT_HILDON2
1191 "ypad", MODEST_MARGIN_DEFAULT,
1192 "xpad", MODEST_MARGIN_DOUBLE,
1194 "scale", PANGO_SCALE_X_SMALL,
1197 "alignment", PANGO_ALIGN_RIGHT,
1201 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1202 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1203 messages_cell_data, treeview, NULL);
1205 /* Set selection mode */
1206 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1207 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1209 /* Set treeview appearance */
1210 gtk_tree_view_column_set_spacing (column, 2);
1211 gtk_tree_view_column_set_resizable (column, TRUE);
1212 gtk_tree_view_column_set_fixed_width (column, TRUE);
1213 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1214 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1217 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1221 modest_folder_view_init (ModestFolderView *obj)
1223 ModestFolderViewPrivate *priv;
1226 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1228 priv->timer_expander = 0;
1229 priv->account_store = NULL;
1231 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1232 priv->cur_folder_store = NULL;
1233 priv->visible_account_id = NULL;
1234 priv->mailbox = NULL;
1235 priv->folder_to_select = NULL;
1236 priv->outbox_deleted_handler = 0;
1237 priv->reexpand = TRUE;
1239 /* Initialize the local account name */
1240 conf = modest_runtime_get_conf();
1241 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1243 /* Init email clipboard */
1244 priv->clipboard = modest_runtime_get_email_clipboard ();
1245 priv->hidding_ids = NULL;
1246 priv->n_selected = 0;
1247 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1248 priv->reselect = FALSE;
1249 priv->show_non_move = TRUE;
1250 priv->list_to_move = NULL;
1251 priv->show_message_count = TRUE;
1253 /* Build treeview */
1254 add_columns (GTK_WIDGET (obj));
1256 /* Setup drag and drop */
1257 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1259 /* Connect signals */
1260 g_signal_connect (G_OBJECT (obj),
1262 G_CALLBACK (on_key_pressed), NULL);
1264 priv->display_name_changed_signal =
1265 g_signal_connect (modest_runtime_get_account_mgr (),
1266 "display_name_changed",
1267 G_CALLBACK (on_display_name_changed),
1271 * Track changes in the local account name (in the device it
1272 * will be the device name)
1274 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1276 G_CALLBACK(on_configuration_key_changed),
1280 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1286 tny_account_store_view_init (gpointer g, gpointer iface_data)
1288 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1290 klass->set_account_store = modest_folder_view_set_account_store;
1294 modest_folder_view_finalize (GObject *obj)
1296 ModestFolderViewPrivate *priv;
1297 GtkTreeSelection *sel;
1298 TnyAccount *local_account;
1300 g_return_if_fail (obj);
1302 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1304 if (priv->timer_expander != 0) {
1305 g_source_remove (priv->timer_expander);
1306 priv->timer_expander = 0;
1309 local_account = (TnyAccount *)
1310 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1311 if (local_account) {
1312 if (g_signal_handler_is_connected (local_account,
1313 priv->outbox_deleted_handler))
1314 g_signal_handler_disconnect (local_account,
1315 priv->outbox_deleted_handler);
1316 g_object_unref (local_account);
1319 if (priv->account_store) {
1320 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1321 priv->account_inserted_signal);
1322 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1323 priv->account_removed_signal);
1324 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1325 priv->account_changed_signal);
1326 g_object_unref (G_OBJECT(priv->account_store));
1327 priv->account_store = NULL;
1330 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1331 priv->display_name_changed_signal)) {
1332 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1333 priv->display_name_changed_signal);
1334 priv->display_name_changed_signal = 0;
1338 g_object_unref (G_OBJECT (priv->query));
1342 if (priv->folder_to_select) {
1343 g_object_unref (G_OBJECT(priv->folder_to_select));
1344 priv->folder_to_select = NULL;
1347 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1349 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1351 g_free (priv->local_account_name);
1352 g_free (priv->visible_account_id);
1353 g_free (priv->mailbox);
1355 if (priv->conf_key_signal) {
1356 g_signal_handler_disconnect (modest_runtime_get_conf (),
1357 priv->conf_key_signal);
1358 priv->conf_key_signal = 0;
1361 if (priv->cur_folder_store) {
1362 g_object_unref (priv->cur_folder_store);
1363 priv->cur_folder_store = NULL;
1366 if (priv->list_to_move) {
1367 g_object_unref (priv->list_to_move);
1368 priv->list_to_move = NULL;
1371 /* Clear hidding array created by cut operation */
1372 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1374 G_OBJECT_CLASS(parent_class)->finalize (obj);
1379 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1381 ModestFolderViewPrivate *priv;
1384 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1385 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1387 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1388 device = tny_account_store_get_device (account_store);
1390 if (G_UNLIKELY (priv->account_store)) {
1392 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1393 priv->account_inserted_signal))
1394 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1395 priv->account_inserted_signal);
1396 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1397 priv->account_removed_signal))
1398 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1399 priv->account_removed_signal);
1400 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1401 priv->account_changed_signal))
1402 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1403 priv->account_changed_signal);
1404 g_object_unref (G_OBJECT (priv->account_store));
1407 priv->account_store = g_object_ref (G_OBJECT (account_store));
1409 priv->account_removed_signal =
1410 g_signal_connect (G_OBJECT(account_store), "account_removed",
1411 G_CALLBACK (on_account_removed), self);
1413 priv->account_inserted_signal =
1414 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1415 G_CALLBACK (on_account_inserted), self);
1417 priv->account_changed_signal =
1418 g_signal_connect (G_OBJECT(account_store), "account_changed",
1419 G_CALLBACK (on_account_changed), self);
1421 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1422 priv->reselect = FALSE;
1423 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1425 g_object_unref (G_OBJECT (device));
1429 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1432 ModestFolderView *self;
1433 GtkTreeModel *model, *filter_model;
1436 self = MODEST_FOLDER_VIEW (user_data);
1438 if (!get_inner_models (self, &filter_model, NULL, &model))
1441 /* Remove outbox from model */
1442 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1443 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1444 g_object_unref (outbox);
1447 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1451 on_account_inserted (TnyAccountStore *account_store,
1452 TnyAccount *account,
1455 ModestFolderViewPrivate *priv;
1456 GtkTreeModel *model, *filter_model;
1458 /* Ignore transport account insertions, we're not showing them
1459 in the folder view */
1460 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1463 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1466 /* If we're adding a new account, and there is no previous
1467 one, we need to select the visible server account */
1468 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1469 !priv->visible_account_id)
1470 modest_widget_memory_restore (modest_runtime_get_conf(),
1471 G_OBJECT (user_data),
1472 MODEST_CONF_FOLDER_VIEW_KEY);
1476 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1477 &filter_model, NULL, &model))
1480 /* Insert the account in the model */
1481 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1483 /* When the model is a list store (plain representation) the
1484 outbox is not a child of any account so we have to manually
1485 delete it because removing the local folders account won't
1486 delete it (because tny_folder_get_account() is not defined
1487 for a merge folder */
1488 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1489 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1491 priv->outbox_deleted_handler =
1492 g_signal_connect (account,
1494 G_CALLBACK (on_outbox_deleted_cb),
1498 /* Refilter the model */
1499 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1504 same_account_selected (ModestFolderView *self,
1505 TnyAccount *account)
1507 ModestFolderViewPrivate *priv;
1508 gboolean same_account = FALSE;
1510 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1512 if (priv->cur_folder_store) {
1513 TnyAccount *selected_folder_account = NULL;
1515 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1516 selected_folder_account =
1517 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1519 selected_folder_account =
1520 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1523 if (selected_folder_account == account)
1524 same_account = TRUE;
1526 g_object_unref (selected_folder_account);
1528 return same_account;
1533 * Selects the first inbox or the local account in an idle
1536 on_idle_select_first_inbox_or_local (gpointer user_data)
1538 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1540 gdk_threads_enter ();
1541 modest_folder_view_select_first_inbox_or_local (self);
1542 gdk_threads_leave ();
1548 on_account_changed (TnyAccountStore *account_store,
1549 TnyAccount *tny_account,
1552 ModestFolderView *self;
1553 ModestFolderViewPrivate *priv;
1554 GtkTreeModel *model, *filter_model;
1555 GtkTreeSelection *sel;
1556 gboolean same_account;
1558 /* Ignore transport account insertions, we're not showing them
1559 in the folder view */
1560 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1563 self = MODEST_FOLDER_VIEW (user_data);
1564 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1566 /* Get the inner model */
1567 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1568 &filter_model, NULL, &model))
1571 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1573 /* Invalidate the cur_folder_store only if the selected folder
1574 belongs to the account that is being removed */
1575 same_account = same_account_selected (self, tny_account);
1577 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1578 gtk_tree_selection_unselect_all (sel);
1581 /* Remove the account from the model */
1582 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1584 /* Insert the account in the model */
1585 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1587 /* Refilter the model */
1588 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1590 /* Select the first INBOX if the currently selected folder
1591 belongs to the account that is being deleted */
1592 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1593 g_idle_add (on_idle_select_first_inbox_or_local, self);
1597 on_account_removed (TnyAccountStore *account_store,
1598 TnyAccount *account,
1601 ModestFolderView *self = NULL;
1602 ModestFolderViewPrivate *priv;
1603 GtkTreeModel *model, *filter_model;
1604 GtkTreeSelection *sel = NULL;
1605 gboolean same_account = FALSE;
1607 /* Ignore transport account removals, we're not showing them
1608 in the folder view */
1609 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1612 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1613 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1617 self = MODEST_FOLDER_VIEW (user_data);
1618 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1620 /* Invalidate the cur_folder_store only if the selected folder
1621 belongs to the account that is being removed */
1622 same_account = same_account_selected (self, account);
1624 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1625 gtk_tree_selection_unselect_all (sel);
1628 /* Invalidate row to select only if the folder to select
1629 belongs to the account that is being removed*/
1630 if (priv->folder_to_select) {
1631 TnyAccount *folder_to_select_account = NULL;
1633 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1634 if (folder_to_select_account == account) {
1635 modest_folder_view_disable_next_folder_selection (self);
1636 g_object_unref (priv->folder_to_select);
1637 priv->folder_to_select = NULL;
1639 g_object_unref (folder_to_select_account);
1642 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1643 &filter_model, NULL, &model))
1646 /* Disconnect the signal handler */
1647 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1648 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1649 if (g_signal_handler_is_connected (account,
1650 priv->outbox_deleted_handler))
1651 g_signal_handler_disconnect (account,
1652 priv->outbox_deleted_handler);
1655 /* Remove the account from the model */
1656 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1658 /* If the removed account is the currently viewed one then
1659 clear the configuration value. The new visible account will be the default account */
1660 if (priv->visible_account_id &&
1661 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1663 /* Clear the current visible account_id */
1664 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1665 modest_folder_view_set_mailbox (self, NULL);
1667 /* Call the restore method, this will set the new visible account */
1668 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1669 MODEST_CONF_FOLDER_VIEW_KEY);
1672 /* Refilter the model */
1673 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1675 /* Select the first INBOX if the currently selected folder
1676 belongs to the account that is being deleted */
1678 g_idle_add (on_idle_select_first_inbox_or_local, self);
1682 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1684 GtkTreeViewColumn *col;
1686 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1688 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1690 g_printerr ("modest: failed get column for title\n");
1694 gtk_tree_view_column_set_title (col, title);
1695 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1700 modest_folder_view_on_map (ModestFolderView *self,
1701 GdkEventExpose *event,
1704 ModestFolderViewPrivate *priv;
1706 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1708 /* This won't happen often */
1709 if (G_UNLIKELY (priv->reselect)) {
1710 /* Select the first inbox or the local account if not found */
1712 /* TODO: this could cause a lock at startup, so we
1713 comment it for the moment. We know that this will
1714 be a bug, because the INBOX is not selected, but we
1715 need to rewrite some parts of Modest to avoid the
1716 deathlock situation */
1717 /* TODO: check if this is still the case */
1718 priv->reselect = FALSE;
1719 modest_folder_view_select_first_inbox_or_local (self);
1720 /* Notify the display name observers */
1721 g_signal_emit (G_OBJECT(self),
1722 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1726 if (priv->reexpand) {
1727 expand_root_items (self);
1728 priv->reexpand = FALSE;
1735 modest_folder_view_new (TnyFolderStoreQuery *query)
1738 ModestFolderViewPrivate *priv;
1739 GtkTreeSelection *sel;
1741 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1742 #ifdef MODEST_TOOLKIT_HILDON2
1743 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1746 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1749 priv->query = g_object_ref (query);
1751 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1752 priv->changed_signal = g_signal_connect (sel, "changed",
1753 G_CALLBACK (on_selection_changed), self);
1755 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1757 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1759 return GTK_WIDGET(self);
1762 /* this feels dirty; any other way to expand all the root items? */
1764 expand_root_items (ModestFolderView *self)
1767 GtkTreeModel *model;
1770 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1771 path = gtk_tree_path_new_first ();
1773 /* all folders should have child items, so.. */
1775 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1776 gtk_tree_path_next (path);
1777 } while (gtk_tree_model_get_iter (model, &iter, path));
1779 gtk_tree_path_free (path);
1783 is_parent_of (TnyFolder *a, TnyFolder *b)
1786 gboolean retval = FALSE;
1788 a_id = tny_folder_get_id (a);
1790 gchar *string_to_match;
1793 string_to_match = g_strconcat (a_id, "/", NULL);
1794 b_id = tny_folder_get_id (b);
1795 retval = g_str_has_prefix (b_id, string_to_match);
1796 g_free (string_to_match);
1802 typedef struct _ForeachFolderInfo {
1805 } ForeachFolderInfo;
1808 foreach_folder_with_id (GtkTreeModel *model,
1813 ForeachFolderInfo *info;
1816 info = (ForeachFolderInfo *) data;
1817 gtk_tree_model_get (model, iter,
1818 INSTANCE_COLUMN, &instance,
1821 if (TNY_IS_FOLDER (instance)) {
1824 id = tny_folder_get_id (TNY_FOLDER (instance));
1826 collate = g_utf8_collate_key (id, -1);
1827 info->found = !strcmp (info->needle, collate);
1833 g_object_unref (instance);
1841 has_folder_with_id (ModestFolderView *self, const gchar *id)
1843 GtkTreeModel *model;
1844 ForeachFolderInfo info = {NULL, FALSE};
1846 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1847 info.needle = g_utf8_collate_key (id, -1);
1849 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1850 g_free (info.needle);
1856 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1859 gboolean retval = FALSE;
1861 a_id = tny_folder_get_id (a);
1864 b_id = tny_folder_get_id (b);
1867 const gchar *last_bar;
1868 gchar *string_to_match;
1869 last_bar = g_strrstr (b_id, "/");
1874 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1875 retval = has_folder_with_id (self, string_to_match);
1876 g_free (string_to_match);
1884 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1886 ModestFolderViewPrivate *priv;
1887 TnyIterator *iterator;
1888 gboolean retval = TRUE;
1890 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1891 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1893 for (iterator = tny_list_create_iterator (priv->list_to_move);
1894 retval && !tny_iterator_is_done (iterator);
1895 tny_iterator_next (iterator)) {
1897 instance = tny_iterator_get_current (iterator);
1898 if (instance == (GObject *) folder) {
1900 } else if (TNY_IS_FOLDER (instance)) {
1901 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1903 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1906 g_object_unref (instance);
1908 g_object_unref (iterator);
1915 * We use this function to implement the
1916 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1917 * account in this case, and the local folders.
1920 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1922 ModestFolderViewPrivate *priv;
1923 gboolean retval = TRUE;
1924 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1925 GObject *instance = NULL;
1926 const gchar *id = NULL;
1928 gboolean found = FALSE;
1929 gboolean cleared = FALSE;
1930 ModestTnyFolderRules rules = 0;
1933 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1934 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1936 gtk_tree_model_get (model, iter,
1937 NAME_COLUMN, &fname,
1939 INSTANCE_COLUMN, &instance,
1942 /* Do not show if there is no instance, this could indeed
1943 happen when the model is being modified while it's being
1944 drawn. This could occur for example when moving folders
1951 if (TNY_IS_ACCOUNT (instance)) {
1952 TnyAccount *acc = TNY_ACCOUNT (instance);
1953 const gchar *account_id = tny_account_get_id (acc);
1955 /* If it isn't a special folder,
1956 * don't show it unless it is the visible account: */
1957 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1958 !modest_tny_account_is_virtual_local_folders (acc) &&
1959 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1961 /* Show only the visible account id */
1962 if (priv->visible_account_id) {
1963 if (strcmp (account_id, priv->visible_account_id))
1970 /* Never show these to the user. They are merged into one folder
1971 * in the local-folders account instead: */
1972 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1975 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1976 /* Only show special folders for current account if needed */
1977 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1978 TnyAccount *account;
1980 account = tny_folder_get_account (TNY_FOLDER (instance));
1982 if (TNY_IS_ACCOUNT (account)) {
1983 const gchar *account_id = tny_account_get_id (account);
1985 if (!modest_tny_account_is_virtual_local_folders (account) &&
1986 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1987 /* Show only the visible account id */
1988 if (priv->visible_account_id) {
1989 if (strcmp (account_id, priv->visible_account_id)) {
1991 } else if (priv->mailbox) {
1992 /* Filter mailboxes */
1993 if (!g_str_has_prefix (fname, priv->mailbox)) {
1995 } else if (!strcmp (fname, priv->mailbox)) {
1996 /* Hide mailbox parent */
2002 g_object_unref (account);
2009 /* Check hiding (if necessary) */
2010 cleared = modest_email_clipboard_cleared (priv->clipboard);
2011 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2012 id = tny_folder_get_id (TNY_FOLDER(instance));
2013 if (priv->hidding_ids != NULL)
2014 for (i=0; i < priv->n_selected && !found; i++)
2015 if (priv->hidding_ids[i] != NULL && id != NULL)
2016 found = (!strcmp (priv->hidding_ids[i], id));
2021 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2022 folder as no message can be move there according to UI specs */
2023 if (retval && !priv->show_non_move) {
2024 if (priv->list_to_move &&
2025 tny_list_get_length (priv->list_to_move) > 0 &&
2026 TNY_IS_FOLDER (instance)) {
2027 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2029 if (retval && TNY_IS_FOLDER (instance) &&
2030 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2032 case TNY_FOLDER_TYPE_OUTBOX:
2033 case TNY_FOLDER_TYPE_SENT:
2034 case TNY_FOLDER_TYPE_DRAFTS:
2037 case TNY_FOLDER_TYPE_UNKNOWN:
2038 case TNY_FOLDER_TYPE_NORMAL:
2039 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2040 if (type == TNY_FOLDER_TYPE_INVALID)
2041 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2043 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2044 type == TNY_FOLDER_TYPE_SENT
2045 || type == TNY_FOLDER_TYPE_DRAFTS)
2052 if (retval && TNY_IS_ACCOUNT (instance) &&
2053 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2054 ModestProtocolType protocol_type;
2056 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2057 retval = !modest_protocol_registry_protocol_type_has_tag
2058 (modest_runtime_get_protocol_registry (),
2060 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2064 /* apply special filters */
2065 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2066 if (TNY_IS_ACCOUNT (instance))
2070 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2071 if (TNY_IS_FOLDER (instance))
2075 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2076 if (TNY_IS_ACCOUNT (instance)) {
2077 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2079 } else if (TNY_IS_FOLDER (instance)) {
2080 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2085 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2086 if (TNY_IS_ACCOUNT (instance)) {
2087 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2089 } else if (TNY_IS_FOLDER (instance)) {
2090 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2095 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2096 /* A mailbox is a fake folder with an @ in the middle of the name */
2097 if (!TNY_IS_FOLDER (instance) ||
2098 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2101 const gchar *folder_name;
2102 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2103 if (!folder_name || strchr (folder_name, '@') == NULL)
2109 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2110 if (TNY_IS_FOLDER (instance)) {
2111 /* Check folder rules */
2112 ModestTnyFolderRules rules;
2114 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2115 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2116 } else if (TNY_IS_ACCOUNT (instance)) {
2117 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2125 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2126 if (TNY_IS_FOLDER (instance)) {
2127 TnyFolderType guess_type;
2129 if (TNY_FOLDER_TYPE_NORMAL) {
2130 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2136 case TNY_FOLDER_TYPE_OUTBOX:
2137 case TNY_FOLDER_TYPE_SENT:
2138 case TNY_FOLDER_TYPE_DRAFTS:
2139 case TNY_FOLDER_TYPE_ARCHIVE:
2140 case TNY_FOLDER_TYPE_INBOX:
2143 case TNY_FOLDER_TYPE_UNKNOWN:
2144 case TNY_FOLDER_TYPE_NORMAL:
2150 } else if (TNY_IS_ACCOUNT (instance)) {
2155 if (retval && TNY_IS_FOLDER (instance)) {
2156 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2159 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2160 if (TNY_IS_FOLDER (instance)) {
2161 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2162 } else if (TNY_IS_ACCOUNT (instance)) {
2167 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2168 if (TNY_IS_FOLDER (instance)) {
2169 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2170 } else if (TNY_IS_ACCOUNT (instance)) {
2175 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2176 if (TNY_IS_FOLDER (instance)) {
2177 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2178 } else if (TNY_IS_ACCOUNT (instance)) {
2184 g_object_unref (instance);
2192 modest_folder_view_update_model (ModestFolderView *self,
2193 TnyAccountStore *account_store)
2195 ModestFolderViewPrivate *priv;
2196 GtkTreeModel *model /* , *old_model */;
2197 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2199 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2200 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2203 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2205 /* Notify that there is no folder selected */
2206 g_signal_emit (G_OBJECT(self),
2207 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2209 if (priv->cur_folder_store) {
2210 g_object_unref (priv->cur_folder_store);
2211 priv->cur_folder_store = NULL;
2214 /* FIXME: the local accounts are not shown when the query
2215 selects only the subscribed folders */
2216 #ifdef MODEST_TOOLKIT_HILDON2
2217 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2218 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2219 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2220 MODEST_FOLDER_PATH_SEPARATOR);
2222 model = tny_gtk_folder_store_tree_model_new (NULL);
2225 /* When the model is a list store (plain representation) the
2226 outbox is not a child of any account so we have to manually
2227 delete it because removing the local folders account won't
2228 delete it (because tny_folder_get_account() is not defined
2229 for a merge folder */
2230 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2231 TnyAccount *account;
2232 ModestTnyAccountStore *acc_store;
2234 acc_store = modest_runtime_get_account_store ();
2235 account = modest_tny_account_store_get_local_folders_account (acc_store);
2237 if (g_signal_handler_is_connected (account,
2238 priv->outbox_deleted_handler))
2239 g_signal_handler_disconnect (account,
2240 priv->outbox_deleted_handler);
2242 priv->outbox_deleted_handler =
2243 g_signal_connect (account,
2245 G_CALLBACK (on_outbox_deleted_cb),
2247 g_object_unref (account);
2250 /* Get the accounts: */
2251 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2253 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2255 sortable = gtk_tree_model_sort_new_with_model (model);
2256 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2258 GTK_SORT_ASCENDING);
2259 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2261 cmp_rows, NULL, NULL);
2263 /* Create filter model */
2264 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2265 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2271 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2272 #ifndef MODEST_TOOLKIT_HILDON2
2273 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2274 (GCallback) on_row_inserted_maybe_select_folder, self);
2277 g_object_unref (model);
2278 g_object_unref (filter_model);
2279 g_object_unref (sortable);
2281 /* Force a reselection of the INBOX next time the widget is shown */
2282 priv->reselect = TRUE;
2289 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2291 GtkTreeModel *model = NULL;
2292 TnyFolderStore *folder = NULL;
2294 ModestFolderView *tree_view = NULL;
2295 ModestFolderViewPrivate *priv = NULL;
2296 gboolean selected = FALSE;
2298 g_return_if_fail (sel);
2299 g_return_if_fail (user_data);
2301 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2303 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2305 tree_view = MODEST_FOLDER_VIEW (user_data);
2308 gtk_tree_model_get (model, &iter,
2309 INSTANCE_COLUMN, &folder,
2312 /* If the folder is the same do not notify */
2313 if (folder && priv->cur_folder_store == folder) {
2314 g_object_unref (folder);
2319 /* Current folder was unselected */
2320 if (priv->cur_folder_store) {
2321 /* We must do this firstly because a libtinymail-camel
2322 implementation detail. If we issue the signal
2323 before doing the sync_async, then that signal could
2324 cause (and it actually does it) a free of the
2325 summary of the folder (because the main window will
2326 clear the headers view */
2327 if (TNY_IS_FOLDER(priv->cur_folder_store))
2328 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2329 FALSE, NULL, NULL, NULL);
2331 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2332 priv->cur_folder_store, FALSE);
2334 g_object_unref (priv->cur_folder_store);
2335 priv->cur_folder_store = NULL;
2338 /* New current references */
2339 priv->cur_folder_store = folder;
2341 /* New folder has been selected. Do not notify if there is
2342 nothing new selected */
2344 g_signal_emit (G_OBJECT(tree_view),
2345 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2346 0, priv->cur_folder_store, TRUE);
2351 on_row_activated (GtkTreeView *treeview,
2352 GtkTreePath *treepath,
2353 GtkTreeViewColumn *column,
2356 GtkTreeModel *model = NULL;
2357 TnyFolderStore *folder = NULL;
2359 ModestFolderView *self = NULL;
2360 ModestFolderViewPrivate *priv = NULL;
2362 g_return_if_fail (treeview);
2363 g_return_if_fail (user_data);
2365 self = MODEST_FOLDER_VIEW (user_data);
2366 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2368 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2370 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2373 gtk_tree_model_get (model, &iter,
2374 INSTANCE_COLUMN, &folder,
2377 g_signal_emit (G_OBJECT(self),
2378 signals[FOLDER_ACTIVATED_SIGNAL],
2381 #ifdef MODEST_TOOLKIT_HILDON2
2382 HildonUIMode ui_mode;
2383 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2384 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2385 if (priv->cur_folder_store)
2386 g_object_unref (priv->cur_folder_store);
2387 priv->cur_folder_store = g_object_ref (folder);
2391 g_object_unref (folder);
2395 modest_folder_view_get_selected (ModestFolderView *self)
2397 ModestFolderViewPrivate *priv;
2399 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2401 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2402 if (priv->cur_folder_store)
2403 g_object_ref (priv->cur_folder_store);
2405 return priv->cur_folder_store;
2409 get_cmp_rows_type_pos (GObject *folder)
2411 /* Remote accounts -> Local account -> MMC account .*/
2414 if (TNY_IS_ACCOUNT (folder) &&
2415 modest_tny_account_is_virtual_local_folders (
2416 TNY_ACCOUNT (folder))) {
2418 } else if (TNY_IS_ACCOUNT (folder)) {
2419 TnyAccount *account = TNY_ACCOUNT (folder);
2420 const gchar *account_id = tny_account_get_id (account);
2421 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2427 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2428 return -1; /* Should never happen */
2433 inbox_is_special (TnyFolderStore *folder_store)
2435 gboolean is_special = TRUE;
2437 if (TNY_IS_FOLDER (folder_store)) {
2441 gchar *last_inbox_bar;
2443 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2444 downcase = g_utf8_strdown (id, -1);
2445 last_bar = g_strrstr (downcase, "/");
2447 last_inbox_bar = g_strrstr (downcase, "inbox/");
2448 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2459 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2461 TnyAccount *account;
2462 gboolean is_special;
2463 /* Inbox, Outbox, Drafts, Sent, User */
2466 if (!TNY_IS_FOLDER (folder_store))
2469 case TNY_FOLDER_TYPE_INBOX:
2471 account = tny_folder_get_account (folder_store);
2472 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2474 /* In inbox case we need to know if the inbox is really the top
2475 * inbox of the account, or if it's a submailbox inbox. To do
2476 * this we'll apply an heuristic rule: Find last "/" and check
2477 * if it's preceeded by another Inbox */
2478 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2479 g_object_unref (account);
2480 return is_special?0:4;
2483 case TNY_FOLDER_TYPE_OUTBOX:
2484 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2486 case TNY_FOLDER_TYPE_DRAFTS:
2488 account = tny_folder_get_account (folder_store);
2489 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2490 g_object_unref (account);
2491 return is_special?1:4;
2494 case TNY_FOLDER_TYPE_SENT:
2496 account = tny_folder_get_account (folder_store);
2497 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2498 g_object_unref (account);
2499 return is_special?3:4;
2508 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2510 const gchar *a1_name, *a2_name;
2512 a1_name = tny_account_get_name (a1);
2513 a2_name = tny_account_get_name (a2);
2515 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2519 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2521 TnyAccount *a1 = NULL, *a2 = NULL;
2524 if (TNY_IS_ACCOUNT (s1)) {
2525 a1 = TNY_ACCOUNT (g_object_ref (s1));
2526 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2527 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2530 if (TNY_IS_ACCOUNT (s2)) {
2531 a2 = TNY_ACCOUNT (g_object_ref (s2));
2532 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2533 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2550 /* First we sort with the type of account */
2551 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2555 cmp = compare_account_names (a1, a2);
2559 g_object_unref (a1);
2561 g_object_unref (a2);
2567 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2569 gint is_account1, is_account2;
2571 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2572 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2574 return is_account2 - is_account1;
2578 * This function orders the mail accounts according to these rules:
2579 * 1st - remote accounts
2580 * 2nd - local account
2584 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2588 gchar *name1 = NULL;
2589 gchar *name2 = NULL;
2590 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2591 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2592 GObject *folder1 = NULL;
2593 GObject *folder2 = NULL;
2595 gtk_tree_model_get (tree_model, iter1,
2596 NAME_COLUMN, &name1,
2598 INSTANCE_COLUMN, &folder1,
2600 gtk_tree_model_get (tree_model, iter2,
2601 NAME_COLUMN, &name2,
2602 TYPE_COLUMN, &type2,
2603 INSTANCE_COLUMN, &folder2,
2606 /* Return if we get no folder. This could happen when folder
2607 operations are happening. The model is updated after the
2608 folder copy/move actually occurs, so there could be
2609 situations where the model to be drawn is not correct */
2610 if (!folder1 || !folder2)
2613 /* Sort by type. First the special folders, then the archives */
2614 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2618 /* Now we sort using the account of each folder */
2619 if (TNY_IS_FOLDER_STORE (folder1) &&
2620 TNY_IS_FOLDER_STORE (folder2)) {
2621 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2625 /* Each group is preceeded by its account */
2626 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2631 /* Pure sort by name */
2632 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2635 g_object_unref(G_OBJECT(folder1));
2637 g_object_unref(G_OBJECT(folder2));
2645 /*****************************************************************************/
2646 /* DRAG and DROP stuff */
2647 /*****************************************************************************/
2649 * This function fills the #GtkSelectionData with the row and the
2650 * model that has been dragged. It's called when this widget is a
2651 * source for dnd after the event drop happened
2654 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2655 guint info, guint time, gpointer data)
2657 GtkTreeSelection *selection;
2658 GtkTreeModel *model;
2660 GtkTreePath *source_row;
2662 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2663 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2665 source_row = gtk_tree_model_get_path (model, &iter);
2666 gtk_tree_set_row_drag_data (selection_data,
2670 gtk_tree_path_free (source_row);
2674 typedef struct _DndHelper {
2675 ModestFolderView *folder_view;
2676 gboolean delete_source;
2677 GtkTreePath *source_row;
2681 dnd_helper_destroyer (DndHelper *helper)
2683 /* Free the helper */
2684 gtk_tree_path_free (helper->source_row);
2685 g_slice_free (DndHelper, helper);
2689 xfer_folder_cb (ModestMailOperation *mail_op,
2690 TnyFolder *new_folder,
2694 /* Select the folder */
2695 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2701 /* get the folder for the row the treepath refers to. */
2702 /* folder must be unref'd */
2703 static TnyFolderStore *
2704 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2707 TnyFolderStore *folder = NULL;
2709 if (gtk_tree_model_get_iter (model,&iter, path))
2710 gtk_tree_model_get (model, &iter,
2711 INSTANCE_COLUMN, &folder,
2718 * This function is used by drag_data_received_cb to manage drag and
2719 * drop of a header, i.e, and drag from the header view to the folder
2723 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2724 GtkTreeModel *dest_model,
2725 GtkTreePath *dest_row,
2726 GtkSelectionData *selection_data)
2728 TnyList *headers = NULL;
2729 TnyFolder *folder = NULL, *src_folder = NULL;
2730 TnyFolderType folder_type;
2731 GtkTreeIter source_iter, dest_iter;
2732 ModestWindowMgr *mgr = NULL;
2733 ModestWindow *main_win = NULL;
2734 gchar **uris, **tmp;
2736 /* Build the list of headers */
2737 mgr = modest_runtime_get_window_mgr ();
2738 headers = tny_simple_list_new ();
2739 uris = modest_dnd_selection_data_get_paths (selection_data);
2742 while (*tmp != NULL) {
2745 gboolean first = TRUE;
2748 path = gtk_tree_path_new_from_string (*tmp);
2749 gtk_tree_model_get_iter (source_model, &source_iter, path);
2750 gtk_tree_model_get (source_model, &source_iter,
2751 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2754 /* Do not enable d&d of headers already opened */
2755 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2756 tny_list_append (headers, G_OBJECT (header));
2758 if (G_UNLIKELY (first)) {
2759 src_folder = tny_header_get_folder (header);
2763 /* Free and go on */
2764 gtk_tree_path_free (path);
2765 g_object_unref (header);
2770 /* This could happen ig we perform a d&d very quickly over the
2771 same row that row could dissapear because message is
2773 if (!TNY_IS_FOLDER (src_folder))
2776 /* Get the target folder */
2777 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2778 gtk_tree_model_get (dest_model, &dest_iter,
2782 if (!folder || !TNY_IS_FOLDER(folder)) {
2783 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2787 folder_type = modest_tny_folder_guess_folder_type (folder);
2788 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2789 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2790 goto cleanup; /* cannot move messages there */
2793 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2794 /* g_warning ("folder not writable"); */
2795 goto cleanup; /* verboten! */
2798 /* Ask for confirmation to move */
2799 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2801 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2805 /* Transfer messages */
2806 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2811 if (G_IS_OBJECT (src_folder))
2812 g_object_unref (src_folder);
2813 if (G_IS_OBJECT(folder))
2814 g_object_unref (G_OBJECT (folder));
2815 if (G_IS_OBJECT(headers))
2816 g_object_unref (headers);
2820 TnyFolderStore *src_folder;
2821 TnyFolderStore *dst_folder;
2822 ModestFolderView *folder_view;
2827 dnd_folder_info_destroyer (DndFolderInfo *info)
2829 if (info->src_folder)
2830 g_object_unref (info->src_folder);
2831 if (info->dst_folder)
2832 g_object_unref (info->dst_folder);
2833 g_slice_free (DndFolderInfo, info);
2837 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2838 GtkWindow *parent_window,
2839 TnyAccount *account)
2842 modest_ui_actions_on_account_connection_error (parent_window, account);
2844 /* Free the helper & info */
2845 dnd_helper_destroyer (info->helper);
2846 dnd_folder_info_destroyer (info);
2850 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2852 GtkWindow *parent_window,
2853 TnyAccount *account,
2856 DndFolderInfo *info = NULL;
2857 ModestMailOperation *mail_op;
2859 info = (DndFolderInfo *) user_data;
2861 if (err || canceled) {
2862 dnd_on_connection_failed_destroyer (info, parent_window, account);
2866 /* Do the mail operation */
2867 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2868 modest_ui_actions_move_folder_error_handler,
2869 info->src_folder, NULL);
2871 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2874 /* Transfer the folder */
2875 modest_mail_operation_xfer_folder (mail_op,
2876 TNY_FOLDER (info->src_folder),
2878 info->helper->delete_source,
2880 info->helper->folder_view);
2883 g_object_unref (G_OBJECT (mail_op));
2884 dnd_helper_destroyer (info->helper);
2885 dnd_folder_info_destroyer (info);
2890 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2892 GtkWindow *parent_window,
2893 TnyAccount *account,
2896 DndFolderInfo *info = NULL;
2898 info = (DndFolderInfo *) user_data;
2900 if (err || canceled) {
2901 dnd_on_connection_failed_destroyer (info, parent_window, account);
2905 /* Connect to source folder and perform the copy/move */
2906 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2908 drag_and_drop_from_folder_view_src_folder_performer,
2913 * This function is used by drag_data_received_cb to manage drag and
2914 * drop of a folder, i.e, and drag from the folder view to the same
2918 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2919 GtkTreeModel *dest_model,
2920 GtkTreePath *dest_row,
2921 GtkSelectionData *selection_data,
2924 GtkTreeIter dest_iter, iter;
2925 TnyFolderStore *dest_folder = NULL;
2926 TnyFolderStore *folder = NULL;
2927 gboolean forbidden = FALSE;
2929 DndFolderInfo *info = NULL;
2931 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2933 g_warning ("%s: BUG: no main window", __FUNCTION__);
2934 dnd_helper_destroyer (helper);
2939 /* check the folder rules for the destination */
2940 folder = tree_path_to_folder (dest_model, dest_row);
2941 if (TNY_IS_FOLDER(folder)) {
2942 ModestTnyFolderRules rules =
2943 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2944 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2945 } else if (TNY_IS_FOLDER_STORE(folder)) {
2946 /* enable local root as destination for folders */
2947 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2948 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2951 g_object_unref (folder);
2954 /* check the folder rules for the source */
2955 folder = tree_path_to_folder (source_model, helper->source_row);
2956 if (TNY_IS_FOLDER(folder)) {
2957 ModestTnyFolderRules rules =
2958 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2959 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2962 g_object_unref (folder);
2966 /* Check if the drag is possible */
2967 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2969 modest_platform_run_information_dialog ((GtkWindow *) win,
2970 _("mail_in_ui_folder_move_target_error"),
2972 /* Restore the previous selection */
2973 folder = tree_path_to_folder (source_model, helper->source_row);
2975 if (TNY_IS_FOLDER (folder))
2976 modest_folder_view_select_folder (helper->folder_view,
2977 TNY_FOLDER (folder), FALSE);
2978 g_object_unref (folder);
2980 dnd_helper_destroyer (helper);
2985 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2986 gtk_tree_model_get (dest_model, &dest_iter,
2989 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2990 gtk_tree_model_get (source_model, &iter,
2994 /* Create the info for the performer */
2995 info = g_slice_new0 (DndFolderInfo);
2996 info->src_folder = g_object_ref (folder);
2997 info->dst_folder = g_object_ref (dest_folder);
2998 info->helper = helper;
3000 /* Connect to the destination folder and perform the copy/move */
3001 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3003 drag_and_drop_from_folder_view_dst_folder_performer,
3007 g_object_unref (dest_folder);
3008 g_object_unref (folder);
3012 * This function receives the data set by the "drag-data-get" signal
3013 * handler. This information comes within the #GtkSelectionData. This
3014 * function will manage both the drags of folders of the treeview and
3015 * drags of headers of the header view widget.
3018 on_drag_data_received (GtkWidget *widget,
3019 GdkDragContext *context,
3022 GtkSelectionData *selection_data,
3027 GtkWidget *source_widget;
3028 GtkTreeModel *dest_model, *source_model;
3029 GtkTreePath *source_row, *dest_row;
3030 GtkTreeViewDropPosition pos;
3031 gboolean delete_source = FALSE;
3032 gboolean success = FALSE;
3034 /* Do not allow further process */
3035 g_signal_stop_emission_by_name (widget, "drag-data-received");
3036 source_widget = gtk_drag_get_source_widget (context);
3038 /* Get the action */
3039 if (context->action == GDK_ACTION_MOVE) {
3040 delete_source = TRUE;
3042 /* Notify that there is no folder selected. We need to
3043 do this in order to update the headers view (and
3044 its monitors, because when moving, the old folder
3045 won't longer exist. We can not wait for the end of
3046 the operation, because the operation won't start if
3047 the folder is in use */
3048 if (source_widget == widget) {
3049 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3050 gtk_tree_selection_unselect_all (sel);
3054 /* Check if the get_data failed */
3055 if (selection_data == NULL || selection_data->length < 0)
3058 /* Select the destination model */
3059 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3061 /* Get the path to the destination row. Can not call
3062 gtk_tree_view_get_drag_dest_row() because the source row
3063 is not selected anymore */
3064 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3067 /* Only allow drops IN other rows */
3069 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3070 pos == GTK_TREE_VIEW_DROP_AFTER)
3074 /* Drags from the header view */
3075 if (source_widget != widget) {
3076 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3078 drag_and_drop_from_header_view (source_model,
3083 DndHelper *helper = NULL;
3085 /* Get the source model and row */
3086 gtk_tree_get_row_drag_data (selection_data,
3090 /* Create the helper */
3091 helper = g_slice_new0 (DndHelper);
3092 helper->delete_source = delete_source;
3093 helper->source_row = gtk_tree_path_copy (source_row);
3094 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3096 drag_and_drop_from_folder_view (source_model,
3102 gtk_tree_path_free (source_row);
3106 gtk_tree_path_free (dest_row);
3109 /* Finish the drag and drop */
3110 gtk_drag_finish (context, success, FALSE, time);
3114 * We define a "drag-drop" signal handler because we do not want to
3115 * use the default one, because the default one always calls
3116 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3117 * signal handler, because there we have all the information available
3118 * to know if the dnd was a success or not.
3121 drag_drop_cb (GtkWidget *widget,
3122 GdkDragContext *context,
3130 if (!context->targets)
3133 /* Check if we're dragging a folder row */
3134 target = gtk_drag_dest_find_target (widget, context, NULL);
3136 /* Request the data from the source. */
3137 gtk_drag_get_data(widget, context, target, time);
3143 * This function expands a node of a tree view if it's not expanded
3144 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3145 * does that, so that's why they're here.
3148 expand_row_timeout (gpointer data)
3150 GtkTreeView *tree_view = data;
3151 GtkTreePath *dest_path = NULL;
3152 GtkTreeViewDropPosition pos;
3153 gboolean result = FALSE;
3155 gdk_threads_enter ();
3157 gtk_tree_view_get_drag_dest_row (tree_view,
3162 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3163 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3164 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3165 gtk_tree_path_free (dest_path);
3169 gtk_tree_path_free (dest_path);
3174 gdk_threads_leave ();
3180 * This function is called whenever the pointer is moved over a widget
3181 * while dragging some data. It installs a timeout that will expand a
3182 * node of the treeview if not expanded yet. This function also calls
3183 * gdk_drag_status in order to set the suggested action that will be
3184 * used by the "drag-data-received" signal handler to know if we
3185 * should do a move or just a copy of the data.
3188 on_drag_motion (GtkWidget *widget,
3189 GdkDragContext *context,
3195 GtkTreeViewDropPosition pos;
3196 GtkTreePath *dest_row;
3197 GtkTreeModel *dest_model;
3198 ModestFolderViewPrivate *priv;
3199 GdkDragAction suggested_action;
3200 gboolean valid_location = FALSE;
3201 TnyFolderStore *folder = NULL;
3203 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3205 if (priv->timer_expander != 0) {
3206 g_source_remove (priv->timer_expander);
3207 priv->timer_expander = 0;
3210 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3215 /* Do not allow drops between folders */
3217 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3218 pos == GTK_TREE_VIEW_DROP_AFTER) {
3219 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3220 gdk_drag_status(context, 0, time);
3221 valid_location = FALSE;
3224 valid_location = TRUE;
3227 /* Check that the destination folder is writable */
3228 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3229 folder = tree_path_to_folder (dest_model, dest_row);
3230 if (folder && TNY_IS_FOLDER (folder)) {
3231 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3233 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3234 valid_location = FALSE;
3239 /* Expand the selected row after 1/2 second */
3240 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3241 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3243 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3245 /* Select the desired action. By default we pick MOVE */
3246 suggested_action = GDK_ACTION_MOVE;
3248 if (context->actions == GDK_ACTION_COPY)
3249 gdk_drag_status(context, GDK_ACTION_COPY, time);
3250 else if (context->actions == GDK_ACTION_MOVE)
3251 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3252 else if (context->actions & suggested_action)
3253 gdk_drag_status(context, suggested_action, time);
3255 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3259 g_object_unref (folder);
3261 gtk_tree_path_free (dest_row);
3263 g_signal_stop_emission_by_name (widget, "drag-motion");
3265 return valid_location;
3269 * This function sets the treeview as a source and a target for dnd
3270 * events. It also connects all the requirede signals.
3273 setup_drag_and_drop (GtkTreeView *self)
3275 /* Set up the folder view as a dnd destination. Set only the
3276 highlight flag, otherwise gtk will have a different
3278 #ifdef MODEST_TOOLKIT_HILDON2
3281 gtk_drag_dest_set (GTK_WIDGET (self),
3282 GTK_DEST_DEFAULT_HIGHLIGHT,
3283 folder_view_drag_types,
3284 G_N_ELEMENTS (folder_view_drag_types),
3285 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3287 g_signal_connect (G_OBJECT (self),
3288 "drag_data_received",
3289 G_CALLBACK (on_drag_data_received),
3293 /* Set up the treeview as a dnd source */
3294 gtk_drag_source_set (GTK_WIDGET (self),
3296 folder_view_drag_types,
3297 G_N_ELEMENTS (folder_view_drag_types),
3298 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3300 g_signal_connect (G_OBJECT (self),
3302 G_CALLBACK (on_drag_motion),
3305 g_signal_connect (G_OBJECT (self),
3307 G_CALLBACK (on_drag_data_get),
3310 g_signal_connect (G_OBJECT (self),
3312 G_CALLBACK (drag_drop_cb),
3317 * This function manages the navigation through the folders using the
3318 * keyboard or the hardware keys in the device
3321 on_key_pressed (GtkWidget *self,
3325 GtkTreeSelection *selection;
3327 GtkTreeModel *model;
3328 gboolean retval = FALSE;
3330 /* Up and Down are automatically managed by the treeview */
3331 if (event->keyval == GDK_Return) {
3332 /* Expand/Collapse the selected row */
3333 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3334 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3337 path = gtk_tree_model_get_path (model, &iter);
3339 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3340 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3342 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3343 gtk_tree_path_free (path);
3345 /* No further processing */
3353 * We listen to the changes in the local folder account name key,
3354 * because we want to show the right name in the view. The local
3355 * folder account name corresponds to the device name in the Maemo
3356 * version. We do this because we do not want to query gconf on each
3357 * tree view refresh. It's better to cache it and change whenever
3361 on_configuration_key_changed (ModestConf* conf,
3363 ModestConfEvent event,
3364 ModestConfNotificationId id,
3365 ModestFolderView *self)
3367 ModestFolderViewPrivate *priv;
3370 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3371 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3373 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3374 g_free (priv->local_account_name);
3376 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3377 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3379 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3380 MODEST_CONF_DEVICE_NAME, NULL);
3382 /* Force a redraw */
3383 #if GTK_CHECK_VERSION(2, 8, 0)
3384 GtkTreeViewColumn * tree_column;
3386 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3388 gtk_tree_view_column_queue_resize (tree_column);
3390 gtk_widget_queue_draw (GTK_WIDGET (self));
3396 modest_folder_view_set_style (ModestFolderView *self,
3397 ModestFolderViewStyle style)
3399 ModestFolderViewPrivate *priv;
3401 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3402 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3403 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3405 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3408 priv->style = style;
3412 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3413 const gchar *account_id)
3415 ModestFolderViewPrivate *priv;
3416 GtkTreeModel *model;
3418 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3420 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3422 /* This will be used by the filter_row callback,
3423 * to decided which rows to show: */
3424 if (priv->visible_account_id) {
3425 g_free (priv->visible_account_id);
3426 priv->visible_account_id = NULL;
3429 priv->visible_account_id = g_strdup (account_id);
3432 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3433 if (GTK_IS_TREE_MODEL_FILTER (model))
3434 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3436 /* Save settings to gconf */
3437 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3438 MODEST_CONF_FOLDER_VIEW_KEY);
3440 /* Notify observers */
3441 g_signal_emit (G_OBJECT(self),
3442 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3447 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3449 ModestFolderViewPrivate *priv;
3451 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3453 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3455 return (const gchar *) priv->visible_account_id;
3459 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3463 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3465 gtk_tree_model_get (model, iter,
3469 gboolean result = FALSE;
3470 if (type == TNY_FOLDER_TYPE_INBOX) {
3474 *inbox_iter = *iter;
3478 if (gtk_tree_model_iter_children (model, &child, iter)) {
3479 if (find_inbox_iter (model, &child, inbox_iter))
3483 } while (gtk_tree_model_iter_next (model, iter));
3492 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3494 #ifndef MODEST_TOOLKIT_HILDON2
3495 GtkTreeModel *model;
3496 GtkTreeIter iter, inbox_iter;
3497 GtkTreeSelection *sel;
3498 GtkTreePath *path = NULL;
3500 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3502 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3506 expand_root_items (self);
3507 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3509 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3510 g_warning ("%s: model is empty", __FUNCTION__);
3514 if (find_inbox_iter (model, &iter, &inbox_iter))
3515 path = gtk_tree_model_get_path (model, &inbox_iter);
3517 path = gtk_tree_path_new_first ();
3519 /* Select the row and free */
3520 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3521 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3522 gtk_tree_path_free (path);
3525 gtk_widget_grab_focus (GTK_WIDGET(self));
3532 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3537 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3538 TnyFolder* a_folder;
3541 gtk_tree_model_get (model, iter,
3542 INSTANCE_COLUMN, &a_folder,
3548 if (folder == a_folder) {
3549 g_object_unref (a_folder);
3550 *folder_iter = *iter;
3553 g_object_unref (a_folder);
3555 if (gtk_tree_model_iter_children (model, &child, iter)) {
3556 if (find_folder_iter (model, &child, folder_iter, folder))
3560 } while (gtk_tree_model_iter_next (model, iter));
3565 #ifndef MODEST_TOOLKIT_HILDON2
3567 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3570 ModestFolderView *self)
3572 ModestFolderViewPrivate *priv = NULL;
3573 GtkTreeSelection *sel;
3574 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3575 GObject *instance = NULL;
3577 if (!MODEST_IS_FOLDER_VIEW(self))
3580 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3582 priv->reexpand = TRUE;
3584 gtk_tree_model_get (tree_model, iter,
3586 INSTANCE_COLUMN, &instance,
3592 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3593 priv->folder_to_select = g_object_ref (instance);
3595 g_object_unref (instance);
3597 if (priv->folder_to_select) {
3599 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3602 path = gtk_tree_model_get_path (tree_model, iter);
3603 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3605 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3607 gtk_tree_selection_select_iter (sel, iter);
3608 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3610 gtk_tree_path_free (path);
3614 modest_folder_view_disable_next_folder_selection (self);
3616 /* Refilter the model */
3617 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3623 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3625 ModestFolderViewPrivate *priv;
3627 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3629 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3631 if (priv->folder_to_select)
3632 g_object_unref(priv->folder_to_select);
3634 priv->folder_to_select = NULL;
3638 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3639 gboolean after_change)
3641 GtkTreeModel *model;
3642 GtkTreeIter iter, folder_iter;
3643 GtkTreeSelection *sel;
3644 ModestFolderViewPrivate *priv = NULL;
3646 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3647 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3649 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3652 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3653 gtk_tree_selection_unselect_all (sel);
3655 if (priv->folder_to_select)
3656 g_object_unref(priv->folder_to_select);
3657 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3661 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3666 /* Refilter the model, before selecting the folder */
3667 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3669 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3670 g_warning ("%s: model is empty", __FUNCTION__);
3674 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3677 path = gtk_tree_model_get_path (model, &folder_iter);
3678 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3680 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3681 gtk_tree_selection_select_iter (sel, &folder_iter);
3682 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3684 gtk_tree_path_free (path);
3692 modest_folder_view_copy_selection (ModestFolderView *self)
3694 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3696 /* Copy selection */
3697 _clipboard_set_selected_data (self, FALSE);
3701 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3703 ModestFolderViewPrivate *priv = NULL;
3704 GtkTreeModel *model = NULL;
3705 const gchar **hidding = NULL;
3706 guint i, n_selected;
3708 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3709 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3711 /* Copy selection */
3712 if (!_clipboard_set_selected_data (folder_view, TRUE))
3715 /* Get hidding ids */
3716 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3718 /* Clear hidding array created by previous cut operation */
3719 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3721 /* Copy hidding array */
3722 priv->n_selected = n_selected;
3723 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3724 for (i=0; i < n_selected; i++)
3725 priv->hidding_ids[i] = g_strdup(hidding[i]);
3727 /* Hide cut folders */
3728 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3729 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3733 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3734 ModestFolderView *folder_view_dst)
3736 GtkTreeModel *filter_model = NULL;
3737 GtkTreeModel *model = NULL;
3738 GtkTreeModel *new_filter_model = NULL;
3740 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3741 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3744 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3745 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3747 /* Build new filter model */
3748 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3749 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3753 /* Set copied model */
3754 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3755 #ifndef MODEST_TOOLKIT_HILDON2
3756 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3757 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3761 g_object_unref (new_filter_model);
3765 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3768 GtkTreeModel *model = NULL;
3769 ModestFolderViewPrivate* priv;
3771 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3773 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3774 priv->show_non_move = show;
3775 /* modest_folder_view_update_model(folder_view, */
3776 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3778 /* Hide special folders */
3779 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3780 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3781 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3786 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3789 ModestFolderViewPrivate* priv;
3791 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3793 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3794 priv->show_message_count = show;
3796 g_object_set (G_OBJECT (priv->messages_renderer),
3797 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3801 /* Returns FALSE if it did not selected anything */
3803 _clipboard_set_selected_data (ModestFolderView *folder_view,
3806 ModestFolderViewPrivate *priv = NULL;
3807 TnyFolderStore *folder = NULL;
3808 gboolean retval = FALSE;
3810 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3811 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3813 /* Set selected data on clipboard */
3814 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3815 folder = modest_folder_view_get_selected (folder_view);
3817 /* Do not allow to select an account */
3818 if (TNY_IS_FOLDER (folder)) {
3819 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3824 g_object_unref (folder);
3830 _clear_hidding_filter (ModestFolderView *folder_view)
3832 ModestFolderViewPrivate *priv;
3835 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3836 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3838 if (priv->hidding_ids != NULL) {
3839 for (i=0; i < priv->n_selected; i++)
3840 g_free (priv->hidding_ids[i]);
3841 g_free(priv->hidding_ids);
3847 on_display_name_changed (ModestAccountMgr *mgr,
3848 const gchar *account,
3851 ModestFolderView *self;
3853 self = MODEST_FOLDER_VIEW (user_data);
3855 /* Force a redraw */
3856 #if GTK_CHECK_VERSION(2, 8, 0)
3857 GtkTreeViewColumn * tree_column;
3859 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3861 gtk_tree_view_column_queue_resize (tree_column);
3863 gtk_widget_queue_draw (GTK_WIDGET (self));
3868 modest_folder_view_set_cell_style (ModestFolderView *self,
3869 ModestFolderViewCellStyle cell_style)
3871 ModestFolderViewPrivate *priv = NULL;
3873 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3874 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3876 priv->cell_style = cell_style;
3878 g_object_set (G_OBJECT (priv->messages_renderer),
3879 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3882 gtk_widget_queue_draw (GTK_WIDGET (self));
3886 update_style (ModestFolderView *self)
3888 ModestFolderViewPrivate *priv;
3889 GdkColor style_color;
3890 PangoAttrList *attr_list;
3892 PangoAttribute *attr;
3894 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3895 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3899 attr_list = pango_attr_list_new ();
3900 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3901 gdk_color_parse ("grey", &style_color);
3903 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3904 pango_attr_list_insert (attr_list, attr);
3907 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3909 "SmallSystemFont", NULL,
3912 attr = pango_attr_font_desc_new (pango_font_description_copy
3913 (style->font_desc));
3914 pango_attr_list_insert (attr_list, attr);
3916 g_object_set (G_OBJECT (priv->messages_renderer),
3917 "foreground-gdk", &style_color,
3918 "foreground-set", TRUE,
3919 "attributes", attr_list,
3921 pango_attr_list_unref (attr_list);
3926 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3928 if (strcmp ("style", spec->name) == 0) {
3929 update_style (MODEST_FOLDER_VIEW (obj));
3930 gtk_widget_queue_draw (GTK_WIDGET (obj));
3935 modest_folder_view_set_filter (ModestFolderView *self,
3936 ModestFolderViewFilter filter)
3938 ModestFolderViewPrivate *priv;
3939 GtkTreeModel *filter_model;
3941 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3942 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3944 priv->filter |= filter;
3946 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3947 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3948 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3953 modest_folder_view_unset_filter (ModestFolderView *self,
3954 ModestFolderViewFilter filter)
3956 ModestFolderViewPrivate *priv;
3957 GtkTreeModel *filter_model;
3959 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3960 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3962 priv->filter &= ~filter;
3964 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3965 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3966 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3971 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3972 ModestTnyFolderRules rules)
3974 GtkTreeModel *filter_model;
3976 gboolean fulfil = FALSE;
3978 if (!get_inner_models (self, &filter_model, NULL, NULL))
3981 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3985 TnyFolderStore *folder;
3987 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3989 if (TNY_IS_FOLDER (folder)) {
3990 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3991 /* Folder rules are negative: non_writable, non_deletable... */
3992 if (!(folder_rules & rules))
3995 g_object_unref (folder);
3998 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4004 modest_folder_view_set_list_to_move (ModestFolderView *self,
4007 ModestFolderViewPrivate *priv;
4009 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4010 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4012 if (priv->list_to_move)
4013 g_object_unref (priv->list_to_move);
4016 g_object_ref (list);
4018 priv->list_to_move = list;
4022 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4024 ModestFolderViewPrivate *priv;
4026 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4027 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4030 g_free (priv->mailbox);
4032 priv->mailbox = g_strdup (mailbox);
4034 /* Notify observers */
4035 g_signal_emit (G_OBJECT(self),
4036 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4037 priv->visible_account_id);
4041 modest_folder_view_get_mailbox (ModestFolderView *self)
4043 ModestFolderViewPrivate *priv;
4045 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4046 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4048 return (const gchar *) priv->mailbox;