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) {
858 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_va_new_messages"),
860 } else if (number == 1) {
861 item_name = g_strdup_printf (drafts?_("mcen_ti_message"):_("mcen_va_new_message"),
869 item_name = g_strdup ("");
872 /* Set the name in the treeview cell: */
873 g_object_set (rendobj,"text", item_name, NULL);
881 g_object_unref (G_OBJECT (instance));
887 GdkPixbuf *pixbuf_open;
888 GdkPixbuf *pixbuf_close;
892 static inline GdkPixbuf *
893 get_composite_pixbuf (const gchar *icon_name,
895 GdkPixbuf *base_pixbuf)
897 GdkPixbuf *emblem, *retval = NULL;
899 emblem = modest_platform_get_icon (icon_name, size);
901 retval = gdk_pixbuf_copy (base_pixbuf);
902 gdk_pixbuf_composite (emblem, retval, 0, 0,
903 MIN (gdk_pixbuf_get_width (emblem),
904 gdk_pixbuf_get_width (retval)),
905 MIN (gdk_pixbuf_get_height (emblem),
906 gdk_pixbuf_get_height (retval)),
907 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
908 g_object_unref (emblem);
913 static inline ThreePixbufs *
914 get_composite_icons (const gchar *icon_code,
916 GdkPixbuf **pixbuf_open,
917 GdkPixbuf **pixbuf_close)
919 ThreePixbufs *retval;
923 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
925 *pixbuf = gdk_pixbuf_copy (icon);
929 if (!*pixbuf_open && pixbuf && *pixbuf)
930 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
934 if (!*pixbuf_close && pixbuf && *pixbuf)
935 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
939 retval = g_slice_new0 (ThreePixbufs);
941 retval->pixbuf = g_object_ref (*pixbuf);
943 retval->pixbuf_open = g_object_ref (*pixbuf_open);
945 retval->pixbuf_close = g_object_ref (*pixbuf_close);
950 static inline ThreePixbufs *
951 get_account_protocol_pixbufs (ModestFolderView *folder_view,
952 ModestProtocolType protocol_type,
955 ModestProtocol *protocol;
956 const GdkPixbuf *pixbuf = NULL;
957 ModestFolderViewPrivate *priv;
959 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
961 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
964 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
965 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
966 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
967 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
968 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
969 object, FOLDER_ICON_SIZE);
973 ThreePixbufs *retval;
974 retval = g_slice_new0 (ThreePixbufs);
975 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
976 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
977 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
984 static inline ThreePixbufs*
985 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
987 TnyAccount *account = NULL;
988 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
989 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
990 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
991 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
992 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
994 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
995 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
996 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
997 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
998 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1000 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1001 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1002 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1003 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1004 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1006 ThreePixbufs *retval = NULL;
1008 if (TNY_IS_ACCOUNT (instance)) {
1009 account = g_object_ref (instance);
1010 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1011 account = tny_folder_get_account (TNY_FOLDER (instance));
1015 ModestProtocolType account_store_protocol;
1017 account_store_protocol = modest_tny_account_get_protocol_type (account);
1018 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1019 g_object_unref (account);
1025 /* Sometimes an special folder is reported by the server as
1026 NORMAL, like some versions of Dovecot */
1027 if (type == TNY_FOLDER_TYPE_NORMAL ||
1028 type == TNY_FOLDER_TYPE_UNKNOWN) {
1029 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1032 /* It's not enough with check the folder type. We need to
1033 ensure that we're not giving a special folder icon to a
1034 normal folder with the same name than a special folder */
1035 if (TNY_IS_FOLDER (instance) &&
1036 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1037 type = TNY_FOLDER_TYPE_NORMAL;
1039 /* Remote folders should not be treated as special folders */
1040 if (TNY_IS_FOLDER_STORE (instance) &&
1041 !TNY_IS_ACCOUNT (instance) &&
1042 type != TNY_FOLDER_TYPE_INBOX &&
1043 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1044 #ifdef MODEST_TOOLKIT_HILDON2
1045 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1048 &anorm_pixbuf_close);
1050 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1052 &normal_pixbuf_open,
1053 &normal_pixbuf_close);
1059 case TNY_FOLDER_TYPE_INVALID:
1060 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1063 case TNY_FOLDER_TYPE_ROOT:
1064 if (TNY_IS_ACCOUNT (instance)) {
1066 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1067 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1070 &avirt_pixbuf_close);
1072 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1074 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1075 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1078 &ammc_pixbuf_close);
1080 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1083 &anorm_pixbuf_close);
1088 case TNY_FOLDER_TYPE_INBOX:
1089 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1092 &inbox_pixbuf_close);
1094 case TNY_FOLDER_TYPE_OUTBOX:
1095 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1097 &outbox_pixbuf_open,
1098 &outbox_pixbuf_close);
1100 case TNY_FOLDER_TYPE_JUNK:
1101 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1104 &junk_pixbuf_close);
1106 case TNY_FOLDER_TYPE_SENT:
1107 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1110 &sent_pixbuf_close);
1112 case TNY_FOLDER_TYPE_TRASH:
1113 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1116 &trash_pixbuf_close);
1118 case TNY_FOLDER_TYPE_DRAFTS:
1119 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1122 &draft_pixbuf_close);
1124 case TNY_FOLDER_TYPE_ARCHIVE:
1125 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1130 case TNY_FOLDER_TYPE_NORMAL:
1132 /* Memory card folders could have an special icon */
1133 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1134 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1139 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1141 &normal_pixbuf_open,
1142 &normal_pixbuf_close);
1151 free_pixbufs (ThreePixbufs *pixbufs)
1153 if (pixbufs->pixbuf)
1154 g_object_unref (pixbufs->pixbuf);
1155 if (pixbufs->pixbuf_open)
1156 g_object_unref (pixbufs->pixbuf_open);
1157 if (pixbufs->pixbuf_close)
1158 g_object_unref (pixbufs->pixbuf_close);
1159 g_slice_free (ThreePixbufs, pixbufs);
1163 icon_cell_data (GtkTreeViewColumn *column,
1164 GtkCellRenderer *renderer,
1165 GtkTreeModel *tree_model,
1169 GObject *rendobj = NULL, *instance = NULL;
1170 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1171 gboolean has_children;
1172 ThreePixbufs *pixbufs;
1173 ModestFolderView *folder_view = (ModestFolderView *) data;
1175 rendobj = (GObject *) renderer;
1177 gtk_tree_model_get (tree_model, iter,
1179 INSTANCE_COLUMN, &instance,
1185 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1186 pixbufs = get_folder_icons (folder_view, type, instance);
1187 g_object_unref (instance);
1190 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1193 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1194 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1197 free_pixbufs (pixbufs);
1201 add_columns (GtkWidget *treeview)
1203 GtkTreeViewColumn *column;
1204 GtkCellRenderer *renderer;
1205 GtkTreeSelection *sel;
1206 ModestFolderViewPrivate *priv;
1208 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1211 column = gtk_tree_view_column_new ();
1213 /* Set icon and text render function */
1214 renderer = gtk_cell_renderer_pixbuf_new();
1215 #ifdef MODEST_TOOLKIT_HILDON2
1216 g_object_set (renderer,
1217 "xpad", MODEST_MARGIN_DEFAULT,
1218 "ypad", MODEST_MARGIN_DEFAULT,
1221 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1222 gtk_tree_view_column_set_cell_data_func(column, renderer,
1223 icon_cell_data, treeview, NULL);
1225 renderer = gtk_cell_renderer_text_new();
1226 g_object_set (renderer,
1227 #ifdef MODEST_TOOLKIT_HILDON2
1228 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1229 "ypad", MODEST_MARGIN_DEFAULT,
1230 "xpad", MODEST_MARGIN_DEFAULT,
1232 "ellipsize", PANGO_ELLIPSIZE_END,
1234 "ellipsize-set", TRUE, NULL);
1235 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1236 gtk_tree_view_column_set_cell_data_func(column, renderer,
1237 text_cell_data, treeview, NULL);
1239 priv->messages_renderer = gtk_cell_renderer_text_new ();
1240 g_object_set (priv->messages_renderer,
1241 #ifdef MODEST_TOOLKIT_HILDON2
1243 "ypad", MODEST_MARGIN_DEFAULT,
1244 "xpad", MODEST_MARGIN_DOUBLE,
1246 "scale", PANGO_SCALE_X_SMALL,
1249 "alignment", PANGO_ALIGN_RIGHT,
1253 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1254 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1255 messages_cell_data, treeview, NULL);
1257 /* Set selection mode */
1258 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1259 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1261 /* Set treeview appearance */
1262 gtk_tree_view_column_set_spacing (column, 2);
1263 gtk_tree_view_column_set_resizable (column, TRUE);
1264 gtk_tree_view_column_set_fixed_width (column, TRUE);
1265 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1266 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1269 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1273 modest_folder_view_init (ModestFolderView *obj)
1275 ModestFolderViewPrivate *priv;
1278 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1280 priv->timer_expander = 0;
1281 priv->account_store = NULL;
1283 priv->do_refresh = TRUE;
1284 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1285 priv->cur_folder_store = NULL;
1286 priv->visible_account_id = NULL;
1287 priv->mailbox = NULL;
1288 priv->folder_to_select = NULL;
1289 priv->reexpand = TRUE;
1290 priv->signal_handlers = 0;
1292 /* Initialize the local account name */
1293 conf = modest_runtime_get_conf();
1294 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1296 /* Init email clipboard */
1297 priv->clipboard = modest_runtime_get_email_clipboard ();
1298 priv->hidding_ids = NULL;
1299 priv->n_selected = 0;
1300 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1301 priv->reselect = FALSE;
1302 priv->show_non_move = TRUE;
1303 priv->list_to_move = NULL;
1304 priv->show_message_count = TRUE;
1306 /* Build treeview */
1307 add_columns (GTK_WIDGET (obj));
1309 /* Setup drag and drop */
1310 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1312 /* Connect signals */
1313 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1314 G_OBJECT (obj), "key-press-event",
1315 G_CALLBACK (on_key_pressed), NULL);
1317 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1318 (GObject*) modest_runtime_get_account_mgr (),
1319 "display_name_changed",
1320 G_CALLBACK (on_display_name_changed),
1324 * Track changes in the local account name (in the device it
1325 * will be the device name)
1327 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1330 G_CALLBACK(on_configuration_key_changed),
1333 priv->active_color = NULL;
1336 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1337 G_OBJECT (obj), "notify::style",
1338 G_CALLBACK (on_notify_style), (gpointer) obj);
1342 tny_account_store_view_init (gpointer g, gpointer iface_data)
1344 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1346 klass->set_account_store = modest_folder_view_set_account_store;
1350 modest_folder_view_dispose (GObject *obj)
1352 static gboolean disposed = FALSE;
1353 ModestFolderViewPrivate *priv;
1358 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1360 #ifdef MODEST_TOOLKIT_HILDON2
1361 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1364 /* Free external references */
1365 if (priv->account_store) {
1366 g_object_unref (G_OBJECT(priv->account_store));
1367 priv->account_store = NULL;
1371 g_object_unref (G_OBJECT (priv->query));
1375 if (priv->folder_to_select) {
1376 g_object_unref (G_OBJECT(priv->folder_to_select));
1377 priv->folder_to_select = NULL;
1380 if (priv->cur_folder_store) {
1381 g_object_unref (priv->cur_folder_store);
1382 priv->cur_folder_store = NULL;
1385 if (priv->list_to_move) {
1386 g_object_unref (priv->list_to_move);
1387 priv->list_to_move = NULL;
1392 modest_folder_view_finalize (GObject *obj)
1394 ModestFolderViewPrivate *priv;
1396 g_return_if_fail (obj);
1398 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1400 if (priv->active_color) {
1401 gdk_color_free (priv->active_color);
1402 priv->active_color = NULL;
1405 if (priv->timer_expander != 0) {
1406 g_source_remove (priv->timer_expander);
1407 priv->timer_expander = 0;
1410 g_free (priv->local_account_name);
1411 g_free (priv->visible_account_id);
1412 g_free (priv->mailbox);
1414 /* Clear hidding array created by cut operation */
1415 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1417 G_OBJECT_CLASS(parent_class)->finalize (obj);
1422 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1424 ModestFolderViewPrivate *priv;
1427 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1428 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1430 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1431 device = tny_account_store_get_device (account_store);
1433 if (G_UNLIKELY (priv->account_store)) {
1435 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1436 G_OBJECT (priv->account_store),
1437 "account_inserted"))
1438 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1439 G_OBJECT (priv->account_store),
1440 "account_inserted");
1441 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1442 G_OBJECT (priv->account_store),
1444 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1445 G_OBJECT (priv->account_store),
1447 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1448 G_OBJECT (priv->account_store),
1450 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1451 G_OBJECT (priv->account_store),
1453 g_object_unref (G_OBJECT (priv->account_store));
1456 priv->account_store = g_object_ref (G_OBJECT (account_store));
1458 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1459 G_OBJECT(account_store), "account_removed",
1460 G_CALLBACK (on_account_removed), self);
1462 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1463 G_OBJECT(account_store), "account_inserted",
1464 G_CALLBACK (on_account_inserted), self);
1466 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1467 G_OBJECT(account_store), "account_changed",
1468 G_CALLBACK (on_account_changed), self);
1470 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1471 priv->reselect = FALSE;
1472 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1474 g_object_unref (G_OBJECT (device));
1478 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1481 ModestFolderView *self;
1482 GtkTreeModel *model, *filter_model;
1485 self = MODEST_FOLDER_VIEW (user_data);
1487 if (!get_inner_models (self, &filter_model, NULL, &model))
1490 /* Remove outbox from model */
1491 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1492 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1493 g_object_unref (outbox);
1496 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1500 on_account_inserted (TnyAccountStore *account_store,
1501 TnyAccount *account,
1504 ModestFolderViewPrivate *priv;
1505 GtkTreeModel *model, *filter_model;
1507 /* Ignore transport account insertions, we're not showing them
1508 in the folder view */
1509 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1512 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1515 /* If we're adding a new account, and there is no previous
1516 one, we need to select the visible server account */
1517 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1518 !priv->visible_account_id)
1519 modest_widget_memory_restore (modest_runtime_get_conf(),
1520 G_OBJECT (user_data),
1521 MODEST_CONF_FOLDER_VIEW_KEY);
1525 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1526 &filter_model, NULL, &model))
1529 /* Insert the account in the model */
1530 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1532 /* When the model is a list store (plain representation) the
1533 outbox is not a child of any account so we have to manually
1534 delete it because removing the local folders account won't
1535 delete it (because tny_folder_get_account() is not defined
1536 for a merge folder */
1537 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1538 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1539 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1540 (GObject*) account, "outbox-deleted",
1541 G_CALLBACK (on_outbox_deleted_cb),
1545 /* Refilter the model */
1546 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1551 same_account_selected (ModestFolderView *self,
1552 TnyAccount *account)
1554 ModestFolderViewPrivate *priv;
1555 gboolean same_account = FALSE;
1557 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1559 if (priv->cur_folder_store) {
1560 TnyAccount *selected_folder_account = NULL;
1562 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1563 selected_folder_account =
1564 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1566 selected_folder_account =
1567 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1570 if (selected_folder_account == account)
1571 same_account = TRUE;
1573 g_object_unref (selected_folder_account);
1575 return same_account;
1580 * Selects the first inbox or the local account in an idle
1583 on_idle_select_first_inbox_or_local (gpointer user_data)
1585 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1587 gdk_threads_enter ();
1588 modest_folder_view_select_first_inbox_or_local (self);
1589 gdk_threads_leave ();
1595 on_account_changed (TnyAccountStore *account_store,
1596 TnyAccount *tny_account,
1599 ModestFolderView *self;
1600 ModestFolderViewPrivate *priv;
1601 GtkTreeModel *model, *filter_model;
1602 GtkTreeSelection *sel;
1603 gboolean same_account;
1605 /* Ignore transport account insertions, we're not showing them
1606 in the folder view */
1607 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1610 self = MODEST_FOLDER_VIEW (user_data);
1611 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1613 /* Get the inner model */
1614 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1615 &filter_model, NULL, &model))
1618 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1620 /* Invalidate the cur_folder_store only if the selected folder
1621 belongs to the account that is being removed */
1622 same_account = same_account_selected (self, tny_account);
1624 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1625 gtk_tree_selection_unselect_all (sel);
1628 /* Remove the account from the model */
1629 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1631 /* Insert the account in the model */
1632 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1634 /* Refilter the model */
1635 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1637 /* Select the first INBOX if the currently selected folder
1638 belongs to the account that is being deleted */
1639 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1640 g_idle_add (on_idle_select_first_inbox_or_local, self);
1644 on_account_removed (TnyAccountStore *account_store,
1645 TnyAccount *account,
1648 ModestFolderView *self = NULL;
1649 ModestFolderViewPrivate *priv;
1650 GtkTreeModel *model, *filter_model;
1651 GtkTreeSelection *sel = NULL;
1652 gboolean same_account = FALSE;
1654 /* Ignore transport account removals, we're not showing them
1655 in the folder view */
1656 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1659 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1660 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1664 self = MODEST_FOLDER_VIEW (user_data);
1665 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1667 /* Invalidate the cur_folder_store only if the selected folder
1668 belongs to the account that is being removed */
1669 same_account = same_account_selected (self, account);
1671 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1672 gtk_tree_selection_unselect_all (sel);
1675 /* Invalidate row to select only if the folder to select
1676 belongs to the account that is being removed*/
1677 if (priv->folder_to_select) {
1678 TnyAccount *folder_to_select_account = NULL;
1680 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1681 if (folder_to_select_account == account) {
1682 modest_folder_view_disable_next_folder_selection (self);
1683 g_object_unref (priv->folder_to_select);
1684 priv->folder_to_select = NULL;
1686 g_object_unref (folder_to_select_account);
1689 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1690 &filter_model, NULL, &model))
1693 /* Disconnect the signal handler */
1694 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1695 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1696 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject*) account, "outbox-deleted"))
1697 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1698 (GObject *) account,
1702 /* Remove the account from the model */
1703 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1705 /* If the removed account is the currently viewed one then
1706 clear the configuration value. The new visible account will be the default account */
1707 if (priv->visible_account_id &&
1708 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1710 /* Clear the current visible account_id */
1711 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1712 modest_folder_view_set_mailbox (self, NULL);
1714 /* Call the restore method, this will set the new visible account */
1715 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1716 MODEST_CONF_FOLDER_VIEW_KEY);
1719 /* Refilter the model */
1720 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1722 /* Select the first INBOX if the currently selected folder
1723 belongs to the account that is being deleted */
1725 g_idle_add (on_idle_select_first_inbox_or_local, self);
1729 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1731 GtkTreeViewColumn *col;
1733 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1735 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1737 g_printerr ("modest: failed get column for title\n");
1741 gtk_tree_view_column_set_title (col, title);
1742 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1747 modest_folder_view_on_map (ModestFolderView *self,
1748 GdkEventExpose *event,
1751 ModestFolderViewPrivate *priv;
1753 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1755 /* This won't happen often */
1756 if (G_UNLIKELY (priv->reselect)) {
1757 /* Select the first inbox or the local account if not found */
1759 /* TODO: this could cause a lock at startup, so we
1760 comment it for the moment. We know that this will
1761 be a bug, because the INBOX is not selected, but we
1762 need to rewrite some parts of Modest to avoid the
1763 deathlock situation */
1764 /* TODO: check if this is still the case */
1765 priv->reselect = FALSE;
1766 modest_folder_view_select_first_inbox_or_local (self);
1767 /* Notify the display name observers */
1768 g_signal_emit (G_OBJECT(self),
1769 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1773 if (priv->reexpand) {
1774 expand_root_items (self);
1775 priv->reexpand = FALSE;
1782 modest_folder_view_new (TnyFolderStoreQuery *query)
1784 return modest_folder_view_new_full (query, TRUE);
1788 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1791 ModestFolderViewPrivate *priv;
1792 GtkTreeSelection *sel;
1794 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1795 #ifdef MODEST_TOOLKIT_HILDON2
1796 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1799 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1802 priv->query = g_object_ref (query);
1804 priv->do_refresh = do_refresh;
1806 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1807 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1808 (GObject*) sel, "changed",
1809 G_CALLBACK (on_selection_changed), self);
1811 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1812 self, "row-activated",
1813 G_CALLBACK (on_row_activated), self);
1815 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1816 self, "expose-event",
1817 G_CALLBACK (modest_folder_view_on_map), NULL);
1819 return GTK_WIDGET(self);
1822 /* this feels dirty; any other way to expand all the root items? */
1824 expand_root_items (ModestFolderView *self)
1827 GtkTreeModel *model;
1830 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1831 path = gtk_tree_path_new_first ();
1833 /* all folders should have child items, so.. */
1835 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1836 gtk_tree_path_next (path);
1837 } while (gtk_tree_model_get_iter (model, &iter, path));
1839 gtk_tree_path_free (path);
1843 is_parent_of (TnyFolder *a, TnyFolder *b)
1846 gboolean retval = FALSE;
1848 a_id = tny_folder_get_id (a);
1850 gchar *string_to_match;
1853 string_to_match = g_strconcat (a_id, "/", NULL);
1854 b_id = tny_folder_get_id (b);
1855 retval = g_str_has_prefix (b_id, string_to_match);
1856 g_free (string_to_match);
1862 typedef struct _ForeachFolderInfo {
1865 } ForeachFolderInfo;
1868 foreach_folder_with_id (GtkTreeModel *model,
1873 ForeachFolderInfo *info;
1876 info = (ForeachFolderInfo *) data;
1877 gtk_tree_model_get (model, iter,
1878 INSTANCE_COLUMN, &instance,
1881 if (TNY_IS_FOLDER (instance)) {
1884 id = tny_folder_get_id (TNY_FOLDER (instance));
1886 collate = g_utf8_collate_key (id, -1);
1887 info->found = !strcmp (info->needle, collate);
1893 g_object_unref (instance);
1901 has_folder_with_id (ModestFolderView *self, const gchar *id)
1903 GtkTreeModel *model;
1904 ForeachFolderInfo info = {NULL, FALSE};
1906 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1907 info.needle = g_utf8_collate_key (id, -1);
1909 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1910 g_free (info.needle);
1916 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1919 gboolean retval = FALSE;
1921 a_id = tny_folder_get_id (a);
1924 b_id = tny_folder_get_id (b);
1927 const gchar *last_bar;
1928 gchar *string_to_match;
1929 last_bar = g_strrstr (b_id, "/");
1934 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1935 retval = has_folder_with_id (self, string_to_match);
1936 g_free (string_to_match);
1944 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1946 ModestFolderViewPrivate *priv;
1947 TnyIterator *iterator;
1948 gboolean retval = TRUE;
1950 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1951 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1953 for (iterator = tny_list_create_iterator (priv->list_to_move);
1954 retval && !tny_iterator_is_done (iterator);
1955 tny_iterator_next (iterator)) {
1957 instance = tny_iterator_get_current (iterator);
1958 if (instance == (GObject *) folder) {
1960 } else if (TNY_IS_FOLDER (instance)) {
1961 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1963 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1966 g_object_unref (instance);
1968 g_object_unref (iterator);
1975 * We use this function to implement the
1976 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1977 * account in this case, and the local folders.
1980 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1982 ModestFolderViewPrivate *priv;
1983 gboolean retval = TRUE;
1984 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1985 GObject *instance = NULL;
1986 const gchar *id = NULL;
1988 gboolean found = FALSE;
1989 gboolean cleared = FALSE;
1990 ModestTnyFolderRules rules = 0;
1993 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1994 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1996 gtk_tree_model_get (model, iter,
1997 NAME_COLUMN, &fname,
1999 INSTANCE_COLUMN, &instance,
2002 /* Do not show if there is no instance, this could indeed
2003 happen when the model is being modified while it's being
2004 drawn. This could occur for example when moving folders
2011 if (TNY_IS_ACCOUNT (instance)) {
2012 TnyAccount *acc = TNY_ACCOUNT (instance);
2013 const gchar *account_id = tny_account_get_id (acc);
2015 /* If it isn't a special folder,
2016 * don't show it unless it is the visible account: */
2017 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2018 !modest_tny_account_is_virtual_local_folders (acc) &&
2019 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2021 /* Show only the visible account id */
2022 if (priv->visible_account_id) {
2023 if (strcmp (account_id, priv->visible_account_id))
2030 /* Never show these to the user. They are merged into one folder
2031 * in the local-folders account instead: */
2032 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2035 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2036 /* Only show special folders for current account if needed */
2037 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2038 TnyAccount *account;
2040 account = tny_folder_get_account (TNY_FOLDER (instance));
2042 if (TNY_IS_ACCOUNT (account)) {
2043 const gchar *account_id = tny_account_get_id (account);
2045 if (!modest_tny_account_is_virtual_local_folders (account) &&
2046 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2047 /* Show only the visible account id */
2048 if (priv->visible_account_id) {
2049 if (strcmp (account_id, priv->visible_account_id)) {
2051 } else if (priv->mailbox) {
2052 /* Filter mailboxes */
2053 if (!g_str_has_prefix (fname, priv->mailbox)) {
2055 } else if (!strcmp (fname, priv->mailbox)) {
2056 /* Hide mailbox parent */
2062 g_object_unref (account);
2069 /* Check hiding (if necessary) */
2070 cleared = modest_email_clipboard_cleared (priv->clipboard);
2071 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2072 id = tny_folder_get_id (TNY_FOLDER(instance));
2073 if (priv->hidding_ids != NULL)
2074 for (i=0; i < priv->n_selected && !found; i++)
2075 if (priv->hidding_ids[i] != NULL && id != NULL)
2076 found = (!strcmp (priv->hidding_ids[i], id));
2081 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2082 folder as no message can be move there according to UI specs */
2083 if (retval && !priv->show_non_move) {
2084 if (priv->list_to_move &&
2085 tny_list_get_length (priv->list_to_move) > 0 &&
2086 TNY_IS_FOLDER (instance)) {
2087 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2089 if (retval && TNY_IS_FOLDER (instance) &&
2090 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2092 case TNY_FOLDER_TYPE_OUTBOX:
2093 case TNY_FOLDER_TYPE_SENT:
2094 case TNY_FOLDER_TYPE_DRAFTS:
2097 case TNY_FOLDER_TYPE_UNKNOWN:
2098 case TNY_FOLDER_TYPE_NORMAL:
2099 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2100 if (type == TNY_FOLDER_TYPE_INVALID)
2101 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2103 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2104 type == TNY_FOLDER_TYPE_SENT
2105 || type == TNY_FOLDER_TYPE_DRAFTS)
2112 if (retval && TNY_IS_ACCOUNT (instance) &&
2113 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2114 ModestProtocolType protocol_type;
2116 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2117 retval = !modest_protocol_registry_protocol_type_has_tag
2118 (modest_runtime_get_protocol_registry (),
2120 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2124 /* apply special filters */
2125 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2126 if (TNY_IS_ACCOUNT (instance))
2130 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2131 if (TNY_IS_FOLDER (instance))
2135 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2136 if (TNY_IS_ACCOUNT (instance)) {
2137 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2139 } else if (TNY_IS_FOLDER (instance)) {
2140 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2145 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2146 if (TNY_IS_ACCOUNT (instance)) {
2147 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2149 } else if (TNY_IS_FOLDER (instance)) {
2150 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2155 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2156 /* A mailbox is a fake folder with an @ in the middle of the name */
2157 if (!TNY_IS_FOLDER (instance) ||
2158 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2161 const gchar *folder_name;
2162 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2163 if (!folder_name || strchr (folder_name, '@') == NULL)
2169 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2170 if (TNY_IS_FOLDER (instance)) {
2171 /* Check folder rules */
2172 ModestTnyFolderRules rules;
2174 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2175 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2176 } else if (TNY_IS_ACCOUNT (instance)) {
2177 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2185 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2186 if (TNY_IS_FOLDER (instance)) {
2187 TnyFolderType guess_type;
2189 if (TNY_FOLDER_TYPE_NORMAL) {
2190 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2196 case TNY_FOLDER_TYPE_OUTBOX:
2197 case TNY_FOLDER_TYPE_SENT:
2198 case TNY_FOLDER_TYPE_DRAFTS:
2199 case TNY_FOLDER_TYPE_ARCHIVE:
2200 case TNY_FOLDER_TYPE_INBOX:
2203 case TNY_FOLDER_TYPE_UNKNOWN:
2204 case TNY_FOLDER_TYPE_NORMAL:
2210 } else if (TNY_IS_ACCOUNT (instance)) {
2215 if (retval && TNY_IS_FOLDER (instance)) {
2216 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2219 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2220 if (TNY_IS_FOLDER (instance)) {
2221 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2222 } else if (TNY_IS_ACCOUNT (instance)) {
2227 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2228 if (TNY_IS_FOLDER (instance)) {
2229 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2230 } else if (TNY_IS_ACCOUNT (instance)) {
2235 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2236 if (TNY_IS_FOLDER (instance)) {
2237 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2238 } else if (TNY_IS_ACCOUNT (instance)) {
2244 g_object_unref (instance);
2252 modest_folder_view_update_model (ModestFolderView *self,
2253 TnyAccountStore *account_store)
2255 ModestFolderViewPrivate *priv;
2256 GtkTreeModel *model;
2257 GtkTreeModel *filter_model = NULL, *sortable = NULL, *old_tny_model;
2259 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2260 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2263 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2265 /* Notify that there is no folder selected */
2266 g_signal_emit (G_OBJECT(self),
2267 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2269 if (priv->cur_folder_store) {
2270 g_object_unref (priv->cur_folder_store);
2271 priv->cur_folder_store = NULL;
2274 /* FIXME: the local accounts are not shown when the query
2275 selects only the subscribed folders */
2276 #ifdef MODEST_TOOLKIT_HILDON2
2277 TnyGtkFolderListStoreFlags flags;
2278 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2279 if (!priv->do_refresh)
2280 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2281 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2283 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2284 MODEST_FOLDER_PATH_SEPARATOR);
2286 model = tny_gtk_folder_store_tree_model_new (NULL);
2289 /* When the model is a list store (plain representation) the
2290 outbox is not a child of any account so we have to manually
2291 delete it because removing the local folders account won't
2292 delete it (because tny_folder_get_account() is not defined
2293 for a merge folder */
2294 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2295 TnyAccount *account;
2296 ModestTnyAccountStore *acc_store;
2298 acc_store = modest_runtime_get_account_store ();
2299 account = modest_tny_account_store_get_local_folders_account (acc_store);
2301 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) account,
2303 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2304 (GObject *) account,
2307 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2308 (GObject*) account, "outbox-deleted",
2309 G_CALLBACK (on_outbox_deleted_cb),
2311 g_object_unref (account);
2314 /* Get the accounts: */
2315 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2317 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2319 sortable = gtk_tree_model_sort_new_with_model (model);
2320 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2322 GTK_SORT_ASCENDING);
2323 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2325 cmp_rows, NULL, NULL);
2327 /* Create filter model */
2328 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2329 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2334 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2335 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) old_tny_model,
2336 "activity-changed"))
2337 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2338 G_OBJECT (old_tny_model),
2339 "activity-changed");
2343 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2344 #ifndef MODEST_TOOLKIT_HILDON2
2345 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2346 G_OBJECT(filter_model), "row-inserted",
2347 (GCallback) on_row_inserted_maybe_select_folder, self);
2350 #ifdef MODEST_TOOLKIT_HILDON2
2351 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2354 G_CALLBACK (on_activity_changed),
2358 g_object_unref (model);
2359 g_object_unref (filter_model);
2360 g_object_unref (sortable);
2362 /* Force a reselection of the INBOX next time the widget is shown */
2363 priv->reselect = TRUE;
2370 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2372 GtkTreeModel *model = NULL;
2373 TnyFolderStore *folder = NULL;
2375 ModestFolderView *tree_view = NULL;
2376 ModestFolderViewPrivate *priv = NULL;
2377 gboolean selected = FALSE;
2379 g_return_if_fail (sel);
2380 g_return_if_fail (user_data);
2382 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2384 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2386 tree_view = MODEST_FOLDER_VIEW (user_data);
2389 gtk_tree_model_get (model, &iter,
2390 INSTANCE_COLUMN, &folder,
2393 /* If the folder is the same do not notify */
2394 if (folder && priv->cur_folder_store == folder) {
2395 g_object_unref (folder);
2400 /* Current folder was unselected */
2401 if (priv->cur_folder_store) {
2402 /* We must do this firstly because a libtinymail-camel
2403 implementation detail. If we issue the signal
2404 before doing the sync_async, then that signal could
2405 cause (and it actually does it) a free of the
2406 summary of the folder (because the main window will
2407 clear the headers view */
2408 #ifndef MODEST_TOOLKIT_HILDON2
2409 if (TNY_IS_FOLDER(priv->cur_folder_store))
2410 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2411 FALSE, NULL, NULL, NULL);
2414 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2415 priv->cur_folder_store, FALSE);
2417 g_object_unref (priv->cur_folder_store);
2418 priv->cur_folder_store = NULL;
2421 /* New current references */
2422 priv->cur_folder_store = folder;
2424 /* New folder has been selected. Do not notify if there is
2425 nothing new selected */
2427 g_signal_emit (G_OBJECT(tree_view),
2428 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2429 0, priv->cur_folder_store, TRUE);
2434 on_row_activated (GtkTreeView *treeview,
2435 GtkTreePath *treepath,
2436 GtkTreeViewColumn *column,
2439 GtkTreeModel *model = NULL;
2440 TnyFolderStore *folder = NULL;
2442 ModestFolderView *self = NULL;
2443 ModestFolderViewPrivate *priv = NULL;
2445 g_return_if_fail (treeview);
2446 g_return_if_fail (user_data);
2448 self = MODEST_FOLDER_VIEW (user_data);
2449 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2451 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2453 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2456 gtk_tree_model_get (model, &iter,
2457 INSTANCE_COLUMN, &folder,
2460 g_signal_emit (G_OBJECT(self),
2461 signals[FOLDER_ACTIVATED_SIGNAL],
2464 #ifdef MODEST_TOOLKIT_HILDON2
2465 HildonUIMode ui_mode;
2466 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2467 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2468 if (priv->cur_folder_store)
2469 g_object_unref (priv->cur_folder_store);
2470 priv->cur_folder_store = g_object_ref (folder);
2474 g_object_unref (folder);
2478 modest_folder_view_get_selected (ModestFolderView *self)
2480 ModestFolderViewPrivate *priv;
2482 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2484 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2485 if (priv->cur_folder_store)
2486 g_object_ref (priv->cur_folder_store);
2488 return priv->cur_folder_store;
2492 get_cmp_rows_type_pos (GObject *folder)
2494 /* Remote accounts -> Local account -> MMC account .*/
2497 if (TNY_IS_ACCOUNT (folder) &&
2498 modest_tny_account_is_virtual_local_folders (
2499 TNY_ACCOUNT (folder))) {
2501 } else if (TNY_IS_ACCOUNT (folder)) {
2502 TnyAccount *account = TNY_ACCOUNT (folder);
2503 const gchar *account_id = tny_account_get_id (account);
2504 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2510 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2511 return -1; /* Should never happen */
2516 inbox_is_special (TnyFolderStore *folder_store)
2518 gboolean is_special = TRUE;
2520 if (TNY_IS_FOLDER (folder_store)) {
2524 gchar *last_inbox_bar;
2526 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2527 downcase = g_utf8_strdown (id, -1);
2528 last_bar = g_strrstr (downcase, "/");
2530 last_inbox_bar = g_strrstr (downcase, "inbox/");
2531 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2542 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2544 TnyAccount *account;
2545 gboolean is_special;
2546 /* Inbox, Outbox, Drafts, Sent, User */
2549 if (!TNY_IS_FOLDER (folder_store))
2552 case TNY_FOLDER_TYPE_INBOX:
2554 account = tny_folder_get_account (folder_store);
2555 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2557 /* In inbox case we need to know if the inbox is really the top
2558 * inbox of the account, or if it's a submailbox inbox. To do
2559 * this we'll apply an heuristic rule: Find last "/" and check
2560 * if it's preceeded by another Inbox */
2561 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2562 g_object_unref (account);
2563 return is_special?0:4;
2566 case TNY_FOLDER_TYPE_OUTBOX:
2567 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2569 case TNY_FOLDER_TYPE_DRAFTS:
2571 account = tny_folder_get_account (folder_store);
2572 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2573 g_object_unref (account);
2574 return is_special?1:4;
2577 case TNY_FOLDER_TYPE_SENT:
2579 account = tny_folder_get_account (folder_store);
2580 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2581 g_object_unref (account);
2582 return is_special?3:4;
2591 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2593 const gchar *a1_name, *a2_name;
2595 a1_name = tny_account_get_name (a1);
2596 a2_name = tny_account_get_name (a2);
2598 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2602 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2604 TnyAccount *a1 = NULL, *a2 = NULL;
2607 if (TNY_IS_ACCOUNT (s1)) {
2608 a1 = TNY_ACCOUNT (g_object_ref (s1));
2609 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2610 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2613 if (TNY_IS_ACCOUNT (s2)) {
2614 a2 = TNY_ACCOUNT (g_object_ref (s2));
2615 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2616 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2633 /* First we sort with the type of account */
2634 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2638 cmp = compare_account_names (a1, a2);
2642 g_object_unref (a1);
2644 g_object_unref (a2);
2650 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2652 gint is_account1, is_account2;
2654 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2655 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2657 return is_account2 - is_account1;
2661 compare_folders (const gchar *name1, const gchar *name2)
2663 const gchar *separator1, *separator2;
2664 const gchar *next1, *next2;
2668 if (name1 == NULL || name1[0] == '\0')
2670 if (name2 == NULL || name2[0] == '\0')
2673 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2675 top1 = g_strndup (name1, separator1 - name1);
2677 top1 = g_strdup (name1);
2680 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2682 top2 = g_strndup (name2, separator2 - name2);
2684 top2 = g_strdup (name2);
2688 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2695 if (separator1 == NULL && separator2 == NULL)
2698 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2699 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2701 return compare_folders (next1, next2);
2706 * This function orders the mail accounts according to these rules:
2707 * 1st - remote accounts
2708 * 2nd - local account
2712 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2716 gchar *name1 = NULL;
2717 gchar *name2 = NULL;
2718 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2719 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2720 GObject *folder1 = NULL;
2721 GObject *folder2 = NULL;
2723 gtk_tree_model_get (tree_model, iter1,
2724 NAME_COLUMN, &name1,
2726 INSTANCE_COLUMN, &folder1,
2728 gtk_tree_model_get (tree_model, iter2,
2729 NAME_COLUMN, &name2,
2730 TYPE_COLUMN, &type2,
2731 INSTANCE_COLUMN, &folder2,
2734 /* Return if we get no folder. This could happen when folder
2735 operations are happening. The model is updated after the
2736 folder copy/move actually occurs, so there could be
2737 situations where the model to be drawn is not correct */
2738 if (!folder1 || !folder2)
2741 /* Sort by type. First the special folders, then the archives */
2742 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2746 /* Now we sort using the account of each folder */
2747 if (TNY_IS_FOLDER_STORE (folder1) &&
2748 TNY_IS_FOLDER_STORE (folder2)) {
2749 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2753 /* Each group is preceeded by its account */
2754 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2759 /* Pure sort by name */
2760 cmp = compare_folders (name1, name2);
2763 g_object_unref(G_OBJECT(folder1));
2765 g_object_unref(G_OBJECT(folder2));
2773 /*****************************************************************************/
2774 /* DRAG and DROP stuff */
2775 /*****************************************************************************/
2777 * This function fills the #GtkSelectionData with the row and the
2778 * model that has been dragged. It's called when this widget is a
2779 * source for dnd after the event drop happened
2782 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2783 guint info, guint time, gpointer data)
2785 GtkTreeSelection *selection;
2786 GtkTreeModel *model;
2788 GtkTreePath *source_row;
2790 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2791 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2793 source_row = gtk_tree_model_get_path (model, &iter);
2794 gtk_tree_set_row_drag_data (selection_data,
2798 gtk_tree_path_free (source_row);
2802 typedef struct _DndHelper {
2803 ModestFolderView *folder_view;
2804 gboolean delete_source;
2805 GtkTreePath *source_row;
2809 dnd_helper_destroyer (DndHelper *helper)
2811 /* Free the helper */
2812 gtk_tree_path_free (helper->source_row);
2813 g_slice_free (DndHelper, helper);
2817 xfer_folder_cb (ModestMailOperation *mail_op,
2818 TnyFolder *new_folder,
2822 /* Select the folder */
2823 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2829 /* get the folder for the row the treepath refers to. */
2830 /* folder must be unref'd */
2831 static TnyFolderStore *
2832 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2835 TnyFolderStore *folder = NULL;
2837 if (gtk_tree_model_get_iter (model,&iter, path))
2838 gtk_tree_model_get (model, &iter,
2839 INSTANCE_COLUMN, &folder,
2846 * This function is used by drag_data_received_cb to manage drag and
2847 * drop of a header, i.e, and drag from the header view to the folder
2851 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2852 GtkTreeModel *dest_model,
2853 GtkTreePath *dest_row,
2854 GtkSelectionData *selection_data)
2856 TnyList *headers = NULL;
2857 TnyFolder *folder = NULL, *src_folder = NULL;
2858 TnyFolderType folder_type;
2859 GtkTreeIter source_iter, dest_iter;
2860 ModestWindowMgr *mgr = NULL;
2861 ModestWindow *main_win = NULL;
2862 gchar **uris, **tmp;
2864 /* Build the list of headers */
2865 mgr = modest_runtime_get_window_mgr ();
2866 headers = tny_simple_list_new ();
2867 uris = modest_dnd_selection_data_get_paths (selection_data);
2870 while (*tmp != NULL) {
2873 gboolean first = TRUE;
2876 path = gtk_tree_path_new_from_string (*tmp);
2877 gtk_tree_model_get_iter (source_model, &source_iter, path);
2878 gtk_tree_model_get (source_model, &source_iter,
2879 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2882 /* Do not enable d&d of headers already opened */
2883 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2884 tny_list_append (headers, G_OBJECT (header));
2886 if (G_UNLIKELY (first)) {
2887 src_folder = tny_header_get_folder (header);
2891 /* Free and go on */
2892 gtk_tree_path_free (path);
2893 g_object_unref (header);
2898 /* This could happen ig we perform a d&d very quickly over the
2899 same row that row could dissapear because message is
2901 if (!TNY_IS_FOLDER (src_folder))
2904 /* Get the target folder */
2905 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2906 gtk_tree_model_get (dest_model, &dest_iter,
2910 if (!folder || !TNY_IS_FOLDER(folder)) {
2911 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2915 folder_type = modest_tny_folder_guess_folder_type (folder);
2916 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2917 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2918 goto cleanup; /* cannot move messages there */
2921 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2922 /* g_warning ("folder not writable"); */
2923 goto cleanup; /* verboten! */
2926 /* Ask for confirmation to move */
2927 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2929 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2933 /* Transfer messages */
2934 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2939 if (G_IS_OBJECT (src_folder))
2940 g_object_unref (src_folder);
2941 if (G_IS_OBJECT(folder))
2942 g_object_unref (G_OBJECT (folder));
2943 if (G_IS_OBJECT(headers))
2944 g_object_unref (headers);
2948 TnyFolderStore *src_folder;
2949 TnyFolderStore *dst_folder;
2950 ModestFolderView *folder_view;
2955 dnd_folder_info_destroyer (DndFolderInfo *info)
2957 if (info->src_folder)
2958 g_object_unref (info->src_folder);
2959 if (info->dst_folder)
2960 g_object_unref (info->dst_folder);
2961 g_slice_free (DndFolderInfo, info);
2965 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2966 GtkWindow *parent_window,
2967 TnyAccount *account)
2970 modest_ui_actions_on_account_connection_error (parent_window, account);
2972 /* Free the helper & info */
2973 dnd_helper_destroyer (info->helper);
2974 dnd_folder_info_destroyer (info);
2978 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2980 GtkWindow *parent_window,
2981 TnyAccount *account,
2984 DndFolderInfo *info = NULL;
2985 ModestMailOperation *mail_op;
2987 info = (DndFolderInfo *) user_data;
2989 if (err || canceled) {
2990 dnd_on_connection_failed_destroyer (info, parent_window, account);
2994 /* Do the mail operation */
2995 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2996 modest_ui_actions_move_folder_error_handler,
2997 info->src_folder, NULL);
2999 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3002 /* Transfer the folder */
3003 modest_mail_operation_xfer_folder (mail_op,
3004 TNY_FOLDER (info->src_folder),
3006 info->helper->delete_source,
3008 info->helper->folder_view);
3011 g_object_unref (G_OBJECT (mail_op));
3012 dnd_helper_destroyer (info->helper);
3013 dnd_folder_info_destroyer (info);
3018 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3020 GtkWindow *parent_window,
3021 TnyAccount *account,
3024 DndFolderInfo *info = NULL;
3026 info = (DndFolderInfo *) user_data;
3028 if (err || canceled) {
3029 dnd_on_connection_failed_destroyer (info, parent_window, account);
3033 /* Connect to source folder and perform the copy/move */
3034 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3036 drag_and_drop_from_folder_view_src_folder_performer,
3041 * This function is used by drag_data_received_cb to manage drag and
3042 * drop of a folder, i.e, and drag from the folder view to the same
3046 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3047 GtkTreeModel *dest_model,
3048 GtkTreePath *dest_row,
3049 GtkSelectionData *selection_data,
3052 GtkTreeIter dest_iter, iter;
3053 TnyFolderStore *dest_folder = NULL;
3054 TnyFolderStore *folder = NULL;
3055 gboolean forbidden = FALSE;
3057 DndFolderInfo *info = NULL;
3059 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3061 g_warning ("%s: BUG: no main window", __FUNCTION__);
3062 dnd_helper_destroyer (helper);
3067 /* check the folder rules for the destination */
3068 folder = tree_path_to_folder (dest_model, dest_row);
3069 if (TNY_IS_FOLDER(folder)) {
3070 ModestTnyFolderRules rules =
3071 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3072 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3073 } else if (TNY_IS_FOLDER_STORE(folder)) {
3074 /* enable local root as destination for folders */
3075 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3076 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3079 g_object_unref (folder);
3082 /* check the folder rules for the source */
3083 folder = tree_path_to_folder (source_model, helper->source_row);
3084 if (TNY_IS_FOLDER(folder)) {
3085 ModestTnyFolderRules rules =
3086 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3087 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3090 g_object_unref (folder);
3094 /* Check if the drag is possible */
3095 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3097 modest_platform_run_information_dialog ((GtkWindow *) win,
3098 _("mail_in_ui_folder_move_target_error"),
3100 /* Restore the previous selection */
3101 folder = tree_path_to_folder (source_model, helper->source_row);
3103 if (TNY_IS_FOLDER (folder))
3104 modest_folder_view_select_folder (helper->folder_view,
3105 TNY_FOLDER (folder), FALSE);
3106 g_object_unref (folder);
3108 dnd_helper_destroyer (helper);
3113 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3114 gtk_tree_model_get (dest_model, &dest_iter,
3117 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3118 gtk_tree_model_get (source_model, &iter,
3122 /* Create the info for the performer */
3123 info = g_slice_new0 (DndFolderInfo);
3124 info->src_folder = g_object_ref (folder);
3125 info->dst_folder = g_object_ref (dest_folder);
3126 info->helper = helper;
3128 /* Connect to the destination folder and perform the copy/move */
3129 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3131 drag_and_drop_from_folder_view_dst_folder_performer,
3135 g_object_unref (dest_folder);
3136 g_object_unref (folder);
3140 * This function receives the data set by the "drag-data-get" signal
3141 * handler. This information comes within the #GtkSelectionData. This
3142 * function will manage both the drags of folders of the treeview and
3143 * drags of headers of the header view widget.
3146 on_drag_data_received (GtkWidget *widget,
3147 GdkDragContext *context,
3150 GtkSelectionData *selection_data,
3155 GtkWidget *source_widget;
3156 GtkTreeModel *dest_model, *source_model;
3157 GtkTreePath *source_row, *dest_row;
3158 GtkTreeViewDropPosition pos;
3159 gboolean delete_source = FALSE;
3160 gboolean success = FALSE;
3162 /* Do not allow further process */
3163 g_signal_stop_emission_by_name (widget, "drag-data-received");
3164 source_widget = gtk_drag_get_source_widget (context);
3166 /* Get the action */
3167 if (context->action == GDK_ACTION_MOVE) {
3168 delete_source = TRUE;
3170 /* Notify that there is no folder selected. We need to
3171 do this in order to update the headers view (and
3172 its monitors, because when moving, the old folder
3173 won't longer exist. We can not wait for the end of
3174 the operation, because the operation won't start if
3175 the folder is in use */
3176 if (source_widget == widget) {
3177 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3178 gtk_tree_selection_unselect_all (sel);
3182 /* Check if the get_data failed */
3183 if (selection_data == NULL || selection_data->length < 0)
3186 /* Select the destination model */
3187 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3189 /* Get the path to the destination row. Can not call
3190 gtk_tree_view_get_drag_dest_row() because the source row
3191 is not selected anymore */
3192 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3195 /* Only allow drops IN other rows */
3197 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3198 pos == GTK_TREE_VIEW_DROP_AFTER)
3202 /* Drags from the header view */
3203 if (source_widget != widget) {
3204 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3206 drag_and_drop_from_header_view (source_model,
3211 DndHelper *helper = NULL;
3213 /* Get the source model and row */
3214 gtk_tree_get_row_drag_data (selection_data,
3218 /* Create the helper */
3219 helper = g_slice_new0 (DndHelper);
3220 helper->delete_source = delete_source;
3221 helper->source_row = gtk_tree_path_copy (source_row);
3222 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3224 drag_and_drop_from_folder_view (source_model,
3230 gtk_tree_path_free (source_row);
3234 gtk_tree_path_free (dest_row);
3237 /* Finish the drag and drop */
3238 gtk_drag_finish (context, success, FALSE, time);
3242 * We define a "drag-drop" signal handler because we do not want to
3243 * use the default one, because the default one always calls
3244 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3245 * signal handler, because there we have all the information available
3246 * to know if the dnd was a success or not.
3249 drag_drop_cb (GtkWidget *widget,
3250 GdkDragContext *context,
3258 if (!context->targets)
3261 /* Check if we're dragging a folder row */
3262 target = gtk_drag_dest_find_target (widget, context, NULL);
3264 /* Request the data from the source. */
3265 gtk_drag_get_data(widget, context, target, time);
3271 * This function expands a node of a tree view if it's not expanded
3272 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3273 * does that, so that's why they're here.
3276 expand_row_timeout (gpointer data)
3278 GtkTreeView *tree_view = data;
3279 GtkTreePath *dest_path = NULL;
3280 GtkTreeViewDropPosition pos;
3281 gboolean result = FALSE;
3283 gdk_threads_enter ();
3285 gtk_tree_view_get_drag_dest_row (tree_view,
3290 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3291 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3292 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3293 gtk_tree_path_free (dest_path);
3297 gtk_tree_path_free (dest_path);
3302 gdk_threads_leave ();
3308 * This function is called whenever the pointer is moved over a widget
3309 * while dragging some data. It installs a timeout that will expand a
3310 * node of the treeview if not expanded yet. This function also calls
3311 * gdk_drag_status in order to set the suggested action that will be
3312 * used by the "drag-data-received" signal handler to know if we
3313 * should do a move or just a copy of the data.
3316 on_drag_motion (GtkWidget *widget,
3317 GdkDragContext *context,
3323 GtkTreeViewDropPosition pos;
3324 GtkTreePath *dest_row;
3325 GtkTreeModel *dest_model;
3326 ModestFolderViewPrivate *priv;
3327 GdkDragAction suggested_action;
3328 gboolean valid_location = FALSE;
3329 TnyFolderStore *folder = NULL;
3331 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3333 if (priv->timer_expander != 0) {
3334 g_source_remove (priv->timer_expander);
3335 priv->timer_expander = 0;
3338 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3343 /* Do not allow drops between folders */
3345 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3346 pos == GTK_TREE_VIEW_DROP_AFTER) {
3347 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3348 gdk_drag_status(context, 0, time);
3349 valid_location = FALSE;
3352 valid_location = TRUE;
3355 /* Check that the destination folder is writable */
3356 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3357 folder = tree_path_to_folder (dest_model, dest_row);
3358 if (folder && TNY_IS_FOLDER (folder)) {
3359 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3361 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3362 valid_location = FALSE;
3367 /* Expand the selected row after 1/2 second */
3368 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3369 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3371 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3373 /* Select the desired action. By default we pick MOVE */
3374 suggested_action = GDK_ACTION_MOVE;
3376 if (context->actions == GDK_ACTION_COPY)
3377 gdk_drag_status(context, GDK_ACTION_COPY, time);
3378 else if (context->actions == GDK_ACTION_MOVE)
3379 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3380 else if (context->actions & suggested_action)
3381 gdk_drag_status(context, suggested_action, time);
3383 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3387 g_object_unref (folder);
3389 gtk_tree_path_free (dest_row);
3391 g_signal_stop_emission_by_name (widget, "drag-motion");
3393 return valid_location;
3397 * This function sets the treeview as a source and a target for dnd
3398 * events. It also connects all the requirede signals.
3401 setup_drag_and_drop (GtkTreeView *self)
3403 /* Set up the folder view as a dnd destination. Set only the
3404 highlight flag, otherwise gtk will have a different
3406 #ifdef MODEST_TOOLKIT_HILDON2
3409 ModestFolderViewPrivate *priv;
3411 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3413 gtk_drag_dest_set (GTK_WIDGET (self),
3414 GTK_DEST_DEFAULT_HIGHLIGHT,
3415 folder_view_drag_types,
3416 G_N_ELEMENTS (folder_view_drag_types),
3417 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3419 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3420 G_OBJECT (self), "drag_data_received",
3421 G_CALLBACK (on_drag_data_received), NULL);
3424 /* Set up the treeview as a dnd source */
3425 gtk_drag_source_set (GTK_WIDGET (self),
3427 folder_view_drag_types,
3428 G_N_ELEMENTS (folder_view_drag_types),
3429 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3431 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3432 G_OBJECT (self), "drag_motion",
3433 G_CALLBACK (on_drag_motion), NULL);
3435 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3436 G_OBJECT (self), "drag_data_get",
3437 G_CALLBACK (on_drag_data_get), NULL);
3439 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3440 G_OBJECT (self), "drag_drop",
3441 G_CALLBACK (drag_drop_cb), NULL);
3445 * This function manages the navigation through the folders using the
3446 * keyboard or the hardware keys in the device
3449 on_key_pressed (GtkWidget *self,
3453 GtkTreeSelection *selection;
3455 GtkTreeModel *model;
3456 gboolean retval = FALSE;
3458 /* Up and Down are automatically managed by the treeview */
3459 if (event->keyval == GDK_Return) {
3460 /* Expand/Collapse the selected row */
3461 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3462 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3465 path = gtk_tree_model_get_path (model, &iter);
3467 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3468 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3470 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3471 gtk_tree_path_free (path);
3473 /* No further processing */
3481 * We listen to the changes in the local folder account name key,
3482 * because we want to show the right name in the view. The local
3483 * folder account name corresponds to the device name in the Maemo
3484 * version. We do this because we do not want to query gconf on each
3485 * tree view refresh. It's better to cache it and change whenever
3489 on_configuration_key_changed (ModestConf* conf,
3491 ModestConfEvent event,
3492 ModestConfNotificationId id,
3493 ModestFolderView *self)
3495 ModestFolderViewPrivate *priv;
3498 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3499 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3501 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3502 g_free (priv->local_account_name);
3504 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3505 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3507 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3508 MODEST_CONF_DEVICE_NAME, NULL);
3510 /* Force a redraw */
3511 #if GTK_CHECK_VERSION(2, 8, 0)
3512 GtkTreeViewColumn * tree_column;
3514 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3516 gtk_tree_view_column_queue_resize (tree_column);
3518 gtk_widget_queue_draw (GTK_WIDGET (self));
3524 modest_folder_view_set_style (ModestFolderView *self,
3525 ModestFolderViewStyle style)
3527 ModestFolderViewPrivate *priv;
3529 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3530 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3531 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3533 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3536 priv->style = style;
3540 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3541 const gchar *account_id)
3543 ModestFolderViewPrivate *priv;
3544 GtkTreeModel *model;
3546 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3548 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3550 /* This will be used by the filter_row callback,
3551 * to decided which rows to show: */
3552 if (priv->visible_account_id) {
3553 g_free (priv->visible_account_id);
3554 priv->visible_account_id = NULL;
3557 priv->visible_account_id = g_strdup (account_id);
3560 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3561 if (GTK_IS_TREE_MODEL_FILTER (model))
3562 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3564 /* Save settings to gconf */
3565 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3566 MODEST_CONF_FOLDER_VIEW_KEY);
3568 /* Notify observers */
3569 g_signal_emit (G_OBJECT(self),
3570 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3575 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3577 ModestFolderViewPrivate *priv;
3579 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3581 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3583 return (const gchar *) priv->visible_account_id;
3587 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3591 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3593 gtk_tree_model_get (model, iter,
3597 gboolean result = FALSE;
3598 if (type == TNY_FOLDER_TYPE_INBOX) {
3602 *inbox_iter = *iter;
3606 if (gtk_tree_model_iter_children (model, &child, iter)) {
3607 if (find_inbox_iter (model, &child, inbox_iter))
3611 } while (gtk_tree_model_iter_next (model, iter));
3620 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3622 #ifndef MODEST_TOOLKIT_HILDON2
3623 GtkTreeModel *model;
3624 GtkTreeIter iter, inbox_iter;
3625 GtkTreeSelection *sel;
3626 GtkTreePath *path = NULL;
3628 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3630 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3634 expand_root_items (self);
3635 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3637 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3638 g_warning ("%s: model is empty", __FUNCTION__);
3642 if (find_inbox_iter (model, &iter, &inbox_iter))
3643 path = gtk_tree_model_get_path (model, &inbox_iter);
3645 path = gtk_tree_path_new_first ();
3647 /* Select the row and free */
3648 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3649 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3650 gtk_tree_path_free (path);
3653 gtk_widget_grab_focus (GTK_WIDGET(self));
3660 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3665 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3666 TnyFolder* a_folder;
3669 gtk_tree_model_get (model, iter,
3670 INSTANCE_COLUMN, &a_folder,
3676 if (folder == a_folder) {
3677 g_object_unref (a_folder);
3678 *folder_iter = *iter;
3681 g_object_unref (a_folder);
3683 if (gtk_tree_model_iter_children (model, &child, iter)) {
3684 if (find_folder_iter (model, &child, folder_iter, folder))
3688 } while (gtk_tree_model_iter_next (model, iter));
3693 #ifndef MODEST_TOOLKIT_HILDON2
3695 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3698 ModestFolderView *self)
3700 ModestFolderViewPrivate *priv = NULL;
3701 GtkTreeSelection *sel;
3702 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3703 GObject *instance = NULL;
3705 if (!MODEST_IS_FOLDER_VIEW(self))
3708 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3710 priv->reexpand = TRUE;
3712 gtk_tree_model_get (tree_model, iter,
3714 INSTANCE_COLUMN, &instance,
3720 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3721 priv->folder_to_select = g_object_ref (instance);
3723 g_object_unref (instance);
3725 if (priv->folder_to_select) {
3727 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3730 path = gtk_tree_model_get_path (tree_model, iter);
3731 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3733 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3735 gtk_tree_selection_select_iter (sel, iter);
3736 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3738 gtk_tree_path_free (path);
3742 modest_folder_view_disable_next_folder_selection (self);
3744 /* Refilter the model */
3745 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3751 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3753 ModestFolderViewPrivate *priv;
3755 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3757 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3759 if (priv->folder_to_select)
3760 g_object_unref(priv->folder_to_select);
3762 priv->folder_to_select = NULL;
3766 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3767 gboolean after_change)
3769 GtkTreeModel *model;
3770 GtkTreeIter iter, folder_iter;
3771 GtkTreeSelection *sel;
3772 ModestFolderViewPrivate *priv = NULL;
3774 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3775 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3777 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3780 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3781 gtk_tree_selection_unselect_all (sel);
3783 if (priv->folder_to_select)
3784 g_object_unref(priv->folder_to_select);
3785 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3789 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3794 /* Refilter the model, before selecting the folder */
3795 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3797 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3798 g_warning ("%s: model is empty", __FUNCTION__);
3802 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3805 path = gtk_tree_model_get_path (model, &folder_iter);
3806 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3808 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3809 gtk_tree_selection_select_iter (sel, &folder_iter);
3810 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3812 gtk_tree_path_free (path);
3820 modest_folder_view_copy_selection (ModestFolderView *self)
3822 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3824 /* Copy selection */
3825 _clipboard_set_selected_data (self, FALSE);
3829 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3831 ModestFolderViewPrivate *priv = NULL;
3832 GtkTreeModel *model = NULL;
3833 const gchar **hidding = NULL;
3834 guint i, n_selected;
3836 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3837 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3839 /* Copy selection */
3840 if (!_clipboard_set_selected_data (folder_view, TRUE))
3843 /* Get hidding ids */
3844 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3846 /* Clear hidding array created by previous cut operation */
3847 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3849 /* Copy hidding array */
3850 priv->n_selected = n_selected;
3851 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3852 for (i=0; i < n_selected; i++)
3853 priv->hidding_ids[i] = g_strdup(hidding[i]);
3855 /* Hide cut folders */
3856 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3857 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3861 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3862 ModestFolderView *folder_view_dst)
3864 GtkTreeModel *filter_model = NULL;
3865 GtkTreeModel *model = NULL;
3866 GtkTreeModel *new_filter_model = NULL;
3867 GtkTreeModel *old_tny_model = NULL;
3868 GtkTreeModel *new_tny_model = NULL;
3869 ModestFolderViewPrivate *dst_priv;
3871 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3872 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3874 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3875 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3876 new_tny_model = NULL;
3879 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3880 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3881 G_OBJECT (old_tny_model),
3882 "activity-changed");
3884 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3885 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3887 /* Build new filter model */
3888 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3889 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3896 /* Set copied model */
3897 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3898 #ifndef MODEST_TOOLKIT_HILDON2
3899 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3900 G_OBJECT(new_filter_model), "row-inserted",
3901 (GCallback) on_row_inserted_maybe_select_folder,
3904 #ifdef MODEST_TOOLKIT_HILDON2
3905 if (new_tny_model) {
3906 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3907 G_OBJECT (new_tny_model),
3909 G_CALLBACK (on_activity_changed),
3915 g_object_unref (new_filter_model);
3919 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3922 GtkTreeModel *model = NULL;
3923 ModestFolderViewPrivate* priv;
3925 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3927 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3928 priv->show_non_move = show;
3929 /* modest_folder_view_update_model(folder_view, */
3930 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3932 /* Hide special folders */
3933 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3934 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3935 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3940 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3943 ModestFolderViewPrivate* priv;
3945 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3947 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3948 priv->show_message_count = show;
3950 g_object_set (G_OBJECT (priv->messages_renderer),
3951 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3955 /* Returns FALSE if it did not selected anything */
3957 _clipboard_set_selected_data (ModestFolderView *folder_view,
3960 ModestFolderViewPrivate *priv = NULL;
3961 TnyFolderStore *folder = NULL;
3962 gboolean retval = FALSE;
3964 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3965 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3967 /* Set selected data on clipboard */
3968 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3969 folder = modest_folder_view_get_selected (folder_view);
3971 /* Do not allow to select an account */
3972 if (TNY_IS_FOLDER (folder)) {
3973 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3978 g_object_unref (folder);
3984 _clear_hidding_filter (ModestFolderView *folder_view)
3986 ModestFolderViewPrivate *priv;
3989 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3990 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3992 if (priv->hidding_ids != NULL) {
3993 for (i=0; i < priv->n_selected; i++)
3994 g_free (priv->hidding_ids[i]);
3995 g_free(priv->hidding_ids);
4001 on_display_name_changed (ModestAccountMgr *mgr,
4002 const gchar *account,
4005 ModestFolderView *self;
4007 self = MODEST_FOLDER_VIEW (user_data);
4009 /* Force a redraw */
4010 #if GTK_CHECK_VERSION(2, 8, 0)
4011 GtkTreeViewColumn * tree_column;
4013 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4015 gtk_tree_view_column_queue_resize (tree_column);
4017 gtk_widget_queue_draw (GTK_WIDGET (self));
4022 modest_folder_view_set_cell_style (ModestFolderView *self,
4023 ModestFolderViewCellStyle cell_style)
4025 ModestFolderViewPrivate *priv = NULL;
4027 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4028 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4030 priv->cell_style = cell_style;
4032 g_object_set (G_OBJECT (priv->messages_renderer),
4033 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4036 gtk_widget_queue_draw (GTK_WIDGET (self));
4040 update_style (ModestFolderView *self)
4042 ModestFolderViewPrivate *priv;
4043 GdkColor style_color, style_active_color;
4044 PangoAttrList *attr_list;
4046 PangoAttribute *attr;
4048 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4049 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4053 attr_list = pango_attr_list_new ();
4054 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4055 gdk_color_parse ("grey", &style_color);
4057 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4058 pango_attr_list_insert (attr_list, attr);
4061 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4063 "SmallSystemFont", NULL,
4066 attr = pango_attr_font_desc_new (pango_font_description_copy
4067 (style->font_desc));
4068 pango_attr_list_insert (attr_list, attr);
4070 g_object_set (G_OBJECT (priv->messages_renderer),
4071 "foreground-gdk", &style_color,
4072 "foreground-set", TRUE,
4073 "attributes", attr_list,
4075 pango_attr_list_unref (attr_list);
4077 if (priv->active_color)
4078 gdk_color_free (priv->active_color);
4080 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4081 priv->active_color = gdk_color_copy (&style_active_color);
4086 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4088 if (strcmp ("style", spec->name) == 0) {
4089 update_style (MODEST_FOLDER_VIEW (obj));
4090 gtk_widget_queue_draw (GTK_WIDGET (obj));
4095 modest_folder_view_set_filter (ModestFolderView *self,
4096 ModestFolderViewFilter filter)
4098 ModestFolderViewPrivate *priv;
4099 GtkTreeModel *filter_model;
4101 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4102 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4104 priv->filter |= filter;
4106 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4107 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4108 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4113 modest_folder_view_unset_filter (ModestFolderView *self,
4114 ModestFolderViewFilter filter)
4116 ModestFolderViewPrivate *priv;
4117 GtkTreeModel *filter_model;
4119 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4120 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4122 priv->filter &= ~filter;
4124 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4125 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4126 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4131 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4132 ModestTnyFolderRules rules)
4134 GtkTreeModel *filter_model;
4136 gboolean fulfil = FALSE;
4138 if (!get_inner_models (self, &filter_model, NULL, NULL))
4141 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4145 TnyFolderStore *folder;
4147 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4149 if (TNY_IS_FOLDER (folder)) {
4150 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4151 /* Folder rules are negative: non_writable, non_deletable... */
4152 if (!(folder_rules & rules))
4155 g_object_unref (folder);
4158 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4164 modest_folder_view_set_list_to_move (ModestFolderView *self,
4167 ModestFolderViewPrivate *priv;
4169 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4170 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4172 if (priv->list_to_move)
4173 g_object_unref (priv->list_to_move);
4176 g_object_ref (list);
4178 priv->list_to_move = list;
4182 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4184 ModestFolderViewPrivate *priv;
4186 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4187 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4190 g_free (priv->mailbox);
4192 priv->mailbox = g_strdup (mailbox);
4194 /* Notify observers */
4195 g_signal_emit (G_OBJECT(self),
4196 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4197 priv->visible_account_id);
4201 modest_folder_view_get_mailbox (ModestFolderView *self)
4203 ModestFolderViewPrivate *priv;
4205 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4206 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4208 return (const gchar *) priv->mailbox;
4212 modest_folder_view_get_activity (ModestFolderView *self)
4214 ModestFolderViewPrivate *priv;
4215 GtkTreeModel *inner_model;
4217 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4218 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4219 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4221 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4222 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4228 #ifdef MODEST_TOOLKIT_HILDON2
4230 on_activity_changed (TnyGtkFolderListStore *store,
4232 ModestFolderView *folder_view)
4234 ModestFolderViewPrivate *priv;
4236 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4237 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4238 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4240 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,