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 gulong changed_signal;
224 gulong account_inserted_signal;
225 gulong account_removed_signal;
226 gulong account_changed_signal;
227 gulong conf_key_signal;
228 gulong display_name_changed_signal;
230 /* not unref this object, its a singlenton */
231 ModestEmailClipboard *clipboard;
233 /* Filter tree model */
236 ModestFolderViewFilter filter;
238 TnyFolderStoreQuery *query;
240 guint timer_expander;
242 gchar *local_account_name;
243 gchar *visible_account_id;
245 ModestFolderViewStyle style;
246 ModestFolderViewCellStyle cell_style;
247 gboolean show_message_count;
249 gboolean reselect; /* we use this to force a reselection of the INBOX */
250 gboolean show_non_move;
251 TnyList *list_to_move;
252 gboolean reexpand; /* next time we expose, we'll expand all root folders */
254 GtkCellRenderer *messages_renderer;
256 gulong outbox_deleted_handler;
258 GSList *signal_handlers;
259 GdkColor active_color;
261 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
262 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
263 MODEST_TYPE_FOLDER_VIEW, \
264 ModestFolderViewPrivate))
266 static GObjectClass *parent_class = NULL;
268 static guint signals[LAST_SIGNAL] = {0};
271 modest_folder_view_get_type (void)
273 static GType my_type = 0;
275 static const GTypeInfo my_info = {
276 sizeof(ModestFolderViewClass),
277 NULL, /* base init */
278 NULL, /* base finalize */
279 (GClassInitFunc) modest_folder_view_class_init,
280 NULL, /* class finalize */
281 NULL, /* class data */
282 sizeof(ModestFolderView),
284 (GInstanceInitFunc) modest_folder_view_init,
288 static const GInterfaceInfo tny_account_store_view_info = {
289 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
290 NULL, /* interface_finalize */
291 NULL /* interface_data */
295 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
299 g_type_add_interface_static (my_type,
300 TNY_TYPE_ACCOUNT_STORE_VIEW,
301 &tny_account_store_view_info);
307 modest_folder_view_class_init (ModestFolderViewClass *klass)
309 GObjectClass *gobject_class;
310 GtkTreeViewClass *treeview_class;
311 gobject_class = (GObjectClass*) klass;
312 treeview_class = (GtkTreeViewClass*) klass;
314 parent_class = g_type_class_peek_parent (klass);
315 gobject_class->finalize = modest_folder_view_finalize;
316 gobject_class->dispose = modest_folder_view_dispose;
318 g_type_class_add_private (gobject_class,
319 sizeof(ModestFolderViewPrivate));
321 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
322 g_signal_new ("folder_selection_changed",
323 G_TYPE_FROM_CLASS (gobject_class),
325 G_STRUCT_OFFSET (ModestFolderViewClass,
326 folder_selection_changed),
328 modest_marshal_VOID__POINTER_BOOLEAN,
329 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
332 * This signal is emitted whenever the currently selected
333 * folder display name is computed. Note that the name could
334 * be different to the folder name, because we could append
335 * the unread messages count to the folder name to build the
336 * folder display name
338 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
339 g_signal_new ("folder-display-name-changed",
340 G_TYPE_FROM_CLASS (gobject_class),
342 G_STRUCT_OFFSET (ModestFolderViewClass,
343 folder_display_name_changed),
345 g_cclosure_marshal_VOID__STRING,
346 G_TYPE_NONE, 1, G_TYPE_STRING);
348 signals[FOLDER_ACTIVATED_SIGNAL] =
349 g_signal_new ("folder_activated",
350 G_TYPE_FROM_CLASS (gobject_class),
352 G_STRUCT_OFFSET (ModestFolderViewClass,
355 g_cclosure_marshal_VOID__POINTER,
356 G_TYPE_NONE, 1, G_TYPE_POINTER);
359 * Emitted whenever the visible account changes
361 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
362 g_signal_new ("visible-account-changed",
363 G_TYPE_FROM_CLASS (gobject_class),
365 G_STRUCT_OFFSET (ModestFolderViewClass,
366 visible_account_changed),
368 g_cclosure_marshal_VOID__STRING,
369 G_TYPE_NONE, 1, G_TYPE_STRING);
372 * Emitted when the underlying GtkListStore is updating data
374 signals[ACTIVITY_CHANGED_SIGNAL] =
375 g_signal_new ("activity-changed",
376 G_TYPE_FROM_CLASS (gobject_class),
378 G_STRUCT_OFFSET (ModestFolderViewClass,
381 g_cclosure_marshal_VOID__BOOLEAN,
382 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
384 treeview_class->select_cursor_parent = NULL;
386 #ifdef MODEST_TOOLKIT_HILDON2
387 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
393 /* Retrieves the filter, sort and tny models of the folder view. If
394 any of these does not exist then it returns FALSE */
396 get_inner_models (ModestFolderView *self,
397 GtkTreeModel **filter_model,
398 GtkTreeModel **sort_model,
399 GtkTreeModel **tny_model)
401 GtkTreeModel *s_model, *f_model, *t_model;
403 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
404 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
405 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
409 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
410 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
411 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
415 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
419 *filter_model = f_model;
421 *sort_model = s_model;
423 *tny_model = t_model;
428 /* Simplify checks for NULLs: */
430 strings_are_equal (const gchar *a, const gchar *b)
436 return (strcmp (a, b) == 0);
443 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
445 GObject *instance = NULL;
447 gtk_tree_model_get (model, iter,
448 INSTANCE_COLUMN, &instance,
452 return FALSE; /* keep walking */
454 if (!TNY_IS_ACCOUNT (instance)) {
455 g_object_unref (instance);
456 return FALSE; /* keep walking */
459 /* Check if this is the looked-for account: */
460 TnyAccount *this_account = TNY_ACCOUNT (instance);
461 TnyAccount *account = TNY_ACCOUNT (data);
463 const gchar *this_account_id = tny_account_get_id(this_account);
464 const gchar *account_id = tny_account_get_id(account);
465 g_object_unref (instance);
468 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
469 if (strings_are_equal(this_account_id, account_id)) {
470 /* Tell the model that the data has changed, so that
471 * it calls the cell_data_func callbacks again: */
472 /* TODO: This does not seem to actually cause the new string to be shown: */
473 gtk_tree_model_row_changed (model, path, iter);
475 return TRUE; /* stop walking */
478 return FALSE; /* keep walking */
483 ModestFolderView *self;
484 gchar *previous_name;
485 } GetMmcAccountNameData;
488 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
490 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
492 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
494 if (!strings_are_equal (
495 tny_account_get_name(TNY_ACCOUNT(account)),
496 data->previous_name)) {
498 /* Tell the model that the data has changed, so that
499 * it calls the cell_data_func callbacks again: */
500 ModestFolderView *self = data->self;
501 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
503 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
506 g_free (data->previous_name);
507 g_slice_free (GetMmcAccountNameData, data);
511 convert_parent_folders_to_dots (gchar **item_name)
514 gint n_inbox_parents = 0;
517 gchar *last_separator;
519 if (item_name == NULL)
522 path_start = *item_name;
523 for (c = *item_name; *c != '\0'; c++) {
524 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
526 if (c != path_start) {
527 compare = g_strndup (path_start, c - path_start);
528 compare = g_strstrip (compare);
529 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
539 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
540 if (last_separator != NULL) {
541 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
548 buffer = g_string_new ("");
549 for (i = 0; i < n_parents - n_inbox_parents; i++) {
550 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
552 buffer = g_string_append (buffer, last_separator);
554 *item_name = g_string_free (buffer, FALSE);
560 format_compact_style (gchar **item_name,
562 const gchar *mailbox,
564 gboolean multiaccount,
565 gboolean *use_markup)
569 TnyFolderType folder_type;
571 if (!TNY_IS_FOLDER (instance))
574 folder = (TnyFolder *) instance;
576 folder_type = tny_folder_get_folder_type (folder);
577 is_special = (get_cmp_pos (folder_type, folder)!= 4);
580 /* Remove mailbox prefix if any */
581 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
582 if (g_str_has_prefix (*item_name, prefix)) {
583 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
585 *item_name = new_item_name;
589 if (!is_special || multiaccount) {
590 TnyAccount *account = tny_folder_get_account (folder);
591 const gchar *folder_name;
592 gboolean concat_folder_name = FALSE;
595 /* Should not happen */
599 /* convert parent folders to dots */
600 convert_parent_folders_to_dots (item_name);
602 folder_name = tny_folder_get_name (folder);
603 if (g_str_has_suffix (*item_name, folder_name)) {
604 gchar *offset = g_strrstr (*item_name, folder_name);
606 concat_folder_name = TRUE;
609 buffer = g_string_new ("");
611 buffer = g_string_append (buffer, *item_name);
612 if (concat_folder_name) {
613 buffer = g_string_append (buffer, folder_name);
616 g_object_unref (account);
618 *item_name = g_string_free (buffer, FALSE);
626 text_cell_data (GtkTreeViewColumn *column,
627 GtkCellRenderer *renderer,
628 GtkTreeModel *tree_model,
632 ModestFolderViewPrivate *priv;
633 GObject *rendobj = (GObject *) renderer;
635 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
636 GObject *instance = NULL;
637 gboolean use_markup = FALSE;
639 gtk_tree_model_get (tree_model, iter,
642 INSTANCE_COLUMN, &instance,
644 if (!fname || !instance)
647 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
648 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
650 gchar *item_name = NULL;
651 gint item_weight = 400;
653 if (type != TNY_FOLDER_TYPE_ROOT) {
658 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
659 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
662 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
663 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
665 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
668 /* Sometimes an special folder is reported by the server as
669 NORMAL, like some versions of Dovecot */
670 if (type == TNY_FOLDER_TYPE_NORMAL ||
671 type == TNY_FOLDER_TYPE_UNKNOWN) {
672 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
676 /* note: we cannot reliably get the counts from the
677 * tree model, we need to use explicit calls on
678 * tny_folder for some reason. Select the number to
679 * show: the unread or unsent messages. in case of
680 * outbox/drafts, show all */
681 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
682 (type == TNY_FOLDER_TYPE_OUTBOX) ||
683 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
684 number = tny_folder_get_all_count (TNY_FOLDER(instance));
687 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
691 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
692 item_name = g_strdup (fname);
699 /* Use bold font style if there are unread or unset messages */
701 if (priv->show_message_count) {
702 item_name = g_strdup_printf ("%s (%d)", fname, number);
704 item_name = g_strdup (fname);
708 item_name = g_strdup (fname);
713 } else if (TNY_IS_ACCOUNT (instance)) {
714 /* If it's a server account */
715 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
716 item_name = g_strdup (priv->local_account_name);
718 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
719 /* fname is only correct when the items are first
720 * added to the model, not when the account is
721 * changed later, so get the name from the account
723 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
726 item_name = g_strdup (fname);
732 if (type == TNY_FOLDER_TYPE_INBOX &&
733 !g_ascii_strcasecmp (fname, "Inbox")) {
735 item_name = g_strdup (_("mcen_me_folder_inbox"));
739 item_name = g_strdup ("unknown");
741 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
742 gboolean multiaccount;
744 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
745 /* Convert item_name to markup */
746 format_compact_style (&item_name, instance, priv->mailbox,
748 multiaccount, &use_markup);
751 if (item_name && item_weight) {
752 /* Set the name in the treeview cell: */
753 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
754 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
755 g_object_set (rendobj,
758 "foreground-set", TRUE,
759 "foreground-gdk", &(priv->active_color),
762 g_object_set (rendobj,
764 "foreground-set", FALSE,
766 "weight", item_weight,
770 /* Notify display name observers */
771 /* TODO: What listens for this signal, and how can it use only the new name? */
772 if (((GObject *) priv->cur_folder_store) == instance) {
773 g_signal_emit (G_OBJECT(self),
774 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
781 /* If it is a Memory card account, make sure that we have the correct name.
782 * This function will be trigerred again when the name has been retrieved: */
783 if (TNY_IS_STORE_ACCOUNT (instance) &&
784 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
786 /* Get the account name asynchronously: */
787 GetMmcAccountNameData *callback_data =
788 g_slice_new0(GetMmcAccountNameData);
789 callback_data->self = self;
791 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
793 callback_data->previous_name = g_strdup (name);
795 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
796 on_get_mmc_account_name, callback_data);
800 g_object_unref (G_OBJECT (instance));
806 messages_cell_data (GtkTreeViewColumn *column,
807 GtkCellRenderer *renderer,
808 GtkTreeModel *tree_model,
812 ModestFolderView *self;
813 ModestFolderViewPrivate *priv;
814 GObject *rendobj = (GObject *) renderer;
815 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
816 GObject *instance = NULL;
817 gchar *item_name = NULL;
819 gtk_tree_model_get (tree_model, iter,
821 INSTANCE_COLUMN, &instance,
826 self = MODEST_FOLDER_VIEW (data);
827 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
830 if (type != TNY_FOLDER_TYPE_ROOT) {
835 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
836 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
839 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
841 /* Sometimes an special folder is reported by the server as
842 NORMAL, like some versions of Dovecot */
843 if (type == TNY_FOLDER_TYPE_NORMAL ||
844 type == TNY_FOLDER_TYPE_UNKNOWN) {
845 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
849 /* note: we cannot reliably get the counts from the tree model, we need
850 * to use explicit calls on tny_folder for some reason.
852 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
853 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
854 (type == TNY_FOLDER_TYPE_OUTBOX) ||
855 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
856 number = tny_folder_get_all_count (TNY_FOLDER(instance));
859 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
863 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
865 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
866 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
872 item_name = g_strdup ("");
875 /* Set the name in the treeview cell: */
876 g_object_set (rendobj,"text", item_name, NULL);
884 g_object_unref (G_OBJECT (instance));
890 GdkPixbuf *pixbuf_open;
891 GdkPixbuf *pixbuf_close;
895 static inline GdkPixbuf *
896 get_composite_pixbuf (const gchar *icon_name,
898 GdkPixbuf *base_pixbuf)
900 GdkPixbuf *emblem, *retval = NULL;
902 emblem = modest_platform_get_icon (icon_name, size);
904 retval = gdk_pixbuf_copy (base_pixbuf);
905 gdk_pixbuf_composite (emblem, retval, 0, 0,
906 MIN (gdk_pixbuf_get_width (emblem),
907 gdk_pixbuf_get_width (retval)),
908 MIN (gdk_pixbuf_get_height (emblem),
909 gdk_pixbuf_get_height (retval)),
910 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
911 g_object_unref (emblem);
916 static inline ThreePixbufs *
917 get_composite_icons (const gchar *icon_code,
919 GdkPixbuf **pixbuf_open,
920 GdkPixbuf **pixbuf_close)
922 ThreePixbufs *retval;
926 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
928 *pixbuf = gdk_pixbuf_copy (icon);
934 if (!*pixbuf_open && pixbuf && *pixbuf)
935 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
939 if (!*pixbuf_close && pixbuf && *pixbuf)
940 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
944 retval = g_slice_new0 (ThreePixbufs);
946 retval->pixbuf = g_object_ref (*pixbuf);
948 retval->pixbuf_open = g_object_ref (*pixbuf_open);
950 retval->pixbuf_close = g_object_ref (*pixbuf_close);
955 static inline ThreePixbufs *
956 get_account_protocol_pixbufs (ModestFolderView *folder_view,
957 ModestProtocolType protocol_type,
960 ModestProtocol *protocol;
961 const GdkPixbuf *pixbuf = NULL;
962 ModestFolderViewPrivate *priv;
964 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
966 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
969 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
970 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
971 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
972 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
973 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
974 object, FOLDER_ICON_SIZE);
978 ThreePixbufs *retval;
979 retval = g_slice_new0 (ThreePixbufs);
980 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
981 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
982 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
989 static inline ThreePixbufs*
990 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
992 TnyAccount *account = NULL;
993 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
994 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
995 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
996 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
997 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
999 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1000 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1001 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1002 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1003 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1005 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1006 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1007 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1008 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1009 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1011 ThreePixbufs *retval = NULL;
1013 if (TNY_IS_ACCOUNT (instance)) {
1014 account = g_object_ref (instance);
1015 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1016 account = tny_folder_get_account (TNY_FOLDER (instance));
1020 ModestProtocolType account_store_protocol;
1022 account_store_protocol = modest_tny_account_get_protocol_type (account);
1023 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1024 g_object_unref (account);
1030 /* Sometimes an special folder is reported by the server as
1031 NORMAL, like some versions of Dovecot */
1032 if (type == TNY_FOLDER_TYPE_NORMAL ||
1033 type == TNY_FOLDER_TYPE_UNKNOWN) {
1034 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1037 /* It's not enough with check the folder type. We need to
1038 ensure that we're not giving a special folder icon to a
1039 normal folder with the same name than a special folder */
1040 if (TNY_IS_FOLDER (instance) &&
1041 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1042 type = TNY_FOLDER_TYPE_NORMAL;
1044 /* Remote folders should not be treated as special folders */
1045 if (TNY_IS_FOLDER_STORE (instance) &&
1046 !TNY_IS_ACCOUNT (instance) &&
1047 type != TNY_FOLDER_TYPE_INBOX &&
1048 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1049 #ifdef MODEST_TOOLKIT_HILDON2
1050 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1053 &anorm_pixbuf_close);
1055 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1057 &normal_pixbuf_open,
1058 &normal_pixbuf_close);
1064 case TNY_FOLDER_TYPE_INVALID:
1065 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1068 case TNY_FOLDER_TYPE_ROOT:
1069 if (TNY_IS_ACCOUNT (instance)) {
1071 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1072 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1075 &avirt_pixbuf_close);
1077 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1079 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1080 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1083 &ammc_pixbuf_close);
1085 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1088 &anorm_pixbuf_close);
1093 case TNY_FOLDER_TYPE_INBOX:
1094 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1097 &inbox_pixbuf_close);
1099 case TNY_FOLDER_TYPE_OUTBOX:
1100 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1102 &outbox_pixbuf_open,
1103 &outbox_pixbuf_close);
1105 case TNY_FOLDER_TYPE_JUNK:
1106 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1109 &junk_pixbuf_close);
1111 case TNY_FOLDER_TYPE_SENT:
1112 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1115 &sent_pixbuf_close);
1117 case TNY_FOLDER_TYPE_TRASH:
1118 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1121 &trash_pixbuf_close);
1123 case TNY_FOLDER_TYPE_DRAFTS:
1124 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1127 &draft_pixbuf_close);
1129 case TNY_FOLDER_TYPE_ARCHIVE:
1130 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1135 case TNY_FOLDER_TYPE_NORMAL:
1137 /* Memory card folders could have an special icon */
1138 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1139 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1144 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1146 &normal_pixbuf_open,
1147 &normal_pixbuf_close);
1156 free_pixbufs (ThreePixbufs *pixbufs)
1158 if (pixbufs->pixbuf)
1159 g_object_unref (pixbufs->pixbuf);
1160 if (pixbufs->pixbuf_open)
1161 g_object_unref (pixbufs->pixbuf_open);
1162 if (pixbufs->pixbuf_close)
1163 g_object_unref (pixbufs->pixbuf_close);
1164 g_slice_free (ThreePixbufs, pixbufs);
1168 icon_cell_data (GtkTreeViewColumn *column,
1169 GtkCellRenderer *renderer,
1170 GtkTreeModel *tree_model,
1174 GObject *rendobj = NULL, *instance = NULL;
1175 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1176 gboolean has_children;
1177 ThreePixbufs *pixbufs;
1178 ModestFolderView *folder_view = (ModestFolderView *) data;
1180 rendobj = (GObject *) renderer;
1182 gtk_tree_model_get (tree_model, iter,
1184 INSTANCE_COLUMN, &instance,
1190 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1191 pixbufs = get_folder_icons (folder_view, type, instance);
1192 g_object_unref (instance);
1195 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1198 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1199 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1202 free_pixbufs (pixbufs);
1206 add_columns (GtkWidget *treeview)
1208 GtkTreeViewColumn *column;
1209 GtkCellRenderer *renderer;
1210 GtkTreeSelection *sel;
1211 ModestFolderViewPrivate *priv;
1213 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1216 column = gtk_tree_view_column_new ();
1218 /* Set icon and text render function */
1219 renderer = gtk_cell_renderer_pixbuf_new();
1220 #ifdef MODEST_TOOLKIT_HILDON2
1221 g_object_set (renderer,
1222 "xpad", MODEST_MARGIN_DEFAULT,
1223 "ypad", MODEST_MARGIN_DEFAULT,
1226 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1227 gtk_tree_view_column_set_cell_data_func(column, renderer,
1228 icon_cell_data, treeview, NULL);
1230 renderer = gtk_cell_renderer_text_new();
1231 g_object_set (renderer,
1232 #ifdef MODEST_TOOLKIT_HILDON2
1233 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1234 "ypad", MODEST_MARGIN_DEFAULT,
1235 "xpad", MODEST_MARGIN_DEFAULT,
1237 "ellipsize", PANGO_ELLIPSIZE_END,
1239 "ellipsize-set", TRUE, NULL);
1240 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1241 gtk_tree_view_column_set_cell_data_func(column, renderer,
1242 text_cell_data, treeview, NULL);
1244 priv->messages_renderer = gtk_cell_renderer_text_new ();
1245 g_object_set (priv->messages_renderer,
1246 #ifdef MODEST_TOOLKIT_HILDON2
1248 "ypad", MODEST_MARGIN_DEFAULT,
1249 "xpad", MODEST_MARGIN_DOUBLE,
1251 "scale", PANGO_SCALE_X_SMALL,
1254 "alignment", PANGO_ALIGN_RIGHT,
1258 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1259 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1260 messages_cell_data, treeview, NULL);
1262 /* Set selection mode */
1263 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1264 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1266 /* Set treeview appearance */
1267 gtk_tree_view_column_set_spacing (column, 2);
1268 gtk_tree_view_column_set_resizable (column, TRUE);
1269 gtk_tree_view_column_set_fixed_width (column, TRUE);
1270 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1271 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1274 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1278 modest_folder_view_init (ModestFolderView *obj)
1280 ModestFolderViewPrivate *priv;
1283 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1285 priv->timer_expander = 0;
1286 priv->account_store = NULL;
1288 priv->do_refresh = TRUE;
1289 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1290 priv->cur_folder_store = NULL;
1291 priv->visible_account_id = NULL;
1292 priv->mailbox = NULL;
1293 priv->folder_to_select = NULL;
1294 priv->outbox_deleted_handler = 0;
1295 priv->reexpand = TRUE;
1296 priv->signal_handlers = 0;
1298 /* Initialize the local account name */
1299 conf = modest_runtime_get_conf();
1300 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1302 /* Init email clipboard */
1303 priv->clipboard = modest_runtime_get_email_clipboard ();
1304 priv->hidding_ids = NULL;
1305 priv->n_selected = 0;
1306 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1307 priv->reselect = FALSE;
1308 priv->show_non_move = TRUE;
1309 priv->list_to_move = NULL;
1310 priv->show_message_count = TRUE;
1312 /* Build treeview */
1313 add_columns (GTK_WIDGET (obj));
1315 /* Setup drag and drop */
1316 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1318 /* Connect signals */
1319 g_signal_connect (G_OBJECT (obj),
1321 G_CALLBACK (on_key_pressed), NULL);
1323 priv->display_name_changed_signal =
1324 g_signal_connect (modest_runtime_get_account_mgr (),
1325 "display_name_changed",
1326 G_CALLBACK (on_display_name_changed),
1330 * Track changes in the local account name (in the device it
1331 * will be the device name)
1333 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1335 G_CALLBACK(on_configuration_key_changed),
1338 gdk_color_parse ("000", &priv->active_color);
1341 g_signal_connect (G_OBJECT (obj), "notify::style",
1342 G_CALLBACK (on_notify_style), (gpointer) obj);
1346 tny_account_store_view_init (gpointer g, gpointer iface_data)
1348 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1350 klass->set_account_store = modest_folder_view_set_account_store;
1354 modest_folder_view_dispose (GObject *obj)
1356 ModestFolderViewPrivate *priv;
1358 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1360 #ifdef MODEST_TOOLKIT_HILDON2
1361 if (priv->signal_handlers) {
1362 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1363 priv->signal_handlers = NULL;
1367 /* Free external references */
1368 if (priv->account_store) {
1369 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1370 priv->account_inserted_signal);
1371 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1372 priv->account_removed_signal);
1373 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1374 priv->account_changed_signal);
1375 g_object_unref (G_OBJECT(priv->account_store));
1376 priv->account_store = NULL;
1380 g_object_unref (G_OBJECT (priv->query));
1384 if (priv->folder_to_select) {
1385 g_object_unref (G_OBJECT(priv->folder_to_select));
1386 priv->folder_to_select = NULL;
1389 if (priv->cur_folder_store) {
1390 g_object_unref (priv->cur_folder_store);
1391 priv->cur_folder_store = NULL;
1394 if (priv->list_to_move) {
1395 g_object_unref (priv->list_to_move);
1396 priv->list_to_move = NULL;
1399 G_OBJECT_CLASS(parent_class)->dispose (obj);
1403 modest_folder_view_finalize (GObject *obj)
1405 ModestFolderViewPrivate *priv;
1406 GtkTreeSelection *sel;
1407 TnyAccount *local_account;
1409 g_return_if_fail (obj);
1411 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1413 if (priv->timer_expander != 0) {
1414 g_source_remove (priv->timer_expander);
1415 priv->timer_expander = 0;
1418 local_account = (TnyAccount *)
1419 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1420 if (local_account) {
1421 if (g_signal_handler_is_connected (local_account,
1422 priv->outbox_deleted_handler))
1423 g_signal_handler_disconnect (local_account,
1424 priv->outbox_deleted_handler);
1425 g_object_unref (local_account);
1428 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1429 priv->display_name_changed_signal)) {
1430 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1431 priv->display_name_changed_signal);
1432 priv->display_name_changed_signal = 0;
1435 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1437 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1439 g_free (priv->local_account_name);
1440 g_free (priv->visible_account_id);
1441 g_free (priv->mailbox);
1443 if (priv->conf_key_signal) {
1444 g_signal_handler_disconnect (modest_runtime_get_conf (),
1445 priv->conf_key_signal);
1446 priv->conf_key_signal = 0;
1449 /* Clear hidding array created by cut operation */
1450 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1452 gdk_color_parse ("000", &priv->active_color);
1454 G_OBJECT_CLASS(parent_class)->finalize (obj);
1459 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1461 ModestFolderViewPrivate *priv;
1464 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1465 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1467 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1468 device = tny_account_store_get_device (account_store);
1470 if (G_UNLIKELY (priv->account_store)) {
1472 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1473 priv->account_inserted_signal))
1474 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1475 priv->account_inserted_signal);
1476 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1477 priv->account_removed_signal))
1478 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1479 priv->account_removed_signal);
1480 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1481 priv->account_changed_signal))
1482 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1483 priv->account_changed_signal);
1484 g_object_unref (G_OBJECT (priv->account_store));
1487 priv->account_store = g_object_ref (G_OBJECT (account_store));
1489 priv->account_removed_signal =
1490 g_signal_connect (G_OBJECT(account_store), "account_removed",
1491 G_CALLBACK (on_account_removed), self);
1493 priv->account_inserted_signal =
1494 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1495 G_CALLBACK (on_account_inserted), self);
1497 priv->account_changed_signal =
1498 g_signal_connect (G_OBJECT(account_store), "account_changed",
1499 G_CALLBACK (on_account_changed), self);
1501 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1502 priv->reselect = FALSE;
1503 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1505 g_object_unref (G_OBJECT (device));
1509 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1512 ModestFolderView *self;
1513 GtkTreeModel *model, *filter_model;
1516 self = MODEST_FOLDER_VIEW (user_data);
1518 if (!get_inner_models (self, &filter_model, NULL, &model))
1521 /* Remove outbox from model */
1522 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1523 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1524 g_object_unref (outbox);
1527 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1531 on_account_inserted (TnyAccountStore *account_store,
1532 TnyAccount *account,
1535 ModestFolderViewPrivate *priv;
1536 GtkTreeModel *model, *filter_model;
1538 /* Ignore transport account insertions, we're not showing them
1539 in the folder view */
1540 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1543 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1546 /* If we're adding a new account, and there is no previous
1547 one, we need to select the visible server account */
1548 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1549 !priv->visible_account_id)
1550 modest_widget_memory_restore (modest_runtime_get_conf(),
1551 G_OBJECT (user_data),
1552 MODEST_CONF_FOLDER_VIEW_KEY);
1556 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1557 &filter_model, NULL, &model))
1560 /* Insert the account in the model */
1561 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1563 /* When the model is a list store (plain representation) the
1564 outbox is not a child of any account so we have to manually
1565 delete it because removing the local folders account won't
1566 delete it (because tny_folder_get_account() is not defined
1567 for a merge folder */
1568 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1569 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1571 priv->outbox_deleted_handler =
1572 g_signal_connect (account,
1574 G_CALLBACK (on_outbox_deleted_cb),
1578 /* Refilter the model */
1579 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1584 same_account_selected (ModestFolderView *self,
1585 TnyAccount *account)
1587 ModestFolderViewPrivate *priv;
1588 gboolean same_account = FALSE;
1590 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1592 if (priv->cur_folder_store) {
1593 TnyAccount *selected_folder_account = NULL;
1595 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1596 selected_folder_account =
1597 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1599 selected_folder_account =
1600 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1603 if (selected_folder_account == account)
1604 same_account = TRUE;
1606 g_object_unref (selected_folder_account);
1608 return same_account;
1613 * Selects the first inbox or the local account in an idle
1616 on_idle_select_first_inbox_or_local (gpointer user_data)
1618 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1620 gdk_threads_enter ();
1621 modest_folder_view_select_first_inbox_or_local (self);
1622 gdk_threads_leave ();
1628 on_account_changed (TnyAccountStore *account_store,
1629 TnyAccount *tny_account,
1632 ModestFolderView *self;
1633 ModestFolderViewPrivate *priv;
1634 GtkTreeModel *model, *filter_model;
1635 GtkTreeSelection *sel;
1636 gboolean same_account;
1638 /* Ignore transport account insertions, we're not showing them
1639 in the folder view */
1640 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1643 self = MODEST_FOLDER_VIEW (user_data);
1644 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1646 /* Get the inner model */
1647 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1648 &filter_model, NULL, &model))
1651 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1653 /* Invalidate the cur_folder_store only if the selected folder
1654 belongs to the account that is being removed */
1655 same_account = same_account_selected (self, tny_account);
1657 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1658 gtk_tree_selection_unselect_all (sel);
1661 /* Remove the account from the model */
1662 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1664 /* Insert the account in the model */
1665 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1667 /* Refilter the model */
1668 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1670 /* Select the first INBOX if the currently selected folder
1671 belongs to the account that is being deleted */
1672 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1673 g_idle_add (on_idle_select_first_inbox_or_local, self);
1677 on_account_removed (TnyAccountStore *account_store,
1678 TnyAccount *account,
1681 ModestFolderView *self = NULL;
1682 ModestFolderViewPrivate *priv;
1683 GtkTreeModel *model, *filter_model;
1684 GtkTreeSelection *sel = NULL;
1685 gboolean same_account = FALSE;
1687 /* Ignore transport account removals, we're not showing them
1688 in the folder view */
1689 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1692 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1693 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1697 self = MODEST_FOLDER_VIEW (user_data);
1698 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1700 /* Invalidate the cur_folder_store only if the selected folder
1701 belongs to the account that is being removed */
1702 same_account = same_account_selected (self, account);
1704 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1705 gtk_tree_selection_unselect_all (sel);
1708 /* Invalidate row to select only if the folder to select
1709 belongs to the account that is being removed*/
1710 if (priv->folder_to_select) {
1711 TnyAccount *folder_to_select_account = NULL;
1713 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1714 if (folder_to_select_account == account) {
1715 modest_folder_view_disable_next_folder_selection (self);
1716 g_object_unref (priv->folder_to_select);
1717 priv->folder_to_select = NULL;
1719 g_object_unref (folder_to_select_account);
1722 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1723 &filter_model, NULL, &model))
1726 /* Disconnect the signal handler */
1727 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1728 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1729 if (g_signal_handler_is_connected (account,
1730 priv->outbox_deleted_handler))
1731 g_signal_handler_disconnect (account,
1732 priv->outbox_deleted_handler);
1735 /* Remove the account from the model */
1736 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1738 /* If the removed account is the currently viewed one then
1739 clear the configuration value. The new visible account will be the default account */
1740 if (priv->visible_account_id &&
1741 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1743 /* Clear the current visible account_id */
1744 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1745 modest_folder_view_set_mailbox (self, NULL);
1747 /* Call the restore method, this will set the new visible account */
1748 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1749 MODEST_CONF_FOLDER_VIEW_KEY);
1752 /* Refilter the model */
1753 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1755 /* Select the first INBOX if the currently selected folder
1756 belongs to the account that is being deleted */
1758 g_idle_add (on_idle_select_first_inbox_or_local, self);
1762 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1764 GtkTreeViewColumn *col;
1766 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1768 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1770 g_printerr ("modest: failed get column for title\n");
1774 gtk_tree_view_column_set_title (col, title);
1775 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1780 modest_folder_view_on_map (ModestFolderView *self,
1781 GdkEventExpose *event,
1784 ModestFolderViewPrivate *priv;
1786 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1788 /* This won't happen often */
1789 if (G_UNLIKELY (priv->reselect)) {
1790 /* Select the first inbox or the local account if not found */
1792 /* TODO: this could cause a lock at startup, so we
1793 comment it for the moment. We know that this will
1794 be a bug, because the INBOX is not selected, but we
1795 need to rewrite some parts of Modest to avoid the
1796 deathlock situation */
1797 /* TODO: check if this is still the case */
1798 priv->reselect = FALSE;
1799 modest_folder_view_select_first_inbox_or_local (self);
1800 /* Notify the display name observers */
1801 g_signal_emit (G_OBJECT(self),
1802 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1806 if (priv->reexpand) {
1807 expand_root_items (self);
1808 priv->reexpand = FALSE;
1815 modest_folder_view_new (TnyFolderStoreQuery *query)
1817 return modest_folder_view_new_full (query, TRUE);
1821 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1824 ModestFolderViewPrivate *priv;
1825 GtkTreeSelection *sel;
1827 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1828 #ifdef MODEST_TOOLKIT_HILDON2
1829 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1832 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1835 priv->query = g_object_ref (query);
1837 priv->do_refresh = do_refresh;
1839 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1840 priv->changed_signal = g_signal_connect (sel, "changed",
1841 G_CALLBACK (on_selection_changed), self);
1843 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1845 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1847 return GTK_WIDGET(self);
1850 /* this feels dirty; any other way to expand all the root items? */
1852 expand_root_items (ModestFolderView *self)
1855 GtkTreeModel *model;
1858 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1859 path = gtk_tree_path_new_first ();
1861 /* all folders should have child items, so.. */
1863 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1864 gtk_tree_path_next (path);
1865 } while (gtk_tree_model_get_iter (model, &iter, path));
1867 gtk_tree_path_free (path);
1871 is_parent_of (TnyFolder *a, TnyFolder *b)
1874 gboolean retval = FALSE;
1876 a_id = tny_folder_get_id (a);
1878 gchar *string_to_match;
1881 string_to_match = g_strconcat (a_id, "/", NULL);
1882 b_id = tny_folder_get_id (b);
1883 retval = g_str_has_prefix (b_id, string_to_match);
1884 g_free (string_to_match);
1890 typedef struct _ForeachFolderInfo {
1893 } ForeachFolderInfo;
1896 foreach_folder_with_id (GtkTreeModel *model,
1901 ForeachFolderInfo *info;
1904 info = (ForeachFolderInfo *) data;
1905 gtk_tree_model_get (model, iter,
1906 INSTANCE_COLUMN, &instance,
1909 if (TNY_IS_FOLDER (instance)) {
1912 id = tny_folder_get_id (TNY_FOLDER (instance));
1914 collate = g_utf8_collate_key (id, -1);
1915 info->found = !strcmp (info->needle, collate);
1921 g_object_unref (instance);
1929 has_folder_with_id (ModestFolderView *self, const gchar *id)
1931 GtkTreeModel *model;
1932 ForeachFolderInfo info = {NULL, FALSE};
1934 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1935 info.needle = g_utf8_collate_key (id, -1);
1937 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1938 g_free (info.needle);
1944 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1947 gboolean retval = FALSE;
1949 a_id = tny_folder_get_id (a);
1952 b_id = tny_folder_get_id (b);
1955 const gchar *last_bar;
1956 gchar *string_to_match;
1957 last_bar = g_strrstr (b_id, "/");
1962 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1963 retval = has_folder_with_id (self, string_to_match);
1964 g_free (string_to_match);
1972 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1974 ModestFolderViewPrivate *priv;
1975 TnyIterator *iterator;
1976 gboolean retval = TRUE;
1978 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1979 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1981 for (iterator = tny_list_create_iterator (priv->list_to_move);
1982 retval && !tny_iterator_is_done (iterator);
1983 tny_iterator_next (iterator)) {
1985 instance = tny_iterator_get_current (iterator);
1986 if (instance == (GObject *) folder) {
1988 } else if (TNY_IS_FOLDER (instance)) {
1989 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1991 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1994 g_object_unref (instance);
1996 g_object_unref (iterator);
2003 * We use this function to implement the
2004 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
2005 * account in this case, and the local folders.
2008 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2010 ModestFolderViewPrivate *priv;
2011 gboolean retval = TRUE;
2012 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2013 GObject *instance = NULL;
2014 const gchar *id = NULL;
2016 gboolean found = FALSE;
2017 gboolean cleared = FALSE;
2018 ModestTnyFolderRules rules = 0;
2021 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2022 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2024 gtk_tree_model_get (model, iter,
2025 NAME_COLUMN, &fname,
2027 INSTANCE_COLUMN, &instance,
2030 /* Do not show if there is no instance, this could indeed
2031 happen when the model is being modified while it's being
2032 drawn. This could occur for example when moving folders
2039 if (TNY_IS_ACCOUNT (instance)) {
2040 TnyAccount *acc = TNY_ACCOUNT (instance);
2041 const gchar *account_id = tny_account_get_id (acc);
2043 /* If it isn't a special folder,
2044 * don't show it unless it is the visible account: */
2045 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2046 !modest_tny_account_is_virtual_local_folders (acc) &&
2047 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))
2058 /* Never show these to the user. They are merged into one folder
2059 * in the local-folders account instead: */
2060 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2063 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2064 /* Only show special folders for current account if needed */
2065 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2066 TnyAccount *account;
2068 account = tny_folder_get_account (TNY_FOLDER (instance));
2070 if (TNY_IS_ACCOUNT (account)) {
2071 const gchar *account_id = tny_account_get_id (account);
2073 if (!modest_tny_account_is_virtual_local_folders (account) &&
2074 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2075 /* Show only the visible account id */
2076 if (priv->visible_account_id) {
2077 if (strcmp (account_id, priv->visible_account_id)) {
2079 } else if (priv->mailbox) {
2080 /* Filter mailboxes */
2081 if (!g_str_has_prefix (fname, priv->mailbox)) {
2083 } else if (!strcmp (fname, priv->mailbox)) {
2084 /* Hide mailbox parent */
2090 g_object_unref (account);
2097 /* Check hiding (if necessary) */
2098 cleared = modest_email_clipboard_cleared (priv->clipboard);
2099 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2100 id = tny_folder_get_id (TNY_FOLDER(instance));
2101 if (priv->hidding_ids != NULL)
2102 for (i=0; i < priv->n_selected && !found; i++)
2103 if (priv->hidding_ids[i] != NULL && id != NULL)
2104 found = (!strcmp (priv->hidding_ids[i], id));
2109 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2110 folder as no message can be move there according to UI specs */
2111 if (retval && !priv->show_non_move) {
2112 if (priv->list_to_move &&
2113 tny_list_get_length (priv->list_to_move) > 0 &&
2114 TNY_IS_FOLDER (instance)) {
2115 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2117 if (retval && TNY_IS_FOLDER (instance) &&
2118 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2120 case TNY_FOLDER_TYPE_OUTBOX:
2121 case TNY_FOLDER_TYPE_SENT:
2122 case TNY_FOLDER_TYPE_DRAFTS:
2125 case TNY_FOLDER_TYPE_UNKNOWN:
2126 case TNY_FOLDER_TYPE_NORMAL:
2127 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2128 if (type == TNY_FOLDER_TYPE_INVALID)
2129 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2131 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2132 type == TNY_FOLDER_TYPE_SENT
2133 || type == TNY_FOLDER_TYPE_DRAFTS)
2140 if (retval && TNY_IS_ACCOUNT (instance) &&
2141 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2142 ModestProtocolType protocol_type;
2144 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2145 retval = !modest_protocol_registry_protocol_type_has_tag
2146 (modest_runtime_get_protocol_registry (),
2148 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2152 /* apply special filters */
2153 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2154 if (TNY_IS_ACCOUNT (instance))
2158 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2159 if (TNY_IS_FOLDER (instance))
2163 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2164 if (TNY_IS_ACCOUNT (instance)) {
2165 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2167 } else if (TNY_IS_FOLDER (instance)) {
2168 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2173 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2174 if (TNY_IS_ACCOUNT (instance)) {
2175 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2177 } else if (TNY_IS_FOLDER (instance)) {
2178 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2183 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2184 /* A mailbox is a fake folder with an @ in the middle of the name */
2185 if (!TNY_IS_FOLDER (instance) ||
2186 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2189 const gchar *folder_name;
2190 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2191 if (!folder_name || strchr (folder_name, '@') == NULL)
2197 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2198 if (TNY_IS_FOLDER (instance)) {
2199 /* Check folder rules */
2200 ModestTnyFolderRules rules;
2202 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2203 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2204 } else if (TNY_IS_ACCOUNT (instance)) {
2205 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2213 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2214 if (TNY_IS_FOLDER (instance)) {
2215 TnyFolderType guess_type;
2217 if (TNY_FOLDER_TYPE_NORMAL) {
2218 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2224 case TNY_FOLDER_TYPE_OUTBOX:
2225 case TNY_FOLDER_TYPE_SENT:
2226 case TNY_FOLDER_TYPE_DRAFTS:
2227 case TNY_FOLDER_TYPE_ARCHIVE:
2228 case TNY_FOLDER_TYPE_INBOX:
2231 case TNY_FOLDER_TYPE_UNKNOWN:
2232 case TNY_FOLDER_TYPE_NORMAL:
2238 } else if (TNY_IS_ACCOUNT (instance)) {
2243 if (retval && TNY_IS_FOLDER (instance)) {
2244 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2247 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2248 if (TNY_IS_FOLDER (instance)) {
2249 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2250 } else if (TNY_IS_ACCOUNT (instance)) {
2255 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2256 if (TNY_IS_FOLDER (instance)) {
2257 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2258 } else if (TNY_IS_ACCOUNT (instance)) {
2263 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2264 if (TNY_IS_FOLDER (instance)) {
2265 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2266 } else if (TNY_IS_ACCOUNT (instance)) {
2272 g_object_unref (instance);
2280 modest_folder_view_update_model (ModestFolderView *self,
2281 TnyAccountStore *account_store)
2283 ModestFolderViewPrivate *priv;
2284 GtkTreeModel *model;
2285 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2287 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2288 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2291 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2293 /* Notify that there is no folder selected */
2294 g_signal_emit (G_OBJECT(self),
2295 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2297 if (priv->cur_folder_store) {
2298 g_object_unref (priv->cur_folder_store);
2299 priv->cur_folder_store = NULL;
2302 /* FIXME: the local accounts are not shown when the query
2303 selects only the subscribed folders */
2304 #ifdef MODEST_TOOLKIT_HILDON2
2305 TnyGtkFolderListStoreFlags flags;
2306 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2307 if (priv->do_refresh)
2308 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2310 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2311 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2313 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2314 MODEST_FOLDER_PATH_SEPARATOR);
2316 model = tny_gtk_folder_store_tree_model_new (NULL);
2319 /* When the model is a list store (plain representation) the
2320 outbox is not a child of any account so we have to manually
2321 delete it because removing the local folders account won't
2322 delete it (because tny_folder_get_account() is not defined
2323 for a merge folder */
2324 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2325 TnyAccount *account;
2326 ModestTnyAccountStore *acc_store;
2328 acc_store = modest_runtime_get_account_store ();
2329 account = modest_tny_account_store_get_local_folders_account (acc_store);
2331 if (g_signal_handler_is_connected (account,
2332 priv->outbox_deleted_handler))
2333 g_signal_handler_disconnect (account,
2334 priv->outbox_deleted_handler);
2336 priv->outbox_deleted_handler =
2337 g_signal_connect (account,
2339 G_CALLBACK (on_outbox_deleted_cb),
2341 g_object_unref (account);
2344 /* Get the accounts: */
2345 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2347 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2349 sortable = gtk_tree_model_sort_new_with_model (model);
2350 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2352 GTK_SORT_ASCENDING);
2353 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2355 cmp_rows, NULL, NULL);
2357 /* Create filter model */
2358 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2359 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2364 GtkTreeModel *old_tny_model;
2365 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2366 if (priv->signal_handlers > 0) {
2367 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2368 G_OBJECT (old_tny_model),
2369 "activity-changed");
2374 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2375 #ifndef MODEST_TOOLKIT_HILDON2
2376 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2377 (GCallback) on_row_inserted_maybe_select_folder, self);
2380 #ifdef MODEST_TOOLKIT_HILDON2
2381 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2384 G_CALLBACK (on_activity_changed),
2388 g_object_unref (model);
2389 g_object_unref (filter_model);
2390 g_object_unref (sortable);
2392 /* Force a reselection of the INBOX next time the widget is shown */
2393 priv->reselect = TRUE;
2400 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2402 GtkTreeModel *model = NULL;
2403 TnyFolderStore *folder = NULL;
2405 ModestFolderView *tree_view = NULL;
2406 ModestFolderViewPrivate *priv = NULL;
2407 gboolean selected = FALSE;
2409 g_return_if_fail (sel);
2410 g_return_if_fail (user_data);
2412 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2414 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2416 tree_view = MODEST_FOLDER_VIEW (user_data);
2419 gtk_tree_model_get (model, &iter,
2420 INSTANCE_COLUMN, &folder,
2423 /* If the folder is the same do not notify */
2424 if (folder && priv->cur_folder_store == folder) {
2425 g_object_unref (folder);
2430 /* Current folder was unselected */
2431 if (priv->cur_folder_store) {
2432 /* We must do this firstly because a libtinymail-camel
2433 implementation detail. If we issue the signal
2434 before doing the sync_async, then that signal could
2435 cause (and it actually does it) a free of the
2436 summary of the folder (because the main window will
2437 clear the headers view */
2438 #ifndef MODEST_TOOLKIT_HILDON2
2439 if (TNY_IS_FOLDER(priv->cur_folder_store))
2440 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2441 FALSE, NULL, NULL, NULL);
2444 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2445 priv->cur_folder_store, FALSE);
2447 g_object_unref (priv->cur_folder_store);
2448 priv->cur_folder_store = NULL;
2451 /* New current references */
2452 priv->cur_folder_store = folder;
2454 /* New folder has been selected. Do not notify if there is
2455 nothing new selected */
2457 g_signal_emit (G_OBJECT(tree_view),
2458 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2459 0, priv->cur_folder_store, TRUE);
2464 on_row_activated (GtkTreeView *treeview,
2465 GtkTreePath *treepath,
2466 GtkTreeViewColumn *column,
2469 GtkTreeModel *model = NULL;
2470 TnyFolderStore *folder = NULL;
2472 ModestFolderView *self = NULL;
2473 ModestFolderViewPrivate *priv = NULL;
2475 g_return_if_fail (treeview);
2476 g_return_if_fail (user_data);
2478 self = MODEST_FOLDER_VIEW (user_data);
2479 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2481 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2483 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2486 gtk_tree_model_get (model, &iter,
2487 INSTANCE_COLUMN, &folder,
2490 g_signal_emit (G_OBJECT(self),
2491 signals[FOLDER_ACTIVATED_SIGNAL],
2494 #ifdef MODEST_TOOLKIT_HILDON2
2495 HildonUIMode ui_mode;
2496 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2497 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2498 if (priv->cur_folder_store)
2499 g_object_unref (priv->cur_folder_store);
2500 priv->cur_folder_store = g_object_ref (folder);
2504 g_object_unref (folder);
2508 modest_folder_view_get_selected (ModestFolderView *self)
2510 ModestFolderViewPrivate *priv;
2512 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2514 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2515 if (priv->cur_folder_store)
2516 g_object_ref (priv->cur_folder_store);
2518 return priv->cur_folder_store;
2522 get_cmp_rows_type_pos (GObject *folder)
2524 /* Remote accounts -> Local account -> MMC account .*/
2527 if (TNY_IS_ACCOUNT (folder) &&
2528 modest_tny_account_is_virtual_local_folders (
2529 TNY_ACCOUNT (folder))) {
2531 } else if (TNY_IS_ACCOUNT (folder)) {
2532 TnyAccount *account = TNY_ACCOUNT (folder);
2533 const gchar *account_id = tny_account_get_id (account);
2534 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2540 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2541 return -1; /* Should never happen */
2546 inbox_is_special (TnyFolderStore *folder_store)
2548 gboolean is_special = TRUE;
2550 if (TNY_IS_FOLDER (folder_store)) {
2554 gchar *last_inbox_bar;
2556 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2557 downcase = g_utf8_strdown (id, -1);
2558 last_bar = g_strrstr (downcase, "/");
2560 last_inbox_bar = g_strrstr (downcase, "inbox/");
2561 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2572 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2574 TnyAccount *account;
2575 gboolean is_special;
2576 /* Inbox, Outbox, Drafts, Sent, User */
2579 if (!TNY_IS_FOLDER (folder_store))
2582 case TNY_FOLDER_TYPE_INBOX:
2584 account = tny_folder_get_account (folder_store);
2585 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2587 /* In inbox case we need to know if the inbox is really the top
2588 * inbox of the account, or if it's a submailbox inbox. To do
2589 * this we'll apply an heuristic rule: Find last "/" and check
2590 * if it's preceeded by another Inbox */
2591 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2592 g_object_unref (account);
2593 return is_special?0:4;
2596 case TNY_FOLDER_TYPE_OUTBOX:
2597 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2599 case TNY_FOLDER_TYPE_DRAFTS:
2601 account = tny_folder_get_account (folder_store);
2602 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2603 g_object_unref (account);
2604 return is_special?1:4;
2607 case TNY_FOLDER_TYPE_SENT:
2609 account = tny_folder_get_account (folder_store);
2610 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2611 g_object_unref (account);
2612 return is_special?3:4;
2621 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2623 const gchar *a1_name, *a2_name;
2625 a1_name = tny_account_get_name (a1);
2626 a2_name = tny_account_get_name (a2);
2628 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2632 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2634 TnyAccount *a1 = NULL, *a2 = NULL;
2637 if (TNY_IS_ACCOUNT (s1)) {
2638 a1 = TNY_ACCOUNT (g_object_ref (s1));
2639 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2640 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2643 if (TNY_IS_ACCOUNT (s2)) {
2644 a2 = TNY_ACCOUNT (g_object_ref (s2));
2645 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2646 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2663 /* First we sort with the type of account */
2664 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2668 cmp = compare_account_names (a1, a2);
2672 g_object_unref (a1);
2674 g_object_unref (a2);
2680 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2682 gint is_account1, is_account2;
2684 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2685 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2687 return is_account2 - is_account1;
2691 compare_folders (const gchar *name1, const gchar *name2)
2693 const gchar *separator1, *separator2;
2694 const gchar *next1, *next2;
2698 if (name1 == NULL || name1[0] == '\0')
2700 if (name2 == NULL || name2[0] == '\0')
2703 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2705 top1 = g_strndup (name1, separator1 - name1);
2707 top1 = g_strdup (name1);
2710 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2712 top2 = g_strndup (name2, separator2 - name2);
2714 top2 = g_strdup (name2);
2718 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2725 if (separator1 == NULL && separator2 == NULL)
2728 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2729 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2731 return compare_folders (next1, next2);
2736 * This function orders the mail accounts according to these rules:
2737 * 1st - remote accounts
2738 * 2nd - local account
2742 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2746 gchar *name1 = NULL;
2747 gchar *name2 = NULL;
2748 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2749 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2750 GObject *folder1 = NULL;
2751 GObject *folder2 = NULL;
2753 gtk_tree_model_get (tree_model, iter1,
2754 NAME_COLUMN, &name1,
2756 INSTANCE_COLUMN, &folder1,
2758 gtk_tree_model_get (tree_model, iter2,
2759 NAME_COLUMN, &name2,
2760 TYPE_COLUMN, &type2,
2761 INSTANCE_COLUMN, &folder2,
2764 /* Return if we get no folder. This could happen when folder
2765 operations are happening. The model is updated after the
2766 folder copy/move actually occurs, so there could be
2767 situations where the model to be drawn is not correct */
2768 if (!folder1 || !folder2)
2771 /* Sort by type. First the special folders, then the archives */
2772 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2776 /* Now we sort using the account of each folder */
2777 if (TNY_IS_FOLDER_STORE (folder1) &&
2778 TNY_IS_FOLDER_STORE (folder2)) {
2779 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2783 /* Each group is preceeded by its account */
2784 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2789 /* Pure sort by name */
2790 cmp = compare_folders (name1, name2);
2793 g_object_unref(G_OBJECT(folder1));
2795 g_object_unref(G_OBJECT(folder2));
2803 /*****************************************************************************/
2804 /* DRAG and DROP stuff */
2805 /*****************************************************************************/
2807 * This function fills the #GtkSelectionData with the row and the
2808 * model that has been dragged. It's called when this widget is a
2809 * source for dnd after the event drop happened
2812 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2813 guint info, guint time, gpointer data)
2815 GtkTreeSelection *selection;
2816 GtkTreeModel *model;
2818 GtkTreePath *source_row;
2820 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2821 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2823 source_row = gtk_tree_model_get_path (model, &iter);
2824 gtk_tree_set_row_drag_data (selection_data,
2828 gtk_tree_path_free (source_row);
2832 typedef struct _DndHelper {
2833 ModestFolderView *folder_view;
2834 gboolean delete_source;
2835 GtkTreePath *source_row;
2839 dnd_helper_destroyer (DndHelper *helper)
2841 /* Free the helper */
2842 gtk_tree_path_free (helper->source_row);
2843 g_slice_free (DndHelper, helper);
2847 xfer_folder_cb (ModestMailOperation *mail_op,
2848 TnyFolder *new_folder,
2852 /* Select the folder */
2853 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2859 /* get the folder for the row the treepath refers to. */
2860 /* folder must be unref'd */
2861 static TnyFolderStore *
2862 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2865 TnyFolderStore *folder = NULL;
2867 if (gtk_tree_model_get_iter (model,&iter, path))
2868 gtk_tree_model_get (model, &iter,
2869 INSTANCE_COLUMN, &folder,
2876 * This function is used by drag_data_received_cb to manage drag and
2877 * drop of a header, i.e, and drag from the header view to the folder
2881 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2882 GtkTreeModel *dest_model,
2883 GtkTreePath *dest_row,
2884 GtkSelectionData *selection_data)
2886 TnyList *headers = NULL;
2887 TnyFolder *folder = NULL, *src_folder = NULL;
2888 TnyFolderType folder_type;
2889 GtkTreeIter source_iter, dest_iter;
2890 ModestWindowMgr *mgr = NULL;
2891 ModestWindow *main_win = NULL;
2892 gchar **uris, **tmp;
2894 /* Build the list of headers */
2895 mgr = modest_runtime_get_window_mgr ();
2896 headers = tny_simple_list_new ();
2897 uris = modest_dnd_selection_data_get_paths (selection_data);
2900 while (*tmp != NULL) {
2903 gboolean first = TRUE;
2906 path = gtk_tree_path_new_from_string (*tmp);
2907 gtk_tree_model_get_iter (source_model, &source_iter, path);
2908 gtk_tree_model_get (source_model, &source_iter,
2909 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2912 /* Do not enable d&d of headers already opened */
2913 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2914 tny_list_append (headers, G_OBJECT (header));
2916 if (G_UNLIKELY (first)) {
2917 src_folder = tny_header_get_folder (header);
2921 /* Free and go on */
2922 gtk_tree_path_free (path);
2923 g_object_unref (header);
2928 /* This could happen ig we perform a d&d very quickly over the
2929 same row that row could dissapear because message is
2931 if (!TNY_IS_FOLDER (src_folder))
2934 /* Get the target folder */
2935 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2936 gtk_tree_model_get (dest_model, &dest_iter,
2940 if (!folder || !TNY_IS_FOLDER(folder)) {
2941 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2945 folder_type = modest_tny_folder_guess_folder_type (folder);
2946 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2947 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2948 goto cleanup; /* cannot move messages there */
2951 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2952 /* g_warning ("folder not writable"); */
2953 goto cleanup; /* verboten! */
2956 /* Ask for confirmation to move */
2957 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2959 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2963 /* Transfer messages */
2964 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2969 if (G_IS_OBJECT (src_folder))
2970 g_object_unref (src_folder);
2971 if (G_IS_OBJECT(folder))
2972 g_object_unref (G_OBJECT (folder));
2973 if (G_IS_OBJECT(headers))
2974 g_object_unref (headers);
2978 TnyFolderStore *src_folder;
2979 TnyFolderStore *dst_folder;
2980 ModestFolderView *folder_view;
2985 dnd_folder_info_destroyer (DndFolderInfo *info)
2987 if (info->src_folder)
2988 g_object_unref (info->src_folder);
2989 if (info->dst_folder)
2990 g_object_unref (info->dst_folder);
2991 g_slice_free (DndFolderInfo, info);
2995 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2996 GtkWindow *parent_window,
2997 TnyAccount *account)
3000 modest_ui_actions_on_account_connection_error (parent_window, account);
3002 /* Free the helper & info */
3003 dnd_helper_destroyer (info->helper);
3004 dnd_folder_info_destroyer (info);
3008 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
3010 GtkWindow *parent_window,
3011 TnyAccount *account,
3014 DndFolderInfo *info = NULL;
3015 ModestMailOperation *mail_op;
3017 info = (DndFolderInfo *) user_data;
3019 if (err || canceled) {
3020 dnd_on_connection_failed_destroyer (info, parent_window, account);
3024 /* Do the mail operation */
3025 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
3026 modest_ui_actions_move_folder_error_handler,
3027 info->src_folder, NULL);
3029 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3032 /* Transfer the folder */
3033 modest_mail_operation_xfer_folder (mail_op,
3034 TNY_FOLDER (info->src_folder),
3036 info->helper->delete_source,
3038 info->helper->folder_view);
3041 g_object_unref (G_OBJECT (mail_op));
3042 dnd_helper_destroyer (info->helper);
3043 dnd_folder_info_destroyer (info);
3048 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3050 GtkWindow *parent_window,
3051 TnyAccount *account,
3054 DndFolderInfo *info = NULL;
3056 info = (DndFolderInfo *) user_data;
3058 if (err || canceled) {
3059 dnd_on_connection_failed_destroyer (info, parent_window, account);
3063 /* Connect to source folder and perform the copy/move */
3064 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3066 drag_and_drop_from_folder_view_src_folder_performer,
3071 * This function is used by drag_data_received_cb to manage drag and
3072 * drop of a folder, i.e, and drag from the folder view to the same
3076 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3077 GtkTreeModel *dest_model,
3078 GtkTreePath *dest_row,
3079 GtkSelectionData *selection_data,
3082 GtkTreeIter dest_iter, iter;
3083 TnyFolderStore *dest_folder = NULL;
3084 TnyFolderStore *folder = NULL;
3085 gboolean forbidden = FALSE;
3087 DndFolderInfo *info = NULL;
3089 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3091 g_warning ("%s: BUG: no main window", __FUNCTION__);
3092 dnd_helper_destroyer (helper);
3097 /* check the folder rules for the destination */
3098 folder = tree_path_to_folder (dest_model, dest_row);
3099 if (TNY_IS_FOLDER(folder)) {
3100 ModestTnyFolderRules rules =
3101 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3102 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3103 } else if (TNY_IS_FOLDER_STORE(folder)) {
3104 /* enable local root as destination for folders */
3105 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3106 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3109 g_object_unref (folder);
3112 /* check the folder rules for the source */
3113 folder = tree_path_to_folder (source_model, helper->source_row);
3114 if (TNY_IS_FOLDER(folder)) {
3115 ModestTnyFolderRules rules =
3116 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3117 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3120 g_object_unref (folder);
3124 /* Check if the drag is possible */
3125 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3127 modest_platform_run_information_dialog ((GtkWindow *) win,
3128 _("mail_in_ui_folder_move_target_error"),
3130 /* Restore the previous selection */
3131 folder = tree_path_to_folder (source_model, helper->source_row);
3133 if (TNY_IS_FOLDER (folder))
3134 modest_folder_view_select_folder (helper->folder_view,
3135 TNY_FOLDER (folder), FALSE);
3136 g_object_unref (folder);
3138 dnd_helper_destroyer (helper);
3143 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3144 gtk_tree_model_get (dest_model, &dest_iter,
3147 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3148 gtk_tree_model_get (source_model, &iter,
3152 /* Create the info for the performer */
3153 info = g_slice_new0 (DndFolderInfo);
3154 info->src_folder = g_object_ref (folder);
3155 info->dst_folder = g_object_ref (dest_folder);
3156 info->helper = helper;
3158 /* Connect to the destination folder and perform the copy/move */
3159 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3161 drag_and_drop_from_folder_view_dst_folder_performer,
3165 g_object_unref (dest_folder);
3166 g_object_unref (folder);
3170 * This function receives the data set by the "drag-data-get" signal
3171 * handler. This information comes within the #GtkSelectionData. This
3172 * function will manage both the drags of folders of the treeview and
3173 * drags of headers of the header view widget.
3176 on_drag_data_received (GtkWidget *widget,
3177 GdkDragContext *context,
3180 GtkSelectionData *selection_data,
3185 GtkWidget *source_widget;
3186 GtkTreeModel *dest_model, *source_model;
3187 GtkTreePath *source_row, *dest_row;
3188 GtkTreeViewDropPosition pos;
3189 gboolean delete_source = FALSE;
3190 gboolean success = FALSE;
3192 /* Do not allow further process */
3193 g_signal_stop_emission_by_name (widget, "drag-data-received");
3194 source_widget = gtk_drag_get_source_widget (context);
3196 /* Get the action */
3197 if (context->action == GDK_ACTION_MOVE) {
3198 delete_source = TRUE;
3200 /* Notify that there is no folder selected. We need to
3201 do this in order to update the headers view (and
3202 its monitors, because when moving, the old folder
3203 won't longer exist. We can not wait for the end of
3204 the operation, because the operation won't start if
3205 the folder is in use */
3206 if (source_widget == widget) {
3207 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3208 gtk_tree_selection_unselect_all (sel);
3212 /* Check if the get_data failed */
3213 if (selection_data == NULL || selection_data->length < 0)
3216 /* Select the destination model */
3217 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3219 /* Get the path to the destination row. Can not call
3220 gtk_tree_view_get_drag_dest_row() because the source row
3221 is not selected anymore */
3222 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3225 /* Only allow drops IN other rows */
3227 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3228 pos == GTK_TREE_VIEW_DROP_AFTER)
3232 /* Drags from the header view */
3233 if (source_widget != widget) {
3234 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3236 drag_and_drop_from_header_view (source_model,
3241 DndHelper *helper = NULL;
3243 /* Get the source model and row */
3244 gtk_tree_get_row_drag_data (selection_data,
3248 /* Create the helper */
3249 helper = g_slice_new0 (DndHelper);
3250 helper->delete_source = delete_source;
3251 helper->source_row = gtk_tree_path_copy (source_row);
3252 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3254 drag_and_drop_from_folder_view (source_model,
3260 gtk_tree_path_free (source_row);
3264 gtk_tree_path_free (dest_row);
3267 /* Finish the drag and drop */
3268 gtk_drag_finish (context, success, FALSE, time);
3272 * We define a "drag-drop" signal handler because we do not want to
3273 * use the default one, because the default one always calls
3274 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3275 * signal handler, because there we have all the information available
3276 * to know if the dnd was a success or not.
3279 drag_drop_cb (GtkWidget *widget,
3280 GdkDragContext *context,
3288 if (!context->targets)
3291 /* Check if we're dragging a folder row */
3292 target = gtk_drag_dest_find_target (widget, context, NULL);
3294 /* Request the data from the source. */
3295 gtk_drag_get_data(widget, context, target, time);
3301 * This function expands a node of a tree view if it's not expanded
3302 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3303 * does that, so that's why they're here.
3306 expand_row_timeout (gpointer data)
3308 GtkTreeView *tree_view = data;
3309 GtkTreePath *dest_path = NULL;
3310 GtkTreeViewDropPosition pos;
3311 gboolean result = FALSE;
3313 gdk_threads_enter ();
3315 gtk_tree_view_get_drag_dest_row (tree_view,
3320 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3321 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3322 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3323 gtk_tree_path_free (dest_path);
3327 gtk_tree_path_free (dest_path);
3332 gdk_threads_leave ();
3338 * This function is called whenever the pointer is moved over a widget
3339 * while dragging some data. It installs a timeout that will expand a
3340 * node of the treeview if not expanded yet. This function also calls
3341 * gdk_drag_status in order to set the suggested action that will be
3342 * used by the "drag-data-received" signal handler to know if we
3343 * should do a move or just a copy of the data.
3346 on_drag_motion (GtkWidget *widget,
3347 GdkDragContext *context,
3353 GtkTreeViewDropPosition pos;
3354 GtkTreePath *dest_row;
3355 GtkTreeModel *dest_model;
3356 ModestFolderViewPrivate *priv;
3357 GdkDragAction suggested_action;
3358 gboolean valid_location = FALSE;
3359 TnyFolderStore *folder = NULL;
3361 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3363 if (priv->timer_expander != 0) {
3364 g_source_remove (priv->timer_expander);
3365 priv->timer_expander = 0;
3368 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3373 /* Do not allow drops between folders */
3375 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3376 pos == GTK_TREE_VIEW_DROP_AFTER) {
3377 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3378 gdk_drag_status(context, 0, time);
3379 valid_location = FALSE;
3382 valid_location = TRUE;
3385 /* Check that the destination folder is writable */
3386 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3387 folder = tree_path_to_folder (dest_model, dest_row);
3388 if (folder && TNY_IS_FOLDER (folder)) {
3389 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3391 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3392 valid_location = FALSE;
3397 /* Expand the selected row after 1/2 second */
3398 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3399 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3401 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3403 /* Select the desired action. By default we pick MOVE */
3404 suggested_action = GDK_ACTION_MOVE;
3406 if (context->actions == GDK_ACTION_COPY)
3407 gdk_drag_status(context, GDK_ACTION_COPY, time);
3408 else if (context->actions == GDK_ACTION_MOVE)
3409 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3410 else if (context->actions & suggested_action)
3411 gdk_drag_status(context, suggested_action, time);
3413 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3417 g_object_unref (folder);
3419 gtk_tree_path_free (dest_row);
3421 g_signal_stop_emission_by_name (widget, "drag-motion");
3423 return valid_location;
3427 * This function sets the treeview as a source and a target for dnd
3428 * events. It also connects all the requirede signals.
3431 setup_drag_and_drop (GtkTreeView *self)
3433 /* Set up the folder view as a dnd destination. Set only the
3434 highlight flag, otherwise gtk will have a different
3436 #ifdef MODEST_TOOLKIT_HILDON2
3439 gtk_drag_dest_set (GTK_WIDGET (self),
3440 GTK_DEST_DEFAULT_HIGHLIGHT,
3441 folder_view_drag_types,
3442 G_N_ELEMENTS (folder_view_drag_types),
3443 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3445 g_signal_connect (G_OBJECT (self),
3446 "drag_data_received",
3447 G_CALLBACK (on_drag_data_received),
3451 /* Set up the treeview as a dnd source */
3452 gtk_drag_source_set (GTK_WIDGET (self),
3454 folder_view_drag_types,
3455 G_N_ELEMENTS (folder_view_drag_types),
3456 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3458 g_signal_connect (G_OBJECT (self),
3460 G_CALLBACK (on_drag_motion),
3463 g_signal_connect (G_OBJECT (self),
3465 G_CALLBACK (on_drag_data_get),
3468 g_signal_connect (G_OBJECT (self),
3470 G_CALLBACK (drag_drop_cb),
3475 * This function manages the navigation through the folders using the
3476 * keyboard or the hardware keys in the device
3479 on_key_pressed (GtkWidget *self,
3483 GtkTreeSelection *selection;
3485 GtkTreeModel *model;
3486 gboolean retval = FALSE;
3488 /* Up and Down are automatically managed by the treeview */
3489 if (event->keyval == GDK_Return) {
3490 /* Expand/Collapse the selected row */
3491 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3492 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3495 path = gtk_tree_model_get_path (model, &iter);
3497 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3498 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3500 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3501 gtk_tree_path_free (path);
3503 /* No further processing */
3511 * We listen to the changes in the local folder account name key,
3512 * because we want to show the right name in the view. The local
3513 * folder account name corresponds to the device name in the Maemo
3514 * version. We do this because we do not want to query gconf on each
3515 * tree view refresh. It's better to cache it and change whenever
3519 on_configuration_key_changed (ModestConf* conf,
3521 ModestConfEvent event,
3522 ModestConfNotificationId id,
3523 ModestFolderView *self)
3525 ModestFolderViewPrivate *priv;
3528 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3529 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3531 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3532 g_free (priv->local_account_name);
3534 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3535 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3537 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3538 MODEST_CONF_DEVICE_NAME, NULL);
3540 /* Force a redraw */
3541 #if GTK_CHECK_VERSION(2, 8, 0)
3542 GtkTreeViewColumn * tree_column;
3544 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3546 gtk_tree_view_column_queue_resize (tree_column);
3548 gtk_widget_queue_draw (GTK_WIDGET (self));
3554 modest_folder_view_set_style (ModestFolderView *self,
3555 ModestFolderViewStyle style)
3557 ModestFolderViewPrivate *priv;
3559 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3560 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3561 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3563 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3566 priv->style = style;
3570 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3571 const gchar *account_id)
3573 ModestFolderViewPrivate *priv;
3574 GtkTreeModel *model;
3576 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3578 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3580 /* This will be used by the filter_row callback,
3581 * to decided which rows to show: */
3582 if (priv->visible_account_id) {
3583 g_free (priv->visible_account_id);
3584 priv->visible_account_id = NULL;
3587 priv->visible_account_id = g_strdup (account_id);
3590 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3591 if (GTK_IS_TREE_MODEL_FILTER (model))
3592 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3594 /* Save settings to gconf */
3595 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3596 MODEST_CONF_FOLDER_VIEW_KEY);
3598 /* Notify observers */
3599 g_signal_emit (G_OBJECT(self),
3600 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3605 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3607 ModestFolderViewPrivate *priv;
3609 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3611 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3613 return (const gchar *) priv->visible_account_id;
3617 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3621 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3623 gtk_tree_model_get (model, iter,
3627 gboolean result = FALSE;
3628 if (type == TNY_FOLDER_TYPE_INBOX) {
3632 *inbox_iter = *iter;
3636 if (gtk_tree_model_iter_children (model, &child, iter)) {
3637 if (find_inbox_iter (model, &child, inbox_iter))
3641 } while (gtk_tree_model_iter_next (model, iter));
3650 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3652 #ifndef MODEST_TOOLKIT_HILDON2
3653 GtkTreeModel *model;
3654 GtkTreeIter iter, inbox_iter;
3655 GtkTreeSelection *sel;
3656 GtkTreePath *path = NULL;
3658 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3660 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3664 expand_root_items (self);
3665 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3667 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3668 g_warning ("%s: model is empty", __FUNCTION__);
3672 if (find_inbox_iter (model, &iter, &inbox_iter))
3673 path = gtk_tree_model_get_path (model, &inbox_iter);
3675 path = gtk_tree_path_new_first ();
3677 /* Select the row and free */
3678 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3679 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3680 gtk_tree_path_free (path);
3683 gtk_widget_grab_focus (GTK_WIDGET(self));
3690 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3695 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3696 TnyFolder* a_folder;
3699 gtk_tree_model_get (model, iter,
3700 INSTANCE_COLUMN, &a_folder,
3706 if (folder == a_folder) {
3707 g_object_unref (a_folder);
3708 *folder_iter = *iter;
3711 g_object_unref (a_folder);
3713 if (gtk_tree_model_iter_children (model, &child, iter)) {
3714 if (find_folder_iter (model, &child, folder_iter, folder))
3718 } while (gtk_tree_model_iter_next (model, iter));
3723 #ifndef MODEST_TOOLKIT_HILDON2
3725 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3728 ModestFolderView *self)
3730 ModestFolderViewPrivate *priv = NULL;
3731 GtkTreeSelection *sel;
3732 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3733 GObject *instance = NULL;
3735 if (!MODEST_IS_FOLDER_VIEW(self))
3738 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3740 priv->reexpand = TRUE;
3742 gtk_tree_model_get (tree_model, iter,
3744 INSTANCE_COLUMN, &instance,
3750 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3751 priv->folder_to_select = g_object_ref (instance);
3753 g_object_unref (instance);
3755 if (priv->folder_to_select) {
3757 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3760 path = gtk_tree_model_get_path (tree_model, iter);
3761 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3763 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3765 gtk_tree_selection_select_iter (sel, iter);
3766 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3768 gtk_tree_path_free (path);
3772 modest_folder_view_disable_next_folder_selection (self);
3774 /* Refilter the model */
3775 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3781 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3783 ModestFolderViewPrivate *priv;
3785 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3787 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3789 if (priv->folder_to_select)
3790 g_object_unref(priv->folder_to_select);
3792 priv->folder_to_select = NULL;
3796 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3797 gboolean after_change)
3799 GtkTreeModel *model;
3800 GtkTreeIter iter, folder_iter;
3801 GtkTreeSelection *sel;
3802 ModestFolderViewPrivate *priv = NULL;
3804 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3805 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3807 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3810 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3811 gtk_tree_selection_unselect_all (sel);
3813 if (priv->folder_to_select)
3814 g_object_unref(priv->folder_to_select);
3815 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3819 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3824 /* Refilter the model, before selecting the folder */
3825 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3827 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3828 g_warning ("%s: model is empty", __FUNCTION__);
3832 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3835 path = gtk_tree_model_get_path (model, &folder_iter);
3836 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3838 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3839 gtk_tree_selection_select_iter (sel, &folder_iter);
3840 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3842 gtk_tree_path_free (path);
3850 modest_folder_view_copy_selection (ModestFolderView *self)
3852 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3854 /* Copy selection */
3855 _clipboard_set_selected_data (self, FALSE);
3859 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3861 ModestFolderViewPrivate *priv = NULL;
3862 GtkTreeModel *model = NULL;
3863 const gchar **hidding = NULL;
3864 guint i, n_selected;
3866 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3867 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3869 /* Copy selection */
3870 if (!_clipboard_set_selected_data (folder_view, TRUE))
3873 /* Get hidding ids */
3874 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3876 /* Clear hidding array created by previous cut operation */
3877 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3879 /* Copy hidding array */
3880 priv->n_selected = n_selected;
3881 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3882 for (i=0; i < n_selected; i++)
3883 priv->hidding_ids[i] = g_strdup(hidding[i]);
3885 /* Hide cut folders */
3886 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3887 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3891 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3892 ModestFolderView *folder_view_dst)
3894 GtkTreeModel *filter_model = NULL;
3895 GtkTreeModel *model = NULL;
3896 GtkTreeModel *new_filter_model = NULL;
3897 GtkTreeModel *old_tny_model = NULL;
3898 GtkTreeModel *new_tny_model = NULL;
3899 ModestFolderViewPrivate *dst_priv;
3901 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3902 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3904 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3905 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3906 new_tny_model = NULL;
3909 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3910 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3911 G_OBJECT (old_tny_model),
3912 "activity-changed");
3914 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3915 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3917 /* Build new filter model */
3918 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3919 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3926 /* Set copied model */
3927 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3928 #ifndef MODEST_TOOLKIT_HILDON2
3929 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3930 G_OBJECT(new_filter_model), "row-inserted",
3931 (GCallback) on_row_inserted_maybe_select_folder,
3934 #ifdef MODEST_TOOLKIT_HILDON2
3935 if (new_tny_model) {
3936 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3937 G_OBJECT (new_tny_model),
3939 G_CALLBACK (on_activity_changed),
3945 g_object_unref (new_filter_model);
3949 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3952 GtkTreeModel *model = NULL;
3953 ModestFolderViewPrivate* priv;
3955 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3957 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3958 priv->show_non_move = show;
3959 /* modest_folder_view_update_model(folder_view, */
3960 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3962 /* Hide special folders */
3963 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3964 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3965 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3970 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3973 ModestFolderViewPrivate* priv;
3975 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3977 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3978 priv->show_message_count = show;
3980 g_object_set (G_OBJECT (priv->messages_renderer),
3981 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3985 /* Returns FALSE if it did not selected anything */
3987 _clipboard_set_selected_data (ModestFolderView *folder_view,
3990 ModestFolderViewPrivate *priv = NULL;
3991 TnyFolderStore *folder = NULL;
3992 gboolean retval = FALSE;
3994 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3995 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3997 /* Set selected data on clipboard */
3998 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3999 folder = modest_folder_view_get_selected (folder_view);
4001 /* Do not allow to select an account */
4002 if (TNY_IS_FOLDER (folder)) {
4003 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
4008 g_object_unref (folder);
4014 _clear_hidding_filter (ModestFolderView *folder_view)
4016 ModestFolderViewPrivate *priv;
4019 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4020 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4022 if (priv->hidding_ids != NULL) {
4023 for (i=0; i < priv->n_selected; i++)
4024 g_free (priv->hidding_ids[i]);
4025 g_free(priv->hidding_ids);
4031 on_display_name_changed (ModestAccountMgr *mgr,
4032 const gchar *account,
4035 ModestFolderView *self;
4037 self = MODEST_FOLDER_VIEW (user_data);
4039 /* Force a redraw */
4040 #if GTK_CHECK_VERSION(2, 8, 0)
4041 GtkTreeViewColumn * tree_column;
4043 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4045 gtk_tree_view_column_queue_resize (tree_column);
4047 gtk_widget_queue_draw (GTK_WIDGET (self));
4052 modest_folder_view_set_cell_style (ModestFolderView *self,
4053 ModestFolderViewCellStyle cell_style)
4055 ModestFolderViewPrivate *priv = NULL;
4057 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4058 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4060 priv->cell_style = cell_style;
4062 g_object_set (G_OBJECT (priv->messages_renderer),
4063 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4066 gtk_widget_queue_draw (GTK_WIDGET (self));
4070 update_style (ModestFolderView *self)
4072 ModestFolderViewPrivate *priv;
4073 GdkColor style_color, style_active_color;
4074 PangoAttrList *attr_list;
4076 PangoAttribute *attr;
4078 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4079 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4083 attr_list = pango_attr_list_new ();
4084 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4085 gdk_color_parse ("grey", &style_color);
4087 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4088 pango_attr_list_insert (attr_list, attr);
4091 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4093 "SmallSystemFont", NULL,
4096 attr = pango_attr_font_desc_new (pango_font_description_copy
4097 (style->font_desc));
4098 pango_attr_list_insert (attr_list, attr);
4100 g_object_set (G_OBJECT (priv->messages_renderer),
4101 "foreground-gdk", &style_color,
4102 "foreground-set", TRUE,
4103 "attributes", attr_list,
4105 pango_attr_list_unref (attr_list);
4108 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4109 priv->active_color = style_active_color;
4111 gdk_color_parse ("000", &(priv->active_color));
4116 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4118 if (strcmp ("style", spec->name) == 0) {
4119 update_style (MODEST_FOLDER_VIEW (obj));
4120 gtk_widget_queue_draw (GTK_WIDGET (obj));
4125 modest_folder_view_set_filter (ModestFolderView *self,
4126 ModestFolderViewFilter filter)
4128 ModestFolderViewPrivate *priv;
4129 GtkTreeModel *filter_model;
4131 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4132 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4134 priv->filter |= filter;
4136 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4137 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4138 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4143 modest_folder_view_unset_filter (ModestFolderView *self,
4144 ModestFolderViewFilter filter)
4146 ModestFolderViewPrivate *priv;
4147 GtkTreeModel *filter_model;
4149 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4150 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4152 priv->filter &= ~filter;
4154 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4155 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4156 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4161 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4162 ModestTnyFolderRules rules)
4164 GtkTreeModel *filter_model;
4166 gboolean fulfil = FALSE;
4168 if (!get_inner_models (self, &filter_model, NULL, NULL))
4171 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4175 TnyFolderStore *folder;
4177 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4179 if (TNY_IS_FOLDER (folder)) {
4180 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4181 /* Folder rules are negative: non_writable, non_deletable... */
4182 if (!(folder_rules & rules))
4185 g_object_unref (folder);
4188 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4194 modest_folder_view_set_list_to_move (ModestFolderView *self,
4197 ModestFolderViewPrivate *priv;
4199 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4200 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4202 if (priv->list_to_move)
4203 g_object_unref (priv->list_to_move);
4206 g_object_ref (list);
4208 priv->list_to_move = list;
4212 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4214 ModestFolderViewPrivate *priv;
4216 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4217 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4220 g_free (priv->mailbox);
4222 priv->mailbox = g_strdup (mailbox);
4224 /* Notify observers */
4225 g_signal_emit (G_OBJECT(self),
4226 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4227 priv->visible_account_id);
4231 modest_folder_view_get_mailbox (ModestFolderView *self)
4233 ModestFolderViewPrivate *priv;
4235 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4236 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4238 return (const gchar *) priv->mailbox;
4242 modest_folder_view_get_activity (ModestFolderView *self)
4244 ModestFolderViewPrivate *priv;
4245 GtkTreeModel *inner_model;
4247 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4248 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4249 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4251 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4252 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4258 #ifdef MODEST_TOOLKIT_HILDON2
4260 on_activity_changed (TnyGtkFolderListStore *store,
4262 ModestFolderView *folder_view)
4264 ModestFolderViewPrivate *priv;
4266 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4267 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4268 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4270 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,