1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
65 #include <modest-account-protocol.h>
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
98 static void modest_folder_view_init (ModestFolderView *obj);
99 static void modest_folder_view_finalize (GObject *obj);
100 static void modest_folder_view_dispose (GObject *obj);
102 static void tny_account_store_view_init (gpointer g,
103 gpointer iface_data);
105 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
106 TnyAccountStore *account_store);
108 static void on_selection_changed (GtkTreeSelection *sel,
111 static void on_row_activated (GtkTreeView *treeview,
113 GtkTreeViewColumn *column,
116 static void on_account_removed (TnyAccountStore *self,
120 static void on_account_inserted (TnyAccountStore *self,
124 static void on_account_changed (TnyAccountStore *self,
128 static gint cmp_rows (GtkTreeModel *tree_model,
133 static gboolean filter_row (GtkTreeModel *model,
137 static gboolean on_key_pressed (GtkWidget *self,
141 static void on_configuration_key_changed (ModestConf* conf,
143 ModestConfEvent event,
144 ModestConfNotificationId notification_id,
145 ModestFolderView *self);
148 static void on_drag_data_get (GtkWidget *widget,
149 GdkDragContext *context,
150 GtkSelectionData *selection_data,
155 static void on_drag_data_received (GtkWidget *widget,
156 GdkDragContext *context,
159 GtkSelectionData *selection_data,
164 static gboolean on_drag_motion (GtkWidget *widget,
165 GdkDragContext *context,
171 static void expand_root_items (ModestFolderView *self);
173 static gint expand_row_timeout (gpointer data);
175 static void setup_drag_and_drop (GtkTreeView *self);
177 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
180 static void _clear_hidding_filter (ModestFolderView *folder_view);
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
186 ModestFolderView *self);
189 static void on_display_name_changed (ModestAccountMgr *self,
190 const gchar *account,
192 static void update_style (ModestFolderView *self);
193 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
195 static gboolean inbox_is_special (TnyFolderStore *folder_store);
197 static gboolean get_inner_models (ModestFolderView *self,
198 GtkTreeModel **filter_model,
199 GtkTreeModel **sort_model,
200 GtkTreeModel **tny_model);
201 #ifdef MODEST_TOOLKIT_HILDON2
202 static void on_activity_changed (TnyGtkFolderListStore *store,
204 ModestFolderView *folder_view);
208 FOLDER_SELECTION_CHANGED_SIGNAL,
209 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
210 FOLDER_ACTIVATED_SIGNAL,
211 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
212 ACTIVITY_CHANGED_SIGNAL,
216 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
217 struct _ModestFolderViewPrivate {
218 TnyAccountStore *account_store;
219 TnyFolderStore *cur_folder_store;
221 TnyFolder *folder_to_select; /* folder to select after the next update */
223 /* not unref this object, its a singlenton */
224 ModestEmailClipboard *clipboard;
226 /* Filter tree model */
229 ModestFolderViewFilter filter;
231 TnyFolderStoreQuery *query;
233 guint timer_expander;
235 gchar *local_account_name;
236 gchar *visible_account_id;
238 ModestFolderViewStyle style;
239 ModestFolderViewCellStyle cell_style;
240 gboolean show_message_count;
242 gboolean reselect; /* we use this to force a reselection of the INBOX */
243 gboolean show_non_move;
244 TnyList *list_to_move;
245 gboolean reexpand; /* next time we expose, we'll expand all root folders */
247 GtkCellRenderer *messages_renderer;
249 GSList *signal_handlers;
250 GdkColor active_color;
252 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
253 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
254 MODEST_TYPE_FOLDER_VIEW, \
255 ModestFolderViewPrivate))
257 static GObjectClass *parent_class = NULL;
259 static guint signals[LAST_SIGNAL] = {0};
262 modest_folder_view_get_type (void)
264 static GType my_type = 0;
266 static const GTypeInfo my_info = {
267 sizeof(ModestFolderViewClass),
268 NULL, /* base init */
269 NULL, /* base finalize */
270 (GClassInitFunc) modest_folder_view_class_init,
271 NULL, /* class finalize */
272 NULL, /* class data */
273 sizeof(ModestFolderView),
275 (GInstanceInitFunc) modest_folder_view_init,
279 static const GInterfaceInfo tny_account_store_view_info = {
280 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
281 NULL, /* interface_finalize */
282 NULL /* interface_data */
286 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
290 g_type_add_interface_static (my_type,
291 TNY_TYPE_ACCOUNT_STORE_VIEW,
292 &tny_account_store_view_info);
298 modest_folder_view_class_init (ModestFolderViewClass *klass)
300 GObjectClass *gobject_class;
301 GtkTreeViewClass *treeview_class;
302 gobject_class = (GObjectClass*) klass;
303 treeview_class = (GtkTreeViewClass*) klass;
305 parent_class = g_type_class_peek_parent (klass);
306 gobject_class->finalize = modest_folder_view_finalize;
307 gobject_class->finalize = modest_folder_view_dispose;
309 g_type_class_add_private (gobject_class,
310 sizeof(ModestFolderViewPrivate));
312 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
313 g_signal_new ("folder_selection_changed",
314 G_TYPE_FROM_CLASS (gobject_class),
316 G_STRUCT_OFFSET (ModestFolderViewClass,
317 folder_selection_changed),
319 modest_marshal_VOID__POINTER_BOOLEAN,
320 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
323 * This signal is emitted whenever the currently selected
324 * folder display name is computed. Note that the name could
325 * be different to the folder name, because we could append
326 * the unread messages count to the folder name to build the
327 * folder display name
329 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
330 g_signal_new ("folder-display-name-changed",
331 G_TYPE_FROM_CLASS (gobject_class),
333 G_STRUCT_OFFSET (ModestFolderViewClass,
334 folder_display_name_changed),
336 g_cclosure_marshal_VOID__STRING,
337 G_TYPE_NONE, 1, G_TYPE_STRING);
339 signals[FOLDER_ACTIVATED_SIGNAL] =
340 g_signal_new ("folder_activated",
341 G_TYPE_FROM_CLASS (gobject_class),
343 G_STRUCT_OFFSET (ModestFolderViewClass,
346 g_cclosure_marshal_VOID__POINTER,
347 G_TYPE_NONE, 1, G_TYPE_POINTER);
350 * Emitted whenever the visible account changes
352 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
353 g_signal_new ("visible-account-changed",
354 G_TYPE_FROM_CLASS (gobject_class),
356 G_STRUCT_OFFSET (ModestFolderViewClass,
357 visible_account_changed),
359 g_cclosure_marshal_VOID__STRING,
360 G_TYPE_NONE, 1, G_TYPE_STRING);
363 * Emitted when the underlying GtkListStore is updating data
365 signals[ACTIVITY_CHANGED_SIGNAL] =
366 g_signal_new ("activity-changed",
367 G_TYPE_FROM_CLASS (gobject_class),
369 G_STRUCT_OFFSET (ModestFolderViewClass,
372 g_cclosure_marshal_VOID__BOOLEAN,
373 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
375 treeview_class->select_cursor_parent = NULL;
377 #ifdef MODEST_TOOLKIT_HILDON2
378 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
384 /* Retrieves the filter, sort and tny models of the folder view. If
385 any of these does not exist then it returns FALSE */
387 get_inner_models (ModestFolderView *self,
388 GtkTreeModel **filter_model,
389 GtkTreeModel **sort_model,
390 GtkTreeModel **tny_model)
392 GtkTreeModel *s_model, *f_model, *t_model;
394 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
395 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
396 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
400 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
401 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
402 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
406 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
410 *filter_model = f_model;
412 *sort_model = s_model;
414 *tny_model = t_model;
419 /* Simplify checks for NULLs: */
421 strings_are_equal (const gchar *a, const gchar *b)
427 return (strcmp (a, b) == 0);
434 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
436 GObject *instance = NULL;
438 gtk_tree_model_get (model, iter,
439 INSTANCE_COLUMN, &instance,
443 return FALSE; /* keep walking */
445 if (!TNY_IS_ACCOUNT (instance)) {
446 g_object_unref (instance);
447 return FALSE; /* keep walking */
450 /* Check if this is the looked-for account: */
451 TnyAccount *this_account = TNY_ACCOUNT (instance);
452 TnyAccount *account = TNY_ACCOUNT (data);
454 const gchar *this_account_id = tny_account_get_id(this_account);
455 const gchar *account_id = tny_account_get_id(account);
456 g_object_unref (instance);
459 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
460 if (strings_are_equal(this_account_id, account_id)) {
461 /* Tell the model that the data has changed, so that
462 * it calls the cell_data_func callbacks again: */
463 /* TODO: This does not seem to actually cause the new string to be shown: */
464 gtk_tree_model_row_changed (model, path, iter);
466 return TRUE; /* stop walking */
469 return FALSE; /* keep walking */
474 ModestFolderView *self;
475 gchar *previous_name;
476 } GetMmcAccountNameData;
479 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
481 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
483 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
485 if (!strings_are_equal (
486 tny_account_get_name(TNY_ACCOUNT(account)),
487 data->previous_name)) {
489 /* Tell the model that the data has changed, so that
490 * it calls the cell_data_func callbacks again: */
491 ModestFolderView *self = data->self;
492 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
494 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
497 g_free (data->previous_name);
498 g_slice_free (GetMmcAccountNameData, data);
502 convert_parent_folders_to_dots (gchar **item_name)
505 gint n_inbox_parents = 0;
508 gchar *last_separator;
510 if (item_name == NULL)
513 path_start = *item_name;
514 for (c = *item_name; *c != '\0'; c++) {
515 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
517 if (c != path_start) {
518 compare = g_strndup (path_start, c - path_start);
519 compare = g_strstrip (compare);
520 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
530 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
531 if (last_separator != NULL) {
532 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
539 buffer = g_string_new ("");
540 for (i = 0; i < n_parents - n_inbox_parents; i++) {
541 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
543 buffer = g_string_append (buffer, last_separator);
545 *item_name = g_string_free (buffer, FALSE);
551 format_compact_style (gchar **item_name,
553 const gchar *mailbox,
555 gboolean multiaccount,
556 gboolean *use_markup)
560 TnyFolderType folder_type;
562 if (!TNY_IS_FOLDER (instance))
565 folder = (TnyFolder *) instance;
567 folder_type = tny_folder_get_folder_type (folder);
568 is_special = (get_cmp_pos (folder_type, folder)!= 4);
571 /* Remove mailbox prefix if any */
572 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
573 if (g_str_has_prefix (*item_name, prefix)) {
574 gchar *new_item_name;
576 new_item_name = g_strdup (*item_name + strlen (prefix));
577 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
578 g_free (new_item_name);
579 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
582 *item_name = new_item_name;
584 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
587 *item_name = g_strdup (_("mcen_me_folder_inbox"));
590 if (!is_special || multiaccount) {
591 TnyAccount *account = tny_folder_get_account (folder);
592 const gchar *folder_name;
593 gboolean concat_folder_name = FALSE;
596 /* Should not happen */
600 /* convert parent folders to dots */
601 convert_parent_folders_to_dots (item_name);
603 folder_name = tny_folder_get_name (folder);
604 if (g_str_has_suffix (*item_name, folder_name)) {
605 gchar *offset = g_strrstr (*item_name, folder_name);
607 concat_folder_name = TRUE;
610 buffer = g_string_new ("");
612 buffer = g_string_append (buffer, *item_name);
613 if (concat_folder_name) {
614 buffer = g_string_append (buffer, folder_name);
617 g_object_unref (account);
619 *item_name = g_string_free (buffer, FALSE);
627 text_cell_data (GtkTreeViewColumn *column,
628 GtkCellRenderer *renderer,
629 GtkTreeModel *tree_model,
633 ModestFolderViewPrivate *priv;
634 GObject *rendobj = (GObject *) renderer;
636 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
637 GObject *instance = NULL;
638 gboolean use_markup = FALSE;
640 gtk_tree_model_get (tree_model, iter,
643 INSTANCE_COLUMN, &instance,
645 if (!fname || !instance)
648 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
649 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
651 gchar *item_name = NULL;
652 gint item_weight = 400;
654 if (type != TNY_FOLDER_TYPE_ROOT) {
659 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
660 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
663 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
664 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
666 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
669 /* Sometimes an special folder is reported by the server as
670 NORMAL, like some versions of Dovecot */
671 if (type == TNY_FOLDER_TYPE_NORMAL ||
672 type == TNY_FOLDER_TYPE_UNKNOWN) {
673 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
677 /* note: we cannot reliably get the counts from the
678 * tree model, we need to use explicit calls on
679 * tny_folder for some reason. Select the number to
680 * show: the unread or unsent messages. in case of
681 * outbox/drafts, show all */
682 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
683 (type == TNY_FOLDER_TYPE_OUTBOX) ||
684 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
685 number = tny_folder_get_all_count (TNY_FOLDER(instance));
688 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
692 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
693 item_name = g_strdup (fname);
700 /* Use bold font style if there are unread or unset messages */
702 if (priv->show_message_count) {
703 item_name = g_strdup_printf ("%s (%d)", fname, number);
705 item_name = g_strdup (fname);
709 item_name = g_strdup (fname);
714 } else if (TNY_IS_ACCOUNT (instance)) {
715 /* If it's a server account */
716 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
717 item_name = g_strdup (priv->local_account_name);
719 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
720 /* fname is only correct when the items are first
721 * added to the model, not when the account is
722 * changed later, so get the name from the account
724 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
727 item_name = g_strdup (fname);
733 item_name = g_strdup ("unknown");
735 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
736 gboolean multiaccount;
738 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
739 /* Convert item_name to markup */
740 format_compact_style (&item_name, instance, priv->mailbox,
742 multiaccount, &use_markup);
745 if (item_name && item_weight) {
746 /* Set the name in the treeview cell: */
747 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
748 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
749 g_object_set (rendobj,
752 "foreground-set", TRUE,
753 "foreground-gdk", &(priv->active_color),
756 g_object_set (rendobj,
758 "foreground-set", FALSE,
760 "weight", item_weight,
764 /* Notify display name observers */
765 /* TODO: What listens for this signal, and how can it use only the new name? */
766 if (((GObject *) priv->cur_folder_store) == instance) {
767 g_signal_emit (G_OBJECT(self),
768 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
775 /* If it is a Memory card account, make sure that we have the correct name.
776 * This function will be trigerred again when the name has been retrieved: */
777 if (TNY_IS_STORE_ACCOUNT (instance) &&
778 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
780 /* Get the account name asynchronously: */
781 GetMmcAccountNameData *callback_data =
782 g_slice_new0(GetMmcAccountNameData);
783 callback_data->self = self;
785 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
787 callback_data->previous_name = g_strdup (name);
789 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
790 on_get_mmc_account_name, callback_data);
794 g_object_unref (G_OBJECT (instance));
800 messages_cell_data (GtkTreeViewColumn *column,
801 GtkCellRenderer *renderer,
802 GtkTreeModel *tree_model,
806 ModestFolderView *self;
807 ModestFolderViewPrivate *priv;
808 GObject *rendobj = (GObject *) renderer;
809 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
810 GObject *instance = NULL;
811 gchar *item_name = NULL;
813 gtk_tree_model_get (tree_model, iter,
815 INSTANCE_COLUMN, &instance,
820 self = MODEST_FOLDER_VIEW (data);
821 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
824 if (type != TNY_FOLDER_TYPE_ROOT) {
829 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
830 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
833 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
835 /* Sometimes an special folder is reported by the server as
836 NORMAL, like some versions of Dovecot */
837 if (type == TNY_FOLDER_TYPE_NORMAL ||
838 type == TNY_FOLDER_TYPE_UNKNOWN) {
839 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
843 /* note: we cannot reliably get the counts from the tree model, we need
844 * to use explicit calls on tny_folder for some reason.
846 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
847 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
848 (type == TNY_FOLDER_TYPE_OUTBOX) ||
849 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
850 number = tny_folder_get_all_count (TNY_FOLDER(instance));
853 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
857 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
859 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
860 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
866 item_name = g_strdup ("");
869 /* Set the name in the treeview cell: */
870 g_object_set (rendobj,"text", item_name, NULL);
878 g_object_unref (G_OBJECT (instance));
884 GdkPixbuf *pixbuf_open;
885 GdkPixbuf *pixbuf_close;
889 static inline GdkPixbuf *
890 get_composite_pixbuf (const gchar *icon_name,
892 GdkPixbuf *base_pixbuf)
894 GdkPixbuf *emblem, *retval = NULL;
896 emblem = modest_platform_get_icon (icon_name, size);
898 retval = gdk_pixbuf_copy (base_pixbuf);
899 gdk_pixbuf_composite (emblem, retval, 0, 0,
900 MIN (gdk_pixbuf_get_width (emblem),
901 gdk_pixbuf_get_width (retval)),
902 MIN (gdk_pixbuf_get_height (emblem),
903 gdk_pixbuf_get_height (retval)),
904 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
905 g_object_unref (emblem);
910 static inline ThreePixbufs *
911 get_composite_icons (const gchar *icon_code,
913 GdkPixbuf **pixbuf_open,
914 GdkPixbuf **pixbuf_close)
916 ThreePixbufs *retval;
920 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
922 *pixbuf = gdk_pixbuf_copy (icon);
928 if (!*pixbuf_open && pixbuf && *pixbuf)
929 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
933 if (!*pixbuf_close && pixbuf && *pixbuf)
934 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
938 retval = g_slice_new0 (ThreePixbufs);
940 retval->pixbuf = g_object_ref (*pixbuf);
942 retval->pixbuf_open = g_object_ref (*pixbuf_open);
944 retval->pixbuf_close = g_object_ref (*pixbuf_close);
949 static inline ThreePixbufs *
950 get_account_protocol_pixbufs (ModestFolderView *folder_view,
951 ModestProtocolType protocol_type,
954 ModestProtocol *protocol;
955 const GdkPixbuf *pixbuf = NULL;
956 ModestFolderViewPrivate *priv;
958 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
960 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
963 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
964 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
965 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
966 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
967 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
968 object, FOLDER_ICON_SIZE);
972 ThreePixbufs *retval;
973 retval = g_slice_new0 (ThreePixbufs);
974 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
975 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
976 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
983 static inline ThreePixbufs*
984 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
986 TnyAccount *account = NULL;
987 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
988 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
989 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
990 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
991 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
993 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
994 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
995 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
996 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
997 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
999 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1000 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1001 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1002 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1003 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1005 ThreePixbufs *retval = NULL;
1007 if (TNY_IS_ACCOUNT (instance)) {
1008 account = g_object_ref (instance);
1009 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1010 account = tny_folder_get_account (TNY_FOLDER (instance));
1014 ModestProtocolType account_store_protocol;
1016 account_store_protocol = modest_tny_account_get_protocol_type (account);
1017 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1018 g_object_unref (account);
1024 /* Sometimes an special folder is reported by the server as
1025 NORMAL, like some versions of Dovecot */
1026 if (type == TNY_FOLDER_TYPE_NORMAL ||
1027 type == TNY_FOLDER_TYPE_UNKNOWN) {
1028 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1031 /* It's not enough with check the folder type. We need to
1032 ensure that we're not giving a special folder icon to a
1033 normal folder with the same name than a special folder */
1034 if (TNY_IS_FOLDER (instance) &&
1035 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1036 type = TNY_FOLDER_TYPE_NORMAL;
1038 /* Remote folders should not be treated as special folders */
1039 if (TNY_IS_FOLDER_STORE (instance) &&
1040 !TNY_IS_ACCOUNT (instance) &&
1041 type != TNY_FOLDER_TYPE_INBOX &&
1042 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1043 #ifdef MODEST_TOOLKIT_HILDON2
1044 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1047 &anorm_pixbuf_close);
1049 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1051 &normal_pixbuf_open,
1052 &normal_pixbuf_close);
1058 case TNY_FOLDER_TYPE_INVALID:
1059 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1062 case TNY_FOLDER_TYPE_ROOT:
1063 if (TNY_IS_ACCOUNT (instance)) {
1065 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1066 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1069 &avirt_pixbuf_close);
1071 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1073 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1074 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1077 &ammc_pixbuf_close);
1079 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1082 &anorm_pixbuf_close);
1087 case TNY_FOLDER_TYPE_INBOX:
1088 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1091 &inbox_pixbuf_close);
1093 case TNY_FOLDER_TYPE_OUTBOX:
1094 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1096 &outbox_pixbuf_open,
1097 &outbox_pixbuf_close);
1099 case TNY_FOLDER_TYPE_JUNK:
1100 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1103 &junk_pixbuf_close);
1105 case TNY_FOLDER_TYPE_SENT:
1106 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1109 &sent_pixbuf_close);
1111 case TNY_FOLDER_TYPE_TRASH:
1112 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1115 &trash_pixbuf_close);
1117 case TNY_FOLDER_TYPE_DRAFTS:
1118 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1121 &draft_pixbuf_close);
1123 case TNY_FOLDER_TYPE_ARCHIVE:
1124 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1129 case TNY_FOLDER_TYPE_NORMAL:
1131 /* Memory card folders could have an special icon */
1132 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1133 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1138 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1140 &normal_pixbuf_open,
1141 &normal_pixbuf_close);
1150 free_pixbufs (ThreePixbufs *pixbufs)
1152 if (pixbufs->pixbuf)
1153 g_object_unref (pixbufs->pixbuf);
1154 if (pixbufs->pixbuf_open)
1155 g_object_unref (pixbufs->pixbuf_open);
1156 if (pixbufs->pixbuf_close)
1157 g_object_unref (pixbufs->pixbuf_close);
1158 g_slice_free (ThreePixbufs, pixbufs);
1162 icon_cell_data (GtkTreeViewColumn *column,
1163 GtkCellRenderer *renderer,
1164 GtkTreeModel *tree_model,
1168 GObject *rendobj = NULL, *instance = NULL;
1169 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1170 gboolean has_children;
1171 ThreePixbufs *pixbufs;
1172 ModestFolderView *folder_view = (ModestFolderView *) data;
1174 rendobj = (GObject *) renderer;
1176 gtk_tree_model_get (tree_model, iter,
1178 INSTANCE_COLUMN, &instance,
1184 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1185 pixbufs = get_folder_icons (folder_view, type, instance);
1186 g_object_unref (instance);
1189 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1192 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1193 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1196 free_pixbufs (pixbufs);
1200 add_columns (GtkWidget *treeview)
1202 GtkTreeViewColumn *column;
1203 GtkCellRenderer *renderer;
1204 GtkTreeSelection *sel;
1205 ModestFolderViewPrivate *priv;
1207 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1210 column = gtk_tree_view_column_new ();
1212 /* Set icon and text render function */
1213 renderer = gtk_cell_renderer_pixbuf_new();
1214 #ifdef MODEST_TOOLKIT_HILDON2
1215 g_object_set (renderer,
1216 "xpad", MODEST_MARGIN_DEFAULT,
1217 "ypad", MODEST_MARGIN_DEFAULT,
1220 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1221 gtk_tree_view_column_set_cell_data_func(column, renderer,
1222 icon_cell_data, treeview, NULL);
1224 renderer = gtk_cell_renderer_text_new();
1225 g_object_set (renderer,
1226 #ifdef MODEST_TOOLKIT_HILDON2
1227 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1228 "ypad", MODEST_MARGIN_DEFAULT,
1229 "xpad", MODEST_MARGIN_DEFAULT,
1231 "ellipsize", PANGO_ELLIPSIZE_END,
1233 "ellipsize-set", TRUE, NULL);
1234 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1235 gtk_tree_view_column_set_cell_data_func(column, renderer,
1236 text_cell_data, treeview, NULL);
1238 priv->messages_renderer = gtk_cell_renderer_text_new ();
1239 g_object_set (priv->messages_renderer,
1240 #ifdef MODEST_TOOLKIT_HILDON2
1242 "ypad", MODEST_MARGIN_DEFAULT,
1243 "xpad", MODEST_MARGIN_DOUBLE,
1245 "scale", PANGO_SCALE_X_SMALL,
1248 "alignment", PANGO_ALIGN_RIGHT,
1252 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1253 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1254 messages_cell_data, treeview, NULL);
1256 /* Set selection mode */
1257 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1258 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1260 /* Set treeview appearance */
1261 gtk_tree_view_column_set_spacing (column, 2);
1262 gtk_tree_view_column_set_resizable (column, TRUE);
1263 gtk_tree_view_column_set_fixed_width (column, TRUE);
1264 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1265 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1268 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1272 modest_folder_view_init (ModestFolderView *obj)
1274 ModestFolderViewPrivate *priv;
1277 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1279 priv->timer_expander = 0;
1280 priv->account_store = NULL;
1282 priv->do_refresh = TRUE;
1283 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1284 priv->cur_folder_store = NULL;
1285 priv->visible_account_id = NULL;
1286 priv->mailbox = NULL;
1287 priv->folder_to_select = NULL;
1288 priv->reexpand = TRUE;
1289 priv->signal_handlers = 0;
1291 /* Initialize the local account name */
1292 conf = modest_runtime_get_conf();
1293 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1295 /* Init email clipboard */
1296 priv->clipboard = modest_runtime_get_email_clipboard ();
1297 priv->hidding_ids = NULL;
1298 priv->n_selected = 0;
1299 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1300 priv->reselect = FALSE;
1301 priv->show_non_move = TRUE;
1302 priv->list_to_move = NULL;
1303 priv->show_message_count = TRUE;
1305 /* Build treeview */
1306 add_columns (GTK_WIDGET (obj));
1308 /* Setup drag and drop */
1309 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1311 /* Connect signals */
1312 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1313 G_OBJECT (obj), "key-press-event",
1314 G_CALLBACK (on_key_pressed), NULL);
1316 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1317 (GObject*) modest_runtime_get_account_mgr (),
1318 "display_name_changed",
1319 G_CALLBACK (on_display_name_changed),
1323 * Track changes in the local account name (in the device it
1324 * will be the device name)
1326 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1329 G_CALLBACK(on_configuration_key_changed),
1332 gdk_color_parse ("000", &priv->active_color);
1335 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1336 G_OBJECT (obj), "notify::style",
1337 G_CALLBACK (on_notify_style), (gpointer) obj);
1341 tny_account_store_view_init (gpointer g, gpointer iface_data)
1343 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1345 klass->set_account_store = modest_folder_view_set_account_store;
1349 modest_folder_view_dispose (GObject *obj)
1351 static gboolean disposed = FALSE;
1352 ModestFolderViewPrivate *priv;
1357 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1359 #ifdef MODEST_TOOLKIT_HILDON2
1360 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1363 /* Free external references */
1364 if (priv->account_store) {
1365 g_object_unref (G_OBJECT(priv->account_store));
1366 priv->account_store = NULL;
1370 g_object_unref (G_OBJECT (priv->query));
1374 if (priv->folder_to_select) {
1375 g_object_unref (G_OBJECT(priv->folder_to_select));
1376 priv->folder_to_select = NULL;
1379 if (priv->cur_folder_store) {
1380 g_object_unref (priv->cur_folder_store);
1381 priv->cur_folder_store = NULL;
1384 if (priv->list_to_move) {
1385 g_object_unref (priv->list_to_move);
1386 priv->list_to_move = NULL;
1391 modest_folder_view_finalize (GObject *obj)
1393 ModestFolderViewPrivate *priv;
1395 g_return_if_fail (obj);
1397 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1399 if (priv->timer_expander != 0) {
1400 g_source_remove (priv->timer_expander);
1401 priv->timer_expander = 0;
1404 g_free (priv->local_account_name);
1405 g_free (priv->visible_account_id);
1406 g_free (priv->mailbox);
1408 /* Clear hidding array created by cut operation */
1409 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1411 gdk_color_parse ("000", &priv->active_color);
1413 G_OBJECT_CLASS(parent_class)->finalize (obj);
1418 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1420 ModestFolderViewPrivate *priv;
1423 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1424 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1426 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1427 device = tny_account_store_get_device (account_store);
1429 if (G_UNLIKELY (priv->account_store)) {
1431 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1432 G_OBJECT (priv->account_store),
1433 "account_inserted"))
1434 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1435 G_OBJECT (priv->account_store),
1436 "account_inserted");
1437 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1438 G_OBJECT (priv->account_store),
1440 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1441 G_OBJECT (priv->account_store),
1443 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1444 G_OBJECT (priv->account_store),
1446 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1447 G_OBJECT (priv->account_store),
1449 g_object_unref (G_OBJECT (priv->account_store));
1452 priv->account_store = g_object_ref (G_OBJECT (account_store));
1454 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1455 G_OBJECT(account_store), "account_removed",
1456 G_CALLBACK (on_account_removed), self);
1458 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1459 G_OBJECT(account_store), "account_inserted",
1460 G_CALLBACK (on_account_inserted), self);
1462 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1463 G_OBJECT(account_store), "account_changed",
1464 G_CALLBACK (on_account_changed), self);
1466 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1467 priv->reselect = FALSE;
1468 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1470 g_object_unref (G_OBJECT (device));
1474 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1477 ModestFolderView *self;
1478 GtkTreeModel *model, *filter_model;
1481 self = MODEST_FOLDER_VIEW (user_data);
1483 if (!get_inner_models (self, &filter_model, NULL, &model))
1486 /* Remove outbox from model */
1487 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1488 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1489 g_object_unref (outbox);
1492 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1496 on_account_inserted (TnyAccountStore *account_store,
1497 TnyAccount *account,
1500 ModestFolderViewPrivate *priv;
1501 GtkTreeModel *model, *filter_model;
1503 /* Ignore transport account insertions, we're not showing them
1504 in the folder view */
1505 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1508 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1511 /* If we're adding a new account, and there is no previous
1512 one, we need to select the visible server account */
1513 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1514 !priv->visible_account_id)
1515 modest_widget_memory_restore (modest_runtime_get_conf(),
1516 G_OBJECT (user_data),
1517 MODEST_CONF_FOLDER_VIEW_KEY);
1521 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1522 &filter_model, NULL, &model))
1525 /* Insert the account in the model */
1526 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1528 /* When the model is a list store (plain representation) the
1529 outbox is not a child of any account so we have to manually
1530 delete it because removing the local folders account won't
1531 delete it (because tny_folder_get_account() is not defined
1532 for a merge folder */
1533 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1534 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1535 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1536 (GObject*) account, "outbox-deleted",
1537 G_CALLBACK (on_outbox_deleted_cb),
1541 /* Refilter the model */
1542 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1547 same_account_selected (ModestFolderView *self,
1548 TnyAccount *account)
1550 ModestFolderViewPrivate *priv;
1551 gboolean same_account = FALSE;
1553 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1555 if (priv->cur_folder_store) {
1556 TnyAccount *selected_folder_account = NULL;
1558 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1559 selected_folder_account =
1560 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1562 selected_folder_account =
1563 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1566 if (selected_folder_account == account)
1567 same_account = TRUE;
1569 g_object_unref (selected_folder_account);
1571 return same_account;
1576 * Selects the first inbox or the local account in an idle
1579 on_idle_select_first_inbox_or_local (gpointer user_data)
1581 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1583 gdk_threads_enter ();
1584 modest_folder_view_select_first_inbox_or_local (self);
1585 gdk_threads_leave ();
1591 on_account_changed (TnyAccountStore *account_store,
1592 TnyAccount *tny_account,
1595 ModestFolderView *self;
1596 ModestFolderViewPrivate *priv;
1597 GtkTreeModel *model, *filter_model;
1598 GtkTreeSelection *sel;
1599 gboolean same_account;
1601 /* Ignore transport account insertions, we're not showing them
1602 in the folder view */
1603 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1606 self = MODEST_FOLDER_VIEW (user_data);
1607 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1609 /* Get the inner model */
1610 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1611 &filter_model, NULL, &model))
1614 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1616 /* Invalidate the cur_folder_store only if the selected folder
1617 belongs to the account that is being removed */
1618 same_account = same_account_selected (self, tny_account);
1620 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1621 gtk_tree_selection_unselect_all (sel);
1624 /* Remove the account from the model */
1625 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1627 /* Insert the account in the model */
1628 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1630 /* Refilter the model */
1631 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1633 /* Select the first INBOX if the currently selected folder
1634 belongs to the account that is being deleted */
1635 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1636 g_idle_add (on_idle_select_first_inbox_or_local, self);
1640 on_account_removed (TnyAccountStore *account_store,
1641 TnyAccount *account,
1644 ModestFolderView *self = NULL;
1645 ModestFolderViewPrivate *priv;
1646 GtkTreeModel *model, *filter_model;
1647 GtkTreeSelection *sel = NULL;
1648 gboolean same_account = FALSE;
1650 /* Ignore transport account removals, we're not showing them
1651 in the folder view */
1652 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1655 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1656 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1660 self = MODEST_FOLDER_VIEW (user_data);
1661 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1663 /* Invalidate the cur_folder_store only if the selected folder
1664 belongs to the account that is being removed */
1665 same_account = same_account_selected (self, account);
1667 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1668 gtk_tree_selection_unselect_all (sel);
1671 /* Invalidate row to select only if the folder to select
1672 belongs to the account that is being removed*/
1673 if (priv->folder_to_select) {
1674 TnyAccount *folder_to_select_account = NULL;
1676 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1677 if (folder_to_select_account == account) {
1678 modest_folder_view_disable_next_folder_selection (self);
1679 g_object_unref (priv->folder_to_select);
1680 priv->folder_to_select = NULL;
1682 g_object_unref (folder_to_select_account);
1685 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1686 &filter_model, NULL, &model))
1689 /* Disconnect the signal handler */
1690 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1691 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1692 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject*) account, "outbox-deleted"))
1693 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1694 (GObject *) account,
1698 /* Remove the account from the model */
1699 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1701 /* If the removed account is the currently viewed one then
1702 clear the configuration value. The new visible account will be the default account */
1703 if (priv->visible_account_id &&
1704 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1706 /* Clear the current visible account_id */
1707 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1708 modest_folder_view_set_mailbox (self, NULL);
1710 /* Call the restore method, this will set the new visible account */
1711 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1712 MODEST_CONF_FOLDER_VIEW_KEY);
1715 /* Refilter the model */
1716 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1718 /* Select the first INBOX if the currently selected folder
1719 belongs to the account that is being deleted */
1721 g_idle_add (on_idle_select_first_inbox_or_local, self);
1725 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1727 GtkTreeViewColumn *col;
1729 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1731 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1733 g_printerr ("modest: failed get column for title\n");
1737 gtk_tree_view_column_set_title (col, title);
1738 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1743 modest_folder_view_on_map (ModestFolderView *self,
1744 GdkEventExpose *event,
1747 ModestFolderViewPrivate *priv;
1749 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1751 /* This won't happen often */
1752 if (G_UNLIKELY (priv->reselect)) {
1753 /* Select the first inbox or the local account if not found */
1755 /* TODO: this could cause a lock at startup, so we
1756 comment it for the moment. We know that this will
1757 be a bug, because the INBOX is not selected, but we
1758 need to rewrite some parts of Modest to avoid the
1759 deathlock situation */
1760 /* TODO: check if this is still the case */
1761 priv->reselect = FALSE;
1762 modest_folder_view_select_first_inbox_or_local (self);
1763 /* Notify the display name observers */
1764 g_signal_emit (G_OBJECT(self),
1765 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1769 if (priv->reexpand) {
1770 expand_root_items (self);
1771 priv->reexpand = FALSE;
1778 modest_folder_view_new (TnyFolderStoreQuery *query)
1780 return modest_folder_view_new_full (query, TRUE);
1784 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1787 ModestFolderViewPrivate *priv;
1788 GtkTreeSelection *sel;
1790 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1791 #ifdef MODEST_TOOLKIT_HILDON2
1792 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1795 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1798 priv->query = g_object_ref (query);
1800 priv->do_refresh = do_refresh;
1802 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1803 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1804 (GObject*) sel, "changed",
1805 G_CALLBACK (on_selection_changed), self);
1807 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1808 self, "row-activated",
1809 G_CALLBACK (on_row_activated), self);
1811 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1812 self, "expose-event",
1813 G_CALLBACK (modest_folder_view_on_map), NULL);
1815 return GTK_WIDGET(self);
1818 /* this feels dirty; any other way to expand all the root items? */
1820 expand_root_items (ModestFolderView *self)
1823 GtkTreeModel *model;
1826 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1827 path = gtk_tree_path_new_first ();
1829 /* all folders should have child items, so.. */
1831 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1832 gtk_tree_path_next (path);
1833 } while (gtk_tree_model_get_iter (model, &iter, path));
1835 gtk_tree_path_free (path);
1839 is_parent_of (TnyFolder *a, TnyFolder *b)
1842 gboolean retval = FALSE;
1844 a_id = tny_folder_get_id (a);
1846 gchar *string_to_match;
1849 string_to_match = g_strconcat (a_id, "/", NULL);
1850 b_id = tny_folder_get_id (b);
1851 retval = g_str_has_prefix (b_id, string_to_match);
1852 g_free (string_to_match);
1858 typedef struct _ForeachFolderInfo {
1861 } ForeachFolderInfo;
1864 foreach_folder_with_id (GtkTreeModel *model,
1869 ForeachFolderInfo *info;
1872 info = (ForeachFolderInfo *) data;
1873 gtk_tree_model_get (model, iter,
1874 INSTANCE_COLUMN, &instance,
1877 if (TNY_IS_FOLDER (instance)) {
1880 id = tny_folder_get_id (TNY_FOLDER (instance));
1882 collate = g_utf8_collate_key (id, -1);
1883 info->found = !strcmp (info->needle, collate);
1889 g_object_unref (instance);
1897 has_folder_with_id (ModestFolderView *self, const gchar *id)
1899 GtkTreeModel *model;
1900 ForeachFolderInfo info = {NULL, FALSE};
1902 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1903 info.needle = g_utf8_collate_key (id, -1);
1905 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1906 g_free (info.needle);
1912 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1915 gboolean retval = FALSE;
1917 a_id = tny_folder_get_id (a);
1920 b_id = tny_folder_get_id (b);
1923 const gchar *last_bar;
1924 gchar *string_to_match;
1925 last_bar = g_strrstr (b_id, "/");
1930 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1931 retval = has_folder_with_id (self, string_to_match);
1932 g_free (string_to_match);
1940 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1942 ModestFolderViewPrivate *priv;
1943 TnyIterator *iterator;
1944 gboolean retval = TRUE;
1946 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1947 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1949 for (iterator = tny_list_create_iterator (priv->list_to_move);
1950 retval && !tny_iterator_is_done (iterator);
1951 tny_iterator_next (iterator)) {
1953 instance = tny_iterator_get_current (iterator);
1954 if (instance == (GObject *) folder) {
1956 } else if (TNY_IS_FOLDER (instance)) {
1957 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1959 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1962 g_object_unref (instance);
1964 g_object_unref (iterator);
1971 * We use this function to implement the
1972 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1973 * account in this case, and the local folders.
1976 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1978 ModestFolderViewPrivate *priv;
1979 gboolean retval = TRUE;
1980 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1981 GObject *instance = NULL;
1982 const gchar *id = NULL;
1984 gboolean found = FALSE;
1985 gboolean cleared = FALSE;
1986 ModestTnyFolderRules rules = 0;
1989 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1990 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1992 gtk_tree_model_get (model, iter,
1993 NAME_COLUMN, &fname,
1995 INSTANCE_COLUMN, &instance,
1998 /* Do not show if there is no instance, this could indeed
1999 happen when the model is being modified while it's being
2000 drawn. This could occur for example when moving folders
2007 if (TNY_IS_ACCOUNT (instance)) {
2008 TnyAccount *acc = TNY_ACCOUNT (instance);
2009 const gchar *account_id = tny_account_get_id (acc);
2011 /* If it isn't a special folder,
2012 * don't show it unless it is the visible account: */
2013 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2014 !modest_tny_account_is_virtual_local_folders (acc) &&
2015 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2017 /* Show only the visible account id */
2018 if (priv->visible_account_id) {
2019 if (strcmp (account_id, priv->visible_account_id))
2026 /* Never show these to the user. They are merged into one folder
2027 * in the local-folders account instead: */
2028 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2031 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2032 /* Only show special folders for current account if needed */
2033 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2034 TnyAccount *account;
2036 account = tny_folder_get_account (TNY_FOLDER (instance));
2038 if (TNY_IS_ACCOUNT (account)) {
2039 const gchar *account_id = tny_account_get_id (account);
2041 if (!modest_tny_account_is_virtual_local_folders (account) &&
2042 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2043 /* Show only the visible account id */
2044 if (priv->visible_account_id) {
2045 if (strcmp (account_id, priv->visible_account_id)) {
2047 } else if (priv->mailbox) {
2048 /* Filter mailboxes */
2049 if (!g_str_has_prefix (fname, priv->mailbox)) {
2051 } else if (!strcmp (fname, priv->mailbox)) {
2052 /* Hide mailbox parent */
2058 g_object_unref (account);
2065 /* Check hiding (if necessary) */
2066 cleared = modest_email_clipboard_cleared (priv->clipboard);
2067 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2068 id = tny_folder_get_id (TNY_FOLDER(instance));
2069 if (priv->hidding_ids != NULL)
2070 for (i=0; i < priv->n_selected && !found; i++)
2071 if (priv->hidding_ids[i] != NULL && id != NULL)
2072 found = (!strcmp (priv->hidding_ids[i], id));
2077 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2078 folder as no message can be move there according to UI specs */
2079 if (retval && !priv->show_non_move) {
2080 if (priv->list_to_move &&
2081 tny_list_get_length (priv->list_to_move) > 0 &&
2082 TNY_IS_FOLDER (instance)) {
2083 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2085 if (retval && TNY_IS_FOLDER (instance) &&
2086 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2088 case TNY_FOLDER_TYPE_OUTBOX:
2089 case TNY_FOLDER_TYPE_SENT:
2090 case TNY_FOLDER_TYPE_DRAFTS:
2093 case TNY_FOLDER_TYPE_UNKNOWN:
2094 case TNY_FOLDER_TYPE_NORMAL:
2095 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2096 if (type == TNY_FOLDER_TYPE_INVALID)
2097 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2099 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2100 type == TNY_FOLDER_TYPE_SENT
2101 || type == TNY_FOLDER_TYPE_DRAFTS)
2108 if (retval && TNY_IS_ACCOUNT (instance) &&
2109 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2110 ModestProtocolType protocol_type;
2112 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2113 retval = !modest_protocol_registry_protocol_type_has_tag
2114 (modest_runtime_get_protocol_registry (),
2116 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2120 /* apply special filters */
2121 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2122 if (TNY_IS_ACCOUNT (instance))
2126 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2127 if (TNY_IS_FOLDER (instance))
2131 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2132 if (TNY_IS_ACCOUNT (instance)) {
2133 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2135 } else if (TNY_IS_FOLDER (instance)) {
2136 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2141 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2142 if (TNY_IS_ACCOUNT (instance)) {
2143 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2145 } else if (TNY_IS_FOLDER (instance)) {
2146 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2151 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2152 /* A mailbox is a fake folder with an @ in the middle of the name */
2153 if (!TNY_IS_FOLDER (instance) ||
2154 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2157 const gchar *folder_name;
2158 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2159 if (!folder_name || strchr (folder_name, '@') == NULL)
2165 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2166 if (TNY_IS_FOLDER (instance)) {
2167 /* Check folder rules */
2168 ModestTnyFolderRules rules;
2170 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2171 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2172 } else if (TNY_IS_ACCOUNT (instance)) {
2173 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2181 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2182 if (TNY_IS_FOLDER (instance)) {
2183 TnyFolderType guess_type;
2185 if (TNY_FOLDER_TYPE_NORMAL) {
2186 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2192 case TNY_FOLDER_TYPE_OUTBOX:
2193 case TNY_FOLDER_TYPE_SENT:
2194 case TNY_FOLDER_TYPE_DRAFTS:
2195 case TNY_FOLDER_TYPE_ARCHIVE:
2196 case TNY_FOLDER_TYPE_INBOX:
2199 case TNY_FOLDER_TYPE_UNKNOWN:
2200 case TNY_FOLDER_TYPE_NORMAL:
2206 } else if (TNY_IS_ACCOUNT (instance)) {
2211 if (retval && TNY_IS_FOLDER (instance)) {
2212 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2215 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2216 if (TNY_IS_FOLDER (instance)) {
2217 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2218 } else if (TNY_IS_ACCOUNT (instance)) {
2223 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2224 if (TNY_IS_FOLDER (instance)) {
2225 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2226 } else if (TNY_IS_ACCOUNT (instance)) {
2231 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2232 if (TNY_IS_FOLDER (instance)) {
2233 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2234 } else if (TNY_IS_ACCOUNT (instance)) {
2240 g_object_unref (instance);
2248 modest_folder_view_update_model (ModestFolderView *self,
2249 TnyAccountStore *account_store)
2251 ModestFolderViewPrivate *priv;
2252 GtkTreeModel *model;
2253 GtkTreeModel *filter_model = NULL, *sortable = NULL, *old_tny_model;
2255 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2256 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2259 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2261 /* Notify that there is no folder selected */
2262 g_signal_emit (G_OBJECT(self),
2263 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2265 if (priv->cur_folder_store) {
2266 g_object_unref (priv->cur_folder_store);
2267 priv->cur_folder_store = NULL;
2270 /* FIXME: the local accounts are not shown when the query
2271 selects only the subscribed folders */
2272 #ifdef MODEST_TOOLKIT_HILDON2
2273 TnyGtkFolderListStoreFlags flags;
2274 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2275 if (!priv->do_refresh)
2276 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2277 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2279 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2280 MODEST_FOLDER_PATH_SEPARATOR);
2282 model = tny_gtk_folder_store_tree_model_new (NULL);
2285 /* When the model is a list store (plain representation) the
2286 outbox is not a child of any account so we have to manually
2287 delete it because removing the local folders account won't
2288 delete it (because tny_folder_get_account() is not defined
2289 for a merge folder */
2290 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2291 TnyAccount *account;
2292 ModestTnyAccountStore *acc_store;
2294 acc_store = modest_runtime_get_account_store ();
2295 account = modest_tny_account_store_get_local_folders_account (acc_store);
2297 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) account,
2299 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2300 (GObject *) account,
2303 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2304 (GObject*) account, "outbox-deleted",
2305 G_CALLBACK (on_outbox_deleted_cb),
2307 g_object_unref (account);
2310 /* Get the accounts: */
2311 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2313 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2315 sortable = gtk_tree_model_sort_new_with_model (model);
2316 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2318 GTK_SORT_ASCENDING);
2319 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2321 cmp_rows, NULL, NULL);
2323 /* Create filter model */
2324 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2325 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2330 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2331 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) old_tny_model,
2332 "activity-changed"))
2333 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2334 G_OBJECT (old_tny_model),
2335 "activity-changed");
2339 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2340 #ifndef MODEST_TOOLKIT_HILDON2
2341 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2342 G_OBJECT(filter_model), "row-inserted",
2343 (GCallback) on_row_inserted_maybe_select_folder, self);
2346 #ifdef MODEST_TOOLKIT_HILDON2
2347 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2350 G_CALLBACK (on_activity_changed),
2354 g_object_unref (model);
2355 g_object_unref (filter_model);
2356 g_object_unref (sortable);
2358 /* Force a reselection of the INBOX next time the widget is shown */
2359 priv->reselect = TRUE;
2366 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2368 GtkTreeModel *model = NULL;
2369 TnyFolderStore *folder = NULL;
2371 ModestFolderView *tree_view = NULL;
2372 ModestFolderViewPrivate *priv = NULL;
2373 gboolean selected = FALSE;
2375 g_return_if_fail (sel);
2376 g_return_if_fail (user_data);
2378 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2380 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2382 tree_view = MODEST_FOLDER_VIEW (user_data);
2385 gtk_tree_model_get (model, &iter,
2386 INSTANCE_COLUMN, &folder,
2389 /* If the folder is the same do not notify */
2390 if (folder && priv->cur_folder_store == folder) {
2391 g_object_unref (folder);
2396 /* Current folder was unselected */
2397 if (priv->cur_folder_store) {
2398 /* We must do this firstly because a libtinymail-camel
2399 implementation detail. If we issue the signal
2400 before doing the sync_async, then that signal could
2401 cause (and it actually does it) a free of the
2402 summary of the folder (because the main window will
2403 clear the headers view */
2404 #ifndef MODEST_TOOLKIT_HILDON2
2405 if (TNY_IS_FOLDER(priv->cur_folder_store))
2406 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2407 FALSE, NULL, NULL, NULL);
2410 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2411 priv->cur_folder_store, FALSE);
2413 g_object_unref (priv->cur_folder_store);
2414 priv->cur_folder_store = NULL;
2417 /* New current references */
2418 priv->cur_folder_store = folder;
2420 /* New folder has been selected. Do not notify if there is
2421 nothing new selected */
2423 g_signal_emit (G_OBJECT(tree_view),
2424 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2425 0, priv->cur_folder_store, TRUE);
2430 on_row_activated (GtkTreeView *treeview,
2431 GtkTreePath *treepath,
2432 GtkTreeViewColumn *column,
2435 GtkTreeModel *model = NULL;
2436 TnyFolderStore *folder = NULL;
2438 ModestFolderView *self = NULL;
2439 ModestFolderViewPrivate *priv = NULL;
2441 g_return_if_fail (treeview);
2442 g_return_if_fail (user_data);
2444 self = MODEST_FOLDER_VIEW (user_data);
2445 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2447 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2449 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2452 gtk_tree_model_get (model, &iter,
2453 INSTANCE_COLUMN, &folder,
2456 g_signal_emit (G_OBJECT(self),
2457 signals[FOLDER_ACTIVATED_SIGNAL],
2460 #ifdef MODEST_TOOLKIT_HILDON2
2461 HildonUIMode ui_mode;
2462 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2463 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2464 if (priv->cur_folder_store)
2465 g_object_unref (priv->cur_folder_store);
2466 priv->cur_folder_store = g_object_ref (folder);
2470 g_object_unref (folder);
2474 modest_folder_view_get_selected (ModestFolderView *self)
2476 ModestFolderViewPrivate *priv;
2478 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2480 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2481 if (priv->cur_folder_store)
2482 g_object_ref (priv->cur_folder_store);
2484 return priv->cur_folder_store;
2488 get_cmp_rows_type_pos (GObject *folder)
2490 /* Remote accounts -> Local account -> MMC account .*/
2493 if (TNY_IS_ACCOUNT (folder) &&
2494 modest_tny_account_is_virtual_local_folders (
2495 TNY_ACCOUNT (folder))) {
2497 } else if (TNY_IS_ACCOUNT (folder)) {
2498 TnyAccount *account = TNY_ACCOUNT (folder);
2499 const gchar *account_id = tny_account_get_id (account);
2500 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2506 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2507 return -1; /* Should never happen */
2512 inbox_is_special (TnyFolderStore *folder_store)
2514 gboolean is_special = TRUE;
2516 if (TNY_IS_FOLDER (folder_store)) {
2520 gchar *last_inbox_bar;
2522 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2523 downcase = g_utf8_strdown (id, -1);
2524 last_bar = g_strrstr (downcase, "/");
2526 last_inbox_bar = g_strrstr (downcase, "inbox/");
2527 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2538 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2540 TnyAccount *account;
2541 gboolean is_special;
2542 /* Inbox, Outbox, Drafts, Sent, User */
2545 if (!TNY_IS_FOLDER (folder_store))
2548 case TNY_FOLDER_TYPE_INBOX:
2550 account = tny_folder_get_account (folder_store);
2551 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2553 /* In inbox case we need to know if the inbox is really the top
2554 * inbox of the account, or if it's a submailbox inbox. To do
2555 * this we'll apply an heuristic rule: Find last "/" and check
2556 * if it's preceeded by another Inbox */
2557 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2558 g_object_unref (account);
2559 return is_special?0:4;
2562 case TNY_FOLDER_TYPE_OUTBOX:
2563 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2565 case TNY_FOLDER_TYPE_DRAFTS:
2567 account = tny_folder_get_account (folder_store);
2568 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2569 g_object_unref (account);
2570 return is_special?1:4;
2573 case TNY_FOLDER_TYPE_SENT:
2575 account = tny_folder_get_account (folder_store);
2576 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2577 g_object_unref (account);
2578 return is_special?3:4;
2587 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2589 const gchar *a1_name, *a2_name;
2591 a1_name = tny_account_get_name (a1);
2592 a2_name = tny_account_get_name (a2);
2594 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2598 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2600 TnyAccount *a1 = NULL, *a2 = NULL;
2603 if (TNY_IS_ACCOUNT (s1)) {
2604 a1 = TNY_ACCOUNT (g_object_ref (s1));
2605 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2606 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2609 if (TNY_IS_ACCOUNT (s2)) {
2610 a2 = TNY_ACCOUNT (g_object_ref (s2));
2611 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2612 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2629 /* First we sort with the type of account */
2630 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2634 cmp = compare_account_names (a1, a2);
2638 g_object_unref (a1);
2640 g_object_unref (a2);
2646 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2648 gint is_account1, is_account2;
2650 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2651 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2653 return is_account2 - is_account1;
2657 compare_folders (const gchar *name1, const gchar *name2)
2659 const gchar *separator1, *separator2;
2660 const gchar *next1, *next2;
2664 if (name1 == NULL || name1[0] == '\0')
2666 if (name2 == NULL || name2[0] == '\0')
2669 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2671 top1 = g_strndup (name1, separator1 - name1);
2673 top1 = g_strdup (name1);
2676 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2678 top2 = g_strndup (name2, separator2 - name2);
2680 top2 = g_strdup (name2);
2684 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2691 if (separator1 == NULL && separator2 == NULL)
2694 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2695 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2697 return compare_folders (next1, next2);
2702 * This function orders the mail accounts according to these rules:
2703 * 1st - remote accounts
2704 * 2nd - local account
2708 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2712 gchar *name1 = NULL;
2713 gchar *name2 = NULL;
2714 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2715 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2716 GObject *folder1 = NULL;
2717 GObject *folder2 = NULL;
2719 gtk_tree_model_get (tree_model, iter1,
2720 NAME_COLUMN, &name1,
2722 INSTANCE_COLUMN, &folder1,
2724 gtk_tree_model_get (tree_model, iter2,
2725 NAME_COLUMN, &name2,
2726 TYPE_COLUMN, &type2,
2727 INSTANCE_COLUMN, &folder2,
2730 /* Return if we get no folder. This could happen when folder
2731 operations are happening. The model is updated after the
2732 folder copy/move actually occurs, so there could be
2733 situations where the model to be drawn is not correct */
2734 if (!folder1 || !folder2)
2737 /* Sort by type. First the special folders, then the archives */
2738 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2742 /* Now we sort using the account of each folder */
2743 if (TNY_IS_FOLDER_STORE (folder1) &&
2744 TNY_IS_FOLDER_STORE (folder2)) {
2745 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2749 /* Each group is preceeded by its account */
2750 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2755 /* Pure sort by name */
2756 cmp = compare_folders (name1, name2);
2759 g_object_unref(G_OBJECT(folder1));
2761 g_object_unref(G_OBJECT(folder2));
2769 /*****************************************************************************/
2770 /* DRAG and DROP stuff */
2771 /*****************************************************************************/
2773 * This function fills the #GtkSelectionData with the row and the
2774 * model that has been dragged. It's called when this widget is a
2775 * source for dnd after the event drop happened
2778 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2779 guint info, guint time, gpointer data)
2781 GtkTreeSelection *selection;
2782 GtkTreeModel *model;
2784 GtkTreePath *source_row;
2786 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2787 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2789 source_row = gtk_tree_model_get_path (model, &iter);
2790 gtk_tree_set_row_drag_data (selection_data,
2794 gtk_tree_path_free (source_row);
2798 typedef struct _DndHelper {
2799 ModestFolderView *folder_view;
2800 gboolean delete_source;
2801 GtkTreePath *source_row;
2805 dnd_helper_destroyer (DndHelper *helper)
2807 /* Free the helper */
2808 gtk_tree_path_free (helper->source_row);
2809 g_slice_free (DndHelper, helper);
2813 xfer_folder_cb (ModestMailOperation *mail_op,
2814 TnyFolder *new_folder,
2818 /* Select the folder */
2819 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2825 /* get the folder for the row the treepath refers to. */
2826 /* folder must be unref'd */
2827 static TnyFolderStore *
2828 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2831 TnyFolderStore *folder = NULL;
2833 if (gtk_tree_model_get_iter (model,&iter, path))
2834 gtk_tree_model_get (model, &iter,
2835 INSTANCE_COLUMN, &folder,
2842 * This function is used by drag_data_received_cb to manage drag and
2843 * drop of a header, i.e, and drag from the header view to the folder
2847 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2848 GtkTreeModel *dest_model,
2849 GtkTreePath *dest_row,
2850 GtkSelectionData *selection_data)
2852 TnyList *headers = NULL;
2853 TnyFolder *folder = NULL, *src_folder = NULL;
2854 TnyFolderType folder_type;
2855 GtkTreeIter source_iter, dest_iter;
2856 ModestWindowMgr *mgr = NULL;
2857 ModestWindow *main_win = NULL;
2858 gchar **uris, **tmp;
2860 /* Build the list of headers */
2861 mgr = modest_runtime_get_window_mgr ();
2862 headers = tny_simple_list_new ();
2863 uris = modest_dnd_selection_data_get_paths (selection_data);
2866 while (*tmp != NULL) {
2869 gboolean first = TRUE;
2872 path = gtk_tree_path_new_from_string (*tmp);
2873 gtk_tree_model_get_iter (source_model, &source_iter, path);
2874 gtk_tree_model_get (source_model, &source_iter,
2875 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2878 /* Do not enable d&d of headers already opened */
2879 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2880 tny_list_append (headers, G_OBJECT (header));
2882 if (G_UNLIKELY (first)) {
2883 src_folder = tny_header_get_folder (header);
2887 /* Free and go on */
2888 gtk_tree_path_free (path);
2889 g_object_unref (header);
2894 /* This could happen ig we perform a d&d very quickly over the
2895 same row that row could dissapear because message is
2897 if (!TNY_IS_FOLDER (src_folder))
2900 /* Get the target folder */
2901 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2902 gtk_tree_model_get (dest_model, &dest_iter,
2906 if (!folder || !TNY_IS_FOLDER(folder)) {
2907 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2911 folder_type = modest_tny_folder_guess_folder_type (folder);
2912 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2913 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2914 goto cleanup; /* cannot move messages there */
2917 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2918 /* g_warning ("folder not writable"); */
2919 goto cleanup; /* verboten! */
2922 /* Ask for confirmation to move */
2923 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2925 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2929 /* Transfer messages */
2930 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2935 if (G_IS_OBJECT (src_folder))
2936 g_object_unref (src_folder);
2937 if (G_IS_OBJECT(folder))
2938 g_object_unref (G_OBJECT (folder));
2939 if (G_IS_OBJECT(headers))
2940 g_object_unref (headers);
2944 TnyFolderStore *src_folder;
2945 TnyFolderStore *dst_folder;
2946 ModestFolderView *folder_view;
2951 dnd_folder_info_destroyer (DndFolderInfo *info)
2953 if (info->src_folder)
2954 g_object_unref (info->src_folder);
2955 if (info->dst_folder)
2956 g_object_unref (info->dst_folder);
2957 g_slice_free (DndFolderInfo, info);
2961 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2962 GtkWindow *parent_window,
2963 TnyAccount *account)
2966 modest_ui_actions_on_account_connection_error (parent_window, account);
2968 /* Free the helper & info */
2969 dnd_helper_destroyer (info->helper);
2970 dnd_folder_info_destroyer (info);
2974 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2976 GtkWindow *parent_window,
2977 TnyAccount *account,
2980 DndFolderInfo *info = NULL;
2981 ModestMailOperation *mail_op;
2983 info = (DndFolderInfo *) user_data;
2985 if (err || canceled) {
2986 dnd_on_connection_failed_destroyer (info, parent_window, account);
2990 /* Do the mail operation */
2991 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2992 modest_ui_actions_move_folder_error_handler,
2993 info->src_folder, NULL);
2995 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2998 /* Transfer the folder */
2999 modest_mail_operation_xfer_folder (mail_op,
3000 TNY_FOLDER (info->src_folder),
3002 info->helper->delete_source,
3004 info->helper->folder_view);
3007 g_object_unref (G_OBJECT (mail_op));
3008 dnd_helper_destroyer (info->helper);
3009 dnd_folder_info_destroyer (info);
3014 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3016 GtkWindow *parent_window,
3017 TnyAccount *account,
3020 DndFolderInfo *info = NULL;
3022 info = (DndFolderInfo *) user_data;
3024 if (err || canceled) {
3025 dnd_on_connection_failed_destroyer (info, parent_window, account);
3029 /* Connect to source folder and perform the copy/move */
3030 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3032 drag_and_drop_from_folder_view_src_folder_performer,
3037 * This function is used by drag_data_received_cb to manage drag and
3038 * drop of a folder, i.e, and drag from the folder view to the same
3042 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3043 GtkTreeModel *dest_model,
3044 GtkTreePath *dest_row,
3045 GtkSelectionData *selection_data,
3048 GtkTreeIter dest_iter, iter;
3049 TnyFolderStore *dest_folder = NULL;
3050 TnyFolderStore *folder = NULL;
3051 gboolean forbidden = FALSE;
3053 DndFolderInfo *info = NULL;
3055 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3057 g_warning ("%s: BUG: no main window", __FUNCTION__);
3058 dnd_helper_destroyer (helper);
3063 /* check the folder rules for the destination */
3064 folder = tree_path_to_folder (dest_model, dest_row);
3065 if (TNY_IS_FOLDER(folder)) {
3066 ModestTnyFolderRules rules =
3067 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3068 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3069 } else if (TNY_IS_FOLDER_STORE(folder)) {
3070 /* enable local root as destination for folders */
3071 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3072 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3075 g_object_unref (folder);
3078 /* check the folder rules for the source */
3079 folder = tree_path_to_folder (source_model, helper->source_row);
3080 if (TNY_IS_FOLDER(folder)) {
3081 ModestTnyFolderRules rules =
3082 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3083 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3086 g_object_unref (folder);
3090 /* Check if the drag is possible */
3091 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3093 modest_platform_run_information_dialog ((GtkWindow *) win,
3094 _("mail_in_ui_folder_move_target_error"),
3096 /* Restore the previous selection */
3097 folder = tree_path_to_folder (source_model, helper->source_row);
3099 if (TNY_IS_FOLDER (folder))
3100 modest_folder_view_select_folder (helper->folder_view,
3101 TNY_FOLDER (folder), FALSE);
3102 g_object_unref (folder);
3104 dnd_helper_destroyer (helper);
3109 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3110 gtk_tree_model_get (dest_model, &dest_iter,
3113 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3114 gtk_tree_model_get (source_model, &iter,
3118 /* Create the info for the performer */
3119 info = g_slice_new0 (DndFolderInfo);
3120 info->src_folder = g_object_ref (folder);
3121 info->dst_folder = g_object_ref (dest_folder);
3122 info->helper = helper;
3124 /* Connect to the destination folder and perform the copy/move */
3125 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3127 drag_and_drop_from_folder_view_dst_folder_performer,
3131 g_object_unref (dest_folder);
3132 g_object_unref (folder);
3136 * This function receives the data set by the "drag-data-get" signal
3137 * handler. This information comes within the #GtkSelectionData. This
3138 * function will manage both the drags of folders of the treeview and
3139 * drags of headers of the header view widget.
3142 on_drag_data_received (GtkWidget *widget,
3143 GdkDragContext *context,
3146 GtkSelectionData *selection_data,
3151 GtkWidget *source_widget;
3152 GtkTreeModel *dest_model, *source_model;
3153 GtkTreePath *source_row, *dest_row;
3154 GtkTreeViewDropPosition pos;
3155 gboolean delete_source = FALSE;
3156 gboolean success = FALSE;
3158 /* Do not allow further process */
3159 g_signal_stop_emission_by_name (widget, "drag-data-received");
3160 source_widget = gtk_drag_get_source_widget (context);
3162 /* Get the action */
3163 if (context->action == GDK_ACTION_MOVE) {
3164 delete_source = TRUE;
3166 /* Notify that there is no folder selected. We need to
3167 do this in order to update the headers view (and
3168 its monitors, because when moving, the old folder
3169 won't longer exist. We can not wait for the end of
3170 the operation, because the operation won't start if
3171 the folder is in use */
3172 if (source_widget == widget) {
3173 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3174 gtk_tree_selection_unselect_all (sel);
3178 /* Check if the get_data failed */
3179 if (selection_data == NULL || selection_data->length < 0)
3182 /* Select the destination model */
3183 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3185 /* Get the path to the destination row. Can not call
3186 gtk_tree_view_get_drag_dest_row() because the source row
3187 is not selected anymore */
3188 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3191 /* Only allow drops IN other rows */
3193 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3194 pos == GTK_TREE_VIEW_DROP_AFTER)
3198 /* Drags from the header view */
3199 if (source_widget != widget) {
3200 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3202 drag_and_drop_from_header_view (source_model,
3207 DndHelper *helper = NULL;
3209 /* Get the source model and row */
3210 gtk_tree_get_row_drag_data (selection_data,
3214 /* Create the helper */
3215 helper = g_slice_new0 (DndHelper);
3216 helper->delete_source = delete_source;
3217 helper->source_row = gtk_tree_path_copy (source_row);
3218 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3220 drag_and_drop_from_folder_view (source_model,
3226 gtk_tree_path_free (source_row);
3230 gtk_tree_path_free (dest_row);
3233 /* Finish the drag and drop */
3234 gtk_drag_finish (context, success, FALSE, time);
3238 * We define a "drag-drop" signal handler because we do not want to
3239 * use the default one, because the default one always calls
3240 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3241 * signal handler, because there we have all the information available
3242 * to know if the dnd was a success or not.
3245 drag_drop_cb (GtkWidget *widget,
3246 GdkDragContext *context,
3254 if (!context->targets)
3257 /* Check if we're dragging a folder row */
3258 target = gtk_drag_dest_find_target (widget, context, NULL);
3260 /* Request the data from the source. */
3261 gtk_drag_get_data(widget, context, target, time);
3267 * This function expands a node of a tree view if it's not expanded
3268 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3269 * does that, so that's why they're here.
3272 expand_row_timeout (gpointer data)
3274 GtkTreeView *tree_view = data;
3275 GtkTreePath *dest_path = NULL;
3276 GtkTreeViewDropPosition pos;
3277 gboolean result = FALSE;
3279 gdk_threads_enter ();
3281 gtk_tree_view_get_drag_dest_row (tree_view,
3286 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3287 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3288 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3289 gtk_tree_path_free (dest_path);
3293 gtk_tree_path_free (dest_path);
3298 gdk_threads_leave ();
3304 * This function is called whenever the pointer is moved over a widget
3305 * while dragging some data. It installs a timeout that will expand a
3306 * node of the treeview if not expanded yet. This function also calls
3307 * gdk_drag_status in order to set the suggested action that will be
3308 * used by the "drag-data-received" signal handler to know if we
3309 * should do a move or just a copy of the data.
3312 on_drag_motion (GtkWidget *widget,
3313 GdkDragContext *context,
3319 GtkTreeViewDropPosition pos;
3320 GtkTreePath *dest_row;
3321 GtkTreeModel *dest_model;
3322 ModestFolderViewPrivate *priv;
3323 GdkDragAction suggested_action;
3324 gboolean valid_location = FALSE;
3325 TnyFolderStore *folder = NULL;
3327 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3329 if (priv->timer_expander != 0) {
3330 g_source_remove (priv->timer_expander);
3331 priv->timer_expander = 0;
3334 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3339 /* Do not allow drops between folders */
3341 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3342 pos == GTK_TREE_VIEW_DROP_AFTER) {
3343 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3344 gdk_drag_status(context, 0, time);
3345 valid_location = FALSE;
3348 valid_location = TRUE;
3351 /* Check that the destination folder is writable */
3352 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3353 folder = tree_path_to_folder (dest_model, dest_row);
3354 if (folder && TNY_IS_FOLDER (folder)) {
3355 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3357 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3358 valid_location = FALSE;
3363 /* Expand the selected row after 1/2 second */
3364 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3365 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3367 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3369 /* Select the desired action. By default we pick MOVE */
3370 suggested_action = GDK_ACTION_MOVE;
3372 if (context->actions == GDK_ACTION_COPY)
3373 gdk_drag_status(context, GDK_ACTION_COPY, time);
3374 else if (context->actions == GDK_ACTION_MOVE)
3375 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3376 else if (context->actions & suggested_action)
3377 gdk_drag_status(context, suggested_action, time);
3379 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3383 g_object_unref (folder);
3385 gtk_tree_path_free (dest_row);
3387 g_signal_stop_emission_by_name (widget, "drag-motion");
3389 return valid_location;
3393 * This function sets the treeview as a source and a target for dnd
3394 * events. It also connects all the requirede signals.
3397 setup_drag_and_drop (GtkTreeView *self)
3399 /* Set up the folder view as a dnd destination. Set only the
3400 highlight flag, otherwise gtk will have a different
3402 #ifdef MODEST_TOOLKIT_HILDON2
3405 ModestFolderViewPrivate *priv;
3407 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3409 gtk_drag_dest_set (GTK_WIDGET (self),
3410 GTK_DEST_DEFAULT_HIGHLIGHT,
3411 folder_view_drag_types,
3412 G_N_ELEMENTS (folder_view_drag_types),
3413 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3415 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3416 G_OBJECT (self), "drag_data_received",
3417 G_CALLBACK (on_drag_data_received), NULL);
3420 /* Set up the treeview as a dnd source */
3421 gtk_drag_source_set (GTK_WIDGET (self),
3423 folder_view_drag_types,
3424 G_N_ELEMENTS (folder_view_drag_types),
3425 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3427 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3428 G_OBJECT (self), "drag_motion",
3429 G_CALLBACK (on_drag_motion), NULL);
3431 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3432 G_OBJECT (self), "drag_data_get",
3433 G_CALLBACK (on_drag_data_get), NULL);
3435 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3436 G_OBJECT (self), "drag_drop",
3437 G_CALLBACK (drag_drop_cb), NULL);
3441 * This function manages the navigation through the folders using the
3442 * keyboard or the hardware keys in the device
3445 on_key_pressed (GtkWidget *self,
3449 GtkTreeSelection *selection;
3451 GtkTreeModel *model;
3452 gboolean retval = FALSE;
3454 /* Up and Down are automatically managed by the treeview */
3455 if (event->keyval == GDK_Return) {
3456 /* Expand/Collapse the selected row */
3457 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3458 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3461 path = gtk_tree_model_get_path (model, &iter);
3463 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3464 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3466 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3467 gtk_tree_path_free (path);
3469 /* No further processing */
3477 * We listen to the changes in the local folder account name key,
3478 * because we want to show the right name in the view. The local
3479 * folder account name corresponds to the device name in the Maemo
3480 * version. We do this because we do not want to query gconf on each
3481 * tree view refresh. It's better to cache it and change whenever
3485 on_configuration_key_changed (ModestConf* conf,
3487 ModestConfEvent event,
3488 ModestConfNotificationId id,
3489 ModestFolderView *self)
3491 ModestFolderViewPrivate *priv;
3494 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3495 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3497 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3498 g_free (priv->local_account_name);
3500 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3501 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3503 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3504 MODEST_CONF_DEVICE_NAME, NULL);
3506 /* Force a redraw */
3507 #if GTK_CHECK_VERSION(2, 8, 0)
3508 GtkTreeViewColumn * tree_column;
3510 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3512 gtk_tree_view_column_queue_resize (tree_column);
3514 gtk_widget_queue_draw (GTK_WIDGET (self));
3520 modest_folder_view_set_style (ModestFolderView *self,
3521 ModestFolderViewStyle style)
3523 ModestFolderViewPrivate *priv;
3525 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3526 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3527 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3529 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3532 priv->style = style;
3536 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3537 const gchar *account_id)
3539 ModestFolderViewPrivate *priv;
3540 GtkTreeModel *model;
3542 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3544 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3546 /* This will be used by the filter_row callback,
3547 * to decided which rows to show: */
3548 if (priv->visible_account_id) {
3549 g_free (priv->visible_account_id);
3550 priv->visible_account_id = NULL;
3553 priv->visible_account_id = g_strdup (account_id);
3556 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3557 if (GTK_IS_TREE_MODEL_FILTER (model))
3558 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3560 /* Save settings to gconf */
3561 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3562 MODEST_CONF_FOLDER_VIEW_KEY);
3564 /* Notify observers */
3565 g_signal_emit (G_OBJECT(self),
3566 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3571 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3573 ModestFolderViewPrivate *priv;
3575 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3577 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3579 return (const gchar *) priv->visible_account_id;
3583 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3587 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3589 gtk_tree_model_get (model, iter,
3593 gboolean result = FALSE;
3594 if (type == TNY_FOLDER_TYPE_INBOX) {
3598 *inbox_iter = *iter;
3602 if (gtk_tree_model_iter_children (model, &child, iter)) {
3603 if (find_inbox_iter (model, &child, inbox_iter))
3607 } while (gtk_tree_model_iter_next (model, iter));
3616 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3618 #ifndef MODEST_TOOLKIT_HILDON2
3619 GtkTreeModel *model;
3620 GtkTreeIter iter, inbox_iter;
3621 GtkTreeSelection *sel;
3622 GtkTreePath *path = NULL;
3624 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3626 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3630 expand_root_items (self);
3631 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3633 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3634 g_warning ("%s: model is empty", __FUNCTION__);
3638 if (find_inbox_iter (model, &iter, &inbox_iter))
3639 path = gtk_tree_model_get_path (model, &inbox_iter);
3641 path = gtk_tree_path_new_first ();
3643 /* Select the row and free */
3644 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3645 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3646 gtk_tree_path_free (path);
3649 gtk_widget_grab_focus (GTK_WIDGET(self));
3656 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3661 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3662 TnyFolder* a_folder;
3665 gtk_tree_model_get (model, iter,
3666 INSTANCE_COLUMN, &a_folder,
3672 if (folder == a_folder) {
3673 g_object_unref (a_folder);
3674 *folder_iter = *iter;
3677 g_object_unref (a_folder);
3679 if (gtk_tree_model_iter_children (model, &child, iter)) {
3680 if (find_folder_iter (model, &child, folder_iter, folder))
3684 } while (gtk_tree_model_iter_next (model, iter));
3689 #ifndef MODEST_TOOLKIT_HILDON2
3691 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3694 ModestFolderView *self)
3696 ModestFolderViewPrivate *priv = NULL;
3697 GtkTreeSelection *sel;
3698 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3699 GObject *instance = NULL;
3701 if (!MODEST_IS_FOLDER_VIEW(self))
3704 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3706 priv->reexpand = TRUE;
3708 gtk_tree_model_get (tree_model, iter,
3710 INSTANCE_COLUMN, &instance,
3716 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3717 priv->folder_to_select = g_object_ref (instance);
3719 g_object_unref (instance);
3721 if (priv->folder_to_select) {
3723 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3726 path = gtk_tree_model_get_path (tree_model, iter);
3727 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3729 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3731 gtk_tree_selection_select_iter (sel, iter);
3732 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3734 gtk_tree_path_free (path);
3738 modest_folder_view_disable_next_folder_selection (self);
3740 /* Refilter the model */
3741 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3747 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3749 ModestFolderViewPrivate *priv;
3751 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3753 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3755 if (priv->folder_to_select)
3756 g_object_unref(priv->folder_to_select);
3758 priv->folder_to_select = NULL;
3762 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3763 gboolean after_change)
3765 GtkTreeModel *model;
3766 GtkTreeIter iter, folder_iter;
3767 GtkTreeSelection *sel;
3768 ModestFolderViewPrivate *priv = NULL;
3770 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3771 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3773 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3776 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3777 gtk_tree_selection_unselect_all (sel);
3779 if (priv->folder_to_select)
3780 g_object_unref(priv->folder_to_select);
3781 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3785 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3790 /* Refilter the model, before selecting the folder */
3791 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3793 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3794 g_warning ("%s: model is empty", __FUNCTION__);
3798 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3801 path = gtk_tree_model_get_path (model, &folder_iter);
3802 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3804 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3805 gtk_tree_selection_select_iter (sel, &folder_iter);
3806 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3808 gtk_tree_path_free (path);
3816 modest_folder_view_copy_selection (ModestFolderView *self)
3818 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3820 /* Copy selection */
3821 _clipboard_set_selected_data (self, FALSE);
3825 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3827 ModestFolderViewPrivate *priv = NULL;
3828 GtkTreeModel *model = NULL;
3829 const gchar **hidding = NULL;
3830 guint i, n_selected;
3832 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3833 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3835 /* Copy selection */
3836 if (!_clipboard_set_selected_data (folder_view, TRUE))
3839 /* Get hidding ids */
3840 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3842 /* Clear hidding array created by previous cut operation */
3843 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3845 /* Copy hidding array */
3846 priv->n_selected = n_selected;
3847 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3848 for (i=0; i < n_selected; i++)
3849 priv->hidding_ids[i] = g_strdup(hidding[i]);
3851 /* Hide cut folders */
3852 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3853 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3857 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3858 ModestFolderView *folder_view_dst)
3860 GtkTreeModel *filter_model = NULL;
3861 GtkTreeModel *model = NULL;
3862 GtkTreeModel *new_filter_model = NULL;
3863 GtkTreeModel *old_tny_model = NULL;
3864 GtkTreeModel *new_tny_model = NULL;
3865 ModestFolderViewPrivate *dst_priv;
3867 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3868 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3870 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3871 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3872 new_tny_model = NULL;
3875 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3876 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3877 G_OBJECT (old_tny_model),
3878 "activity-changed");
3880 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3881 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3883 /* Build new filter model */
3884 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3885 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3892 /* Set copied model */
3893 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3894 #ifndef MODEST_TOOLKIT_HILDON2
3895 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3896 G_OBJECT(new_filter_model), "row-inserted",
3897 (GCallback) on_row_inserted_maybe_select_folder,
3900 #ifdef MODEST_TOOLKIT_HILDON2
3901 if (new_tny_model) {
3902 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3903 G_OBJECT (new_tny_model),
3905 G_CALLBACK (on_activity_changed),
3911 g_object_unref (new_filter_model);
3915 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3918 GtkTreeModel *model = NULL;
3919 ModestFolderViewPrivate* priv;
3921 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3923 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3924 priv->show_non_move = show;
3925 /* modest_folder_view_update_model(folder_view, */
3926 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3928 /* Hide special folders */
3929 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3930 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3931 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3936 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3939 ModestFolderViewPrivate* priv;
3941 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3943 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3944 priv->show_message_count = show;
3946 g_object_set (G_OBJECT (priv->messages_renderer),
3947 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3951 /* Returns FALSE if it did not selected anything */
3953 _clipboard_set_selected_data (ModestFolderView *folder_view,
3956 ModestFolderViewPrivate *priv = NULL;
3957 TnyFolderStore *folder = NULL;
3958 gboolean retval = FALSE;
3960 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3961 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3963 /* Set selected data on clipboard */
3964 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3965 folder = modest_folder_view_get_selected (folder_view);
3967 /* Do not allow to select an account */
3968 if (TNY_IS_FOLDER (folder)) {
3969 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3974 g_object_unref (folder);
3980 _clear_hidding_filter (ModestFolderView *folder_view)
3982 ModestFolderViewPrivate *priv;
3985 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3986 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3988 if (priv->hidding_ids != NULL) {
3989 for (i=0; i < priv->n_selected; i++)
3990 g_free (priv->hidding_ids[i]);
3991 g_free(priv->hidding_ids);
3997 on_display_name_changed (ModestAccountMgr *mgr,
3998 const gchar *account,
4001 ModestFolderView *self;
4003 self = MODEST_FOLDER_VIEW (user_data);
4005 /* Force a redraw */
4006 #if GTK_CHECK_VERSION(2, 8, 0)
4007 GtkTreeViewColumn * tree_column;
4009 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4011 gtk_tree_view_column_queue_resize (tree_column);
4013 gtk_widget_queue_draw (GTK_WIDGET (self));
4018 modest_folder_view_set_cell_style (ModestFolderView *self,
4019 ModestFolderViewCellStyle cell_style)
4021 ModestFolderViewPrivate *priv = NULL;
4023 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4024 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4026 priv->cell_style = cell_style;
4028 g_object_set (G_OBJECT (priv->messages_renderer),
4029 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4032 gtk_widget_queue_draw (GTK_WIDGET (self));
4036 update_style (ModestFolderView *self)
4038 ModestFolderViewPrivate *priv;
4039 GdkColor style_color, style_active_color;
4040 PangoAttrList *attr_list;
4042 PangoAttribute *attr;
4044 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4045 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4049 attr_list = pango_attr_list_new ();
4050 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4051 gdk_color_parse ("grey", &style_color);
4053 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4054 pango_attr_list_insert (attr_list, attr);
4057 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4059 "SmallSystemFont", NULL,
4062 attr = pango_attr_font_desc_new (pango_font_description_copy
4063 (style->font_desc));
4064 pango_attr_list_insert (attr_list, attr);
4066 g_object_set (G_OBJECT (priv->messages_renderer),
4067 "foreground-gdk", &style_color,
4068 "foreground-set", TRUE,
4069 "attributes", attr_list,
4071 pango_attr_list_unref (attr_list);
4074 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4075 priv->active_color = style_active_color;
4077 gdk_color_parse ("000", &(priv->active_color));
4082 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4084 if (strcmp ("style", spec->name) == 0) {
4085 update_style (MODEST_FOLDER_VIEW (obj));
4086 gtk_widget_queue_draw (GTK_WIDGET (obj));
4091 modest_folder_view_set_filter (ModestFolderView *self,
4092 ModestFolderViewFilter filter)
4094 ModestFolderViewPrivate *priv;
4095 GtkTreeModel *filter_model;
4097 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4098 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4100 priv->filter |= filter;
4102 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4103 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4104 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4109 modest_folder_view_unset_filter (ModestFolderView *self,
4110 ModestFolderViewFilter filter)
4112 ModestFolderViewPrivate *priv;
4113 GtkTreeModel *filter_model;
4115 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4116 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4118 priv->filter &= ~filter;
4120 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4121 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4122 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4127 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4128 ModestTnyFolderRules rules)
4130 GtkTreeModel *filter_model;
4132 gboolean fulfil = FALSE;
4134 if (!get_inner_models (self, &filter_model, NULL, NULL))
4137 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4141 TnyFolderStore *folder;
4143 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4145 if (TNY_IS_FOLDER (folder)) {
4146 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4147 /* Folder rules are negative: non_writable, non_deletable... */
4148 if (!(folder_rules & rules))
4151 g_object_unref (folder);
4154 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4160 modest_folder_view_set_list_to_move (ModestFolderView *self,
4163 ModestFolderViewPrivate *priv;
4165 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4166 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4168 if (priv->list_to_move)
4169 g_object_unref (priv->list_to_move);
4172 g_object_ref (list);
4174 priv->list_to_move = list;
4178 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4180 ModestFolderViewPrivate *priv;
4182 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4183 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4186 g_free (priv->mailbox);
4188 priv->mailbox = g_strdup (mailbox);
4190 /* Notify observers */
4191 g_signal_emit (G_OBJECT(self),
4192 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4193 priv->visible_account_id);
4197 modest_folder_view_get_mailbox (ModestFolderView *self)
4199 ModestFolderViewPrivate *priv;
4201 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4202 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4204 return (const gchar *) priv->mailbox;
4208 modest_folder_view_get_activity (ModestFolderView *self)
4210 ModestFolderViewPrivate *priv;
4211 GtkTreeModel *inner_model;
4213 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4214 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4215 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4217 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4218 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4224 #ifdef MODEST_TOOLKIT_HILDON2
4226 on_activity_changed (TnyGtkFolderListStore *store,
4228 ModestFolderView *folder_view)
4230 ModestFolderViewPrivate *priv;
4232 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4233 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4234 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4236 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,