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);
931 if (!*pixbuf_open && pixbuf && *pixbuf)
932 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
936 if (!*pixbuf_close && pixbuf && *pixbuf)
937 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
941 retval = g_slice_new0 (ThreePixbufs);
943 retval->pixbuf = g_object_ref (*pixbuf);
945 retval->pixbuf_open = g_object_ref (*pixbuf_open);
947 retval->pixbuf_close = g_object_ref (*pixbuf_close);
952 static inline ThreePixbufs *
953 get_account_protocol_pixbufs (ModestFolderView *folder_view,
954 ModestProtocolType protocol_type,
957 ModestProtocol *protocol;
958 const GdkPixbuf *pixbuf = NULL;
959 ModestFolderViewPrivate *priv;
961 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
963 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
966 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
967 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
968 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
969 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
970 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
971 object, FOLDER_ICON_SIZE);
975 ThreePixbufs *retval;
976 retval = g_slice_new0 (ThreePixbufs);
977 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
978 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
979 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
986 static inline ThreePixbufs*
987 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
989 TnyAccount *account = NULL;
990 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
991 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
992 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
993 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
994 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
996 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
997 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
998 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
999 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1000 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1002 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1003 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1004 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1005 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1006 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1008 ThreePixbufs *retval = NULL;
1010 if (TNY_IS_ACCOUNT (instance)) {
1011 account = g_object_ref (instance);
1012 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1013 account = tny_folder_get_account (TNY_FOLDER (instance));
1017 ModestProtocolType account_store_protocol;
1019 account_store_protocol = modest_tny_account_get_protocol_type (account);
1020 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1021 g_object_unref (account);
1027 /* Sometimes an special folder is reported by the server as
1028 NORMAL, like some versions of Dovecot */
1029 if (type == TNY_FOLDER_TYPE_NORMAL ||
1030 type == TNY_FOLDER_TYPE_UNKNOWN) {
1031 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1034 /* It's not enough with check the folder type. We need to
1035 ensure that we're not giving a special folder icon to a
1036 normal folder with the same name than a special folder */
1037 if (TNY_IS_FOLDER (instance) &&
1038 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1039 type = TNY_FOLDER_TYPE_NORMAL;
1041 /* Remote folders should not be treated as special folders */
1042 if (TNY_IS_FOLDER_STORE (instance) &&
1043 !TNY_IS_ACCOUNT (instance) &&
1044 type != TNY_FOLDER_TYPE_INBOX &&
1045 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1046 #ifdef MODEST_TOOLKIT_HILDON2
1047 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1050 &anorm_pixbuf_close);
1052 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1054 &normal_pixbuf_open,
1055 &normal_pixbuf_close);
1061 case TNY_FOLDER_TYPE_INVALID:
1062 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1065 case TNY_FOLDER_TYPE_ROOT:
1066 if (TNY_IS_ACCOUNT (instance)) {
1068 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1069 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1072 &avirt_pixbuf_close);
1074 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1076 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1077 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1080 &ammc_pixbuf_close);
1082 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1085 &anorm_pixbuf_close);
1090 case TNY_FOLDER_TYPE_INBOX:
1091 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1094 &inbox_pixbuf_close);
1096 case TNY_FOLDER_TYPE_OUTBOX:
1097 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1099 &outbox_pixbuf_open,
1100 &outbox_pixbuf_close);
1102 case TNY_FOLDER_TYPE_JUNK:
1103 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1106 &junk_pixbuf_close);
1108 case TNY_FOLDER_TYPE_SENT:
1109 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1112 &sent_pixbuf_close);
1114 case TNY_FOLDER_TYPE_TRASH:
1115 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1118 &trash_pixbuf_close);
1120 case TNY_FOLDER_TYPE_DRAFTS:
1121 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1124 &draft_pixbuf_close);
1126 case TNY_FOLDER_TYPE_ARCHIVE:
1127 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1132 case TNY_FOLDER_TYPE_NORMAL:
1134 /* Memory card folders could have an special icon */
1135 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1136 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1141 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1143 &normal_pixbuf_open,
1144 &normal_pixbuf_close);
1153 free_pixbufs (ThreePixbufs *pixbufs)
1155 if (pixbufs->pixbuf)
1156 g_object_unref (pixbufs->pixbuf);
1157 if (pixbufs->pixbuf_open)
1158 g_object_unref (pixbufs->pixbuf_open);
1159 if (pixbufs->pixbuf_close)
1160 g_object_unref (pixbufs->pixbuf_close);
1161 g_slice_free (ThreePixbufs, pixbufs);
1165 icon_cell_data (GtkTreeViewColumn *column,
1166 GtkCellRenderer *renderer,
1167 GtkTreeModel *tree_model,
1171 GObject *rendobj = NULL, *instance = NULL;
1172 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1173 gboolean has_children;
1174 ThreePixbufs *pixbufs;
1175 ModestFolderView *folder_view = (ModestFolderView *) data;
1177 rendobj = (GObject *) renderer;
1179 gtk_tree_model_get (tree_model, iter,
1181 INSTANCE_COLUMN, &instance,
1187 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1188 pixbufs = get_folder_icons (folder_view, type, instance);
1189 g_object_unref (instance);
1192 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1195 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1196 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1199 free_pixbufs (pixbufs);
1203 add_columns (GtkWidget *treeview)
1205 GtkTreeViewColumn *column;
1206 GtkCellRenderer *renderer;
1207 GtkTreeSelection *sel;
1208 ModestFolderViewPrivate *priv;
1210 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1213 column = gtk_tree_view_column_new ();
1215 /* Set icon and text render function */
1216 renderer = gtk_cell_renderer_pixbuf_new();
1217 #ifdef MODEST_TOOLKIT_HILDON2
1218 g_object_set (renderer,
1219 "xpad", MODEST_MARGIN_DEFAULT,
1220 "ypad", MODEST_MARGIN_DEFAULT,
1223 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1224 gtk_tree_view_column_set_cell_data_func(column, renderer,
1225 icon_cell_data, treeview, NULL);
1227 renderer = gtk_cell_renderer_text_new();
1228 g_object_set (renderer,
1229 #ifdef MODEST_TOOLKIT_HILDON2
1230 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1231 "ypad", MODEST_MARGIN_DEFAULT,
1232 "xpad", MODEST_MARGIN_DEFAULT,
1234 "ellipsize", PANGO_ELLIPSIZE_END,
1236 "ellipsize-set", TRUE, NULL);
1237 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1238 gtk_tree_view_column_set_cell_data_func(column, renderer,
1239 text_cell_data, treeview, NULL);
1241 priv->messages_renderer = gtk_cell_renderer_text_new ();
1242 g_object_set (priv->messages_renderer,
1243 #ifdef MODEST_TOOLKIT_HILDON2
1245 "ypad", MODEST_MARGIN_DEFAULT,
1246 "xpad", MODEST_MARGIN_DOUBLE,
1248 "scale", PANGO_SCALE_X_SMALL,
1251 "alignment", PANGO_ALIGN_RIGHT,
1255 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1256 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1257 messages_cell_data, treeview, NULL);
1259 /* Set selection mode */
1260 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1261 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1263 /* Set treeview appearance */
1264 gtk_tree_view_column_set_spacing (column, 2);
1265 gtk_tree_view_column_set_resizable (column, TRUE);
1266 gtk_tree_view_column_set_fixed_width (column, TRUE);
1267 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1268 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1271 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1275 modest_folder_view_init (ModestFolderView *obj)
1277 ModestFolderViewPrivate *priv;
1280 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1282 priv->timer_expander = 0;
1283 priv->account_store = NULL;
1285 priv->do_refresh = TRUE;
1286 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1287 priv->cur_folder_store = NULL;
1288 priv->visible_account_id = NULL;
1289 priv->mailbox = NULL;
1290 priv->folder_to_select = NULL;
1291 priv->reexpand = TRUE;
1292 priv->signal_handlers = 0;
1294 /* Initialize the local account name */
1295 conf = modest_runtime_get_conf();
1296 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1298 /* Init email clipboard */
1299 priv->clipboard = modest_runtime_get_email_clipboard ();
1300 priv->hidding_ids = NULL;
1301 priv->n_selected = 0;
1302 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1303 priv->reselect = FALSE;
1304 priv->show_non_move = TRUE;
1305 priv->list_to_move = NULL;
1306 priv->show_message_count = TRUE;
1308 /* Build treeview */
1309 add_columns (GTK_WIDGET (obj));
1311 /* Setup drag and drop */
1312 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1314 /* Connect signals */
1315 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1316 G_OBJECT (obj), "key-press-event",
1317 G_CALLBACK (on_key_pressed), NULL);
1319 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1320 (GObject*) modest_runtime_get_account_mgr (),
1321 "display_name_changed",
1322 G_CALLBACK (on_display_name_changed),
1326 * Track changes in the local account name (in the device it
1327 * will be the device name)
1329 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1332 G_CALLBACK(on_configuration_key_changed),
1335 priv->active_color = NULL;
1338 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1339 G_OBJECT (obj), "notify::style",
1340 G_CALLBACK (on_notify_style), (gpointer) obj);
1344 tny_account_store_view_init (gpointer g, gpointer iface_data)
1346 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1348 klass->set_account_store = modest_folder_view_set_account_store;
1352 modest_folder_view_dispose (GObject *obj)
1354 static gboolean disposed = FALSE;
1355 ModestFolderViewPrivate *priv;
1360 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1362 #ifdef MODEST_TOOLKIT_HILDON2
1363 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1366 /* Free external references */
1367 if (priv->account_store) {
1368 g_object_unref (G_OBJECT(priv->account_store));
1369 priv->account_store = NULL;
1373 g_object_unref (G_OBJECT (priv->query));
1377 if (priv->folder_to_select) {
1378 g_object_unref (G_OBJECT(priv->folder_to_select));
1379 priv->folder_to_select = NULL;
1382 if (priv->cur_folder_store) {
1383 g_object_unref (priv->cur_folder_store);
1384 priv->cur_folder_store = NULL;
1387 if (priv->list_to_move) {
1388 g_object_unref (priv->list_to_move);
1389 priv->list_to_move = NULL;
1394 modest_folder_view_finalize (GObject *obj)
1396 ModestFolderViewPrivate *priv;
1398 g_return_if_fail (obj);
1400 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1402 if (priv->active_color) {
1403 gdk_color_free (priv->active_color);
1404 priv->active_color = NULL;
1407 if (priv->timer_expander != 0) {
1408 g_source_remove (priv->timer_expander);
1409 priv->timer_expander = 0;
1412 g_free (priv->local_account_name);
1413 g_free (priv->visible_account_id);
1414 g_free (priv->mailbox);
1416 /* Clear hidding array created by cut operation */
1417 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1419 G_OBJECT_CLASS(parent_class)->finalize (obj);
1424 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1426 ModestFolderViewPrivate *priv;
1429 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1430 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1432 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1433 device = tny_account_store_get_device (account_store);
1435 if (G_UNLIKELY (priv->account_store)) {
1437 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1438 G_OBJECT (priv->account_store),
1439 "account_inserted"))
1440 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1441 G_OBJECT (priv->account_store),
1442 "account_inserted");
1443 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1444 G_OBJECT (priv->account_store),
1446 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1447 G_OBJECT (priv->account_store),
1449 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1450 G_OBJECT (priv->account_store),
1452 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1453 G_OBJECT (priv->account_store),
1455 g_object_unref (G_OBJECT (priv->account_store));
1458 priv->account_store = g_object_ref (G_OBJECT (account_store));
1460 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1461 G_OBJECT(account_store), "account_removed",
1462 G_CALLBACK (on_account_removed), self);
1464 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1465 G_OBJECT(account_store), "account_inserted",
1466 G_CALLBACK (on_account_inserted), self);
1468 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1469 G_OBJECT(account_store), "account_changed",
1470 G_CALLBACK (on_account_changed), self);
1472 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1473 priv->reselect = FALSE;
1474 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1476 g_object_unref (G_OBJECT (device));
1480 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1483 ModestFolderView *self;
1484 GtkTreeModel *model, *filter_model;
1487 self = MODEST_FOLDER_VIEW (user_data);
1489 if (!get_inner_models (self, &filter_model, NULL, &model))
1492 /* Remove outbox from model */
1493 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1494 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1495 g_object_unref (outbox);
1498 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1502 on_account_inserted (TnyAccountStore *account_store,
1503 TnyAccount *account,
1506 ModestFolderViewPrivate *priv;
1507 GtkTreeModel *model, *filter_model;
1509 /* Ignore transport account insertions, we're not showing them
1510 in the folder view */
1511 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1514 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1517 /* If we're adding a new account, and there is no previous
1518 one, we need to select the visible server account */
1519 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1520 !priv->visible_account_id)
1521 modest_widget_memory_restore (modest_runtime_get_conf(),
1522 G_OBJECT (user_data),
1523 MODEST_CONF_FOLDER_VIEW_KEY);
1527 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1528 &filter_model, NULL, &model))
1531 /* Insert the account in the model */
1532 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1534 /* When the model is a list store (plain representation) the
1535 outbox is not a child of any account so we have to manually
1536 delete it because removing the local folders account won't
1537 delete it (because tny_folder_get_account() is not defined
1538 for a merge folder */
1539 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1540 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1541 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1542 (GObject*) account, "outbox-deleted",
1543 G_CALLBACK (on_outbox_deleted_cb),
1547 /* Refilter the model */
1548 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1553 same_account_selected (ModestFolderView *self,
1554 TnyAccount *account)
1556 ModestFolderViewPrivate *priv;
1557 gboolean same_account = FALSE;
1559 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1561 if (priv->cur_folder_store) {
1562 TnyAccount *selected_folder_account = NULL;
1564 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1565 selected_folder_account =
1566 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1568 selected_folder_account =
1569 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1572 if (selected_folder_account == account)
1573 same_account = TRUE;
1575 g_object_unref (selected_folder_account);
1577 return same_account;
1582 * Selects the first inbox or the local account in an idle
1585 on_idle_select_first_inbox_or_local (gpointer user_data)
1587 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1589 gdk_threads_enter ();
1590 modest_folder_view_select_first_inbox_or_local (self);
1591 gdk_threads_leave ();
1597 on_account_changed (TnyAccountStore *account_store,
1598 TnyAccount *tny_account,
1601 ModestFolderView *self;
1602 ModestFolderViewPrivate *priv;
1603 GtkTreeModel *model, *filter_model;
1604 GtkTreeSelection *sel;
1605 gboolean same_account;
1607 /* Ignore transport account insertions, we're not showing them
1608 in the folder view */
1609 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1612 self = MODEST_FOLDER_VIEW (user_data);
1613 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1615 /* Get the inner model */
1616 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1617 &filter_model, NULL, &model))
1620 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1622 /* Invalidate the cur_folder_store only if the selected folder
1623 belongs to the account that is being removed */
1624 same_account = same_account_selected (self, tny_account);
1626 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1627 gtk_tree_selection_unselect_all (sel);
1630 /* Remove the account from the model */
1631 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1633 /* Insert the account in the model */
1634 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1636 /* Refilter the model */
1637 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1639 /* Select the first INBOX if the currently selected folder
1640 belongs to the account that is being deleted */
1641 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1642 g_idle_add (on_idle_select_first_inbox_or_local, self);
1646 on_account_removed (TnyAccountStore *account_store,
1647 TnyAccount *account,
1650 ModestFolderView *self = NULL;
1651 ModestFolderViewPrivate *priv;
1652 GtkTreeModel *model, *filter_model;
1653 GtkTreeSelection *sel = NULL;
1654 gboolean same_account = FALSE;
1656 /* Ignore transport account removals, we're not showing them
1657 in the folder view */
1658 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1661 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1662 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1666 self = MODEST_FOLDER_VIEW (user_data);
1667 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1669 /* Invalidate the cur_folder_store only if the selected folder
1670 belongs to the account that is being removed */
1671 same_account = same_account_selected (self, account);
1673 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1674 gtk_tree_selection_unselect_all (sel);
1677 /* Invalidate row to select only if the folder to select
1678 belongs to the account that is being removed*/
1679 if (priv->folder_to_select) {
1680 TnyAccount *folder_to_select_account = NULL;
1682 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1683 if (folder_to_select_account == account) {
1684 modest_folder_view_disable_next_folder_selection (self);
1685 g_object_unref (priv->folder_to_select);
1686 priv->folder_to_select = NULL;
1688 g_object_unref (folder_to_select_account);
1691 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1692 &filter_model, NULL, &model))
1695 /* Disconnect the signal handler */
1696 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1697 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1698 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject*) account, "outbox-deleted"))
1699 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1700 (GObject *) account,
1704 /* Remove the account from the model */
1705 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1707 /* If the removed account is the currently viewed one then
1708 clear the configuration value. The new visible account will be the default account */
1709 if (priv->visible_account_id &&
1710 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1712 /* Clear the current visible account_id */
1713 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1714 modest_folder_view_set_mailbox (self, NULL);
1716 /* Call the restore method, this will set the new visible account */
1717 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1718 MODEST_CONF_FOLDER_VIEW_KEY);
1721 /* Refilter the model */
1722 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1724 /* Select the first INBOX if the currently selected folder
1725 belongs to the account that is being deleted */
1727 g_idle_add (on_idle_select_first_inbox_or_local, self);
1731 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1733 GtkTreeViewColumn *col;
1735 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1737 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1739 g_printerr ("modest: failed get column for title\n");
1743 gtk_tree_view_column_set_title (col, title);
1744 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1749 modest_folder_view_on_map (ModestFolderView *self,
1750 GdkEventExpose *event,
1753 ModestFolderViewPrivate *priv;
1755 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1757 /* This won't happen often */
1758 if (G_UNLIKELY (priv->reselect)) {
1759 /* Select the first inbox or the local account if not found */
1761 /* TODO: this could cause a lock at startup, so we
1762 comment it for the moment. We know that this will
1763 be a bug, because the INBOX is not selected, but we
1764 need to rewrite some parts of Modest to avoid the
1765 deathlock situation */
1766 /* TODO: check if this is still the case */
1767 priv->reselect = FALSE;
1768 modest_folder_view_select_first_inbox_or_local (self);
1769 /* Notify the display name observers */
1770 g_signal_emit (G_OBJECT(self),
1771 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1775 if (priv->reexpand) {
1776 expand_root_items (self);
1777 priv->reexpand = FALSE;
1784 modest_folder_view_new (TnyFolderStoreQuery *query)
1786 return modest_folder_view_new_full (query, TRUE);
1790 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1793 ModestFolderViewPrivate *priv;
1794 GtkTreeSelection *sel;
1796 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1797 #ifdef MODEST_TOOLKIT_HILDON2
1798 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1801 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1804 priv->query = g_object_ref (query);
1806 priv->do_refresh = do_refresh;
1808 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1809 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1810 (GObject*) sel, "changed",
1811 G_CALLBACK (on_selection_changed), self);
1813 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1814 self, "row-activated",
1815 G_CALLBACK (on_row_activated), self);
1817 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1818 self, "expose-event",
1819 G_CALLBACK (modest_folder_view_on_map), NULL);
1821 return GTK_WIDGET(self);
1824 /* this feels dirty; any other way to expand all the root items? */
1826 expand_root_items (ModestFolderView *self)
1829 GtkTreeModel *model;
1832 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1833 path = gtk_tree_path_new_first ();
1835 /* all folders should have child items, so.. */
1837 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1838 gtk_tree_path_next (path);
1839 } while (gtk_tree_model_get_iter (model, &iter, path));
1841 gtk_tree_path_free (path);
1845 is_parent_of (TnyFolder *a, TnyFolder *b)
1848 gboolean retval = FALSE;
1850 a_id = tny_folder_get_id (a);
1852 gchar *string_to_match;
1855 string_to_match = g_strconcat (a_id, "/", NULL);
1856 b_id = tny_folder_get_id (b);
1857 retval = g_str_has_prefix (b_id, string_to_match);
1858 g_free (string_to_match);
1864 typedef struct _ForeachFolderInfo {
1867 } ForeachFolderInfo;
1870 foreach_folder_with_id (GtkTreeModel *model,
1875 ForeachFolderInfo *info;
1878 info = (ForeachFolderInfo *) data;
1879 gtk_tree_model_get (model, iter,
1880 INSTANCE_COLUMN, &instance,
1883 if (TNY_IS_FOLDER (instance)) {
1886 id = tny_folder_get_id (TNY_FOLDER (instance));
1888 collate = g_utf8_collate_key (id, -1);
1889 info->found = !strcmp (info->needle, collate);
1895 g_object_unref (instance);
1903 has_folder_with_id (ModestFolderView *self, const gchar *id)
1905 GtkTreeModel *model;
1906 ForeachFolderInfo info = {NULL, FALSE};
1908 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1909 info.needle = g_utf8_collate_key (id, -1);
1911 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1912 g_free (info.needle);
1918 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1921 gboolean retval = FALSE;
1923 a_id = tny_folder_get_id (a);
1926 b_id = tny_folder_get_id (b);
1929 const gchar *last_bar;
1930 gchar *string_to_match;
1931 last_bar = g_strrstr (b_id, "/");
1936 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1937 retval = has_folder_with_id (self, string_to_match);
1938 g_free (string_to_match);
1946 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1948 ModestFolderViewPrivate *priv;
1949 TnyIterator *iterator;
1950 gboolean retval = TRUE;
1952 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1953 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1955 for (iterator = tny_list_create_iterator (priv->list_to_move);
1956 retval && !tny_iterator_is_done (iterator);
1957 tny_iterator_next (iterator)) {
1959 instance = tny_iterator_get_current (iterator);
1960 if (instance == (GObject *) folder) {
1962 } else if (TNY_IS_FOLDER (instance)) {
1963 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1965 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1968 g_object_unref (instance);
1970 g_object_unref (iterator);
1977 * We use this function to implement the
1978 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1979 * account in this case, and the local folders.
1982 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1984 ModestFolderViewPrivate *priv;
1985 gboolean retval = TRUE;
1986 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1987 GObject *instance = NULL;
1988 const gchar *id = NULL;
1990 gboolean found = FALSE;
1991 gboolean cleared = FALSE;
1992 ModestTnyFolderRules rules = 0;
1995 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1996 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1998 gtk_tree_model_get (model, iter,
1999 NAME_COLUMN, &fname,
2001 INSTANCE_COLUMN, &instance,
2004 /* Do not show if there is no instance, this could indeed
2005 happen when the model is being modified while it's being
2006 drawn. This could occur for example when moving folders
2013 if (TNY_IS_ACCOUNT (instance)) {
2014 TnyAccount *acc = TNY_ACCOUNT (instance);
2015 const gchar *account_id = tny_account_get_id (acc);
2017 /* If it isn't a special folder,
2018 * don't show it unless it is the visible account: */
2019 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2020 !modest_tny_account_is_virtual_local_folders (acc) &&
2021 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2023 /* Show only the visible account id */
2024 if (priv->visible_account_id) {
2025 if (strcmp (account_id, priv->visible_account_id))
2032 /* Never show these to the user. They are merged into one folder
2033 * in the local-folders account instead: */
2034 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2037 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2038 /* Only show special folders for current account if needed */
2039 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2040 TnyAccount *account;
2042 account = tny_folder_get_account (TNY_FOLDER (instance));
2044 if (TNY_IS_ACCOUNT (account)) {
2045 const gchar *account_id = tny_account_get_id (account);
2047 if (!modest_tny_account_is_virtual_local_folders (account) &&
2048 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2049 /* Show only the visible account id */
2050 if (priv->visible_account_id) {
2051 if (strcmp (account_id, priv->visible_account_id)) {
2053 } else if (priv->mailbox) {
2054 /* Filter mailboxes */
2055 if (!g_str_has_prefix (fname, priv->mailbox)) {
2057 } else if (!strcmp (fname, priv->mailbox)) {
2058 /* Hide mailbox parent */
2064 g_object_unref (account);
2071 /* Check hiding (if necessary) */
2072 cleared = modest_email_clipboard_cleared (priv->clipboard);
2073 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2074 id = tny_folder_get_id (TNY_FOLDER(instance));
2075 if (priv->hidding_ids != NULL)
2076 for (i=0; i < priv->n_selected && !found; i++)
2077 if (priv->hidding_ids[i] != NULL && id != NULL)
2078 found = (!strcmp (priv->hidding_ids[i], id));
2083 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2084 folder as no message can be move there according to UI specs */
2085 if (retval && !priv->show_non_move) {
2086 if (priv->list_to_move &&
2087 tny_list_get_length (priv->list_to_move) > 0 &&
2088 TNY_IS_FOLDER (instance)) {
2089 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2091 if (retval && TNY_IS_FOLDER (instance) &&
2092 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2094 case TNY_FOLDER_TYPE_OUTBOX:
2095 case TNY_FOLDER_TYPE_SENT:
2096 case TNY_FOLDER_TYPE_DRAFTS:
2099 case TNY_FOLDER_TYPE_UNKNOWN:
2100 case TNY_FOLDER_TYPE_NORMAL:
2101 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2102 if (type == TNY_FOLDER_TYPE_INVALID)
2103 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2105 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2106 type == TNY_FOLDER_TYPE_SENT
2107 || type == TNY_FOLDER_TYPE_DRAFTS)
2114 if (retval && TNY_IS_ACCOUNT (instance) &&
2115 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2116 ModestProtocolType protocol_type;
2118 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2119 retval = !modest_protocol_registry_protocol_type_has_tag
2120 (modest_runtime_get_protocol_registry (),
2122 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2126 /* apply special filters */
2127 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2128 if (TNY_IS_ACCOUNT (instance))
2132 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2133 if (TNY_IS_FOLDER (instance))
2137 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2138 if (TNY_IS_ACCOUNT (instance)) {
2139 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2141 } else if (TNY_IS_FOLDER (instance)) {
2142 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2147 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2148 if (TNY_IS_ACCOUNT (instance)) {
2149 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2151 } else if (TNY_IS_FOLDER (instance)) {
2152 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2157 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2158 /* A mailbox is a fake folder with an @ in the middle of the name */
2159 if (!TNY_IS_FOLDER (instance) ||
2160 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2163 const gchar *folder_name;
2164 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2165 if (!folder_name || strchr (folder_name, '@') == NULL)
2171 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2172 if (TNY_IS_FOLDER (instance)) {
2173 /* Check folder rules */
2174 ModestTnyFolderRules rules;
2176 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2177 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2178 } else if (TNY_IS_ACCOUNT (instance)) {
2179 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2187 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2188 if (TNY_IS_FOLDER (instance)) {
2189 TnyFolderType guess_type;
2191 if (TNY_FOLDER_TYPE_NORMAL) {
2192 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2198 case TNY_FOLDER_TYPE_OUTBOX:
2199 case TNY_FOLDER_TYPE_SENT:
2200 case TNY_FOLDER_TYPE_DRAFTS:
2201 case TNY_FOLDER_TYPE_ARCHIVE:
2202 case TNY_FOLDER_TYPE_INBOX:
2205 case TNY_FOLDER_TYPE_UNKNOWN:
2206 case TNY_FOLDER_TYPE_NORMAL:
2212 } else if (TNY_IS_ACCOUNT (instance)) {
2217 if (retval && TNY_IS_FOLDER (instance)) {
2218 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2221 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2222 if (TNY_IS_FOLDER (instance)) {
2223 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2224 } else if (TNY_IS_ACCOUNT (instance)) {
2229 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2230 if (TNY_IS_FOLDER (instance)) {
2231 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2232 } else if (TNY_IS_ACCOUNT (instance)) {
2237 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2238 if (TNY_IS_FOLDER (instance)) {
2239 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2240 } else if (TNY_IS_ACCOUNT (instance)) {
2246 g_object_unref (instance);
2254 modest_folder_view_update_model (ModestFolderView *self,
2255 TnyAccountStore *account_store)
2257 ModestFolderViewPrivate *priv;
2258 GtkTreeModel *model;
2259 GtkTreeModel *filter_model = NULL, *sortable = NULL, *old_tny_model;
2261 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2262 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2265 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2267 /* Notify that there is no folder selected */
2268 g_signal_emit (G_OBJECT(self),
2269 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2271 if (priv->cur_folder_store) {
2272 g_object_unref (priv->cur_folder_store);
2273 priv->cur_folder_store = NULL;
2276 /* FIXME: the local accounts are not shown when the query
2277 selects only the subscribed folders */
2278 #ifdef MODEST_TOOLKIT_HILDON2
2279 TnyGtkFolderListStoreFlags flags;
2280 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2281 if (!priv->do_refresh)
2282 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2283 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2285 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2286 MODEST_FOLDER_PATH_SEPARATOR);
2288 model = tny_gtk_folder_store_tree_model_new (NULL);
2291 /* When the model is a list store (plain representation) the
2292 outbox is not a child of any account so we have to manually
2293 delete it because removing the local folders account won't
2294 delete it (because tny_folder_get_account() is not defined
2295 for a merge folder */
2296 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2297 TnyAccount *account;
2298 ModestTnyAccountStore *acc_store;
2300 acc_store = modest_runtime_get_account_store ();
2301 account = modest_tny_account_store_get_local_folders_account (acc_store);
2303 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) account,
2305 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2306 (GObject *) account,
2309 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2310 (GObject*) account, "outbox-deleted",
2311 G_CALLBACK (on_outbox_deleted_cb),
2313 g_object_unref (account);
2316 /* Get the accounts: */
2317 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2319 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2321 sortable = gtk_tree_model_sort_new_with_model (model);
2322 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2324 GTK_SORT_ASCENDING);
2325 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2327 cmp_rows, NULL, NULL);
2329 /* Create filter model */
2330 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2331 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2336 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2337 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) old_tny_model,
2338 "activity-changed"))
2339 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2340 G_OBJECT (old_tny_model),
2341 "activity-changed");
2345 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2346 #ifndef MODEST_TOOLKIT_HILDON2
2347 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2348 G_OBJECT(filter_model), "row-inserted",
2349 (GCallback) on_row_inserted_maybe_select_folder, self);
2352 #ifdef MODEST_TOOLKIT_HILDON2
2353 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2356 G_CALLBACK (on_activity_changed),
2360 g_object_unref (model);
2361 g_object_unref (filter_model);
2362 g_object_unref (sortable);
2364 /* Force a reselection of the INBOX next time the widget is shown */
2365 priv->reselect = TRUE;
2372 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2374 GtkTreeModel *model = NULL;
2375 TnyFolderStore *folder = NULL;
2377 ModestFolderView *tree_view = NULL;
2378 ModestFolderViewPrivate *priv = NULL;
2379 gboolean selected = FALSE;
2381 g_return_if_fail (sel);
2382 g_return_if_fail (user_data);
2384 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2386 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2388 tree_view = MODEST_FOLDER_VIEW (user_data);
2391 gtk_tree_model_get (model, &iter,
2392 INSTANCE_COLUMN, &folder,
2395 /* If the folder is the same do not notify */
2396 if (folder && priv->cur_folder_store == folder) {
2397 g_object_unref (folder);
2402 /* Current folder was unselected */
2403 if (priv->cur_folder_store) {
2404 /* We must do this firstly because a libtinymail-camel
2405 implementation detail. If we issue the signal
2406 before doing the sync_async, then that signal could
2407 cause (and it actually does it) a free of the
2408 summary of the folder (because the main window will
2409 clear the headers view */
2410 #ifndef MODEST_TOOLKIT_HILDON2
2411 if (TNY_IS_FOLDER(priv->cur_folder_store))
2412 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2413 FALSE, NULL, NULL, NULL);
2416 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2417 priv->cur_folder_store, FALSE);
2419 g_object_unref (priv->cur_folder_store);
2420 priv->cur_folder_store = NULL;
2423 /* New current references */
2424 priv->cur_folder_store = folder;
2426 /* New folder has been selected. Do not notify if there is
2427 nothing new selected */
2429 g_signal_emit (G_OBJECT(tree_view),
2430 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2431 0, priv->cur_folder_store, TRUE);
2436 on_row_activated (GtkTreeView *treeview,
2437 GtkTreePath *treepath,
2438 GtkTreeViewColumn *column,
2441 GtkTreeModel *model = NULL;
2442 TnyFolderStore *folder = NULL;
2444 ModestFolderView *self = NULL;
2445 ModestFolderViewPrivate *priv = NULL;
2447 g_return_if_fail (treeview);
2448 g_return_if_fail (user_data);
2450 self = MODEST_FOLDER_VIEW (user_data);
2451 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2453 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2455 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2458 gtk_tree_model_get (model, &iter,
2459 INSTANCE_COLUMN, &folder,
2462 g_signal_emit (G_OBJECT(self),
2463 signals[FOLDER_ACTIVATED_SIGNAL],
2466 #ifdef MODEST_TOOLKIT_HILDON2
2467 HildonUIMode ui_mode;
2468 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2469 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2470 if (priv->cur_folder_store)
2471 g_object_unref (priv->cur_folder_store);
2472 priv->cur_folder_store = g_object_ref (folder);
2476 g_object_unref (folder);
2480 modest_folder_view_get_selected (ModestFolderView *self)
2482 ModestFolderViewPrivate *priv;
2484 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2486 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2487 if (priv->cur_folder_store)
2488 g_object_ref (priv->cur_folder_store);
2490 return priv->cur_folder_store;
2494 get_cmp_rows_type_pos (GObject *folder)
2496 /* Remote accounts -> Local account -> MMC account .*/
2499 if (TNY_IS_ACCOUNT (folder) &&
2500 modest_tny_account_is_virtual_local_folders (
2501 TNY_ACCOUNT (folder))) {
2503 } else if (TNY_IS_ACCOUNT (folder)) {
2504 TnyAccount *account = TNY_ACCOUNT (folder);
2505 const gchar *account_id = tny_account_get_id (account);
2506 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2512 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2513 return -1; /* Should never happen */
2518 inbox_is_special (TnyFolderStore *folder_store)
2520 gboolean is_special = TRUE;
2522 if (TNY_IS_FOLDER (folder_store)) {
2526 gchar *last_inbox_bar;
2528 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2529 downcase = g_utf8_strdown (id, -1);
2530 last_bar = g_strrstr (downcase, "/");
2532 last_inbox_bar = g_strrstr (downcase, "inbox/");
2533 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2544 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2546 TnyAccount *account;
2547 gboolean is_special;
2548 /* Inbox, Outbox, Drafts, Sent, User */
2551 if (!TNY_IS_FOLDER (folder_store))
2554 case TNY_FOLDER_TYPE_INBOX:
2556 account = tny_folder_get_account (folder_store);
2557 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2559 /* In inbox case we need to know if the inbox is really the top
2560 * inbox of the account, or if it's a submailbox inbox. To do
2561 * this we'll apply an heuristic rule: Find last "/" and check
2562 * if it's preceeded by another Inbox */
2563 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2564 g_object_unref (account);
2565 return is_special?0:4;
2568 case TNY_FOLDER_TYPE_OUTBOX:
2569 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2571 case TNY_FOLDER_TYPE_DRAFTS:
2573 account = tny_folder_get_account (folder_store);
2574 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2575 g_object_unref (account);
2576 return is_special?1:4;
2579 case TNY_FOLDER_TYPE_SENT:
2581 account = tny_folder_get_account (folder_store);
2582 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2583 g_object_unref (account);
2584 return is_special?3:4;
2593 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2595 const gchar *a1_name, *a2_name;
2597 a1_name = tny_account_get_name (a1);
2598 a2_name = tny_account_get_name (a2);
2600 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2604 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2606 TnyAccount *a1 = NULL, *a2 = NULL;
2609 if (TNY_IS_ACCOUNT (s1)) {
2610 a1 = TNY_ACCOUNT (g_object_ref (s1));
2611 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2612 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2615 if (TNY_IS_ACCOUNT (s2)) {
2616 a2 = TNY_ACCOUNT (g_object_ref (s2));
2617 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2618 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2635 /* First we sort with the type of account */
2636 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2640 cmp = compare_account_names (a1, a2);
2644 g_object_unref (a1);
2646 g_object_unref (a2);
2652 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2654 gint is_account1, is_account2;
2656 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2657 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2659 return is_account2 - is_account1;
2663 compare_folders (const gchar *name1, const gchar *name2)
2665 const gchar *separator1, *separator2;
2666 const gchar *next1, *next2;
2670 if (name1 == NULL || name1[0] == '\0')
2672 if (name2 == NULL || name2[0] == '\0')
2675 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2677 top1 = g_strndup (name1, separator1 - name1);
2679 top1 = g_strdup (name1);
2682 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2684 top2 = g_strndup (name2, separator2 - name2);
2686 top2 = g_strdup (name2);
2690 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2697 if (separator1 == NULL && separator2 == NULL)
2700 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2701 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2703 return compare_folders (next1, next2);
2708 * This function orders the mail accounts according to these rules:
2709 * 1st - remote accounts
2710 * 2nd - local account
2714 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2718 gchar *name1 = NULL;
2719 gchar *name2 = NULL;
2720 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2721 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2722 GObject *folder1 = NULL;
2723 GObject *folder2 = NULL;
2725 gtk_tree_model_get (tree_model, iter1,
2726 NAME_COLUMN, &name1,
2728 INSTANCE_COLUMN, &folder1,
2730 gtk_tree_model_get (tree_model, iter2,
2731 NAME_COLUMN, &name2,
2732 TYPE_COLUMN, &type2,
2733 INSTANCE_COLUMN, &folder2,
2736 /* Return if we get no folder. This could happen when folder
2737 operations are happening. The model is updated after the
2738 folder copy/move actually occurs, so there could be
2739 situations where the model to be drawn is not correct */
2740 if (!folder1 || !folder2)
2743 /* Sort by type. First the special folders, then the archives */
2744 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2748 /* Now we sort using the account of each folder */
2749 if (TNY_IS_FOLDER_STORE (folder1) &&
2750 TNY_IS_FOLDER_STORE (folder2)) {
2751 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2755 /* Each group is preceeded by its account */
2756 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2761 /* Pure sort by name */
2762 cmp = compare_folders (name1, name2);
2765 g_object_unref(G_OBJECT(folder1));
2767 g_object_unref(G_OBJECT(folder2));
2775 /*****************************************************************************/
2776 /* DRAG and DROP stuff */
2777 /*****************************************************************************/
2779 * This function fills the #GtkSelectionData with the row and the
2780 * model that has been dragged. It's called when this widget is a
2781 * source for dnd after the event drop happened
2784 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2785 guint info, guint time, gpointer data)
2787 GtkTreeSelection *selection;
2788 GtkTreeModel *model;
2790 GtkTreePath *source_row;
2792 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2793 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2795 source_row = gtk_tree_model_get_path (model, &iter);
2796 gtk_tree_set_row_drag_data (selection_data,
2800 gtk_tree_path_free (source_row);
2804 typedef struct _DndHelper {
2805 ModestFolderView *folder_view;
2806 gboolean delete_source;
2807 GtkTreePath *source_row;
2811 dnd_helper_destroyer (DndHelper *helper)
2813 /* Free the helper */
2814 gtk_tree_path_free (helper->source_row);
2815 g_slice_free (DndHelper, helper);
2819 xfer_folder_cb (ModestMailOperation *mail_op,
2820 TnyFolder *new_folder,
2824 /* Select the folder */
2825 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2831 /* get the folder for the row the treepath refers to. */
2832 /* folder must be unref'd */
2833 static TnyFolderStore *
2834 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2837 TnyFolderStore *folder = NULL;
2839 if (gtk_tree_model_get_iter (model,&iter, path))
2840 gtk_tree_model_get (model, &iter,
2841 INSTANCE_COLUMN, &folder,
2848 * This function is used by drag_data_received_cb to manage drag and
2849 * drop of a header, i.e, and drag from the header view to the folder
2853 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2854 GtkTreeModel *dest_model,
2855 GtkTreePath *dest_row,
2856 GtkSelectionData *selection_data)
2858 TnyList *headers = NULL;
2859 TnyFolder *folder = NULL, *src_folder = NULL;
2860 TnyFolderType folder_type;
2861 GtkTreeIter source_iter, dest_iter;
2862 ModestWindowMgr *mgr = NULL;
2863 ModestWindow *main_win = NULL;
2864 gchar **uris, **tmp;
2866 /* Build the list of headers */
2867 mgr = modest_runtime_get_window_mgr ();
2868 headers = tny_simple_list_new ();
2869 uris = modest_dnd_selection_data_get_paths (selection_data);
2872 while (*tmp != NULL) {
2875 gboolean first = TRUE;
2878 path = gtk_tree_path_new_from_string (*tmp);
2879 gtk_tree_model_get_iter (source_model, &source_iter, path);
2880 gtk_tree_model_get (source_model, &source_iter,
2881 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2884 /* Do not enable d&d of headers already opened */
2885 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2886 tny_list_append (headers, G_OBJECT (header));
2888 if (G_UNLIKELY (first)) {
2889 src_folder = tny_header_get_folder (header);
2893 /* Free and go on */
2894 gtk_tree_path_free (path);
2895 g_object_unref (header);
2900 /* This could happen ig we perform a d&d very quickly over the
2901 same row that row could dissapear because message is
2903 if (!TNY_IS_FOLDER (src_folder))
2906 /* Get the target folder */
2907 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2908 gtk_tree_model_get (dest_model, &dest_iter,
2912 if (!folder || !TNY_IS_FOLDER(folder)) {
2913 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2917 folder_type = modest_tny_folder_guess_folder_type (folder);
2918 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2919 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2920 goto cleanup; /* cannot move messages there */
2923 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2924 /* g_warning ("folder not writable"); */
2925 goto cleanup; /* verboten! */
2928 /* Ask for confirmation to move */
2929 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2931 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2935 /* Transfer messages */
2936 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2941 if (G_IS_OBJECT (src_folder))
2942 g_object_unref (src_folder);
2943 if (G_IS_OBJECT(folder))
2944 g_object_unref (G_OBJECT (folder));
2945 if (G_IS_OBJECT(headers))
2946 g_object_unref (headers);
2950 TnyFolderStore *src_folder;
2951 TnyFolderStore *dst_folder;
2952 ModestFolderView *folder_view;
2957 dnd_folder_info_destroyer (DndFolderInfo *info)
2959 if (info->src_folder)
2960 g_object_unref (info->src_folder);
2961 if (info->dst_folder)
2962 g_object_unref (info->dst_folder);
2963 g_slice_free (DndFolderInfo, info);
2967 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2968 GtkWindow *parent_window,
2969 TnyAccount *account)
2972 modest_ui_actions_on_account_connection_error (parent_window, account);
2974 /* Free the helper & info */
2975 dnd_helper_destroyer (info->helper);
2976 dnd_folder_info_destroyer (info);
2980 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2982 GtkWindow *parent_window,
2983 TnyAccount *account,
2986 DndFolderInfo *info = NULL;
2987 ModestMailOperation *mail_op;
2989 info = (DndFolderInfo *) user_data;
2991 if (err || canceled) {
2992 dnd_on_connection_failed_destroyer (info, parent_window, account);
2996 /* Do the mail operation */
2997 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2998 modest_ui_actions_move_folder_error_handler,
2999 info->src_folder, NULL);
3001 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3004 /* Transfer the folder */
3005 modest_mail_operation_xfer_folder (mail_op,
3006 TNY_FOLDER (info->src_folder),
3008 info->helper->delete_source,
3010 info->helper->folder_view);
3013 g_object_unref (G_OBJECT (mail_op));
3014 dnd_helper_destroyer (info->helper);
3015 dnd_folder_info_destroyer (info);
3020 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3022 GtkWindow *parent_window,
3023 TnyAccount *account,
3026 DndFolderInfo *info = NULL;
3028 info = (DndFolderInfo *) user_data;
3030 if (err || canceled) {
3031 dnd_on_connection_failed_destroyer (info, parent_window, account);
3035 /* Connect to source folder and perform the copy/move */
3036 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3038 drag_and_drop_from_folder_view_src_folder_performer,
3043 * This function is used by drag_data_received_cb to manage drag and
3044 * drop of a folder, i.e, and drag from the folder view to the same
3048 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3049 GtkTreeModel *dest_model,
3050 GtkTreePath *dest_row,
3051 GtkSelectionData *selection_data,
3054 GtkTreeIter dest_iter, iter;
3055 TnyFolderStore *dest_folder = NULL;
3056 TnyFolderStore *folder = NULL;
3057 gboolean forbidden = FALSE;
3059 DndFolderInfo *info = NULL;
3061 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3063 g_warning ("%s: BUG: no main window", __FUNCTION__);
3064 dnd_helper_destroyer (helper);
3069 /* check the folder rules for the destination */
3070 folder = tree_path_to_folder (dest_model, dest_row);
3071 if (TNY_IS_FOLDER(folder)) {
3072 ModestTnyFolderRules rules =
3073 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3074 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3075 } else if (TNY_IS_FOLDER_STORE(folder)) {
3076 /* enable local root as destination for folders */
3077 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3078 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3081 g_object_unref (folder);
3084 /* check the folder rules for the source */
3085 folder = tree_path_to_folder (source_model, helper->source_row);
3086 if (TNY_IS_FOLDER(folder)) {
3087 ModestTnyFolderRules rules =
3088 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3089 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3092 g_object_unref (folder);
3096 /* Check if the drag is possible */
3097 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3099 modest_platform_run_information_dialog ((GtkWindow *) win,
3100 _("mail_in_ui_folder_move_target_error"),
3102 /* Restore the previous selection */
3103 folder = tree_path_to_folder (source_model, helper->source_row);
3105 if (TNY_IS_FOLDER (folder))
3106 modest_folder_view_select_folder (helper->folder_view,
3107 TNY_FOLDER (folder), FALSE);
3108 g_object_unref (folder);
3110 dnd_helper_destroyer (helper);
3115 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3116 gtk_tree_model_get (dest_model, &dest_iter,
3119 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3120 gtk_tree_model_get (source_model, &iter,
3124 /* Create the info for the performer */
3125 info = g_slice_new0 (DndFolderInfo);
3126 info->src_folder = g_object_ref (folder);
3127 info->dst_folder = g_object_ref (dest_folder);
3128 info->helper = helper;
3130 /* Connect to the destination folder and perform the copy/move */
3131 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3133 drag_and_drop_from_folder_view_dst_folder_performer,
3137 g_object_unref (dest_folder);
3138 g_object_unref (folder);
3142 * This function receives the data set by the "drag-data-get" signal
3143 * handler. This information comes within the #GtkSelectionData. This
3144 * function will manage both the drags of folders of the treeview and
3145 * drags of headers of the header view widget.
3148 on_drag_data_received (GtkWidget *widget,
3149 GdkDragContext *context,
3152 GtkSelectionData *selection_data,
3157 GtkWidget *source_widget;
3158 GtkTreeModel *dest_model, *source_model;
3159 GtkTreePath *source_row, *dest_row;
3160 GtkTreeViewDropPosition pos;
3161 gboolean delete_source = FALSE;
3162 gboolean success = FALSE;
3164 /* Do not allow further process */
3165 g_signal_stop_emission_by_name (widget, "drag-data-received");
3166 source_widget = gtk_drag_get_source_widget (context);
3168 /* Get the action */
3169 if (context->action == GDK_ACTION_MOVE) {
3170 delete_source = TRUE;
3172 /* Notify that there is no folder selected. We need to
3173 do this in order to update the headers view (and
3174 its monitors, because when moving, the old folder
3175 won't longer exist. We can not wait for the end of
3176 the operation, because the operation won't start if
3177 the folder is in use */
3178 if (source_widget == widget) {
3179 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3180 gtk_tree_selection_unselect_all (sel);
3184 /* Check if the get_data failed */
3185 if (selection_data == NULL || selection_data->length < 0)
3188 /* Select the destination model */
3189 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3191 /* Get the path to the destination row. Can not call
3192 gtk_tree_view_get_drag_dest_row() because the source row
3193 is not selected anymore */
3194 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3197 /* Only allow drops IN other rows */
3199 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3200 pos == GTK_TREE_VIEW_DROP_AFTER)
3204 /* Drags from the header view */
3205 if (source_widget != widget) {
3206 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3208 drag_and_drop_from_header_view (source_model,
3213 DndHelper *helper = NULL;
3215 /* Get the source model and row */
3216 gtk_tree_get_row_drag_data (selection_data,
3220 /* Create the helper */
3221 helper = g_slice_new0 (DndHelper);
3222 helper->delete_source = delete_source;
3223 helper->source_row = gtk_tree_path_copy (source_row);
3224 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3226 drag_and_drop_from_folder_view (source_model,
3232 gtk_tree_path_free (source_row);
3236 gtk_tree_path_free (dest_row);
3239 /* Finish the drag and drop */
3240 gtk_drag_finish (context, success, FALSE, time);
3244 * We define a "drag-drop" signal handler because we do not want to
3245 * use the default one, because the default one always calls
3246 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3247 * signal handler, because there we have all the information available
3248 * to know if the dnd was a success or not.
3251 drag_drop_cb (GtkWidget *widget,
3252 GdkDragContext *context,
3260 if (!context->targets)
3263 /* Check if we're dragging a folder row */
3264 target = gtk_drag_dest_find_target (widget, context, NULL);
3266 /* Request the data from the source. */
3267 gtk_drag_get_data(widget, context, target, time);
3273 * This function expands a node of a tree view if it's not expanded
3274 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3275 * does that, so that's why they're here.
3278 expand_row_timeout (gpointer data)
3280 GtkTreeView *tree_view = data;
3281 GtkTreePath *dest_path = NULL;
3282 GtkTreeViewDropPosition pos;
3283 gboolean result = FALSE;
3285 gdk_threads_enter ();
3287 gtk_tree_view_get_drag_dest_row (tree_view,
3292 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3293 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3294 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3295 gtk_tree_path_free (dest_path);
3299 gtk_tree_path_free (dest_path);
3304 gdk_threads_leave ();
3310 * This function is called whenever the pointer is moved over a widget
3311 * while dragging some data. It installs a timeout that will expand a
3312 * node of the treeview if not expanded yet. This function also calls
3313 * gdk_drag_status in order to set the suggested action that will be
3314 * used by the "drag-data-received" signal handler to know if we
3315 * should do a move or just a copy of the data.
3318 on_drag_motion (GtkWidget *widget,
3319 GdkDragContext *context,
3325 GtkTreeViewDropPosition pos;
3326 GtkTreePath *dest_row;
3327 GtkTreeModel *dest_model;
3328 ModestFolderViewPrivate *priv;
3329 GdkDragAction suggested_action;
3330 gboolean valid_location = FALSE;
3331 TnyFolderStore *folder = NULL;
3333 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3335 if (priv->timer_expander != 0) {
3336 g_source_remove (priv->timer_expander);
3337 priv->timer_expander = 0;
3340 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3345 /* Do not allow drops between folders */
3347 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3348 pos == GTK_TREE_VIEW_DROP_AFTER) {
3349 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3350 gdk_drag_status(context, 0, time);
3351 valid_location = FALSE;
3354 valid_location = TRUE;
3357 /* Check that the destination folder is writable */
3358 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3359 folder = tree_path_to_folder (dest_model, dest_row);
3360 if (folder && TNY_IS_FOLDER (folder)) {
3361 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3363 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3364 valid_location = FALSE;
3369 /* Expand the selected row after 1/2 second */
3370 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3371 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3373 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3375 /* Select the desired action. By default we pick MOVE */
3376 suggested_action = GDK_ACTION_MOVE;
3378 if (context->actions == GDK_ACTION_COPY)
3379 gdk_drag_status(context, GDK_ACTION_COPY, time);
3380 else if (context->actions == GDK_ACTION_MOVE)
3381 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3382 else if (context->actions & suggested_action)
3383 gdk_drag_status(context, suggested_action, time);
3385 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3389 g_object_unref (folder);
3391 gtk_tree_path_free (dest_row);
3393 g_signal_stop_emission_by_name (widget, "drag-motion");
3395 return valid_location;
3399 * This function sets the treeview as a source and a target for dnd
3400 * events. It also connects all the requirede signals.
3403 setup_drag_and_drop (GtkTreeView *self)
3405 /* Set up the folder view as a dnd destination. Set only the
3406 highlight flag, otherwise gtk will have a different
3408 #ifdef MODEST_TOOLKIT_HILDON2
3411 ModestFolderViewPrivate *priv;
3413 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3415 gtk_drag_dest_set (GTK_WIDGET (self),
3416 GTK_DEST_DEFAULT_HIGHLIGHT,
3417 folder_view_drag_types,
3418 G_N_ELEMENTS (folder_view_drag_types),
3419 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3421 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3422 G_OBJECT (self), "drag_data_received",
3423 G_CALLBACK (on_drag_data_received), NULL);
3426 /* Set up the treeview as a dnd source */
3427 gtk_drag_source_set (GTK_WIDGET (self),
3429 folder_view_drag_types,
3430 G_N_ELEMENTS (folder_view_drag_types),
3431 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3433 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3434 G_OBJECT (self), "drag_motion",
3435 G_CALLBACK (on_drag_motion), NULL);
3437 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3438 G_OBJECT (self), "drag_data_get",
3439 G_CALLBACK (on_drag_data_get), NULL);
3441 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3442 G_OBJECT (self), "drag_drop",
3443 G_CALLBACK (drag_drop_cb), NULL);
3447 * This function manages the navigation through the folders using the
3448 * keyboard or the hardware keys in the device
3451 on_key_pressed (GtkWidget *self,
3455 GtkTreeSelection *selection;
3457 GtkTreeModel *model;
3458 gboolean retval = FALSE;
3460 /* Up and Down are automatically managed by the treeview */
3461 if (event->keyval == GDK_Return) {
3462 /* Expand/Collapse the selected row */
3463 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3464 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3467 path = gtk_tree_model_get_path (model, &iter);
3469 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3470 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3472 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3473 gtk_tree_path_free (path);
3475 /* No further processing */
3483 * We listen to the changes in the local folder account name key,
3484 * because we want to show the right name in the view. The local
3485 * folder account name corresponds to the device name in the Maemo
3486 * version. We do this because we do not want to query gconf on each
3487 * tree view refresh. It's better to cache it and change whenever
3491 on_configuration_key_changed (ModestConf* conf,
3493 ModestConfEvent event,
3494 ModestConfNotificationId id,
3495 ModestFolderView *self)
3497 ModestFolderViewPrivate *priv;
3500 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3501 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3503 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3504 g_free (priv->local_account_name);
3506 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3507 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3509 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3510 MODEST_CONF_DEVICE_NAME, NULL);
3512 /* Force a redraw */
3513 #if GTK_CHECK_VERSION(2, 8, 0)
3514 GtkTreeViewColumn * tree_column;
3516 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3518 gtk_tree_view_column_queue_resize (tree_column);
3520 gtk_widget_queue_draw (GTK_WIDGET (self));
3526 modest_folder_view_set_style (ModestFolderView *self,
3527 ModestFolderViewStyle style)
3529 ModestFolderViewPrivate *priv;
3531 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3532 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3533 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3535 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3538 priv->style = style;
3542 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3543 const gchar *account_id)
3545 ModestFolderViewPrivate *priv;
3546 GtkTreeModel *model;
3548 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3550 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3552 /* This will be used by the filter_row callback,
3553 * to decided which rows to show: */
3554 if (priv->visible_account_id) {
3555 g_free (priv->visible_account_id);
3556 priv->visible_account_id = NULL;
3559 priv->visible_account_id = g_strdup (account_id);
3562 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3563 if (GTK_IS_TREE_MODEL_FILTER (model))
3564 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3566 /* Save settings to gconf */
3567 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3568 MODEST_CONF_FOLDER_VIEW_KEY);
3570 /* Notify observers */
3571 g_signal_emit (G_OBJECT(self),
3572 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3577 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3579 ModestFolderViewPrivate *priv;
3581 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3583 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3585 return (const gchar *) priv->visible_account_id;
3589 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3593 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3595 gtk_tree_model_get (model, iter,
3599 gboolean result = FALSE;
3600 if (type == TNY_FOLDER_TYPE_INBOX) {
3604 *inbox_iter = *iter;
3608 if (gtk_tree_model_iter_children (model, &child, iter)) {
3609 if (find_inbox_iter (model, &child, inbox_iter))
3613 } while (gtk_tree_model_iter_next (model, iter));
3622 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3624 #ifndef MODEST_TOOLKIT_HILDON2
3625 GtkTreeModel *model;
3626 GtkTreeIter iter, inbox_iter;
3627 GtkTreeSelection *sel;
3628 GtkTreePath *path = NULL;
3630 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3632 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3636 expand_root_items (self);
3637 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3639 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3640 g_warning ("%s: model is empty", __FUNCTION__);
3644 if (find_inbox_iter (model, &iter, &inbox_iter))
3645 path = gtk_tree_model_get_path (model, &inbox_iter);
3647 path = gtk_tree_path_new_first ();
3649 /* Select the row and free */
3650 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3651 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3652 gtk_tree_path_free (path);
3655 gtk_widget_grab_focus (GTK_WIDGET(self));
3662 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3667 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3668 TnyFolder* a_folder;
3671 gtk_tree_model_get (model, iter,
3672 INSTANCE_COLUMN, &a_folder,
3678 if (folder == a_folder) {
3679 g_object_unref (a_folder);
3680 *folder_iter = *iter;
3683 g_object_unref (a_folder);
3685 if (gtk_tree_model_iter_children (model, &child, iter)) {
3686 if (find_folder_iter (model, &child, folder_iter, folder))
3690 } while (gtk_tree_model_iter_next (model, iter));
3695 #ifndef MODEST_TOOLKIT_HILDON2
3697 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3700 ModestFolderView *self)
3702 ModestFolderViewPrivate *priv = NULL;
3703 GtkTreeSelection *sel;
3704 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3705 GObject *instance = NULL;
3707 if (!MODEST_IS_FOLDER_VIEW(self))
3710 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3712 priv->reexpand = TRUE;
3714 gtk_tree_model_get (tree_model, iter,
3716 INSTANCE_COLUMN, &instance,
3722 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3723 priv->folder_to_select = g_object_ref (instance);
3725 g_object_unref (instance);
3727 if (priv->folder_to_select) {
3729 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3732 path = gtk_tree_model_get_path (tree_model, iter);
3733 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3735 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3737 gtk_tree_selection_select_iter (sel, iter);
3738 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3740 gtk_tree_path_free (path);
3744 modest_folder_view_disable_next_folder_selection (self);
3746 /* Refilter the model */
3747 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3753 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3755 ModestFolderViewPrivate *priv;
3757 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3759 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3761 if (priv->folder_to_select)
3762 g_object_unref(priv->folder_to_select);
3764 priv->folder_to_select = NULL;
3768 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3769 gboolean after_change)
3771 GtkTreeModel *model;
3772 GtkTreeIter iter, folder_iter;
3773 GtkTreeSelection *sel;
3774 ModestFolderViewPrivate *priv = NULL;
3776 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3777 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3779 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3782 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3783 gtk_tree_selection_unselect_all (sel);
3785 if (priv->folder_to_select)
3786 g_object_unref(priv->folder_to_select);
3787 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3791 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3796 /* Refilter the model, before selecting the folder */
3797 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3799 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3800 g_warning ("%s: model is empty", __FUNCTION__);
3804 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3807 path = gtk_tree_model_get_path (model, &folder_iter);
3808 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3810 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3811 gtk_tree_selection_select_iter (sel, &folder_iter);
3812 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3814 gtk_tree_path_free (path);
3822 modest_folder_view_copy_selection (ModestFolderView *self)
3824 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3826 /* Copy selection */
3827 _clipboard_set_selected_data (self, FALSE);
3831 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3833 ModestFolderViewPrivate *priv = NULL;
3834 GtkTreeModel *model = NULL;
3835 const gchar **hidding = NULL;
3836 guint i, n_selected;
3838 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3839 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3841 /* Copy selection */
3842 if (!_clipboard_set_selected_data (folder_view, TRUE))
3845 /* Get hidding ids */
3846 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3848 /* Clear hidding array created by previous cut operation */
3849 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3851 /* Copy hidding array */
3852 priv->n_selected = n_selected;
3853 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3854 for (i=0; i < n_selected; i++)
3855 priv->hidding_ids[i] = g_strdup(hidding[i]);
3857 /* Hide cut folders */
3858 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3859 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3863 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3864 ModestFolderView *folder_view_dst)
3866 GtkTreeModel *filter_model = NULL;
3867 GtkTreeModel *model = NULL;
3868 GtkTreeModel *new_filter_model = NULL;
3869 GtkTreeModel *old_tny_model = NULL;
3870 GtkTreeModel *new_tny_model = NULL;
3871 ModestFolderViewPrivate *dst_priv;
3873 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3874 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3876 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3877 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3878 new_tny_model = NULL;
3881 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3882 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3883 G_OBJECT (old_tny_model),
3884 "activity-changed");
3886 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3887 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3889 /* Build new filter model */
3890 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3891 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3898 /* Set copied model */
3899 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3900 #ifndef MODEST_TOOLKIT_HILDON2
3901 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3902 G_OBJECT(new_filter_model), "row-inserted",
3903 (GCallback) on_row_inserted_maybe_select_folder,
3906 #ifdef MODEST_TOOLKIT_HILDON2
3907 if (new_tny_model) {
3908 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3909 G_OBJECT (new_tny_model),
3911 G_CALLBACK (on_activity_changed),
3917 g_object_unref (new_filter_model);
3921 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3924 GtkTreeModel *model = NULL;
3925 ModestFolderViewPrivate* priv;
3927 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3929 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3930 priv->show_non_move = show;
3931 /* modest_folder_view_update_model(folder_view, */
3932 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3934 /* Hide special folders */
3935 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3936 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3937 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3942 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3945 ModestFolderViewPrivate* priv;
3947 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3949 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3950 priv->show_message_count = show;
3952 g_object_set (G_OBJECT (priv->messages_renderer),
3953 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3957 /* Returns FALSE if it did not selected anything */
3959 _clipboard_set_selected_data (ModestFolderView *folder_view,
3962 ModestFolderViewPrivate *priv = NULL;
3963 TnyFolderStore *folder = NULL;
3964 gboolean retval = FALSE;
3966 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3967 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3969 /* Set selected data on clipboard */
3970 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3971 folder = modest_folder_view_get_selected (folder_view);
3973 /* Do not allow to select an account */
3974 if (TNY_IS_FOLDER (folder)) {
3975 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3980 g_object_unref (folder);
3986 _clear_hidding_filter (ModestFolderView *folder_view)
3988 ModestFolderViewPrivate *priv;
3991 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3994 if (priv->hidding_ids != NULL) {
3995 for (i=0; i < priv->n_selected; i++)
3996 g_free (priv->hidding_ids[i]);
3997 g_free(priv->hidding_ids);
4003 on_display_name_changed (ModestAccountMgr *mgr,
4004 const gchar *account,
4007 ModestFolderView *self;
4009 self = MODEST_FOLDER_VIEW (user_data);
4011 /* Force a redraw */
4012 #if GTK_CHECK_VERSION(2, 8, 0)
4013 GtkTreeViewColumn * tree_column;
4015 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4017 gtk_tree_view_column_queue_resize (tree_column);
4019 gtk_widget_queue_draw (GTK_WIDGET (self));
4024 modest_folder_view_set_cell_style (ModestFolderView *self,
4025 ModestFolderViewCellStyle cell_style)
4027 ModestFolderViewPrivate *priv = NULL;
4029 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4030 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4032 priv->cell_style = cell_style;
4034 g_object_set (G_OBJECT (priv->messages_renderer),
4035 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4038 gtk_widget_queue_draw (GTK_WIDGET (self));
4042 update_style (ModestFolderView *self)
4044 ModestFolderViewPrivate *priv;
4045 GdkColor style_color, style_active_color;
4046 PangoAttrList *attr_list;
4048 PangoAttribute *attr;
4050 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4051 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4055 attr_list = pango_attr_list_new ();
4056 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4057 gdk_color_parse ("grey", &style_color);
4059 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4060 pango_attr_list_insert (attr_list, attr);
4063 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4065 "SmallSystemFont", NULL,
4068 attr = pango_attr_font_desc_new (pango_font_description_copy
4069 (style->font_desc));
4070 pango_attr_list_insert (attr_list, attr);
4072 g_object_set (G_OBJECT (priv->messages_renderer),
4073 "foreground-gdk", &style_color,
4074 "foreground-set", TRUE,
4075 "attributes", attr_list,
4077 pango_attr_list_unref (attr_list);
4079 if (priv->active_color)
4080 gdk_color_free (priv->active_color);
4082 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4083 priv->active_color = gdk_color_copy (&style_active_color);
4088 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4090 if (strcmp ("style", spec->name) == 0) {
4091 update_style (MODEST_FOLDER_VIEW (obj));
4092 gtk_widget_queue_draw (GTK_WIDGET (obj));
4097 modest_folder_view_set_filter (ModestFolderView *self,
4098 ModestFolderViewFilter filter)
4100 ModestFolderViewPrivate *priv;
4101 GtkTreeModel *filter_model;
4103 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4104 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4106 priv->filter |= filter;
4108 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4109 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4110 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4115 modest_folder_view_unset_filter (ModestFolderView *self,
4116 ModestFolderViewFilter filter)
4118 ModestFolderViewPrivate *priv;
4119 GtkTreeModel *filter_model;
4121 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4122 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4124 priv->filter &= ~filter;
4126 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4127 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4128 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4133 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4134 ModestTnyFolderRules rules)
4136 GtkTreeModel *filter_model;
4138 gboolean fulfil = FALSE;
4140 if (!get_inner_models (self, &filter_model, NULL, NULL))
4143 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4147 TnyFolderStore *folder;
4149 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4151 if (TNY_IS_FOLDER (folder)) {
4152 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4153 /* Folder rules are negative: non_writable, non_deletable... */
4154 if (!(folder_rules & rules))
4157 g_object_unref (folder);
4160 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4166 modest_folder_view_set_list_to_move (ModestFolderView *self,
4169 ModestFolderViewPrivate *priv;
4171 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4172 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4174 if (priv->list_to_move)
4175 g_object_unref (priv->list_to_move);
4178 g_object_ref (list);
4180 priv->list_to_move = list;
4184 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4186 ModestFolderViewPrivate *priv;
4188 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4189 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4192 g_free (priv->mailbox);
4194 priv->mailbox = g_strdup (mailbox);
4196 /* Notify observers */
4197 g_signal_emit (G_OBJECT(self),
4198 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4199 priv->visible_account_id);
4203 modest_folder_view_get_mailbox (ModestFolderView *self)
4205 ModestFolderViewPrivate *priv;
4207 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4208 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4210 return (const gchar *) priv->mailbox;
4214 modest_folder_view_get_activity (ModestFolderView *self)
4216 ModestFolderViewPrivate *priv;
4217 GtkTreeModel *inner_model;
4219 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4220 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4221 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4223 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4224 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4230 #ifdef MODEST_TOOLKIT_HILDON2
4232 on_activity_changed (TnyGtkFolderListStore *store,
4234 ModestFolderView *folder_view)
4236 ModestFolderViewPrivate *priv;
4238 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4239 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4240 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4242 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,