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_DELAYED_REFRESH;
2278 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2279 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2281 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2282 MODEST_FOLDER_PATH_SEPARATOR);
2284 model = tny_gtk_folder_store_tree_model_new (NULL);
2287 /* When the model is a list store (plain representation) the
2288 outbox is not a child of any account so we have to manually
2289 delete it because removing the local folders account won't
2290 delete it (because tny_folder_get_account() is not defined
2291 for a merge folder */
2292 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2293 TnyAccount *account;
2294 ModestTnyAccountStore *acc_store;
2296 acc_store = modest_runtime_get_account_store ();
2297 account = modest_tny_account_store_get_local_folders_account (acc_store);
2299 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) account,
2301 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2302 (GObject *) account,
2305 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2306 (GObject*) account, "outbox-deleted",
2307 G_CALLBACK (on_outbox_deleted_cb),
2309 g_object_unref (account);
2312 /* Get the accounts: */
2313 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2315 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2317 sortable = gtk_tree_model_sort_new_with_model (model);
2318 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2320 GTK_SORT_ASCENDING);
2321 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2323 cmp_rows, NULL, NULL);
2325 /* Create filter model */
2326 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2327 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2332 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2333 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) old_tny_model,
2334 "activity-changed"))
2335 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2336 G_OBJECT (old_tny_model),
2337 "activity-changed");
2341 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2342 #ifndef MODEST_TOOLKIT_HILDON2
2343 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2344 G_OBJECT(filter_model), "row-inserted",
2345 (GCallback) on_row_inserted_maybe_select_folder, self);
2348 #ifdef MODEST_TOOLKIT_HILDON2
2349 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2352 G_CALLBACK (on_activity_changed),
2356 g_object_unref (model);
2357 g_object_unref (filter_model);
2358 g_object_unref (sortable);
2360 /* Force a reselection of the INBOX next time the widget is shown */
2361 priv->reselect = TRUE;
2368 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2370 GtkTreeModel *model = NULL;
2371 TnyFolderStore *folder = NULL;
2373 ModestFolderView *tree_view = NULL;
2374 ModestFolderViewPrivate *priv = NULL;
2375 gboolean selected = FALSE;
2377 g_return_if_fail (sel);
2378 g_return_if_fail (user_data);
2380 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2382 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2384 tree_view = MODEST_FOLDER_VIEW (user_data);
2387 gtk_tree_model_get (model, &iter,
2388 INSTANCE_COLUMN, &folder,
2391 /* If the folder is the same do not notify */
2392 if (folder && priv->cur_folder_store == folder) {
2393 g_object_unref (folder);
2398 /* Current folder was unselected */
2399 if (priv->cur_folder_store) {
2400 /* We must do this firstly because a libtinymail-camel
2401 implementation detail. If we issue the signal
2402 before doing the sync_async, then that signal could
2403 cause (and it actually does it) a free of the
2404 summary of the folder (because the main window will
2405 clear the headers view */
2406 #ifndef MODEST_TOOLKIT_HILDON2
2407 if (TNY_IS_FOLDER(priv->cur_folder_store))
2408 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2409 FALSE, NULL, NULL, NULL);
2412 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2413 priv->cur_folder_store, FALSE);
2415 g_object_unref (priv->cur_folder_store);
2416 priv->cur_folder_store = NULL;
2419 /* New current references */
2420 priv->cur_folder_store = folder;
2422 /* New folder has been selected. Do not notify if there is
2423 nothing new selected */
2425 g_signal_emit (G_OBJECT(tree_view),
2426 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2427 0, priv->cur_folder_store, TRUE);
2432 on_row_activated (GtkTreeView *treeview,
2433 GtkTreePath *treepath,
2434 GtkTreeViewColumn *column,
2437 GtkTreeModel *model = NULL;
2438 TnyFolderStore *folder = NULL;
2440 ModestFolderView *self = NULL;
2441 ModestFolderViewPrivate *priv = NULL;
2443 g_return_if_fail (treeview);
2444 g_return_if_fail (user_data);
2446 self = MODEST_FOLDER_VIEW (user_data);
2447 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2449 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2451 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2454 gtk_tree_model_get (model, &iter,
2455 INSTANCE_COLUMN, &folder,
2458 g_signal_emit (G_OBJECT(self),
2459 signals[FOLDER_ACTIVATED_SIGNAL],
2462 #ifdef MODEST_TOOLKIT_HILDON2
2463 HildonUIMode ui_mode;
2464 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2465 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2466 if (priv->cur_folder_store)
2467 g_object_unref (priv->cur_folder_store);
2468 priv->cur_folder_store = g_object_ref (folder);
2472 g_object_unref (folder);
2476 modest_folder_view_get_selected (ModestFolderView *self)
2478 ModestFolderViewPrivate *priv;
2480 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2483 if (priv->cur_folder_store)
2484 g_object_ref (priv->cur_folder_store);
2486 return priv->cur_folder_store;
2490 get_cmp_rows_type_pos (GObject *folder)
2492 /* Remote accounts -> Local account -> MMC account .*/
2495 if (TNY_IS_ACCOUNT (folder) &&
2496 modest_tny_account_is_virtual_local_folders (
2497 TNY_ACCOUNT (folder))) {
2499 } else if (TNY_IS_ACCOUNT (folder)) {
2500 TnyAccount *account = TNY_ACCOUNT (folder);
2501 const gchar *account_id = tny_account_get_id (account);
2502 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2508 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2509 return -1; /* Should never happen */
2514 inbox_is_special (TnyFolderStore *folder_store)
2516 gboolean is_special = TRUE;
2518 if (TNY_IS_FOLDER (folder_store)) {
2522 gchar *last_inbox_bar;
2524 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2525 downcase = g_utf8_strdown (id, -1);
2526 last_bar = g_strrstr (downcase, "/");
2528 last_inbox_bar = g_strrstr (downcase, "inbox/");
2529 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2540 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2542 TnyAccount *account;
2543 gboolean is_special;
2544 /* Inbox, Outbox, Drafts, Sent, User */
2547 if (!TNY_IS_FOLDER (folder_store))
2550 case TNY_FOLDER_TYPE_INBOX:
2552 account = tny_folder_get_account (folder_store);
2553 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2555 /* In inbox case we need to know if the inbox is really the top
2556 * inbox of the account, or if it's a submailbox inbox. To do
2557 * this we'll apply an heuristic rule: Find last "/" and check
2558 * if it's preceeded by another Inbox */
2559 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2560 g_object_unref (account);
2561 return is_special?0:4;
2564 case TNY_FOLDER_TYPE_OUTBOX:
2565 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2567 case TNY_FOLDER_TYPE_DRAFTS:
2569 account = tny_folder_get_account (folder_store);
2570 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2571 g_object_unref (account);
2572 return is_special?1:4;
2575 case TNY_FOLDER_TYPE_SENT:
2577 account = tny_folder_get_account (folder_store);
2578 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2579 g_object_unref (account);
2580 return is_special?3:4;
2589 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2591 const gchar *a1_name, *a2_name;
2593 a1_name = tny_account_get_name (a1);
2594 a2_name = tny_account_get_name (a2);
2596 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2600 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2602 TnyAccount *a1 = NULL, *a2 = NULL;
2605 if (TNY_IS_ACCOUNT (s1)) {
2606 a1 = TNY_ACCOUNT (g_object_ref (s1));
2607 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2608 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2611 if (TNY_IS_ACCOUNT (s2)) {
2612 a2 = TNY_ACCOUNT (g_object_ref (s2));
2613 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2614 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2631 /* First we sort with the type of account */
2632 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2636 cmp = compare_account_names (a1, a2);
2640 g_object_unref (a1);
2642 g_object_unref (a2);
2648 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2650 gint is_account1, is_account2;
2652 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2653 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2655 return is_account2 - is_account1;
2659 compare_folders (const gchar *name1, const gchar *name2)
2661 const gchar *separator1, *separator2;
2662 const gchar *next1, *next2;
2666 if (name1 == NULL || name1[0] == '\0')
2668 if (name2 == NULL || name2[0] == '\0')
2671 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2673 top1 = g_strndup (name1, separator1 - name1);
2675 top1 = g_strdup (name1);
2678 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2680 top2 = g_strndup (name2, separator2 - name2);
2682 top2 = g_strdup (name2);
2686 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2693 if (separator1 == NULL && separator2 == NULL)
2696 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2697 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2699 return compare_folders (next1, next2);
2704 * This function orders the mail accounts according to these rules:
2705 * 1st - remote accounts
2706 * 2nd - local account
2710 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2714 gchar *name1 = NULL;
2715 gchar *name2 = NULL;
2716 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2717 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2718 GObject *folder1 = NULL;
2719 GObject *folder2 = NULL;
2721 gtk_tree_model_get (tree_model, iter1,
2722 NAME_COLUMN, &name1,
2724 INSTANCE_COLUMN, &folder1,
2726 gtk_tree_model_get (tree_model, iter2,
2727 NAME_COLUMN, &name2,
2728 TYPE_COLUMN, &type2,
2729 INSTANCE_COLUMN, &folder2,
2732 /* Return if we get no folder. This could happen when folder
2733 operations are happening. The model is updated after the
2734 folder copy/move actually occurs, so there could be
2735 situations where the model to be drawn is not correct */
2736 if (!folder1 || !folder2)
2739 /* Sort by type. First the special folders, then the archives */
2740 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2744 /* Now we sort using the account of each folder */
2745 if (TNY_IS_FOLDER_STORE (folder1) &&
2746 TNY_IS_FOLDER_STORE (folder2)) {
2747 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2751 /* Each group is preceeded by its account */
2752 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2757 /* Pure sort by name */
2758 cmp = compare_folders (name1, name2);
2761 g_object_unref(G_OBJECT(folder1));
2763 g_object_unref(G_OBJECT(folder2));
2771 /*****************************************************************************/
2772 /* DRAG and DROP stuff */
2773 /*****************************************************************************/
2775 * This function fills the #GtkSelectionData with the row and the
2776 * model that has been dragged. It's called when this widget is a
2777 * source for dnd after the event drop happened
2780 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2781 guint info, guint time, gpointer data)
2783 GtkTreeSelection *selection;
2784 GtkTreeModel *model;
2786 GtkTreePath *source_row;
2788 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2789 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2791 source_row = gtk_tree_model_get_path (model, &iter);
2792 gtk_tree_set_row_drag_data (selection_data,
2796 gtk_tree_path_free (source_row);
2800 typedef struct _DndHelper {
2801 ModestFolderView *folder_view;
2802 gboolean delete_source;
2803 GtkTreePath *source_row;
2807 dnd_helper_destroyer (DndHelper *helper)
2809 /* Free the helper */
2810 gtk_tree_path_free (helper->source_row);
2811 g_slice_free (DndHelper, helper);
2815 xfer_folder_cb (ModestMailOperation *mail_op,
2816 TnyFolder *new_folder,
2820 /* Select the folder */
2821 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2827 /* get the folder for the row the treepath refers to. */
2828 /* folder must be unref'd */
2829 static TnyFolderStore *
2830 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2833 TnyFolderStore *folder = NULL;
2835 if (gtk_tree_model_get_iter (model,&iter, path))
2836 gtk_tree_model_get (model, &iter,
2837 INSTANCE_COLUMN, &folder,
2844 * This function is used by drag_data_received_cb to manage drag and
2845 * drop of a header, i.e, and drag from the header view to the folder
2849 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2850 GtkTreeModel *dest_model,
2851 GtkTreePath *dest_row,
2852 GtkSelectionData *selection_data)
2854 TnyList *headers = NULL;
2855 TnyFolder *folder = NULL, *src_folder = NULL;
2856 TnyFolderType folder_type;
2857 GtkTreeIter source_iter, dest_iter;
2858 ModestWindowMgr *mgr = NULL;
2859 ModestWindow *main_win = NULL;
2860 gchar **uris, **tmp;
2862 /* Build the list of headers */
2863 mgr = modest_runtime_get_window_mgr ();
2864 headers = tny_simple_list_new ();
2865 uris = modest_dnd_selection_data_get_paths (selection_data);
2868 while (*tmp != NULL) {
2871 gboolean first = TRUE;
2874 path = gtk_tree_path_new_from_string (*tmp);
2875 gtk_tree_model_get_iter (source_model, &source_iter, path);
2876 gtk_tree_model_get (source_model, &source_iter,
2877 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2880 /* Do not enable d&d of headers already opened */
2881 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2882 tny_list_append (headers, G_OBJECT (header));
2884 if (G_UNLIKELY (first)) {
2885 src_folder = tny_header_get_folder (header);
2889 /* Free and go on */
2890 gtk_tree_path_free (path);
2891 g_object_unref (header);
2896 /* This could happen ig we perform a d&d very quickly over the
2897 same row that row could dissapear because message is
2899 if (!TNY_IS_FOLDER (src_folder))
2902 /* Get the target folder */
2903 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2904 gtk_tree_model_get (dest_model, &dest_iter,
2908 if (!folder || !TNY_IS_FOLDER(folder)) {
2909 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2913 folder_type = modest_tny_folder_guess_folder_type (folder);
2914 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2915 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2916 goto cleanup; /* cannot move messages there */
2919 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2920 /* g_warning ("folder not writable"); */
2921 goto cleanup; /* verboten! */
2924 /* Ask for confirmation to move */
2925 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2927 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2931 /* Transfer messages */
2932 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2937 if (G_IS_OBJECT (src_folder))
2938 g_object_unref (src_folder);
2939 if (G_IS_OBJECT(folder))
2940 g_object_unref (G_OBJECT (folder));
2941 if (G_IS_OBJECT(headers))
2942 g_object_unref (headers);
2946 TnyFolderStore *src_folder;
2947 TnyFolderStore *dst_folder;
2948 ModestFolderView *folder_view;
2953 dnd_folder_info_destroyer (DndFolderInfo *info)
2955 if (info->src_folder)
2956 g_object_unref (info->src_folder);
2957 if (info->dst_folder)
2958 g_object_unref (info->dst_folder);
2959 g_slice_free (DndFolderInfo, info);
2963 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2964 GtkWindow *parent_window,
2965 TnyAccount *account)
2968 modest_ui_actions_on_account_connection_error (parent_window, account);
2970 /* Free the helper & info */
2971 dnd_helper_destroyer (info->helper);
2972 dnd_folder_info_destroyer (info);
2976 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2978 GtkWindow *parent_window,
2979 TnyAccount *account,
2982 DndFolderInfo *info = NULL;
2983 ModestMailOperation *mail_op;
2985 info = (DndFolderInfo *) user_data;
2987 if (err || canceled) {
2988 dnd_on_connection_failed_destroyer (info, parent_window, account);
2992 /* Do the mail operation */
2993 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2994 modest_ui_actions_move_folder_error_handler,
2995 info->src_folder, NULL);
2997 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3000 /* Transfer the folder */
3001 modest_mail_operation_xfer_folder (mail_op,
3002 TNY_FOLDER (info->src_folder),
3004 info->helper->delete_source,
3006 info->helper->folder_view);
3009 g_object_unref (G_OBJECT (mail_op));
3010 dnd_helper_destroyer (info->helper);
3011 dnd_folder_info_destroyer (info);
3016 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3018 GtkWindow *parent_window,
3019 TnyAccount *account,
3022 DndFolderInfo *info = NULL;
3024 info = (DndFolderInfo *) user_data;
3026 if (err || canceled) {
3027 dnd_on_connection_failed_destroyer (info, parent_window, account);
3031 /* Connect to source folder and perform the copy/move */
3032 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3034 drag_and_drop_from_folder_view_src_folder_performer,
3039 * This function is used by drag_data_received_cb to manage drag and
3040 * drop of a folder, i.e, and drag from the folder view to the same
3044 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3045 GtkTreeModel *dest_model,
3046 GtkTreePath *dest_row,
3047 GtkSelectionData *selection_data,
3050 GtkTreeIter dest_iter, iter;
3051 TnyFolderStore *dest_folder = NULL;
3052 TnyFolderStore *folder = NULL;
3053 gboolean forbidden = FALSE;
3055 DndFolderInfo *info = NULL;
3057 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3059 g_warning ("%s: BUG: no main window", __FUNCTION__);
3060 dnd_helper_destroyer (helper);
3065 /* check the folder rules for the destination */
3066 folder = tree_path_to_folder (dest_model, dest_row);
3067 if (TNY_IS_FOLDER(folder)) {
3068 ModestTnyFolderRules rules =
3069 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3070 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3071 } else if (TNY_IS_FOLDER_STORE(folder)) {
3072 /* enable local root as destination for folders */
3073 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3074 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3077 g_object_unref (folder);
3080 /* check the folder rules for the source */
3081 folder = tree_path_to_folder (source_model, helper->source_row);
3082 if (TNY_IS_FOLDER(folder)) {
3083 ModestTnyFolderRules rules =
3084 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3085 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3088 g_object_unref (folder);
3092 /* Check if the drag is possible */
3093 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3095 modest_platform_run_information_dialog ((GtkWindow *) win,
3096 _("mail_in_ui_folder_move_target_error"),
3098 /* Restore the previous selection */
3099 folder = tree_path_to_folder (source_model, helper->source_row);
3101 if (TNY_IS_FOLDER (folder))
3102 modest_folder_view_select_folder (helper->folder_view,
3103 TNY_FOLDER (folder), FALSE);
3104 g_object_unref (folder);
3106 dnd_helper_destroyer (helper);
3111 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3112 gtk_tree_model_get (dest_model, &dest_iter,
3115 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3116 gtk_tree_model_get (source_model, &iter,
3120 /* Create the info for the performer */
3121 info = g_slice_new0 (DndFolderInfo);
3122 info->src_folder = g_object_ref (folder);
3123 info->dst_folder = g_object_ref (dest_folder);
3124 info->helper = helper;
3126 /* Connect to the destination folder and perform the copy/move */
3127 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3129 drag_and_drop_from_folder_view_dst_folder_performer,
3133 g_object_unref (dest_folder);
3134 g_object_unref (folder);
3138 * This function receives the data set by the "drag-data-get" signal
3139 * handler. This information comes within the #GtkSelectionData. This
3140 * function will manage both the drags of folders of the treeview and
3141 * drags of headers of the header view widget.
3144 on_drag_data_received (GtkWidget *widget,
3145 GdkDragContext *context,
3148 GtkSelectionData *selection_data,
3153 GtkWidget *source_widget;
3154 GtkTreeModel *dest_model, *source_model;
3155 GtkTreePath *source_row, *dest_row;
3156 GtkTreeViewDropPosition pos;
3157 gboolean delete_source = FALSE;
3158 gboolean success = FALSE;
3160 /* Do not allow further process */
3161 g_signal_stop_emission_by_name (widget, "drag-data-received");
3162 source_widget = gtk_drag_get_source_widget (context);
3164 /* Get the action */
3165 if (context->action == GDK_ACTION_MOVE) {
3166 delete_source = TRUE;
3168 /* Notify that there is no folder selected. We need to
3169 do this in order to update the headers view (and
3170 its monitors, because when moving, the old folder
3171 won't longer exist. We can not wait for the end of
3172 the operation, because the operation won't start if
3173 the folder is in use */
3174 if (source_widget == widget) {
3175 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3176 gtk_tree_selection_unselect_all (sel);
3180 /* Check if the get_data failed */
3181 if (selection_data == NULL || selection_data->length < 0)
3184 /* Select the destination model */
3185 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3187 /* Get the path to the destination row. Can not call
3188 gtk_tree_view_get_drag_dest_row() because the source row
3189 is not selected anymore */
3190 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3193 /* Only allow drops IN other rows */
3195 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3196 pos == GTK_TREE_VIEW_DROP_AFTER)
3200 /* Drags from the header view */
3201 if (source_widget != widget) {
3202 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3204 drag_and_drop_from_header_view (source_model,
3209 DndHelper *helper = NULL;
3211 /* Get the source model and row */
3212 gtk_tree_get_row_drag_data (selection_data,
3216 /* Create the helper */
3217 helper = g_slice_new0 (DndHelper);
3218 helper->delete_source = delete_source;
3219 helper->source_row = gtk_tree_path_copy (source_row);
3220 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3222 drag_and_drop_from_folder_view (source_model,
3228 gtk_tree_path_free (source_row);
3232 gtk_tree_path_free (dest_row);
3235 /* Finish the drag and drop */
3236 gtk_drag_finish (context, success, FALSE, time);
3240 * We define a "drag-drop" signal handler because we do not want to
3241 * use the default one, because the default one always calls
3242 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3243 * signal handler, because there we have all the information available
3244 * to know if the dnd was a success or not.
3247 drag_drop_cb (GtkWidget *widget,
3248 GdkDragContext *context,
3256 if (!context->targets)
3259 /* Check if we're dragging a folder row */
3260 target = gtk_drag_dest_find_target (widget, context, NULL);
3262 /* Request the data from the source. */
3263 gtk_drag_get_data(widget, context, target, time);
3269 * This function expands a node of a tree view if it's not expanded
3270 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3271 * does that, so that's why they're here.
3274 expand_row_timeout (gpointer data)
3276 GtkTreeView *tree_view = data;
3277 GtkTreePath *dest_path = NULL;
3278 GtkTreeViewDropPosition pos;
3279 gboolean result = FALSE;
3281 gdk_threads_enter ();
3283 gtk_tree_view_get_drag_dest_row (tree_view,
3288 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3289 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3290 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3291 gtk_tree_path_free (dest_path);
3295 gtk_tree_path_free (dest_path);
3300 gdk_threads_leave ();
3306 * This function is called whenever the pointer is moved over a widget
3307 * while dragging some data. It installs a timeout that will expand a
3308 * node of the treeview if not expanded yet. This function also calls
3309 * gdk_drag_status in order to set the suggested action that will be
3310 * used by the "drag-data-received" signal handler to know if we
3311 * should do a move or just a copy of the data.
3314 on_drag_motion (GtkWidget *widget,
3315 GdkDragContext *context,
3321 GtkTreeViewDropPosition pos;
3322 GtkTreePath *dest_row;
3323 GtkTreeModel *dest_model;
3324 ModestFolderViewPrivate *priv;
3325 GdkDragAction suggested_action;
3326 gboolean valid_location = FALSE;
3327 TnyFolderStore *folder = NULL;
3329 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3331 if (priv->timer_expander != 0) {
3332 g_source_remove (priv->timer_expander);
3333 priv->timer_expander = 0;
3336 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3341 /* Do not allow drops between folders */
3343 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3344 pos == GTK_TREE_VIEW_DROP_AFTER) {
3345 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3346 gdk_drag_status(context, 0, time);
3347 valid_location = FALSE;
3350 valid_location = TRUE;
3353 /* Check that the destination folder is writable */
3354 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3355 folder = tree_path_to_folder (dest_model, dest_row);
3356 if (folder && TNY_IS_FOLDER (folder)) {
3357 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3359 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3360 valid_location = FALSE;
3365 /* Expand the selected row after 1/2 second */
3366 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3367 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3369 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3371 /* Select the desired action. By default we pick MOVE */
3372 suggested_action = GDK_ACTION_MOVE;
3374 if (context->actions == GDK_ACTION_COPY)
3375 gdk_drag_status(context, GDK_ACTION_COPY, time);
3376 else if (context->actions == GDK_ACTION_MOVE)
3377 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3378 else if (context->actions & suggested_action)
3379 gdk_drag_status(context, suggested_action, time);
3381 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3385 g_object_unref (folder);
3387 gtk_tree_path_free (dest_row);
3389 g_signal_stop_emission_by_name (widget, "drag-motion");
3391 return valid_location;
3395 * This function sets the treeview as a source and a target for dnd
3396 * events. It also connects all the requirede signals.
3399 setup_drag_and_drop (GtkTreeView *self)
3401 /* Set up the folder view as a dnd destination. Set only the
3402 highlight flag, otherwise gtk will have a different
3404 #ifdef MODEST_TOOLKIT_HILDON2
3407 ModestFolderViewPrivate *priv;
3409 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3411 gtk_drag_dest_set (GTK_WIDGET (self),
3412 GTK_DEST_DEFAULT_HIGHLIGHT,
3413 folder_view_drag_types,
3414 G_N_ELEMENTS (folder_view_drag_types),
3415 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3417 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3418 G_OBJECT (self), "drag_data_received",
3419 G_CALLBACK (on_drag_data_received), NULL);
3422 /* Set up the treeview as a dnd source */
3423 gtk_drag_source_set (GTK_WIDGET (self),
3425 folder_view_drag_types,
3426 G_N_ELEMENTS (folder_view_drag_types),
3427 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3429 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3430 G_OBJECT (self), "drag_motion",
3431 G_CALLBACK (on_drag_motion), NULL);
3433 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3434 G_OBJECT (self), "drag_data_get",
3435 G_CALLBACK (on_drag_data_get), NULL);
3437 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3438 G_OBJECT (self), "drag_drop",
3439 G_CALLBACK (drag_drop_cb), NULL);
3443 * This function manages the navigation through the folders using the
3444 * keyboard or the hardware keys in the device
3447 on_key_pressed (GtkWidget *self,
3451 GtkTreeSelection *selection;
3453 GtkTreeModel *model;
3454 gboolean retval = FALSE;
3456 /* Up and Down are automatically managed by the treeview */
3457 if (event->keyval == GDK_Return) {
3458 /* Expand/Collapse the selected row */
3459 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3460 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3463 path = gtk_tree_model_get_path (model, &iter);
3465 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3466 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3468 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3469 gtk_tree_path_free (path);
3471 /* No further processing */
3479 * We listen to the changes in the local folder account name key,
3480 * because we want to show the right name in the view. The local
3481 * folder account name corresponds to the device name in the Maemo
3482 * version. We do this because we do not want to query gconf on each
3483 * tree view refresh. It's better to cache it and change whenever
3487 on_configuration_key_changed (ModestConf* conf,
3489 ModestConfEvent event,
3490 ModestConfNotificationId id,
3491 ModestFolderView *self)
3493 ModestFolderViewPrivate *priv;
3496 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3497 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3499 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3500 g_free (priv->local_account_name);
3502 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3503 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3505 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3506 MODEST_CONF_DEVICE_NAME, NULL);
3508 /* Force a redraw */
3509 #if GTK_CHECK_VERSION(2, 8, 0)
3510 GtkTreeViewColumn * tree_column;
3512 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3514 gtk_tree_view_column_queue_resize (tree_column);
3516 gtk_widget_queue_draw (GTK_WIDGET (self));
3522 modest_folder_view_set_style (ModestFolderView *self,
3523 ModestFolderViewStyle style)
3525 ModestFolderViewPrivate *priv;
3527 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3528 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3529 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3531 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3534 priv->style = style;
3538 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3539 const gchar *account_id)
3541 ModestFolderViewPrivate *priv;
3542 GtkTreeModel *model;
3544 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3546 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3548 /* This will be used by the filter_row callback,
3549 * to decided which rows to show: */
3550 if (priv->visible_account_id) {
3551 g_free (priv->visible_account_id);
3552 priv->visible_account_id = NULL;
3555 priv->visible_account_id = g_strdup (account_id);
3558 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3559 if (GTK_IS_TREE_MODEL_FILTER (model))
3560 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3562 /* Save settings to gconf */
3563 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3564 MODEST_CONF_FOLDER_VIEW_KEY);
3566 /* Notify observers */
3567 g_signal_emit (G_OBJECT(self),
3568 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3573 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3575 ModestFolderViewPrivate *priv;
3577 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3579 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3581 return (const gchar *) priv->visible_account_id;
3585 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3589 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3591 gtk_tree_model_get (model, iter,
3595 gboolean result = FALSE;
3596 if (type == TNY_FOLDER_TYPE_INBOX) {
3600 *inbox_iter = *iter;
3604 if (gtk_tree_model_iter_children (model, &child, iter)) {
3605 if (find_inbox_iter (model, &child, inbox_iter))
3609 } while (gtk_tree_model_iter_next (model, iter));
3618 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3620 #ifndef MODEST_TOOLKIT_HILDON2
3621 GtkTreeModel *model;
3622 GtkTreeIter iter, inbox_iter;
3623 GtkTreeSelection *sel;
3624 GtkTreePath *path = NULL;
3626 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3628 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3632 expand_root_items (self);
3633 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3635 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3636 g_warning ("%s: model is empty", __FUNCTION__);
3640 if (find_inbox_iter (model, &iter, &inbox_iter))
3641 path = gtk_tree_model_get_path (model, &inbox_iter);
3643 path = gtk_tree_path_new_first ();
3645 /* Select the row and free */
3646 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3647 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3648 gtk_tree_path_free (path);
3651 gtk_widget_grab_focus (GTK_WIDGET(self));
3658 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3663 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3664 TnyFolder* a_folder;
3667 gtk_tree_model_get (model, iter,
3668 INSTANCE_COLUMN, &a_folder,
3674 if (folder == a_folder) {
3675 g_object_unref (a_folder);
3676 *folder_iter = *iter;
3679 g_object_unref (a_folder);
3681 if (gtk_tree_model_iter_children (model, &child, iter)) {
3682 if (find_folder_iter (model, &child, folder_iter, folder))
3686 } while (gtk_tree_model_iter_next (model, iter));
3691 #ifndef MODEST_TOOLKIT_HILDON2
3693 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3696 ModestFolderView *self)
3698 ModestFolderViewPrivate *priv = NULL;
3699 GtkTreeSelection *sel;
3700 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3701 GObject *instance = NULL;
3703 if (!MODEST_IS_FOLDER_VIEW(self))
3706 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3708 priv->reexpand = TRUE;
3710 gtk_tree_model_get (tree_model, iter,
3712 INSTANCE_COLUMN, &instance,
3718 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3719 priv->folder_to_select = g_object_ref (instance);
3721 g_object_unref (instance);
3723 if (priv->folder_to_select) {
3725 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3728 path = gtk_tree_model_get_path (tree_model, iter);
3729 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3731 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3733 gtk_tree_selection_select_iter (sel, iter);
3734 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3736 gtk_tree_path_free (path);
3740 modest_folder_view_disable_next_folder_selection (self);
3742 /* Refilter the model */
3743 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3749 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3751 ModestFolderViewPrivate *priv;
3753 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3755 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3757 if (priv->folder_to_select)
3758 g_object_unref(priv->folder_to_select);
3760 priv->folder_to_select = NULL;
3764 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3765 gboolean after_change)
3767 GtkTreeModel *model;
3768 GtkTreeIter iter, folder_iter;
3769 GtkTreeSelection *sel;
3770 ModestFolderViewPrivate *priv = NULL;
3772 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3773 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3775 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3778 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3779 gtk_tree_selection_unselect_all (sel);
3781 if (priv->folder_to_select)
3782 g_object_unref(priv->folder_to_select);
3783 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3787 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3792 /* Refilter the model, before selecting the folder */
3793 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3795 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3796 g_warning ("%s: model is empty", __FUNCTION__);
3800 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3803 path = gtk_tree_model_get_path (model, &folder_iter);
3804 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3806 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3807 gtk_tree_selection_select_iter (sel, &folder_iter);
3808 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3810 gtk_tree_path_free (path);
3818 modest_folder_view_copy_selection (ModestFolderView *self)
3820 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3822 /* Copy selection */
3823 _clipboard_set_selected_data (self, FALSE);
3827 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3829 ModestFolderViewPrivate *priv = NULL;
3830 GtkTreeModel *model = NULL;
3831 const gchar **hidding = NULL;
3832 guint i, n_selected;
3834 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3835 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3837 /* Copy selection */
3838 if (!_clipboard_set_selected_data (folder_view, TRUE))
3841 /* Get hidding ids */
3842 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3844 /* Clear hidding array created by previous cut operation */
3845 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3847 /* Copy hidding array */
3848 priv->n_selected = n_selected;
3849 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3850 for (i=0; i < n_selected; i++)
3851 priv->hidding_ids[i] = g_strdup(hidding[i]);
3853 /* Hide cut folders */
3854 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3855 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3859 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3860 ModestFolderView *folder_view_dst)
3862 GtkTreeModel *filter_model = NULL;
3863 GtkTreeModel *model = NULL;
3864 GtkTreeModel *new_filter_model = NULL;
3865 GtkTreeModel *old_tny_model = NULL;
3866 GtkTreeModel *new_tny_model = NULL;
3867 ModestFolderViewPrivate *dst_priv;
3869 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3870 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3872 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3873 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3874 new_tny_model = NULL;
3877 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3878 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3879 G_OBJECT (old_tny_model),
3880 "activity-changed");
3882 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3883 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3885 /* Build new filter model */
3886 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3887 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3894 /* Set copied model */
3895 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3896 #ifndef MODEST_TOOLKIT_HILDON2
3897 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3898 G_OBJECT(new_filter_model), "row-inserted",
3899 (GCallback) on_row_inserted_maybe_select_folder,
3902 #ifdef MODEST_TOOLKIT_HILDON2
3903 if (new_tny_model) {
3904 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3905 G_OBJECT (new_tny_model),
3907 G_CALLBACK (on_activity_changed),
3913 g_object_unref (new_filter_model);
3917 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3920 GtkTreeModel *model = NULL;
3921 ModestFolderViewPrivate* priv;
3923 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3925 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3926 priv->show_non_move = show;
3927 /* modest_folder_view_update_model(folder_view, */
3928 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3930 /* Hide special folders */
3931 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3932 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3933 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3938 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3941 ModestFolderViewPrivate* priv;
3943 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3945 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3946 priv->show_message_count = show;
3948 g_object_set (G_OBJECT (priv->messages_renderer),
3949 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3953 /* Returns FALSE if it did not selected anything */
3955 _clipboard_set_selected_data (ModestFolderView *folder_view,
3958 ModestFolderViewPrivate *priv = NULL;
3959 TnyFolderStore *folder = NULL;
3960 gboolean retval = FALSE;
3962 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3963 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3965 /* Set selected data on clipboard */
3966 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3967 folder = modest_folder_view_get_selected (folder_view);
3969 /* Do not allow to select an account */
3970 if (TNY_IS_FOLDER (folder)) {
3971 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3976 g_object_unref (folder);
3982 _clear_hidding_filter (ModestFolderView *folder_view)
3984 ModestFolderViewPrivate *priv;
3987 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3988 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3990 if (priv->hidding_ids != NULL) {
3991 for (i=0; i < priv->n_selected; i++)
3992 g_free (priv->hidding_ids[i]);
3993 g_free(priv->hidding_ids);
3999 on_display_name_changed (ModestAccountMgr *mgr,
4000 const gchar *account,
4003 ModestFolderView *self;
4005 self = MODEST_FOLDER_VIEW (user_data);
4007 /* Force a redraw */
4008 #if GTK_CHECK_VERSION(2, 8, 0)
4009 GtkTreeViewColumn * tree_column;
4011 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4013 gtk_tree_view_column_queue_resize (tree_column);
4015 gtk_widget_queue_draw (GTK_WIDGET (self));
4020 modest_folder_view_set_cell_style (ModestFolderView *self,
4021 ModestFolderViewCellStyle cell_style)
4023 ModestFolderViewPrivate *priv = NULL;
4025 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4026 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4028 priv->cell_style = cell_style;
4030 g_object_set (G_OBJECT (priv->messages_renderer),
4031 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4034 gtk_widget_queue_draw (GTK_WIDGET (self));
4038 update_style (ModestFolderView *self)
4040 ModestFolderViewPrivate *priv;
4041 GdkColor style_color, style_active_color;
4042 PangoAttrList *attr_list;
4044 PangoAttribute *attr;
4046 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4047 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4051 attr_list = pango_attr_list_new ();
4052 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4053 gdk_color_parse ("grey", &style_color);
4055 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4056 pango_attr_list_insert (attr_list, attr);
4059 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4061 "SmallSystemFont", NULL,
4064 attr = pango_attr_font_desc_new (pango_font_description_copy
4065 (style->font_desc));
4066 pango_attr_list_insert (attr_list, attr);
4068 g_object_set (G_OBJECT (priv->messages_renderer),
4069 "foreground-gdk", &style_color,
4070 "foreground-set", TRUE,
4071 "attributes", attr_list,
4073 pango_attr_list_unref (attr_list);
4076 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4077 priv->active_color = style_active_color;
4079 gdk_color_parse ("000", &(priv->active_color));
4084 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4086 if (strcmp ("style", spec->name) == 0) {
4087 update_style (MODEST_FOLDER_VIEW (obj));
4088 gtk_widget_queue_draw (GTK_WIDGET (obj));
4093 modest_folder_view_set_filter (ModestFolderView *self,
4094 ModestFolderViewFilter filter)
4096 ModestFolderViewPrivate *priv;
4097 GtkTreeModel *filter_model;
4099 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4100 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4102 priv->filter |= filter;
4104 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4105 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4106 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4111 modest_folder_view_unset_filter (ModestFolderView *self,
4112 ModestFolderViewFilter filter)
4114 ModestFolderViewPrivate *priv;
4115 GtkTreeModel *filter_model;
4117 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4118 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4120 priv->filter &= ~filter;
4122 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4123 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4124 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4129 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4130 ModestTnyFolderRules rules)
4132 GtkTreeModel *filter_model;
4134 gboolean fulfil = FALSE;
4136 if (!get_inner_models (self, &filter_model, NULL, NULL))
4139 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4143 TnyFolderStore *folder;
4145 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4147 if (TNY_IS_FOLDER (folder)) {
4148 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4149 /* Folder rules are negative: non_writable, non_deletable... */
4150 if (!(folder_rules & rules))
4153 g_object_unref (folder);
4156 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4162 modest_folder_view_set_list_to_move (ModestFolderView *self,
4165 ModestFolderViewPrivate *priv;
4167 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4168 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4170 if (priv->list_to_move)
4171 g_object_unref (priv->list_to_move);
4174 g_object_ref (list);
4176 priv->list_to_move = list;
4180 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4182 ModestFolderViewPrivate *priv;
4184 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4185 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4188 g_free (priv->mailbox);
4190 priv->mailbox = g_strdup (mailbox);
4192 /* Notify observers */
4193 g_signal_emit (G_OBJECT(self),
4194 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4195 priv->visible_account_id);
4199 modest_folder_view_get_mailbox (ModestFolderView *self)
4201 ModestFolderViewPrivate *priv;
4203 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4204 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4206 return (const gchar *) priv->mailbox;
4210 modest_folder_view_get_activity (ModestFolderView *self)
4212 ModestFolderViewPrivate *priv;
4213 GtkTreeModel *inner_model;
4215 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4216 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4217 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4219 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4220 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4226 #ifdef MODEST_TOOLKIT_HILDON2
4228 on_activity_changed (TnyGtkFolderListStore *store,
4230 ModestFolderView *folder_view)
4232 ModestFolderViewPrivate *priv;
4234 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4235 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4236 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4238 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,