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);
100 static void modest_folder_view_dispose (GObject *obj);
102 static void tny_account_store_view_init (gpointer g,
103 gpointer iface_data);
105 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
106 TnyAccountStore *account_store);
108 static void on_selection_changed (GtkTreeSelection *sel,
111 static void on_row_activated (GtkTreeView *treeview,
113 GtkTreeViewColumn *column,
116 static void on_account_removed (TnyAccountStore *self,
120 static void on_account_inserted (TnyAccountStore *self,
124 static void on_account_changed (TnyAccountStore *self,
128 static gint cmp_rows (GtkTreeModel *tree_model,
133 static gboolean filter_row (GtkTreeModel *model,
137 static gboolean on_key_pressed (GtkWidget *self,
141 static void on_configuration_key_changed (ModestConf* conf,
143 ModestConfEvent event,
144 ModestConfNotificationId notification_id,
145 ModestFolderView *self);
148 static void on_drag_data_get (GtkWidget *widget,
149 GdkDragContext *context,
150 GtkSelectionData *selection_data,
155 static void on_drag_data_received (GtkWidget *widget,
156 GdkDragContext *context,
159 GtkSelectionData *selection_data,
164 static gboolean on_drag_motion (GtkWidget *widget,
165 GdkDragContext *context,
171 static void expand_root_items (ModestFolderView *self);
173 static gint expand_row_timeout (gpointer data);
175 static void setup_drag_and_drop (GtkTreeView *self);
177 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
180 static void _clear_hidding_filter (ModestFolderView *folder_view);
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
186 ModestFolderView *self);
189 static void on_display_name_changed (ModestAccountMgr *self,
190 const gchar *account,
192 static void update_style (ModestFolderView *self);
193 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
195 static gboolean inbox_is_special (TnyFolderStore *folder_store);
197 static gboolean get_inner_models (ModestFolderView *self,
198 GtkTreeModel **filter_model,
199 GtkTreeModel **sort_model,
200 GtkTreeModel **tny_model);
201 #ifdef MODEST_TOOLKIT_HILDON2
202 static void on_activity_changed (TnyGtkFolderListStore *store,
204 ModestFolderView *folder_view);
208 FOLDER_SELECTION_CHANGED_SIGNAL,
209 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
210 FOLDER_ACTIVATED_SIGNAL,
211 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
212 ACTIVITY_CHANGED_SIGNAL,
216 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
217 struct _ModestFolderViewPrivate {
218 TnyAccountStore *account_store;
219 TnyFolderStore *cur_folder_store;
221 TnyFolder *folder_to_select; /* folder to select after the next update */
223 /* not unref this object, its a singlenton */
224 ModestEmailClipboard *clipboard;
226 /* Filter tree model */
229 ModestFolderViewFilter filter;
231 TnyFolderStoreQuery *query;
233 guint timer_expander;
235 gchar *local_account_name;
236 gchar *visible_account_id;
238 ModestFolderViewStyle style;
239 ModestFolderViewCellStyle cell_style;
240 gboolean show_message_count;
242 gboolean reselect; /* we use this to force a reselection of the INBOX */
243 gboolean show_non_move;
244 TnyList *list_to_move;
245 gboolean reexpand; /* next time we expose, we'll expand all root folders */
247 GtkCellRenderer *messages_renderer;
249 GSList *signal_handlers;
250 GdkColor *active_color;
252 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
253 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
254 MODEST_TYPE_FOLDER_VIEW, \
255 ModestFolderViewPrivate))
257 static GObjectClass *parent_class = NULL;
259 static guint signals[LAST_SIGNAL] = {0};
262 modest_folder_view_get_type (void)
264 static GType my_type = 0;
266 static const GTypeInfo my_info = {
267 sizeof(ModestFolderViewClass),
268 NULL, /* base init */
269 NULL, /* base finalize */
270 (GClassInitFunc) modest_folder_view_class_init,
271 NULL, /* class finalize */
272 NULL, /* class data */
273 sizeof(ModestFolderView),
275 (GInstanceInitFunc) modest_folder_view_init,
279 static const GInterfaceInfo tny_account_store_view_info = {
280 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
281 NULL, /* interface_finalize */
282 NULL /* interface_data */
286 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
290 g_type_add_interface_static (my_type,
291 TNY_TYPE_ACCOUNT_STORE_VIEW,
292 &tny_account_store_view_info);
298 modest_folder_view_class_init (ModestFolderViewClass *klass)
300 GObjectClass *gobject_class;
301 GtkTreeViewClass *treeview_class;
302 gobject_class = (GObjectClass*) klass;
303 treeview_class = (GtkTreeViewClass*) klass;
305 parent_class = g_type_class_peek_parent (klass);
306 gobject_class->finalize = modest_folder_view_finalize;
307 gobject_class->finalize = modest_folder_view_dispose;
309 g_type_class_add_private (gobject_class,
310 sizeof(ModestFolderViewPrivate));
312 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
313 g_signal_new ("folder_selection_changed",
314 G_TYPE_FROM_CLASS (gobject_class),
316 G_STRUCT_OFFSET (ModestFolderViewClass,
317 folder_selection_changed),
319 modest_marshal_VOID__POINTER_BOOLEAN,
320 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
323 * This signal is emitted whenever the currently selected
324 * folder display name is computed. Note that the name could
325 * be different to the folder name, because we could append
326 * the unread messages count to the folder name to build the
327 * folder display name
329 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
330 g_signal_new ("folder-display-name-changed",
331 G_TYPE_FROM_CLASS (gobject_class),
333 G_STRUCT_OFFSET (ModestFolderViewClass,
334 folder_display_name_changed),
336 g_cclosure_marshal_VOID__STRING,
337 G_TYPE_NONE, 1, G_TYPE_STRING);
339 signals[FOLDER_ACTIVATED_SIGNAL] =
340 g_signal_new ("folder_activated",
341 G_TYPE_FROM_CLASS (gobject_class),
343 G_STRUCT_OFFSET (ModestFolderViewClass,
346 g_cclosure_marshal_VOID__POINTER,
347 G_TYPE_NONE, 1, G_TYPE_POINTER);
350 * Emitted whenever the visible account changes
352 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
353 g_signal_new ("visible-account-changed",
354 G_TYPE_FROM_CLASS (gobject_class),
356 G_STRUCT_OFFSET (ModestFolderViewClass,
357 visible_account_changed),
359 g_cclosure_marshal_VOID__STRING,
360 G_TYPE_NONE, 1, G_TYPE_STRING);
363 * Emitted when the underlying GtkListStore is updating data
365 signals[ACTIVITY_CHANGED_SIGNAL] =
366 g_signal_new ("activity-changed",
367 G_TYPE_FROM_CLASS (gobject_class),
369 G_STRUCT_OFFSET (ModestFolderViewClass,
372 g_cclosure_marshal_VOID__BOOLEAN,
373 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
375 treeview_class->select_cursor_parent = NULL;
377 #ifdef MODEST_TOOLKIT_HILDON2
378 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
384 /* Retrieves the filter, sort and tny models of the folder view. If
385 any of these does not exist then it returns FALSE */
387 get_inner_models (ModestFolderView *self,
388 GtkTreeModel **filter_model,
389 GtkTreeModel **sort_model,
390 GtkTreeModel **tny_model)
392 GtkTreeModel *s_model, *f_model, *t_model;
394 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
395 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
396 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
400 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
401 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
402 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
406 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
410 *filter_model = f_model;
412 *sort_model = s_model;
414 *tny_model = t_model;
419 /* Simplify checks for NULLs: */
421 strings_are_equal (const gchar *a, const gchar *b)
427 return (strcmp (a, b) == 0);
434 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
436 GObject *instance = NULL;
438 gtk_tree_model_get (model, iter,
439 INSTANCE_COLUMN, &instance,
443 return FALSE; /* keep walking */
445 if (!TNY_IS_ACCOUNT (instance)) {
446 g_object_unref (instance);
447 return FALSE; /* keep walking */
450 /* Check if this is the looked-for account: */
451 TnyAccount *this_account = TNY_ACCOUNT (instance);
452 TnyAccount *account = TNY_ACCOUNT (data);
454 const gchar *this_account_id = tny_account_get_id(this_account);
455 const gchar *account_id = tny_account_get_id(account);
456 g_object_unref (instance);
459 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
460 if (strings_are_equal(this_account_id, account_id)) {
461 /* Tell the model that the data has changed, so that
462 * it calls the cell_data_func callbacks again: */
463 /* TODO: This does not seem to actually cause the new string to be shown: */
464 gtk_tree_model_row_changed (model, path, iter);
466 return TRUE; /* stop walking */
469 return FALSE; /* keep walking */
474 ModestFolderView *self;
475 gchar *previous_name;
476 } GetMmcAccountNameData;
479 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
481 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
483 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
485 if (!strings_are_equal (
486 tny_account_get_name(TNY_ACCOUNT(account)),
487 data->previous_name)) {
489 /* Tell the model that the data has changed, so that
490 * it calls the cell_data_func callbacks again: */
491 ModestFolderView *self = data->self;
492 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
494 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
497 g_free (data->previous_name);
498 g_slice_free (GetMmcAccountNameData, data);
502 convert_parent_folders_to_dots (gchar **item_name)
505 gint n_inbox_parents = 0;
508 gchar *last_separator;
510 if (item_name == NULL)
513 path_start = *item_name;
514 for (c = *item_name; *c != '\0'; c++) {
515 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
517 if (c != path_start) {
518 compare = g_strndup (path_start, c - path_start);
519 compare = g_strstrip (compare);
520 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
530 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
531 if (last_separator != NULL) {
532 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
539 buffer = g_string_new ("");
540 for (i = 0; i < n_parents - n_inbox_parents; i++) {
541 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
543 buffer = g_string_append (buffer, last_separator);
545 *item_name = g_string_free (buffer, FALSE);
551 format_compact_style (gchar **item_name,
553 const gchar *mailbox,
555 gboolean multiaccount,
556 gboolean *use_markup)
560 TnyFolderType folder_type;
562 if (!TNY_IS_FOLDER (instance))
565 folder = (TnyFolder *) instance;
567 folder_type = tny_folder_get_folder_type (folder);
568 is_special = (get_cmp_pos (folder_type, folder)!= 4);
571 /* Remove mailbox prefix if any */
572 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
573 if (g_str_has_prefix (*item_name, prefix)) {
574 gchar *new_item_name;
576 new_item_name = g_strdup (*item_name + strlen (prefix));
577 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
578 g_free (new_item_name);
579 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
582 *item_name = new_item_name;
584 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
587 *item_name = g_strdup (_("mcen_me_folder_inbox"));
590 if (!is_special || multiaccount) {
591 TnyAccount *account = tny_folder_get_account (folder);
592 const gchar *folder_name;
593 gboolean concat_folder_name = FALSE;
596 /* Should not happen */
600 /* convert parent folders to dots */
601 convert_parent_folders_to_dots (item_name);
603 folder_name = tny_folder_get_name (folder);
604 if (g_str_has_suffix (*item_name, folder_name)) {
605 gchar *offset = g_strrstr (*item_name, folder_name);
607 concat_folder_name = TRUE;
610 buffer = g_string_new ("");
612 buffer = g_string_append (buffer, *item_name);
613 if (concat_folder_name) {
614 buffer = g_string_append (buffer, folder_name);
617 g_object_unref (account);
619 *item_name = g_string_free (buffer, FALSE);
627 text_cell_data (GtkTreeViewColumn *column,
628 GtkCellRenderer *renderer,
629 GtkTreeModel *tree_model,
633 ModestFolderViewPrivate *priv;
634 GObject *rendobj = (GObject *) renderer;
636 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
637 GObject *instance = NULL;
638 gboolean use_markup = FALSE;
640 gtk_tree_model_get (tree_model, iter,
643 INSTANCE_COLUMN, &instance,
645 if (!fname || !instance)
648 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
649 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
651 gchar *item_name = NULL;
652 gint item_weight = 400;
654 if (type != TNY_FOLDER_TYPE_ROOT) {
659 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
660 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
663 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
664 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
666 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
669 /* Sometimes an special folder is reported by the server as
670 NORMAL, like some versions of Dovecot */
671 if (type == TNY_FOLDER_TYPE_NORMAL ||
672 type == TNY_FOLDER_TYPE_UNKNOWN) {
673 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
677 /* note: we cannot reliably get the counts from the
678 * tree model, we need to use explicit calls on
679 * tny_folder for some reason. Select the number to
680 * show: the unread or unsent messages. in case of
681 * outbox/drafts, show all */
682 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
683 (type == TNY_FOLDER_TYPE_OUTBOX) ||
684 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
685 number = tny_folder_get_all_count (TNY_FOLDER(instance));
688 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
692 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
693 item_name = g_strdup (fname);
700 /* Use bold font style if there are unread or unset messages */
702 if (priv->show_message_count) {
703 item_name = g_strdup_printf ("%s (%d)", fname, number);
705 item_name = g_strdup (fname);
709 item_name = g_strdup (fname);
714 } else if (TNY_IS_ACCOUNT (instance)) {
715 /* If it's a server account */
716 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
717 item_name = g_strdup (priv->local_account_name);
719 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
720 /* fname is only correct when the items are first
721 * added to the model, not when the account is
722 * changed later, so get the name from the account
724 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
727 item_name = g_strdup (fname);
733 item_name = g_strdup ("unknown");
735 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
736 gboolean multiaccount;
738 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
739 /* Convert item_name to markup */
740 format_compact_style (&item_name, instance, priv->mailbox,
742 multiaccount, &use_markup);
745 if (item_name && item_weight) {
746 /* Set the name in the treeview cell: */
747 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 && priv->active_color) {
748 g_object_set (rendobj,
751 "foreground-set", TRUE,
752 "foreground-gdk", priv->active_color,
755 g_object_set (rendobj,
757 "foreground-set", FALSE,
759 "weight", item_weight,
763 /* Notify display name observers */
764 /* TODO: What listens for this signal, and how can it use only the new name? */
765 if (((GObject *) priv->cur_folder_store) == instance) {
766 g_signal_emit (G_OBJECT(self),
767 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
774 /* If it is a Memory card account, make sure that we have the correct name.
775 * This function will be trigerred again when the name has been retrieved: */
776 if (TNY_IS_STORE_ACCOUNT (instance) &&
777 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
779 /* Get the account name asynchronously: */
780 GetMmcAccountNameData *callback_data =
781 g_slice_new0(GetMmcAccountNameData);
782 callback_data->self = self;
784 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
786 callback_data->previous_name = g_strdup (name);
788 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
789 on_get_mmc_account_name, callback_data);
793 g_object_unref (G_OBJECT (instance));
799 messages_cell_data (GtkTreeViewColumn *column,
800 GtkCellRenderer *renderer,
801 GtkTreeModel *tree_model,
805 ModestFolderView *self;
806 ModestFolderViewPrivate *priv;
807 GObject *rendobj = (GObject *) renderer;
808 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
809 GObject *instance = NULL;
810 gchar *item_name = NULL;
812 gtk_tree_model_get (tree_model, iter,
814 INSTANCE_COLUMN, &instance,
819 self = MODEST_FOLDER_VIEW (data);
820 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
823 if (type != TNY_FOLDER_TYPE_ROOT) {
828 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
829 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
832 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
834 /* Sometimes an special folder is reported by the server as
835 NORMAL, like some versions of Dovecot */
836 if (type == TNY_FOLDER_TYPE_NORMAL ||
837 type == TNY_FOLDER_TYPE_UNKNOWN) {
838 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
842 /* note: we cannot reliably get the counts from the tree model, we need
843 * to use explicit calls on tny_folder for some reason.
845 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
846 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
847 (type == TNY_FOLDER_TYPE_OUTBOX) ||
848 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
849 number = tny_folder_get_all_count (TNY_FOLDER(instance));
852 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
856 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
858 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
859 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
865 item_name = g_strdup ("");
868 /* Set the name in the treeview cell: */
869 g_object_set (rendobj,"text", item_name, NULL);
877 g_object_unref (G_OBJECT (instance));
883 GdkPixbuf *pixbuf_open;
884 GdkPixbuf *pixbuf_close;
888 static inline GdkPixbuf *
889 get_composite_pixbuf (const gchar *icon_name,
891 GdkPixbuf *base_pixbuf)
893 GdkPixbuf *emblem, *retval = NULL;
895 emblem = modest_platform_get_icon (icon_name, size);
897 retval = gdk_pixbuf_copy (base_pixbuf);
898 gdk_pixbuf_composite (emblem, retval, 0, 0,
899 MIN (gdk_pixbuf_get_width (emblem),
900 gdk_pixbuf_get_width (retval)),
901 MIN (gdk_pixbuf_get_height (emblem),
902 gdk_pixbuf_get_height (retval)),
903 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
904 g_object_unref (emblem);
909 static inline ThreePixbufs *
910 get_composite_icons (const gchar *icon_code,
912 GdkPixbuf **pixbuf_open,
913 GdkPixbuf **pixbuf_close)
915 ThreePixbufs *retval;
919 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
921 *pixbuf = gdk_pixbuf_copy (icon);
927 if (!*pixbuf_open && pixbuf && *pixbuf)
928 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
932 if (!*pixbuf_close && pixbuf && *pixbuf)
933 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
937 retval = g_slice_new0 (ThreePixbufs);
939 retval->pixbuf = g_object_ref (*pixbuf);
941 retval->pixbuf_open = g_object_ref (*pixbuf_open);
943 retval->pixbuf_close = g_object_ref (*pixbuf_close);
948 static inline ThreePixbufs *
949 get_account_protocol_pixbufs (ModestFolderView *folder_view,
950 ModestProtocolType protocol_type,
953 ModestProtocol *protocol;
954 const GdkPixbuf *pixbuf = NULL;
955 ModestFolderViewPrivate *priv;
957 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
959 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
962 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
963 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
964 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
965 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
966 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
967 object, FOLDER_ICON_SIZE);
971 ThreePixbufs *retval;
972 retval = g_slice_new0 (ThreePixbufs);
973 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
974 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
975 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
982 static inline ThreePixbufs*
983 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
985 TnyAccount *account = NULL;
986 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
987 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
988 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
989 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
990 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
992 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
993 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
994 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
995 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
996 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
998 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
999 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1000 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1001 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1002 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1004 ThreePixbufs *retval = NULL;
1006 if (TNY_IS_ACCOUNT (instance)) {
1007 account = g_object_ref (instance);
1008 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1009 account = tny_folder_get_account (TNY_FOLDER (instance));
1013 ModestProtocolType account_store_protocol;
1015 account_store_protocol = modest_tny_account_get_protocol_type (account);
1016 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1017 g_object_unref (account);
1023 /* Sometimes an special folder is reported by the server as
1024 NORMAL, like some versions of Dovecot */
1025 if (type == TNY_FOLDER_TYPE_NORMAL ||
1026 type == TNY_FOLDER_TYPE_UNKNOWN) {
1027 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1030 /* It's not enough with check the folder type. We need to
1031 ensure that we're not giving a special folder icon to a
1032 normal folder with the same name than a special folder */
1033 if (TNY_IS_FOLDER (instance) &&
1034 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1035 type = TNY_FOLDER_TYPE_NORMAL;
1037 /* Remote folders should not be treated as special folders */
1038 if (TNY_IS_FOLDER_STORE (instance) &&
1039 !TNY_IS_ACCOUNT (instance) &&
1040 type != TNY_FOLDER_TYPE_INBOX &&
1041 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1042 #ifdef MODEST_TOOLKIT_HILDON2
1043 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1046 &anorm_pixbuf_close);
1048 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1050 &normal_pixbuf_open,
1051 &normal_pixbuf_close);
1057 case TNY_FOLDER_TYPE_INVALID:
1058 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1061 case TNY_FOLDER_TYPE_ROOT:
1062 if (TNY_IS_ACCOUNT (instance)) {
1064 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1065 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1068 &avirt_pixbuf_close);
1070 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1072 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1073 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1076 &ammc_pixbuf_close);
1078 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1081 &anorm_pixbuf_close);
1086 case TNY_FOLDER_TYPE_INBOX:
1087 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1090 &inbox_pixbuf_close);
1092 case TNY_FOLDER_TYPE_OUTBOX:
1093 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1095 &outbox_pixbuf_open,
1096 &outbox_pixbuf_close);
1098 case TNY_FOLDER_TYPE_JUNK:
1099 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1102 &junk_pixbuf_close);
1104 case TNY_FOLDER_TYPE_SENT:
1105 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1108 &sent_pixbuf_close);
1110 case TNY_FOLDER_TYPE_TRASH:
1111 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1114 &trash_pixbuf_close);
1116 case TNY_FOLDER_TYPE_DRAFTS:
1117 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1120 &draft_pixbuf_close);
1122 case TNY_FOLDER_TYPE_ARCHIVE:
1123 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1128 case TNY_FOLDER_TYPE_NORMAL:
1130 /* Memory card folders could have an special icon */
1131 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1132 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1137 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1139 &normal_pixbuf_open,
1140 &normal_pixbuf_close);
1149 free_pixbufs (ThreePixbufs *pixbufs)
1151 if (pixbufs->pixbuf)
1152 g_object_unref (pixbufs->pixbuf);
1153 if (pixbufs->pixbuf_open)
1154 g_object_unref (pixbufs->pixbuf_open);
1155 if (pixbufs->pixbuf_close)
1156 g_object_unref (pixbufs->pixbuf_close);
1157 g_slice_free (ThreePixbufs, pixbufs);
1161 icon_cell_data (GtkTreeViewColumn *column,
1162 GtkCellRenderer *renderer,
1163 GtkTreeModel *tree_model,
1167 GObject *rendobj = NULL, *instance = NULL;
1168 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1169 gboolean has_children;
1170 ThreePixbufs *pixbufs;
1171 ModestFolderView *folder_view = (ModestFolderView *) data;
1173 rendobj = (GObject *) renderer;
1175 gtk_tree_model_get (tree_model, iter,
1177 INSTANCE_COLUMN, &instance,
1183 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1184 pixbufs = get_folder_icons (folder_view, type, instance);
1185 g_object_unref (instance);
1188 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1191 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1192 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1195 free_pixbufs (pixbufs);
1199 add_columns (GtkWidget *treeview)
1201 GtkTreeViewColumn *column;
1202 GtkCellRenderer *renderer;
1203 GtkTreeSelection *sel;
1204 ModestFolderViewPrivate *priv;
1206 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1209 column = gtk_tree_view_column_new ();
1211 /* Set icon and text render function */
1212 renderer = gtk_cell_renderer_pixbuf_new();
1213 #ifdef MODEST_TOOLKIT_HILDON2
1214 g_object_set (renderer,
1215 "xpad", MODEST_MARGIN_DEFAULT,
1216 "ypad", MODEST_MARGIN_DEFAULT,
1219 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1220 gtk_tree_view_column_set_cell_data_func(column, renderer,
1221 icon_cell_data, treeview, NULL);
1223 renderer = gtk_cell_renderer_text_new();
1224 g_object_set (renderer,
1225 #ifdef MODEST_TOOLKIT_HILDON2
1226 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1227 "ypad", MODEST_MARGIN_DEFAULT,
1228 "xpad", MODEST_MARGIN_DEFAULT,
1230 "ellipsize", PANGO_ELLIPSIZE_END,
1232 "ellipsize-set", TRUE, NULL);
1233 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1234 gtk_tree_view_column_set_cell_data_func(column, renderer,
1235 text_cell_data, treeview, NULL);
1237 priv->messages_renderer = gtk_cell_renderer_text_new ();
1238 g_object_set (priv->messages_renderer,
1239 #ifdef MODEST_TOOLKIT_HILDON2
1241 "ypad", MODEST_MARGIN_DEFAULT,
1242 "xpad", MODEST_MARGIN_DOUBLE,
1244 "scale", PANGO_SCALE_X_SMALL,
1247 "alignment", PANGO_ALIGN_RIGHT,
1251 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1252 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1253 messages_cell_data, treeview, NULL);
1255 /* Set selection mode */
1256 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1257 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1259 /* Set treeview appearance */
1260 gtk_tree_view_column_set_spacing (column, 2);
1261 gtk_tree_view_column_set_resizable (column, TRUE);
1262 gtk_tree_view_column_set_fixed_width (column, TRUE);
1263 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1264 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1267 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1271 modest_folder_view_init (ModestFolderView *obj)
1273 ModestFolderViewPrivate *priv;
1276 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1278 priv->timer_expander = 0;
1279 priv->account_store = NULL;
1281 priv->do_refresh = TRUE;
1282 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1283 priv->cur_folder_store = NULL;
1284 priv->visible_account_id = NULL;
1285 priv->mailbox = NULL;
1286 priv->folder_to_select = NULL;
1287 priv->reexpand = TRUE;
1288 priv->signal_handlers = 0;
1290 /* Initialize the local account name */
1291 conf = modest_runtime_get_conf();
1292 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1294 /* Init email clipboard */
1295 priv->clipboard = modest_runtime_get_email_clipboard ();
1296 priv->hidding_ids = NULL;
1297 priv->n_selected = 0;
1298 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1299 priv->reselect = FALSE;
1300 priv->show_non_move = TRUE;
1301 priv->list_to_move = NULL;
1302 priv->show_message_count = TRUE;
1304 /* Build treeview */
1305 add_columns (GTK_WIDGET (obj));
1307 /* Setup drag and drop */
1308 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1310 /* Connect signals */
1311 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1312 G_OBJECT (obj), "key-press-event",
1313 G_CALLBACK (on_key_pressed), NULL);
1315 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1316 (GObject*) modest_runtime_get_account_mgr (),
1317 "display_name_changed",
1318 G_CALLBACK (on_display_name_changed),
1322 * Track changes in the local account name (in the device it
1323 * will be the device name)
1325 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1328 G_CALLBACK(on_configuration_key_changed),
1331 priv->active_color = NULL;
1334 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1335 G_OBJECT (obj), "notify::style",
1336 G_CALLBACK (on_notify_style), (gpointer) obj);
1340 tny_account_store_view_init (gpointer g, gpointer iface_data)
1342 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1344 klass->set_account_store = modest_folder_view_set_account_store;
1348 modest_folder_view_dispose (GObject *obj)
1350 static gboolean disposed = FALSE;
1351 ModestFolderViewPrivate *priv;
1356 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1358 #ifdef MODEST_TOOLKIT_HILDON2
1359 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1362 /* Free external references */
1363 if (priv->account_store) {
1364 g_object_unref (G_OBJECT(priv->account_store));
1365 priv->account_store = NULL;
1369 g_object_unref (G_OBJECT (priv->query));
1373 if (priv->folder_to_select) {
1374 g_object_unref (G_OBJECT(priv->folder_to_select));
1375 priv->folder_to_select = NULL;
1378 if (priv->cur_folder_store) {
1379 g_object_unref (priv->cur_folder_store);
1380 priv->cur_folder_store = NULL;
1383 if (priv->list_to_move) {
1384 g_object_unref (priv->list_to_move);
1385 priv->list_to_move = NULL;
1390 modest_folder_view_finalize (GObject *obj)
1392 ModestFolderViewPrivate *priv;
1394 g_return_if_fail (obj);
1396 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1398 if (priv->active_color) {
1399 gdk_color_free (priv->active_color);
1400 priv->active_color = NULL;
1403 if (priv->timer_expander != 0) {
1404 g_source_remove (priv->timer_expander);
1405 priv->timer_expander = 0;
1408 g_free (priv->local_account_name);
1409 g_free (priv->visible_account_id);
1410 g_free (priv->mailbox);
1412 /* Clear hidding array created by cut operation */
1413 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1415 G_OBJECT_CLASS(parent_class)->finalize (obj);
1420 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1422 ModestFolderViewPrivate *priv;
1425 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1426 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1428 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1429 device = tny_account_store_get_device (account_store);
1431 if (G_UNLIKELY (priv->account_store)) {
1433 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1434 G_OBJECT (priv->account_store),
1435 "account_inserted"))
1436 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1437 G_OBJECT (priv->account_store),
1438 "account_inserted");
1439 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1440 G_OBJECT (priv->account_store),
1442 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1443 G_OBJECT (priv->account_store),
1445 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1446 G_OBJECT (priv->account_store),
1448 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1449 G_OBJECT (priv->account_store),
1451 g_object_unref (G_OBJECT (priv->account_store));
1454 priv->account_store = g_object_ref (G_OBJECT (account_store));
1456 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1457 G_OBJECT(account_store), "account_removed",
1458 G_CALLBACK (on_account_removed), self);
1460 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1461 G_OBJECT(account_store), "account_inserted",
1462 G_CALLBACK (on_account_inserted), self);
1464 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1465 G_OBJECT(account_store), "account_changed",
1466 G_CALLBACK (on_account_changed), self);
1468 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1469 priv->reselect = FALSE;
1470 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1472 g_object_unref (G_OBJECT (device));
1476 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1479 ModestFolderView *self;
1480 GtkTreeModel *model, *filter_model;
1483 self = MODEST_FOLDER_VIEW (user_data);
1485 if (!get_inner_models (self, &filter_model, NULL, &model))
1488 /* Remove outbox from model */
1489 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1490 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1491 g_object_unref (outbox);
1494 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1498 on_account_inserted (TnyAccountStore *account_store,
1499 TnyAccount *account,
1502 ModestFolderViewPrivate *priv;
1503 GtkTreeModel *model, *filter_model;
1505 /* Ignore transport account insertions, we're not showing them
1506 in the folder view */
1507 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1510 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1513 /* If we're adding a new account, and there is no previous
1514 one, we need to select the visible server account */
1515 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1516 !priv->visible_account_id)
1517 modest_widget_memory_restore (modest_runtime_get_conf(),
1518 G_OBJECT (user_data),
1519 MODEST_CONF_FOLDER_VIEW_KEY);
1523 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1524 &filter_model, NULL, &model))
1527 /* Insert the account in the model */
1528 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1530 /* When the model is a list store (plain representation) the
1531 outbox is not a child of any account so we have to manually
1532 delete it because removing the local folders account won't
1533 delete it (because tny_folder_get_account() is not defined
1534 for a merge folder */
1535 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1536 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1537 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1538 (GObject*) account, "outbox-deleted",
1539 G_CALLBACK (on_outbox_deleted_cb),
1543 /* Refilter the model */
1544 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1549 same_account_selected (ModestFolderView *self,
1550 TnyAccount *account)
1552 ModestFolderViewPrivate *priv;
1553 gboolean same_account = FALSE;
1555 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1557 if (priv->cur_folder_store) {
1558 TnyAccount *selected_folder_account = NULL;
1560 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1561 selected_folder_account =
1562 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1564 selected_folder_account =
1565 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1568 if (selected_folder_account == account)
1569 same_account = TRUE;
1571 g_object_unref (selected_folder_account);
1573 return same_account;
1578 * Selects the first inbox or the local account in an idle
1581 on_idle_select_first_inbox_or_local (gpointer user_data)
1583 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1585 gdk_threads_enter ();
1586 modest_folder_view_select_first_inbox_or_local (self);
1587 gdk_threads_leave ();
1593 on_account_changed (TnyAccountStore *account_store,
1594 TnyAccount *tny_account,
1597 ModestFolderView *self;
1598 ModestFolderViewPrivate *priv;
1599 GtkTreeModel *model, *filter_model;
1600 GtkTreeSelection *sel;
1601 gboolean same_account;
1603 /* Ignore transport account insertions, we're not showing them
1604 in the folder view */
1605 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1608 self = MODEST_FOLDER_VIEW (user_data);
1609 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1611 /* Get the inner model */
1612 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1613 &filter_model, NULL, &model))
1616 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1618 /* Invalidate the cur_folder_store only if the selected folder
1619 belongs to the account that is being removed */
1620 same_account = same_account_selected (self, tny_account);
1622 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1623 gtk_tree_selection_unselect_all (sel);
1626 /* Remove the account from the model */
1627 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1629 /* Insert the account in the model */
1630 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1632 /* Refilter the model */
1633 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1635 /* Select the first INBOX if the currently selected folder
1636 belongs to the account that is being deleted */
1637 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1638 g_idle_add (on_idle_select_first_inbox_or_local, self);
1642 on_account_removed (TnyAccountStore *account_store,
1643 TnyAccount *account,
1646 ModestFolderView *self = NULL;
1647 ModestFolderViewPrivate *priv;
1648 GtkTreeModel *model, *filter_model;
1649 GtkTreeSelection *sel = NULL;
1650 gboolean same_account = FALSE;
1652 /* Ignore transport account removals, we're not showing them
1653 in the folder view */
1654 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1657 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1658 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1662 self = MODEST_FOLDER_VIEW (user_data);
1663 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1665 /* Invalidate the cur_folder_store only if the selected folder
1666 belongs to the account that is being removed */
1667 same_account = same_account_selected (self, account);
1669 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1670 gtk_tree_selection_unselect_all (sel);
1673 /* Invalidate row to select only if the folder to select
1674 belongs to the account that is being removed*/
1675 if (priv->folder_to_select) {
1676 TnyAccount *folder_to_select_account = NULL;
1678 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1679 if (folder_to_select_account == account) {
1680 modest_folder_view_disable_next_folder_selection (self);
1681 g_object_unref (priv->folder_to_select);
1682 priv->folder_to_select = NULL;
1684 g_object_unref (folder_to_select_account);
1687 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1688 &filter_model, NULL, &model))
1691 /* Disconnect the signal handler */
1692 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1693 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1694 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject*) account, "outbox-deleted"))
1695 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1696 (GObject *) account,
1700 /* Remove the account from the model */
1701 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1703 /* If the removed account is the currently viewed one then
1704 clear the configuration value. The new visible account will be the default account */
1705 if (priv->visible_account_id &&
1706 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1708 /* Clear the current visible account_id */
1709 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1710 modest_folder_view_set_mailbox (self, NULL);
1712 /* Call the restore method, this will set the new visible account */
1713 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1714 MODEST_CONF_FOLDER_VIEW_KEY);
1717 /* Refilter the model */
1718 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1720 /* Select the first INBOX if the currently selected folder
1721 belongs to the account that is being deleted */
1723 g_idle_add (on_idle_select_first_inbox_or_local, self);
1727 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1729 GtkTreeViewColumn *col;
1731 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1733 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1735 g_printerr ("modest: failed get column for title\n");
1739 gtk_tree_view_column_set_title (col, title);
1740 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1745 modest_folder_view_on_map (ModestFolderView *self,
1746 GdkEventExpose *event,
1749 ModestFolderViewPrivate *priv;
1751 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1753 /* This won't happen often */
1754 if (G_UNLIKELY (priv->reselect)) {
1755 /* Select the first inbox or the local account if not found */
1757 /* TODO: this could cause a lock at startup, so we
1758 comment it for the moment. We know that this will
1759 be a bug, because the INBOX is not selected, but we
1760 need to rewrite some parts of Modest to avoid the
1761 deathlock situation */
1762 /* TODO: check if this is still the case */
1763 priv->reselect = FALSE;
1764 modest_folder_view_select_first_inbox_or_local (self);
1765 /* Notify the display name observers */
1766 g_signal_emit (G_OBJECT(self),
1767 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1771 if (priv->reexpand) {
1772 expand_root_items (self);
1773 priv->reexpand = FALSE;
1780 modest_folder_view_new (TnyFolderStoreQuery *query)
1782 return modest_folder_view_new_full (query, TRUE);
1786 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1789 ModestFolderViewPrivate *priv;
1790 GtkTreeSelection *sel;
1792 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1793 #ifdef MODEST_TOOLKIT_HILDON2
1794 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1797 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1800 priv->query = g_object_ref (query);
1802 priv->do_refresh = do_refresh;
1804 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1805 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1806 (GObject*) sel, "changed",
1807 G_CALLBACK (on_selection_changed), self);
1809 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1810 self, "row-activated",
1811 G_CALLBACK (on_row_activated), self);
1813 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1814 self, "expose-event",
1815 G_CALLBACK (modest_folder_view_on_map), NULL);
1817 return GTK_WIDGET(self);
1820 /* this feels dirty; any other way to expand all the root items? */
1822 expand_root_items (ModestFolderView *self)
1825 GtkTreeModel *model;
1828 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1829 path = gtk_tree_path_new_first ();
1831 /* all folders should have child items, so.. */
1833 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1834 gtk_tree_path_next (path);
1835 } while (gtk_tree_model_get_iter (model, &iter, path));
1837 gtk_tree_path_free (path);
1841 is_parent_of (TnyFolder *a, TnyFolder *b)
1844 gboolean retval = FALSE;
1846 a_id = tny_folder_get_id (a);
1848 gchar *string_to_match;
1851 string_to_match = g_strconcat (a_id, "/", NULL);
1852 b_id = tny_folder_get_id (b);
1853 retval = g_str_has_prefix (b_id, string_to_match);
1854 g_free (string_to_match);
1860 typedef struct _ForeachFolderInfo {
1863 } ForeachFolderInfo;
1866 foreach_folder_with_id (GtkTreeModel *model,
1871 ForeachFolderInfo *info;
1874 info = (ForeachFolderInfo *) data;
1875 gtk_tree_model_get (model, iter,
1876 INSTANCE_COLUMN, &instance,
1879 if (TNY_IS_FOLDER (instance)) {
1882 id = tny_folder_get_id (TNY_FOLDER (instance));
1884 collate = g_utf8_collate_key (id, -1);
1885 info->found = !strcmp (info->needle, collate);
1891 g_object_unref (instance);
1899 has_folder_with_id (ModestFolderView *self, const gchar *id)
1901 GtkTreeModel *model;
1902 ForeachFolderInfo info = {NULL, FALSE};
1904 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1905 info.needle = g_utf8_collate_key (id, -1);
1907 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1908 g_free (info.needle);
1914 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1917 gboolean retval = FALSE;
1919 a_id = tny_folder_get_id (a);
1922 b_id = tny_folder_get_id (b);
1925 const gchar *last_bar;
1926 gchar *string_to_match;
1927 last_bar = g_strrstr (b_id, "/");
1932 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1933 retval = has_folder_with_id (self, string_to_match);
1934 g_free (string_to_match);
1942 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1944 ModestFolderViewPrivate *priv;
1945 TnyIterator *iterator;
1946 gboolean retval = TRUE;
1948 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1949 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1951 for (iterator = tny_list_create_iterator (priv->list_to_move);
1952 retval && !tny_iterator_is_done (iterator);
1953 tny_iterator_next (iterator)) {
1955 instance = tny_iterator_get_current (iterator);
1956 if (instance == (GObject *) folder) {
1958 } else if (TNY_IS_FOLDER (instance)) {
1959 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1961 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1964 g_object_unref (instance);
1966 g_object_unref (iterator);
1973 * We use this function to implement the
1974 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1975 * account in this case, and the local folders.
1978 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1980 ModestFolderViewPrivate *priv;
1981 gboolean retval = TRUE;
1982 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1983 GObject *instance = NULL;
1984 const gchar *id = NULL;
1986 gboolean found = FALSE;
1987 gboolean cleared = FALSE;
1988 ModestTnyFolderRules rules = 0;
1991 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1994 gtk_tree_model_get (model, iter,
1995 NAME_COLUMN, &fname,
1997 INSTANCE_COLUMN, &instance,
2000 /* Do not show if there is no instance, this could indeed
2001 happen when the model is being modified while it's being
2002 drawn. This could occur for example when moving folders
2009 if (TNY_IS_ACCOUNT (instance)) {
2010 TnyAccount *acc = TNY_ACCOUNT (instance);
2011 const gchar *account_id = tny_account_get_id (acc);
2013 /* If it isn't a special folder,
2014 * don't show it unless it is the visible account: */
2015 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2016 !modest_tny_account_is_virtual_local_folders (acc) &&
2017 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2019 /* Show only the visible account id */
2020 if (priv->visible_account_id) {
2021 if (strcmp (account_id, priv->visible_account_id))
2028 /* Never show these to the user. They are merged into one folder
2029 * in the local-folders account instead: */
2030 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2033 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2034 /* Only show special folders for current account if needed */
2035 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2036 TnyAccount *account;
2038 account = tny_folder_get_account (TNY_FOLDER (instance));
2040 if (TNY_IS_ACCOUNT (account)) {
2041 const gchar *account_id = tny_account_get_id (account);
2043 if (!modest_tny_account_is_virtual_local_folders (account) &&
2044 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2045 /* Show only the visible account id */
2046 if (priv->visible_account_id) {
2047 if (strcmp (account_id, priv->visible_account_id)) {
2049 } else if (priv->mailbox) {
2050 /* Filter mailboxes */
2051 if (!g_str_has_prefix (fname, priv->mailbox)) {
2053 } else if (!strcmp (fname, priv->mailbox)) {
2054 /* Hide mailbox parent */
2060 g_object_unref (account);
2067 /* Check hiding (if necessary) */
2068 cleared = modest_email_clipboard_cleared (priv->clipboard);
2069 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2070 id = tny_folder_get_id (TNY_FOLDER(instance));
2071 if (priv->hidding_ids != NULL)
2072 for (i=0; i < priv->n_selected && !found; i++)
2073 if (priv->hidding_ids[i] != NULL && id != NULL)
2074 found = (!strcmp (priv->hidding_ids[i], id));
2079 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2080 folder as no message can be move there according to UI specs */
2081 if (retval && !priv->show_non_move) {
2082 if (priv->list_to_move &&
2083 tny_list_get_length (priv->list_to_move) > 0 &&
2084 TNY_IS_FOLDER (instance)) {
2085 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2087 if (retval && TNY_IS_FOLDER (instance) &&
2088 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2090 case TNY_FOLDER_TYPE_OUTBOX:
2091 case TNY_FOLDER_TYPE_SENT:
2092 case TNY_FOLDER_TYPE_DRAFTS:
2095 case TNY_FOLDER_TYPE_UNKNOWN:
2096 case TNY_FOLDER_TYPE_NORMAL:
2097 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2098 if (type == TNY_FOLDER_TYPE_INVALID)
2099 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2101 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2102 type == TNY_FOLDER_TYPE_SENT
2103 || type == TNY_FOLDER_TYPE_DRAFTS)
2110 if (retval && TNY_IS_ACCOUNT (instance) &&
2111 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2112 ModestProtocolType protocol_type;
2114 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2115 retval = !modest_protocol_registry_protocol_type_has_tag
2116 (modest_runtime_get_protocol_registry (),
2118 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2122 /* apply special filters */
2123 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2124 if (TNY_IS_ACCOUNT (instance))
2128 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2129 if (TNY_IS_FOLDER (instance))
2133 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2134 if (TNY_IS_ACCOUNT (instance)) {
2135 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2137 } else if (TNY_IS_FOLDER (instance)) {
2138 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2143 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2144 if (TNY_IS_ACCOUNT (instance)) {
2145 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2147 } else if (TNY_IS_FOLDER (instance)) {
2148 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2153 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2154 /* A mailbox is a fake folder with an @ in the middle of the name */
2155 if (!TNY_IS_FOLDER (instance) ||
2156 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2159 const gchar *folder_name;
2160 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2161 if (!folder_name || strchr (folder_name, '@') == NULL)
2167 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2168 if (TNY_IS_FOLDER (instance)) {
2169 /* Check folder rules */
2170 ModestTnyFolderRules rules;
2172 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2173 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2174 } else if (TNY_IS_ACCOUNT (instance)) {
2175 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2183 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2184 if (TNY_IS_FOLDER (instance)) {
2185 TnyFolderType guess_type;
2187 if (TNY_FOLDER_TYPE_NORMAL) {
2188 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2194 case TNY_FOLDER_TYPE_OUTBOX:
2195 case TNY_FOLDER_TYPE_SENT:
2196 case TNY_FOLDER_TYPE_DRAFTS:
2197 case TNY_FOLDER_TYPE_ARCHIVE:
2198 case TNY_FOLDER_TYPE_INBOX:
2201 case TNY_FOLDER_TYPE_UNKNOWN:
2202 case TNY_FOLDER_TYPE_NORMAL:
2208 } else if (TNY_IS_ACCOUNT (instance)) {
2213 if (retval && TNY_IS_FOLDER (instance)) {
2214 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2217 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2218 if (TNY_IS_FOLDER (instance)) {
2219 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2220 } else if (TNY_IS_ACCOUNT (instance)) {
2225 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2226 if (TNY_IS_FOLDER (instance)) {
2227 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2228 } else if (TNY_IS_ACCOUNT (instance)) {
2233 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2234 if (TNY_IS_FOLDER (instance)) {
2235 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2236 } else if (TNY_IS_ACCOUNT (instance)) {
2242 g_object_unref (instance);
2250 modest_folder_view_update_model (ModestFolderView *self,
2251 TnyAccountStore *account_store)
2253 ModestFolderViewPrivate *priv;
2254 GtkTreeModel *model;
2255 GtkTreeModel *filter_model = NULL, *sortable = NULL, *old_tny_model;
2257 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2258 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2261 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2263 /* Notify that there is no folder selected */
2264 g_signal_emit (G_OBJECT(self),
2265 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2267 if (priv->cur_folder_store) {
2268 g_object_unref (priv->cur_folder_store);
2269 priv->cur_folder_store = NULL;
2272 /* FIXME: the local accounts are not shown when the query
2273 selects only the subscribed folders */
2274 #ifdef MODEST_TOOLKIT_HILDON2
2275 TnyGtkFolderListStoreFlags flags;
2276 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2277 if (!priv->do_refresh)
2278 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2279 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2281 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2282 MODEST_FOLDER_PATH_SEPARATOR);
2284 model = tny_gtk_folder_store_tree_model_new (NULL);
2287 /* When the model is a list store (plain representation) the
2288 outbox is not a child of any account so we have to manually
2289 delete it because removing the local folders account won't
2290 delete it (because tny_folder_get_account() is not defined
2291 for a merge folder */
2292 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2293 TnyAccount *account;
2294 ModestTnyAccountStore *acc_store;
2296 acc_store = modest_runtime_get_account_store ();
2297 account = modest_tny_account_store_get_local_folders_account (acc_store);
2299 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) account,
2301 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2302 (GObject *) account,
2305 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2306 (GObject*) account, "outbox-deleted",
2307 G_CALLBACK (on_outbox_deleted_cb),
2309 g_object_unref (account);
2312 /* Get the accounts: */
2313 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2315 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2317 sortable = gtk_tree_model_sort_new_with_model (model);
2318 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2320 GTK_SORT_ASCENDING);
2321 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2323 cmp_rows, NULL, NULL);
2325 /* Create filter model */
2326 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2327 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2332 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2333 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) old_tny_model,
2334 "activity-changed"))
2335 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2336 G_OBJECT (old_tny_model),
2337 "activity-changed");
2341 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2342 #ifndef MODEST_TOOLKIT_HILDON2
2343 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2344 G_OBJECT(filter_model), "row-inserted",
2345 (GCallback) on_row_inserted_maybe_select_folder, self);
2348 #ifdef MODEST_TOOLKIT_HILDON2
2349 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2352 G_CALLBACK (on_activity_changed),
2356 g_object_unref (model);
2357 g_object_unref (filter_model);
2358 g_object_unref (sortable);
2360 /* Force a reselection of the INBOX next time the widget is shown */
2361 priv->reselect = TRUE;
2368 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2370 GtkTreeModel *model = NULL;
2371 TnyFolderStore *folder = NULL;
2373 ModestFolderView *tree_view = NULL;
2374 ModestFolderViewPrivate *priv = NULL;
2375 gboolean selected = FALSE;
2377 g_return_if_fail (sel);
2378 g_return_if_fail (user_data);
2380 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2382 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2384 tree_view = MODEST_FOLDER_VIEW (user_data);
2387 gtk_tree_model_get (model, &iter,
2388 INSTANCE_COLUMN, &folder,
2391 /* If the folder is the same do not notify */
2392 if (folder && priv->cur_folder_store == folder) {
2393 g_object_unref (folder);
2398 /* Current folder was unselected */
2399 if (priv->cur_folder_store) {
2400 /* We must do this firstly because a libtinymail-camel
2401 implementation detail. If we issue the signal
2402 before doing the sync_async, then that signal could
2403 cause (and it actually does it) a free of the
2404 summary of the folder (because the main window will
2405 clear the headers view */
2406 #ifndef MODEST_TOOLKIT_HILDON2
2407 if (TNY_IS_FOLDER(priv->cur_folder_store))
2408 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2409 FALSE, NULL, NULL, NULL);
2412 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2413 priv->cur_folder_store, FALSE);
2415 g_object_unref (priv->cur_folder_store);
2416 priv->cur_folder_store = NULL;
2419 /* New current references */
2420 priv->cur_folder_store = folder;
2422 /* New folder has been selected. Do not notify if there is
2423 nothing new selected */
2425 g_signal_emit (G_OBJECT(tree_view),
2426 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2427 0, priv->cur_folder_store, TRUE);
2432 on_row_activated (GtkTreeView *treeview,
2433 GtkTreePath *treepath,
2434 GtkTreeViewColumn *column,
2437 GtkTreeModel *model = NULL;
2438 TnyFolderStore *folder = NULL;
2440 ModestFolderView *self = NULL;
2441 ModestFolderViewPrivate *priv = NULL;
2443 g_return_if_fail (treeview);
2444 g_return_if_fail (user_data);
2446 self = MODEST_FOLDER_VIEW (user_data);
2447 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2449 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2451 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2454 gtk_tree_model_get (model, &iter,
2455 INSTANCE_COLUMN, &folder,
2458 g_signal_emit (G_OBJECT(self),
2459 signals[FOLDER_ACTIVATED_SIGNAL],
2462 #ifdef MODEST_TOOLKIT_HILDON2
2463 HildonUIMode ui_mode;
2464 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2465 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2466 if (priv->cur_folder_store)
2467 g_object_unref (priv->cur_folder_store);
2468 priv->cur_folder_store = g_object_ref (folder);
2472 g_object_unref (folder);
2476 modest_folder_view_get_selected (ModestFolderView *self)
2478 ModestFolderViewPrivate *priv;
2480 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2483 if (priv->cur_folder_store)
2484 g_object_ref (priv->cur_folder_store);
2486 return priv->cur_folder_store;
2490 get_cmp_rows_type_pos (GObject *folder)
2492 /* Remote accounts -> Local account -> MMC account .*/
2495 if (TNY_IS_ACCOUNT (folder) &&
2496 modest_tny_account_is_virtual_local_folders (
2497 TNY_ACCOUNT (folder))) {
2499 } else if (TNY_IS_ACCOUNT (folder)) {
2500 TnyAccount *account = TNY_ACCOUNT (folder);
2501 const gchar *account_id = tny_account_get_id (account);
2502 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2508 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2509 return -1; /* Should never happen */
2514 inbox_is_special (TnyFolderStore *folder_store)
2516 gboolean is_special = TRUE;
2518 if (TNY_IS_FOLDER (folder_store)) {
2522 gchar *last_inbox_bar;
2524 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2525 downcase = g_utf8_strdown (id, -1);
2526 last_bar = g_strrstr (downcase, "/");
2528 last_inbox_bar = g_strrstr (downcase, "inbox/");
2529 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2540 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2542 TnyAccount *account;
2543 gboolean is_special;
2544 /* Inbox, Outbox, Drafts, Sent, User */
2547 if (!TNY_IS_FOLDER (folder_store))
2550 case TNY_FOLDER_TYPE_INBOX:
2552 account = tny_folder_get_account (folder_store);
2553 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2555 /* In inbox case we need to know if the inbox is really the top
2556 * inbox of the account, or if it's a submailbox inbox. To do
2557 * this we'll apply an heuristic rule: Find last "/" and check
2558 * if it's preceeded by another Inbox */
2559 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2560 g_object_unref (account);
2561 return is_special?0:4;
2564 case TNY_FOLDER_TYPE_OUTBOX:
2565 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2567 case TNY_FOLDER_TYPE_DRAFTS:
2569 account = tny_folder_get_account (folder_store);
2570 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2571 g_object_unref (account);
2572 return is_special?1:4;
2575 case TNY_FOLDER_TYPE_SENT:
2577 account = tny_folder_get_account (folder_store);
2578 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2579 g_object_unref (account);
2580 return is_special?3:4;
2589 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2591 const gchar *a1_name, *a2_name;
2593 a1_name = tny_account_get_name (a1);
2594 a2_name = tny_account_get_name (a2);
2596 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2600 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2602 TnyAccount *a1 = NULL, *a2 = NULL;
2605 if (TNY_IS_ACCOUNT (s1)) {
2606 a1 = TNY_ACCOUNT (g_object_ref (s1));
2607 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2608 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2611 if (TNY_IS_ACCOUNT (s2)) {
2612 a2 = TNY_ACCOUNT (g_object_ref (s2));
2613 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2614 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2631 /* First we sort with the type of account */
2632 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2636 cmp = compare_account_names (a1, a2);
2640 g_object_unref (a1);
2642 g_object_unref (a2);
2648 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2650 gint is_account1, is_account2;
2652 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2653 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2655 return is_account2 - is_account1;
2659 compare_folders (const gchar *name1, const gchar *name2)
2661 const gchar *separator1, *separator2;
2662 const gchar *next1, *next2;
2666 if (name1 == NULL || name1[0] == '\0')
2668 if (name2 == NULL || name2[0] == '\0')
2671 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2673 top1 = g_strndup (name1, separator1 - name1);
2675 top1 = g_strdup (name1);
2678 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2680 top2 = g_strndup (name2, separator2 - name2);
2682 top2 = g_strdup (name2);
2686 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2693 if (separator1 == NULL && separator2 == NULL)
2696 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2697 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2699 return compare_folders (next1, next2);
2704 * This function orders the mail accounts according to these rules:
2705 * 1st - remote accounts
2706 * 2nd - local account
2710 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2714 gchar *name1 = NULL;
2715 gchar *name2 = NULL;
2716 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2717 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2718 GObject *folder1 = NULL;
2719 GObject *folder2 = NULL;
2721 gtk_tree_model_get (tree_model, iter1,
2722 NAME_COLUMN, &name1,
2724 INSTANCE_COLUMN, &folder1,
2726 gtk_tree_model_get (tree_model, iter2,
2727 NAME_COLUMN, &name2,
2728 TYPE_COLUMN, &type2,
2729 INSTANCE_COLUMN, &folder2,
2732 /* Return if we get no folder. This could happen when folder
2733 operations are happening. The model is updated after the
2734 folder copy/move actually occurs, so there could be
2735 situations where the model to be drawn is not correct */
2736 if (!folder1 || !folder2)
2739 /* Sort by type. First the special folders, then the archives */
2740 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2744 /* Now we sort using the account of each folder */
2745 if (TNY_IS_FOLDER_STORE (folder1) &&
2746 TNY_IS_FOLDER_STORE (folder2)) {
2747 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2751 /* Each group is preceeded by its account */
2752 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2757 /* Pure sort by name */
2758 cmp = compare_folders (name1, name2);
2761 g_object_unref(G_OBJECT(folder1));
2763 g_object_unref(G_OBJECT(folder2));
2771 /*****************************************************************************/
2772 /* DRAG and DROP stuff */
2773 /*****************************************************************************/
2775 * This function fills the #GtkSelectionData with the row and the
2776 * model that has been dragged. It's called when this widget is a
2777 * source for dnd after the event drop happened
2780 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2781 guint info, guint time, gpointer data)
2783 GtkTreeSelection *selection;
2784 GtkTreeModel *model;
2786 GtkTreePath *source_row;
2788 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2789 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2791 source_row = gtk_tree_model_get_path (model, &iter);
2792 gtk_tree_set_row_drag_data (selection_data,
2796 gtk_tree_path_free (source_row);
2800 typedef struct _DndHelper {
2801 ModestFolderView *folder_view;
2802 gboolean delete_source;
2803 GtkTreePath *source_row;
2807 dnd_helper_destroyer (DndHelper *helper)
2809 /* Free the helper */
2810 gtk_tree_path_free (helper->source_row);
2811 g_slice_free (DndHelper, helper);
2815 xfer_folder_cb (ModestMailOperation *mail_op,
2816 TnyFolder *new_folder,
2820 /* Select the folder */
2821 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2827 /* get the folder for the row the treepath refers to. */
2828 /* folder must be unref'd */
2829 static TnyFolderStore *
2830 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2833 TnyFolderStore *folder = NULL;
2835 if (gtk_tree_model_get_iter (model,&iter, path))
2836 gtk_tree_model_get (model, &iter,
2837 INSTANCE_COLUMN, &folder,
2844 * This function is used by drag_data_received_cb to manage drag and
2845 * drop of a header, i.e, and drag from the header view to the folder
2849 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2850 GtkTreeModel *dest_model,
2851 GtkTreePath *dest_row,
2852 GtkSelectionData *selection_data)
2854 TnyList *headers = NULL;
2855 TnyFolder *folder = NULL, *src_folder = NULL;
2856 TnyFolderType folder_type;
2857 GtkTreeIter source_iter, dest_iter;
2858 ModestWindowMgr *mgr = NULL;
2859 ModestWindow *main_win = NULL;
2860 gchar **uris, **tmp;
2862 /* Build the list of headers */
2863 mgr = modest_runtime_get_window_mgr ();
2864 headers = tny_simple_list_new ();
2865 uris = modest_dnd_selection_data_get_paths (selection_data);
2868 while (*tmp != NULL) {
2871 gboolean first = TRUE;
2874 path = gtk_tree_path_new_from_string (*tmp);
2875 gtk_tree_model_get_iter (source_model, &source_iter, path);
2876 gtk_tree_model_get (source_model, &source_iter,
2877 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2880 /* Do not enable d&d of headers already opened */
2881 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2882 tny_list_append (headers, G_OBJECT (header));
2884 if (G_UNLIKELY (first)) {
2885 src_folder = tny_header_get_folder (header);
2889 /* Free and go on */
2890 gtk_tree_path_free (path);
2891 g_object_unref (header);
2896 /* This could happen ig we perform a d&d very quickly over the
2897 same row that row could dissapear because message is
2899 if (!TNY_IS_FOLDER (src_folder))
2902 /* Get the target folder */
2903 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2904 gtk_tree_model_get (dest_model, &dest_iter,
2908 if (!folder || !TNY_IS_FOLDER(folder)) {
2909 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2913 folder_type = modest_tny_folder_guess_folder_type (folder);
2914 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2915 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2916 goto cleanup; /* cannot move messages there */
2919 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2920 /* g_warning ("folder not writable"); */
2921 goto cleanup; /* verboten! */
2924 /* Ask for confirmation to move */
2925 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2927 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2931 /* Transfer messages */
2932 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2937 if (G_IS_OBJECT (src_folder))
2938 g_object_unref (src_folder);
2939 if (G_IS_OBJECT(folder))
2940 g_object_unref (G_OBJECT (folder));
2941 if (G_IS_OBJECT(headers))
2942 g_object_unref (headers);
2946 TnyFolderStore *src_folder;
2947 TnyFolderStore *dst_folder;
2948 ModestFolderView *folder_view;
2953 dnd_folder_info_destroyer (DndFolderInfo *info)
2955 if (info->src_folder)
2956 g_object_unref (info->src_folder);
2957 if (info->dst_folder)
2958 g_object_unref (info->dst_folder);
2959 g_slice_free (DndFolderInfo, info);
2963 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2964 GtkWindow *parent_window,
2965 TnyAccount *account)
2968 modest_ui_actions_on_account_connection_error (parent_window, account);
2970 /* Free the helper & info */
2971 dnd_helper_destroyer (info->helper);
2972 dnd_folder_info_destroyer (info);
2976 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2978 GtkWindow *parent_window,
2979 TnyAccount *account,
2982 DndFolderInfo *info = NULL;
2983 ModestMailOperation *mail_op;
2985 info = (DndFolderInfo *) user_data;
2987 if (err || canceled) {
2988 dnd_on_connection_failed_destroyer (info, parent_window, account);
2992 /* Do the mail operation */
2993 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2994 modest_ui_actions_move_folder_error_handler,
2995 info->src_folder, NULL);
2997 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3000 /* Transfer the folder */
3001 modest_mail_operation_xfer_folder (mail_op,
3002 TNY_FOLDER (info->src_folder),
3004 info->helper->delete_source,
3006 info->helper->folder_view);
3009 g_object_unref (G_OBJECT (mail_op));
3010 dnd_helper_destroyer (info->helper);
3011 dnd_folder_info_destroyer (info);
3016 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3018 GtkWindow *parent_window,
3019 TnyAccount *account,
3022 DndFolderInfo *info = NULL;
3024 info = (DndFolderInfo *) user_data;
3026 if (err || canceled) {
3027 dnd_on_connection_failed_destroyer (info, parent_window, account);
3031 /* Connect to source folder and perform the copy/move */
3032 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3034 drag_and_drop_from_folder_view_src_folder_performer,
3039 * This function is used by drag_data_received_cb to manage drag and
3040 * drop of a folder, i.e, and drag from the folder view to the same
3044 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3045 GtkTreeModel *dest_model,
3046 GtkTreePath *dest_row,
3047 GtkSelectionData *selection_data,
3050 GtkTreeIter dest_iter, iter;
3051 TnyFolderStore *dest_folder = NULL;
3052 TnyFolderStore *folder = NULL;
3053 gboolean forbidden = FALSE;
3055 DndFolderInfo *info = NULL;
3057 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3059 g_warning ("%s: BUG: no main window", __FUNCTION__);
3060 dnd_helper_destroyer (helper);
3065 /* check the folder rules for the destination */
3066 folder = tree_path_to_folder (dest_model, dest_row);
3067 if (TNY_IS_FOLDER(folder)) {
3068 ModestTnyFolderRules rules =
3069 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3070 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3071 } else if (TNY_IS_FOLDER_STORE(folder)) {
3072 /* enable local root as destination for folders */
3073 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3074 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3077 g_object_unref (folder);
3080 /* check the folder rules for the source */
3081 folder = tree_path_to_folder (source_model, helper->source_row);
3082 if (TNY_IS_FOLDER(folder)) {
3083 ModestTnyFolderRules rules =
3084 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3085 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3088 g_object_unref (folder);
3092 /* Check if the drag is possible */
3093 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3095 modest_platform_run_information_dialog ((GtkWindow *) win,
3096 _("mail_in_ui_folder_move_target_error"),
3098 /* Restore the previous selection */
3099 folder = tree_path_to_folder (source_model, helper->source_row);
3101 if (TNY_IS_FOLDER (folder))
3102 modest_folder_view_select_folder (helper->folder_view,
3103 TNY_FOLDER (folder), FALSE);
3104 g_object_unref (folder);
3106 dnd_helper_destroyer (helper);
3111 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3112 gtk_tree_model_get (dest_model, &dest_iter,
3115 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3116 gtk_tree_model_get (source_model, &iter,
3120 /* Create the info for the performer */
3121 info = g_slice_new0 (DndFolderInfo);
3122 info->src_folder = g_object_ref (folder);
3123 info->dst_folder = g_object_ref (dest_folder);
3124 info->helper = helper;
3126 /* Connect to the destination folder and perform the copy/move */
3127 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3129 drag_and_drop_from_folder_view_dst_folder_performer,
3133 g_object_unref (dest_folder);
3134 g_object_unref (folder);
3138 * This function receives the data set by the "drag-data-get" signal
3139 * handler. This information comes within the #GtkSelectionData. This
3140 * function will manage both the drags of folders of the treeview and
3141 * drags of headers of the header view widget.
3144 on_drag_data_received (GtkWidget *widget,
3145 GdkDragContext *context,
3148 GtkSelectionData *selection_data,
3153 GtkWidget *source_widget;
3154 GtkTreeModel *dest_model, *source_model;
3155 GtkTreePath *source_row, *dest_row;
3156 GtkTreeViewDropPosition pos;
3157 gboolean delete_source = FALSE;
3158 gboolean success = FALSE;
3160 /* Do not allow further process */
3161 g_signal_stop_emission_by_name (widget, "drag-data-received");
3162 source_widget = gtk_drag_get_source_widget (context);
3164 /* Get the action */
3165 if (context->action == GDK_ACTION_MOVE) {
3166 delete_source = TRUE;
3168 /* Notify that there is no folder selected. We need to
3169 do this in order to update the headers view (and
3170 its monitors, because when moving, the old folder
3171 won't longer exist. We can not wait for the end of
3172 the operation, because the operation won't start if
3173 the folder is in use */
3174 if (source_widget == widget) {
3175 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3176 gtk_tree_selection_unselect_all (sel);
3180 /* Check if the get_data failed */
3181 if (selection_data == NULL || selection_data->length < 0)
3184 /* Select the destination model */
3185 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3187 /* Get the path to the destination row. Can not call
3188 gtk_tree_view_get_drag_dest_row() because the source row
3189 is not selected anymore */
3190 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3193 /* Only allow drops IN other rows */
3195 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3196 pos == GTK_TREE_VIEW_DROP_AFTER)
3200 /* Drags from the header view */
3201 if (source_widget != widget) {
3202 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3204 drag_and_drop_from_header_view (source_model,
3209 DndHelper *helper = NULL;
3211 /* Get the source model and row */
3212 gtk_tree_get_row_drag_data (selection_data,
3216 /* Create the helper */
3217 helper = g_slice_new0 (DndHelper);
3218 helper->delete_source = delete_source;
3219 helper->source_row = gtk_tree_path_copy (source_row);
3220 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3222 drag_and_drop_from_folder_view (source_model,
3228 gtk_tree_path_free (source_row);
3232 gtk_tree_path_free (dest_row);
3235 /* Finish the drag and drop */
3236 gtk_drag_finish (context, success, FALSE, time);
3240 * We define a "drag-drop" signal handler because we do not want to
3241 * use the default one, because the default one always calls
3242 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3243 * signal handler, because there we have all the information available
3244 * to know if the dnd was a success or not.
3247 drag_drop_cb (GtkWidget *widget,
3248 GdkDragContext *context,
3256 if (!context->targets)
3259 /* Check if we're dragging a folder row */
3260 target = gtk_drag_dest_find_target (widget, context, NULL);
3262 /* Request the data from the source. */
3263 gtk_drag_get_data(widget, context, target, time);
3269 * This function expands a node of a tree view if it's not expanded
3270 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3271 * does that, so that's why they're here.
3274 expand_row_timeout (gpointer data)
3276 GtkTreeView *tree_view = data;
3277 GtkTreePath *dest_path = NULL;
3278 GtkTreeViewDropPosition pos;
3279 gboolean result = FALSE;
3281 gdk_threads_enter ();
3283 gtk_tree_view_get_drag_dest_row (tree_view,
3288 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3289 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3290 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3291 gtk_tree_path_free (dest_path);
3295 gtk_tree_path_free (dest_path);
3300 gdk_threads_leave ();
3306 * This function is called whenever the pointer is moved over a widget
3307 * while dragging some data. It installs a timeout that will expand a
3308 * node of the treeview if not expanded yet. This function also calls
3309 * gdk_drag_status in order to set the suggested action that will be
3310 * used by the "drag-data-received" signal handler to know if we
3311 * should do a move or just a copy of the data.
3314 on_drag_motion (GtkWidget *widget,
3315 GdkDragContext *context,
3321 GtkTreeViewDropPosition pos;
3322 GtkTreePath *dest_row;
3323 GtkTreeModel *dest_model;
3324 ModestFolderViewPrivate *priv;
3325 GdkDragAction suggested_action;
3326 gboolean valid_location = FALSE;
3327 TnyFolderStore *folder = NULL;
3329 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3331 if (priv->timer_expander != 0) {
3332 g_source_remove (priv->timer_expander);
3333 priv->timer_expander = 0;
3336 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3341 /* Do not allow drops between folders */
3343 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3344 pos == GTK_TREE_VIEW_DROP_AFTER) {
3345 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3346 gdk_drag_status(context, 0, time);
3347 valid_location = FALSE;
3350 valid_location = TRUE;
3353 /* Check that the destination folder is writable */
3354 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3355 folder = tree_path_to_folder (dest_model, dest_row);
3356 if (folder && TNY_IS_FOLDER (folder)) {
3357 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3359 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3360 valid_location = FALSE;
3365 /* Expand the selected row after 1/2 second */
3366 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3367 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3369 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3371 /* Select the desired action. By default we pick MOVE */
3372 suggested_action = GDK_ACTION_MOVE;
3374 if (context->actions == GDK_ACTION_COPY)
3375 gdk_drag_status(context, GDK_ACTION_COPY, time);
3376 else if (context->actions == GDK_ACTION_MOVE)
3377 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3378 else if (context->actions & suggested_action)
3379 gdk_drag_status(context, suggested_action, time);
3381 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3385 g_object_unref (folder);
3387 gtk_tree_path_free (dest_row);
3389 g_signal_stop_emission_by_name (widget, "drag-motion");
3391 return valid_location;
3395 * This function sets the treeview as a source and a target for dnd
3396 * events. It also connects all the requirede signals.
3399 setup_drag_and_drop (GtkTreeView *self)
3401 /* Set up the folder view as a dnd destination. Set only the
3402 highlight flag, otherwise gtk will have a different
3404 #ifdef MODEST_TOOLKIT_HILDON2
3407 ModestFolderViewPrivate *priv;
3409 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3411 gtk_drag_dest_set (GTK_WIDGET (self),
3412 GTK_DEST_DEFAULT_HIGHLIGHT,
3413 folder_view_drag_types,
3414 G_N_ELEMENTS (folder_view_drag_types),
3415 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3417 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3418 G_OBJECT (self), "drag_data_received",
3419 G_CALLBACK (on_drag_data_received), NULL);
3422 /* Set up the treeview as a dnd source */
3423 gtk_drag_source_set (GTK_WIDGET (self),
3425 folder_view_drag_types,
3426 G_N_ELEMENTS (folder_view_drag_types),
3427 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3429 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3430 G_OBJECT (self), "drag_motion",
3431 G_CALLBACK (on_drag_motion), NULL);
3433 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3434 G_OBJECT (self), "drag_data_get",
3435 G_CALLBACK (on_drag_data_get), NULL);
3437 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3438 G_OBJECT (self), "drag_drop",
3439 G_CALLBACK (drag_drop_cb), NULL);
3443 * This function manages the navigation through the folders using the
3444 * keyboard or the hardware keys in the device
3447 on_key_pressed (GtkWidget *self,
3451 GtkTreeSelection *selection;
3453 GtkTreeModel *model;
3454 gboolean retval = FALSE;
3456 /* Up and Down are automatically managed by the treeview */
3457 if (event->keyval == GDK_Return) {
3458 /* Expand/Collapse the selected row */
3459 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3460 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3463 path = gtk_tree_model_get_path (model, &iter);
3465 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3466 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3468 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3469 gtk_tree_path_free (path);
3471 /* No further processing */
3479 * We listen to the changes in the local folder account name key,
3480 * because we want to show the right name in the view. The local
3481 * folder account name corresponds to the device name in the Maemo
3482 * version. We do this because we do not want to query gconf on each
3483 * tree view refresh. It's better to cache it and change whenever
3487 on_configuration_key_changed (ModestConf* conf,
3489 ModestConfEvent event,
3490 ModestConfNotificationId id,
3491 ModestFolderView *self)
3493 ModestFolderViewPrivate *priv;
3496 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3497 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3499 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3500 g_free (priv->local_account_name);
3502 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3503 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3505 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3506 MODEST_CONF_DEVICE_NAME, NULL);
3508 /* Force a redraw */
3509 #if GTK_CHECK_VERSION(2, 8, 0)
3510 GtkTreeViewColumn * tree_column;
3512 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3514 gtk_tree_view_column_queue_resize (tree_column);
3516 gtk_widget_queue_draw (GTK_WIDGET (self));
3522 modest_folder_view_set_style (ModestFolderView *self,
3523 ModestFolderViewStyle style)
3525 ModestFolderViewPrivate *priv;
3527 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3528 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3529 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3531 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3534 priv->style = style;
3538 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3539 const gchar *account_id)
3541 ModestFolderViewPrivate *priv;
3542 GtkTreeModel *model;
3544 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3546 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3548 /* This will be used by the filter_row callback,
3549 * to decided which rows to show: */
3550 if (priv->visible_account_id) {
3551 g_free (priv->visible_account_id);
3552 priv->visible_account_id = NULL;
3555 priv->visible_account_id = g_strdup (account_id);
3558 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3559 if (GTK_IS_TREE_MODEL_FILTER (model))
3560 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3562 /* Save settings to gconf */
3563 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3564 MODEST_CONF_FOLDER_VIEW_KEY);
3566 /* Notify observers */
3567 g_signal_emit (G_OBJECT(self),
3568 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3573 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3575 ModestFolderViewPrivate *priv;
3577 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3579 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3581 return (const gchar *) priv->visible_account_id;
3585 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3589 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3591 gtk_tree_model_get (model, iter,
3595 gboolean result = FALSE;
3596 if (type == TNY_FOLDER_TYPE_INBOX) {
3600 *inbox_iter = *iter;
3604 if (gtk_tree_model_iter_children (model, &child, iter)) {
3605 if (find_inbox_iter (model, &child, inbox_iter))
3609 } while (gtk_tree_model_iter_next (model, iter));
3618 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3620 #ifndef MODEST_TOOLKIT_HILDON2
3621 GtkTreeModel *model;
3622 GtkTreeIter iter, inbox_iter;
3623 GtkTreeSelection *sel;
3624 GtkTreePath *path = NULL;
3626 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3628 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3632 expand_root_items (self);
3633 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3635 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3636 g_warning ("%s: model is empty", __FUNCTION__);
3640 if (find_inbox_iter (model, &iter, &inbox_iter))
3641 path = gtk_tree_model_get_path (model, &inbox_iter);
3643 path = gtk_tree_path_new_first ();
3645 /* Select the row and free */
3646 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3647 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3648 gtk_tree_path_free (path);
3651 gtk_widget_grab_focus (GTK_WIDGET(self));
3658 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3663 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3664 TnyFolder* a_folder;
3667 gtk_tree_model_get (model, iter,
3668 INSTANCE_COLUMN, &a_folder,
3674 if (folder == a_folder) {
3675 g_object_unref (a_folder);
3676 *folder_iter = *iter;
3679 g_object_unref (a_folder);
3681 if (gtk_tree_model_iter_children (model, &child, iter)) {
3682 if (find_folder_iter (model, &child, folder_iter, folder))
3686 } while (gtk_tree_model_iter_next (model, iter));
3691 #ifndef MODEST_TOOLKIT_HILDON2
3693 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3696 ModestFolderView *self)
3698 ModestFolderViewPrivate *priv = NULL;
3699 GtkTreeSelection *sel;
3700 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3701 GObject *instance = NULL;
3703 if (!MODEST_IS_FOLDER_VIEW(self))
3706 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3708 priv->reexpand = TRUE;
3710 gtk_tree_model_get (tree_model, iter,
3712 INSTANCE_COLUMN, &instance,
3718 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3719 priv->folder_to_select = g_object_ref (instance);
3721 g_object_unref (instance);
3723 if (priv->folder_to_select) {
3725 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3728 path = gtk_tree_model_get_path (tree_model, iter);
3729 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3731 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3733 gtk_tree_selection_select_iter (sel, iter);
3734 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3736 gtk_tree_path_free (path);
3740 modest_folder_view_disable_next_folder_selection (self);
3742 /* Refilter the model */
3743 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3749 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3751 ModestFolderViewPrivate *priv;
3753 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3755 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3757 if (priv->folder_to_select)
3758 g_object_unref(priv->folder_to_select);
3760 priv->folder_to_select = NULL;
3764 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3765 gboolean after_change)
3767 GtkTreeModel *model;
3768 GtkTreeIter iter, folder_iter;
3769 GtkTreeSelection *sel;
3770 ModestFolderViewPrivate *priv = NULL;
3772 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3773 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3775 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3778 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3779 gtk_tree_selection_unselect_all (sel);
3781 if (priv->folder_to_select)
3782 g_object_unref(priv->folder_to_select);
3783 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3787 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3792 /* Refilter the model, before selecting the folder */
3793 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3795 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3796 g_warning ("%s: model is empty", __FUNCTION__);
3800 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3803 path = gtk_tree_model_get_path (model, &folder_iter);
3804 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3806 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3807 gtk_tree_selection_select_iter (sel, &folder_iter);
3808 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3810 gtk_tree_path_free (path);
3818 modest_folder_view_copy_selection (ModestFolderView *self)
3820 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3822 /* Copy selection */
3823 _clipboard_set_selected_data (self, FALSE);
3827 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3829 ModestFolderViewPrivate *priv = NULL;
3830 GtkTreeModel *model = NULL;
3831 const gchar **hidding = NULL;
3832 guint i, n_selected;
3834 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3835 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3837 /* Copy selection */
3838 if (!_clipboard_set_selected_data (folder_view, TRUE))
3841 /* Get hidding ids */
3842 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3844 /* Clear hidding array created by previous cut operation */
3845 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3847 /* Copy hidding array */
3848 priv->n_selected = n_selected;
3849 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3850 for (i=0; i < n_selected; i++)
3851 priv->hidding_ids[i] = g_strdup(hidding[i]);
3853 /* Hide cut folders */
3854 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3855 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3859 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3860 ModestFolderView *folder_view_dst)
3862 GtkTreeModel *filter_model = NULL;
3863 GtkTreeModel *model = NULL;
3864 GtkTreeModel *new_filter_model = NULL;
3865 GtkTreeModel *old_tny_model = NULL;
3866 GtkTreeModel *new_tny_model = NULL;
3867 ModestFolderViewPrivate *dst_priv;
3869 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3870 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3872 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3873 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3874 new_tny_model = NULL;
3877 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3878 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3879 G_OBJECT (old_tny_model),
3880 "activity-changed");
3882 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3883 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3885 /* Build new filter model */
3886 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3887 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3894 /* Set copied model */
3895 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3896 #ifndef MODEST_TOOLKIT_HILDON2
3897 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3898 G_OBJECT(new_filter_model), "row-inserted",
3899 (GCallback) on_row_inserted_maybe_select_folder,
3902 #ifdef MODEST_TOOLKIT_HILDON2
3903 if (new_tny_model) {
3904 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3905 G_OBJECT (new_tny_model),
3907 G_CALLBACK (on_activity_changed),
3913 g_object_unref (new_filter_model);
3917 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3920 GtkTreeModel *model = NULL;
3921 ModestFolderViewPrivate* priv;
3923 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3925 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3926 priv->show_non_move = show;
3927 /* modest_folder_view_update_model(folder_view, */
3928 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3930 /* Hide special folders */
3931 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3932 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3933 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3938 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3941 ModestFolderViewPrivate* priv;
3943 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3945 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3946 priv->show_message_count = show;
3948 g_object_set (G_OBJECT (priv->messages_renderer),
3949 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3953 /* Returns FALSE if it did not selected anything */
3955 _clipboard_set_selected_data (ModestFolderView *folder_view,
3958 ModestFolderViewPrivate *priv = NULL;
3959 TnyFolderStore *folder = NULL;
3960 gboolean retval = FALSE;
3962 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3963 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3965 /* Set selected data on clipboard */
3966 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3967 folder = modest_folder_view_get_selected (folder_view);
3969 /* Do not allow to select an account */
3970 if (TNY_IS_FOLDER (folder)) {
3971 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3976 g_object_unref (folder);
3982 _clear_hidding_filter (ModestFolderView *folder_view)
3984 ModestFolderViewPrivate *priv;
3987 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3988 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3990 if (priv->hidding_ids != NULL) {
3991 for (i=0; i < priv->n_selected; i++)
3992 g_free (priv->hidding_ids[i]);
3993 g_free(priv->hidding_ids);
3999 on_display_name_changed (ModestAccountMgr *mgr,
4000 const gchar *account,
4003 ModestFolderView *self;
4005 self = MODEST_FOLDER_VIEW (user_data);
4007 /* Force a redraw */
4008 #if GTK_CHECK_VERSION(2, 8, 0)
4009 GtkTreeViewColumn * tree_column;
4011 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4013 gtk_tree_view_column_queue_resize (tree_column);
4015 gtk_widget_queue_draw (GTK_WIDGET (self));
4020 modest_folder_view_set_cell_style (ModestFolderView *self,
4021 ModestFolderViewCellStyle cell_style)
4023 ModestFolderViewPrivate *priv = NULL;
4025 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4026 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4028 priv->cell_style = cell_style;
4030 g_object_set (G_OBJECT (priv->messages_renderer),
4031 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4034 gtk_widget_queue_draw (GTK_WIDGET (self));
4038 update_style (ModestFolderView *self)
4040 ModestFolderViewPrivate *priv;
4041 GdkColor style_color, style_active_color;
4042 PangoAttrList *attr_list;
4044 PangoAttribute *attr;
4046 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4047 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4051 attr_list = pango_attr_list_new ();
4052 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4053 gdk_color_parse ("grey", &style_color);
4055 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4056 pango_attr_list_insert (attr_list, attr);
4059 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4061 "SmallSystemFont", NULL,
4064 attr = pango_attr_font_desc_new (pango_font_description_copy
4065 (style->font_desc));
4066 pango_attr_list_insert (attr_list, attr);
4068 g_object_set (G_OBJECT (priv->messages_renderer),
4069 "foreground-gdk", &style_color,
4070 "foreground-set", TRUE,
4071 "attributes", attr_list,
4073 pango_attr_list_unref (attr_list);
4075 if (priv->active_color)
4076 gdk_color_free (priv->active_color);
4078 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4079 priv->active_color = gdk_color_copy (&style_active_color);
4084 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4086 if (strcmp ("style", spec->name) == 0) {
4087 update_style (MODEST_FOLDER_VIEW (obj));
4088 gtk_widget_queue_draw (GTK_WIDGET (obj));
4093 modest_folder_view_set_filter (ModestFolderView *self,
4094 ModestFolderViewFilter filter)
4096 ModestFolderViewPrivate *priv;
4097 GtkTreeModel *filter_model;
4099 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4100 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4102 priv->filter |= filter;
4104 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4105 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4106 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4111 modest_folder_view_unset_filter (ModestFolderView *self,
4112 ModestFolderViewFilter filter)
4114 ModestFolderViewPrivate *priv;
4115 GtkTreeModel *filter_model;
4117 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4118 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4120 priv->filter &= ~filter;
4122 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4123 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4124 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4129 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4130 ModestTnyFolderRules rules)
4132 GtkTreeModel *filter_model;
4134 gboolean fulfil = FALSE;
4136 if (!get_inner_models (self, &filter_model, NULL, NULL))
4139 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4143 TnyFolderStore *folder;
4145 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4147 if (TNY_IS_FOLDER (folder)) {
4148 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4149 /* Folder rules are negative: non_writable, non_deletable... */
4150 if (!(folder_rules & rules))
4153 g_object_unref (folder);
4156 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4162 modest_folder_view_set_list_to_move (ModestFolderView *self,
4165 ModestFolderViewPrivate *priv;
4167 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4168 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4170 if (priv->list_to_move)
4171 g_object_unref (priv->list_to_move);
4174 g_object_ref (list);
4176 priv->list_to_move = list;
4180 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4182 ModestFolderViewPrivate *priv;
4184 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4185 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4188 g_free (priv->mailbox);
4190 priv->mailbox = g_strdup (mailbox);
4192 /* Notify observers */
4193 g_signal_emit (G_OBJECT(self),
4194 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4195 priv->visible_account_id);
4199 modest_folder_view_get_mailbox (ModestFolderView *self)
4201 ModestFolderViewPrivate *priv;
4203 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4204 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4206 return (const gchar *) priv->mailbox;
4210 modest_folder_view_get_activity (ModestFolderView *self)
4212 ModestFolderViewPrivate *priv;
4213 GtkTreeModel *inner_model;
4215 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4216 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4217 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4219 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4220 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4226 #ifdef MODEST_TOOLKIT_HILDON2
4228 on_activity_changed (TnyGtkFolderListStore *store,
4230 ModestFolderView *folder_view)
4232 ModestFolderViewPrivate *priv;
4234 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4235 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4236 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4238 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,