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-tny-account.h>
48 #include <modest-tny-folder.h>
49 #include <modest-tny-local-folders-account.h>
50 #include <modest-tny-outbox-account.h>
51 #include <modest-marshal.h>
52 #include <modest-icon-names.h>
53 #include <modest-tny-account-store.h>
54 #include <modest-tny-local-folders-account.h>
55 #include <modest-text-utils.h>
56 #include <modest-runtime.h>
57 #include "modest-folder-view.h"
58 #include <modest-platform.h>
59 #include <modest-widget-memory.h>
60 #include <modest-ui-actions.h>
61 #include "modest-dnd.h"
62 #include "modest-ui-constants.h"
63 #include "widgets/modest-window.h"
65 /* Folder view drag types */
66 const GtkTargetEntry folder_view_drag_types[] =
68 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
69 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
72 /* Default icon sizes for Fremantle style are different */
73 #ifdef MODEST_TOOLKIT_HILDON2
74 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
79 /* Column names depending on we use list store or tree store */
80 #ifdef MODEST_TOOLKIT_HILDON2
81 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
82 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
83 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
84 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
85 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
87 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
88 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
89 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
90 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
91 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
94 /* 'private'/'protected' functions */
95 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
96 static void modest_folder_view_init (ModestFolderView *obj);
97 static void modest_folder_view_finalize (GObject *obj);
99 static void tny_account_store_view_init (gpointer g,
100 gpointer iface_data);
102 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
103 TnyAccountStore *account_store);
105 static void on_selection_changed (GtkTreeSelection *sel,
108 static void on_row_activated (GtkTreeView *treeview,
110 GtkTreeViewColumn *column,
113 static void on_account_removed (TnyAccountStore *self,
117 static void on_account_inserted (TnyAccountStore *self,
121 static void on_account_changed (TnyAccountStore *self,
125 static gint cmp_rows (GtkTreeModel *tree_model,
130 static gboolean filter_row (GtkTreeModel *model,
134 static gboolean on_key_pressed (GtkWidget *self,
138 static void on_configuration_key_changed (ModestConf* conf,
140 ModestConfEvent event,
141 ModestConfNotificationId notification_id,
142 ModestFolderView *self);
145 static void on_drag_data_get (GtkWidget *widget,
146 GdkDragContext *context,
147 GtkSelectionData *selection_data,
152 static void on_drag_data_received (GtkWidget *widget,
153 GdkDragContext *context,
156 GtkSelectionData *selection_data,
161 static gboolean on_drag_motion (GtkWidget *widget,
162 GdkDragContext *context,
168 static void expand_root_items (ModestFolderView *self);
170 static gint expand_row_timeout (gpointer data);
172 static void setup_drag_and_drop (GtkTreeView *self);
174 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
177 static void _clear_hidding_filter (ModestFolderView *folder_view);
179 #ifndef MODEST_TOOLKIT_HILDON2
180 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
183 ModestFolderView *self);
186 static void on_display_name_changed (ModestAccountMgr *self,
187 const gchar *account,
189 static void update_style (ModestFolderView *self);
190 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
191 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
193 static gboolean get_inner_models (ModestFolderView *self,
194 GtkTreeModel **filter_model,
195 GtkTreeModel **sort_model,
196 GtkTreeModel **tny_model);
199 FOLDER_SELECTION_CHANGED_SIGNAL,
200 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
201 FOLDER_ACTIVATED_SIGNAL,
205 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
206 struct _ModestFolderViewPrivate {
207 TnyAccountStore *account_store;
208 TnyFolderStore *cur_folder_store;
210 TnyFolder *folder_to_select; /* folder to select after the next update */
212 gulong changed_signal;
213 gulong account_inserted_signal;
214 gulong account_removed_signal;
215 gulong account_changed_signal;
216 gulong conf_key_signal;
217 gulong display_name_changed_signal;
219 /* not unref this object, its a singlenton */
220 ModestEmailClipboard *clipboard;
222 /* Filter tree model */
225 ModestFolderViewFilter filter;
227 TnyFolderStoreQuery *query;
228 guint timer_expander;
230 gchar *local_account_name;
231 gchar *visible_account_id;
232 ModestFolderViewStyle style;
233 ModestFolderViewCellStyle cell_style;
235 gboolean reselect; /* we use this to force a reselection of the INBOX */
236 gboolean show_non_move;
237 gboolean reexpand; /* next time we expose, we'll expand all root folders */
239 GtkCellRenderer *messages_renderer;
241 gulong outbox_deleted_handler;
243 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
244 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
245 MODEST_TYPE_FOLDER_VIEW, \
246 ModestFolderViewPrivate))
248 static GObjectClass *parent_class = NULL;
250 static guint signals[LAST_SIGNAL] = {0};
253 modest_folder_view_get_type (void)
255 static GType my_type = 0;
257 static const GTypeInfo my_info = {
258 sizeof(ModestFolderViewClass),
259 NULL, /* base init */
260 NULL, /* base finalize */
261 (GClassInitFunc) modest_folder_view_class_init,
262 NULL, /* class finalize */
263 NULL, /* class data */
264 sizeof(ModestFolderView),
266 (GInstanceInitFunc) modest_folder_view_init,
270 static const GInterfaceInfo tny_account_store_view_info = {
271 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
272 NULL, /* interface_finalize */
273 NULL /* interface_data */
277 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
281 g_type_add_interface_static (my_type,
282 TNY_TYPE_ACCOUNT_STORE_VIEW,
283 &tny_account_store_view_info);
289 modest_folder_view_class_init (ModestFolderViewClass *klass)
291 GObjectClass *gobject_class;
292 GtkTreeViewClass *treeview_class;
293 gobject_class = (GObjectClass*) klass;
294 treeview_class = (GtkTreeViewClass*) klass;
296 parent_class = g_type_class_peek_parent (klass);
297 gobject_class->finalize = modest_folder_view_finalize;
299 g_type_class_add_private (gobject_class,
300 sizeof(ModestFolderViewPrivate));
302 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
303 g_signal_new ("folder_selection_changed",
304 G_TYPE_FROM_CLASS (gobject_class),
306 G_STRUCT_OFFSET (ModestFolderViewClass,
307 folder_selection_changed),
309 modest_marshal_VOID__POINTER_BOOLEAN,
310 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
313 * This signal is emitted whenever the currently selected
314 * folder display name is computed. Note that the name could
315 * be different to the folder name, because we could append
316 * the unread messages count to the folder name to build the
317 * folder display name
319 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
320 g_signal_new ("folder-display-name-changed",
321 G_TYPE_FROM_CLASS (gobject_class),
323 G_STRUCT_OFFSET (ModestFolderViewClass,
324 folder_display_name_changed),
326 g_cclosure_marshal_VOID__STRING,
327 G_TYPE_NONE, 1, G_TYPE_STRING);
329 signals[FOLDER_ACTIVATED_SIGNAL] =
330 g_signal_new ("folder_activated",
331 G_TYPE_FROM_CLASS (gobject_class),
333 G_STRUCT_OFFSET (ModestFolderViewClass,
336 g_cclosure_marshal_VOID__POINTER,
337 G_TYPE_NONE, 1, G_TYPE_POINTER);
339 treeview_class->select_cursor_parent = NULL;
341 #ifdef MODEST_TOOLKIT_HILDON2
342 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
348 /* Retrieves the filter, sort and tny models of the folder view. If
349 any of these does not exist then it returns FALSE */
351 get_inner_models (ModestFolderView *self,
352 GtkTreeModel **filter_model,
353 GtkTreeModel **sort_model,
354 GtkTreeModel **tny_model)
356 GtkTreeModel *s_model, *f_model, *t_model;
358 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
359 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
360 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
364 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
365 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
366 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
370 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
374 *filter_model = f_model;
376 *sort_model = s_model;
378 *tny_model = t_model;
383 /* Simplify checks for NULLs: */
385 strings_are_equal (const gchar *a, const gchar *b)
391 return (strcmp (a, b) == 0);
398 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
400 GObject *instance = NULL;
402 gtk_tree_model_get (model, iter,
403 INSTANCE_COLUMN, &instance,
407 return FALSE; /* keep walking */
409 if (!TNY_IS_ACCOUNT (instance)) {
410 g_object_unref (instance);
411 return FALSE; /* keep walking */
414 /* Check if this is the looked-for account: */
415 TnyAccount *this_account = TNY_ACCOUNT (instance);
416 TnyAccount *account = TNY_ACCOUNT (data);
418 const gchar *this_account_id = tny_account_get_id(this_account);
419 const gchar *account_id = tny_account_get_id(account);
420 g_object_unref (instance);
423 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
424 if (strings_are_equal(this_account_id, account_id)) {
425 /* Tell the model that the data has changed, so that
426 * it calls the cell_data_func callbacks again: */
427 /* TODO: This does not seem to actually cause the new string to be shown: */
428 gtk_tree_model_row_changed (model, path, iter);
430 return TRUE; /* stop walking */
433 return FALSE; /* keep walking */
438 ModestFolderView *self;
439 gchar *previous_name;
440 } GetMmcAccountNameData;
443 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
445 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
447 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
449 if (!strings_are_equal (
450 tny_account_get_name(TNY_ACCOUNT(account)),
451 data->previous_name)) {
453 /* Tell the model that the data has changed, so that
454 * it calls the cell_data_func callbacks again: */
455 ModestFolderView *self = data->self;
456 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
458 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
461 g_free (data->previous_name);
462 g_slice_free (GetMmcAccountNameData, data);
466 convert_parent_folders_to_dots (gchar **item_name)
470 gchar *last_separator;
472 if (item_name == NULL)
475 for (c = *item_name; *c != '\0'; c++) {
476 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
481 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
482 if (last_separator != NULL) {
483 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
490 buffer = g_string_new ("");
491 for (i = 0; i < n_parents; i++) {
492 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
494 buffer = g_string_append (buffer, last_separator);
496 *item_name = g_string_free (buffer, FALSE);
502 format_compact_style (gchar **item_name,
505 gboolean multiaccount,
506 gboolean *use_markup)
510 TnyFolderType folder_type;
512 if (!TNY_IS_FOLDER (instance))
515 folder = (TnyFolder *) instance;
517 folder_type = tny_folder_get_folder_type (folder);
518 is_special = (get_cmp_pos (folder_type, folder)!= 4);
520 if (!is_special || multiaccount) {
521 TnyAccount *account = tny_folder_get_account (folder);
522 const gchar *folder_name;
523 gboolean concat_folder_name = FALSE;
526 /* Should not happen */
530 /* convert parent folders to dots */
531 convert_parent_folders_to_dots (item_name);
533 folder_name = tny_folder_get_name (folder);
534 if (g_str_has_suffix (*item_name, folder_name)) {
535 gchar *offset = g_strrstr (*item_name, folder_name);
537 concat_folder_name = TRUE;
540 buffer = g_string_new ("");
542 buffer = g_string_append (buffer, *item_name);
543 if (concat_folder_name) {
544 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
545 buffer = g_string_append (buffer, folder_name);
546 if (bold) buffer = g_string_append (buffer, "</span>");
549 g_object_unref (account);
551 *item_name = g_string_free (buffer, FALSE);
559 text_cell_data (GtkTreeViewColumn *column,
560 GtkCellRenderer *renderer,
561 GtkTreeModel *tree_model,
565 ModestFolderViewPrivate *priv;
566 GObject *rendobj = (GObject *) renderer;
568 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
569 GObject *instance = NULL;
570 gboolean use_markup = FALSE;
572 gtk_tree_model_get (tree_model, iter,
575 INSTANCE_COLUMN, &instance,
577 if (!fname || !instance)
580 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
581 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
583 gchar *item_name = NULL;
584 gint item_weight = 400;
586 if (type != TNY_FOLDER_TYPE_ROOT) {
590 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
591 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
592 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
593 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
595 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
598 /* Sometimes an special folder is reported by the server as
599 NORMAL, like some versions of Dovecot */
600 if (type == TNY_FOLDER_TYPE_NORMAL ||
601 type == TNY_FOLDER_TYPE_UNKNOWN) {
602 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
606 if (type == TNY_FOLDER_TYPE_INBOX) {
608 fname = g_strdup (_("mcen_me_folder_inbox"));
611 /* note: we cannot reliably get the counts from the tree model, we need
612 * to use explicit calls on tny_folder for some reason.
614 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
615 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
616 (type == TNY_FOLDER_TYPE_OUTBOX) ||
617 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
618 number = tny_folder_get_all_count (TNY_FOLDER(instance));
621 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
625 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
626 item_name = g_strdup (fname);
633 /* Use bold font style if there are unread or unset messages */
635 item_name = g_strdup_printf ("%s (%d)", fname, number);
638 item_name = g_strdup (fname);
643 } else if (TNY_IS_ACCOUNT (instance)) {
644 /* If it's a server account */
645 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
646 item_name = g_strdup (priv->local_account_name);
648 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
649 /* fname is only correct when the items are first
650 * added to the model, not when the account is
651 * changed later, so get the name from the account
653 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
656 item_name = g_strdup (fname);
662 item_name = g_strdup ("unknown");
664 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
665 gboolean multiaccount;
667 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
668 /* Convert item_name to markup */
669 format_compact_style (&item_name, instance,
671 multiaccount, &use_markup);
674 if (item_name && item_weight) {
675 /* Set the name in the treeview cell: */
677 g_object_set (rendobj, "markup", item_name, NULL);
679 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
681 /* Notify display name observers */
682 /* TODO: What listens for this signal, and how can it use only the new name? */
683 if (((GObject *) priv->cur_folder_store) == instance) {
684 g_signal_emit (G_OBJECT(self),
685 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
692 /* If it is a Memory card account, make sure that we have the correct name.
693 * This function will be trigerred again when the name has been retrieved: */
694 if (TNY_IS_STORE_ACCOUNT (instance) &&
695 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
697 /* Get the account name asynchronously: */
698 GetMmcAccountNameData *callback_data =
699 g_slice_new0(GetMmcAccountNameData);
700 callback_data->self = self;
702 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
704 callback_data->previous_name = g_strdup (name);
706 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
707 on_get_mmc_account_name, callback_data);
711 g_object_unref (G_OBJECT (instance));
717 messages_cell_data (GtkTreeViewColumn *column,
718 GtkCellRenderer *renderer,
719 GtkTreeModel *tree_model,
723 ModestFolderView *self;
724 ModestFolderViewPrivate *priv;
725 GObject *rendobj = (GObject *) renderer;
726 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
727 GObject *instance = NULL;
728 gchar *item_name = NULL;
730 gtk_tree_model_get (tree_model, iter,
732 INSTANCE_COLUMN, &instance,
737 self = MODEST_FOLDER_VIEW (data);
738 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
741 if (type != TNY_FOLDER_TYPE_ROOT) {
745 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
746 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
747 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
749 /* Sometimes an special folder is reported by the server as
750 NORMAL, like some versions of Dovecot */
751 if (type == TNY_FOLDER_TYPE_NORMAL ||
752 type == TNY_FOLDER_TYPE_UNKNOWN) {
753 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
757 /* note: we cannot reliably get the counts from the tree model, we need
758 * to use explicit calls on tny_folder for some reason.
760 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
761 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
762 (type == TNY_FOLDER_TYPE_OUTBOX) ||
763 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
764 number = tny_folder_get_all_count (TNY_FOLDER(instance));
767 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
771 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
773 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
781 item_name = g_strdup ("");
784 /* Set the name in the treeview cell: */
785 g_object_set (rendobj,"text", item_name, NULL);
793 g_object_unref (G_OBJECT (instance));
799 GdkPixbuf *pixbuf_open;
800 GdkPixbuf *pixbuf_close;
804 static inline GdkPixbuf *
805 get_composite_pixbuf (const gchar *icon_name,
807 GdkPixbuf *base_pixbuf)
809 GdkPixbuf *emblem, *retval = NULL;
811 emblem = modest_platform_get_icon (icon_name, size);
813 retval = gdk_pixbuf_copy (base_pixbuf);
814 gdk_pixbuf_composite (emblem, retval, 0, 0,
815 MIN (gdk_pixbuf_get_width (emblem),
816 gdk_pixbuf_get_width (retval)),
817 MIN (gdk_pixbuf_get_height (emblem),
818 gdk_pixbuf_get_height (retval)),
819 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
820 g_object_unref (emblem);
825 static inline ThreePixbufs *
826 get_composite_icons (const gchar *icon_code,
828 GdkPixbuf **pixbuf_open,
829 GdkPixbuf **pixbuf_close)
831 ThreePixbufs *retval;
834 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
837 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
842 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
846 retval = g_slice_new0 (ThreePixbufs);
848 retval->pixbuf = g_object_ref (*pixbuf);
850 retval->pixbuf_open = g_object_ref (*pixbuf_open);
852 retval->pixbuf_close = g_object_ref (*pixbuf_close);
857 static inline ThreePixbufs*
858 get_folder_icons (TnyFolderType type, GObject *instance)
860 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
861 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
862 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
863 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
864 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
866 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
867 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
868 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
869 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
870 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
872 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
873 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
874 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
875 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
876 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
878 ThreePixbufs *retval = NULL;
880 /* Sometimes an special folder is reported by the server as
881 NORMAL, like some versions of Dovecot */
882 if (type == TNY_FOLDER_TYPE_NORMAL ||
883 type == TNY_FOLDER_TYPE_UNKNOWN) {
884 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
887 /* Remote folders should not be treated as special folders */
888 if (TNY_IS_FOLDER_STORE (instance) &&
889 !TNY_IS_ACCOUNT (instance) &&
890 type != TNY_FOLDER_TYPE_INBOX &&
891 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
892 #ifdef MODEST_TOOLKIT_HILDON2
893 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
896 &anorm_pixbuf_close);
898 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
901 &normal_pixbuf_close);
907 case TNY_FOLDER_TYPE_INVALID:
908 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
911 case TNY_FOLDER_TYPE_ROOT:
912 if (TNY_IS_ACCOUNT (instance)) {
914 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
915 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
918 &avirt_pixbuf_close);
920 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
922 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
923 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
928 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
931 &anorm_pixbuf_close);
936 case TNY_FOLDER_TYPE_INBOX:
937 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
940 &inbox_pixbuf_close);
942 case TNY_FOLDER_TYPE_OUTBOX:
943 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
946 &outbox_pixbuf_close);
948 case TNY_FOLDER_TYPE_JUNK:
949 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
954 case TNY_FOLDER_TYPE_SENT:
955 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
960 case TNY_FOLDER_TYPE_TRASH:
961 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
964 &trash_pixbuf_close);
966 case TNY_FOLDER_TYPE_DRAFTS:
967 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
970 &draft_pixbuf_close);
972 case TNY_FOLDER_TYPE_ARCHIVE:
973 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
978 case TNY_FOLDER_TYPE_NORMAL:
980 /* Memory card folders could have an special icon */
981 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
982 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
987 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
990 &normal_pixbuf_close);
999 free_pixbufs (ThreePixbufs *pixbufs)
1001 if (pixbufs->pixbuf)
1002 g_object_unref (pixbufs->pixbuf);
1003 if (pixbufs->pixbuf_open)
1004 g_object_unref (pixbufs->pixbuf_open);
1005 if (pixbufs->pixbuf_close)
1006 g_object_unref (pixbufs->pixbuf_close);
1007 g_slice_free (ThreePixbufs, pixbufs);
1011 icon_cell_data (GtkTreeViewColumn *column,
1012 GtkCellRenderer *renderer,
1013 GtkTreeModel *tree_model,
1017 GObject *rendobj = NULL, *instance = NULL;
1018 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1019 gboolean has_children;
1020 ThreePixbufs *pixbufs;
1022 rendobj = (GObject *) renderer;
1024 gtk_tree_model_get (tree_model, iter,
1026 INSTANCE_COLUMN, &instance,
1032 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1033 pixbufs = get_folder_icons (type, instance);
1034 g_object_unref (instance);
1037 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1040 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1041 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1044 free_pixbufs (pixbufs);
1048 add_columns (GtkWidget *treeview)
1050 GtkTreeViewColumn *column;
1051 GtkCellRenderer *renderer;
1052 GtkTreeSelection *sel;
1053 ModestFolderViewPrivate *priv;
1055 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1058 column = gtk_tree_view_column_new ();
1060 /* Set icon and text render function */
1061 renderer = gtk_cell_renderer_pixbuf_new();
1062 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1063 gtk_tree_view_column_set_cell_data_func(column, renderer,
1064 icon_cell_data, treeview, NULL);
1066 renderer = gtk_cell_renderer_text_new();
1067 g_object_set (renderer,
1068 #ifdef MODEST_TOOLKIT_HILDON2
1069 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1071 "ellipsize", PANGO_ELLIPSIZE_END,
1073 "ellipsize-set", TRUE, NULL);
1074 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1075 gtk_tree_view_column_set_cell_data_func(column, renderer,
1076 text_cell_data, treeview, NULL);
1078 priv->messages_renderer = gtk_cell_renderer_text_new ();
1079 g_object_set (priv->messages_renderer,
1080 "scale", PANGO_SCALE_X_SMALL,
1082 "alignment", PANGO_ALIGN_RIGHT,
1086 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1087 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1088 messages_cell_data, treeview, NULL);
1090 /* Set selection mode */
1091 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1092 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1094 /* Set treeview appearance */
1095 gtk_tree_view_column_set_spacing (column, 2);
1096 gtk_tree_view_column_set_resizable (column, TRUE);
1097 gtk_tree_view_column_set_fixed_width (column, TRUE);
1098 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1099 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1102 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1106 modest_folder_view_init (ModestFolderView *obj)
1108 ModestFolderViewPrivate *priv;
1111 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1113 priv->timer_expander = 0;
1114 priv->account_store = NULL;
1116 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1117 priv->cur_folder_store = NULL;
1118 priv->visible_account_id = NULL;
1119 priv->folder_to_select = NULL;
1120 priv->outbox_deleted_handler = 0;
1121 priv->reexpand = TRUE;
1123 /* Initialize the local account name */
1124 conf = modest_runtime_get_conf();
1125 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1127 /* Init email clipboard */
1128 priv->clipboard = modest_runtime_get_email_clipboard ();
1129 priv->hidding_ids = NULL;
1130 priv->n_selected = 0;
1131 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1132 priv->reselect = FALSE;
1133 priv->show_non_move = TRUE;
1135 /* Build treeview */
1136 add_columns (GTK_WIDGET (obj));
1138 /* Setup drag and drop */
1139 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1141 /* Connect signals */
1142 g_signal_connect (G_OBJECT (obj),
1144 G_CALLBACK (on_key_pressed), NULL);
1146 priv->display_name_changed_signal =
1147 g_signal_connect (modest_runtime_get_account_mgr (),
1148 "display_name_changed",
1149 G_CALLBACK (on_display_name_changed),
1153 * Track changes in the local account name (in the device it
1154 * will be the device name)
1156 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1158 G_CALLBACK(on_configuration_key_changed),
1162 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1168 tny_account_store_view_init (gpointer g, gpointer iface_data)
1170 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1172 klass->set_account_store = modest_folder_view_set_account_store;
1176 modest_folder_view_finalize (GObject *obj)
1178 ModestFolderViewPrivate *priv;
1179 GtkTreeSelection *sel;
1180 TnyAccount *local_account;
1182 g_return_if_fail (obj);
1184 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1186 if (priv->timer_expander != 0) {
1187 g_source_remove (priv->timer_expander);
1188 priv->timer_expander = 0;
1191 local_account = (TnyAccount *)
1192 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1193 if (local_account) {
1194 if (g_signal_handler_is_connected (local_account,
1195 priv->outbox_deleted_handler))
1196 g_signal_handler_disconnect (local_account,
1197 priv->outbox_deleted_handler);
1198 g_object_unref (local_account);
1201 if (priv->account_store) {
1202 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1203 priv->account_inserted_signal);
1204 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1205 priv->account_removed_signal);
1206 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1207 priv->account_changed_signal);
1208 g_object_unref (G_OBJECT(priv->account_store));
1209 priv->account_store = NULL;
1212 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1213 priv->display_name_changed_signal)) {
1214 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1215 priv->display_name_changed_signal);
1216 priv->display_name_changed_signal = 0;
1220 g_object_unref (G_OBJECT (priv->query));
1224 if (priv->folder_to_select) {
1225 g_object_unref (G_OBJECT(priv->folder_to_select));
1226 priv->folder_to_select = NULL;
1229 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1231 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1233 g_free (priv->local_account_name);
1234 g_free (priv->visible_account_id);
1236 if (priv->conf_key_signal) {
1237 g_signal_handler_disconnect (modest_runtime_get_conf (),
1238 priv->conf_key_signal);
1239 priv->conf_key_signal = 0;
1242 if (priv->cur_folder_store) {
1243 g_object_unref (priv->cur_folder_store);
1244 priv->cur_folder_store = NULL;
1247 /* Clear hidding array created by cut operation */
1248 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1250 G_OBJECT_CLASS(parent_class)->finalize (obj);
1255 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1257 ModestFolderViewPrivate *priv;
1260 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1261 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1263 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1264 device = tny_account_store_get_device (account_store);
1266 if (G_UNLIKELY (priv->account_store)) {
1268 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1269 priv->account_inserted_signal))
1270 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1271 priv->account_inserted_signal);
1272 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1273 priv->account_removed_signal))
1274 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1275 priv->account_removed_signal);
1276 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1277 priv->account_changed_signal))
1278 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1279 priv->account_changed_signal);
1280 g_object_unref (G_OBJECT (priv->account_store));
1283 priv->account_store = g_object_ref (G_OBJECT (account_store));
1285 priv->account_removed_signal =
1286 g_signal_connect (G_OBJECT(account_store), "account_removed",
1287 G_CALLBACK (on_account_removed), self);
1289 priv->account_inserted_signal =
1290 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1291 G_CALLBACK (on_account_inserted), self);
1293 priv->account_changed_signal =
1294 g_signal_connect (G_OBJECT(account_store), "account_changed",
1295 G_CALLBACK (on_account_changed), self);
1297 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1298 priv->reselect = FALSE;
1299 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1301 g_object_unref (G_OBJECT (device));
1305 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1308 ModestFolderView *self;
1309 GtkTreeModel *model, *filter_model;
1312 self = MODEST_FOLDER_VIEW (user_data);
1314 if (!get_inner_models (self, &filter_model, NULL, &model))
1317 /* Remove outbox from model */
1318 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1319 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1320 g_object_unref (outbox);
1323 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1327 on_account_inserted (TnyAccountStore *account_store,
1328 TnyAccount *account,
1331 ModestFolderViewPrivate *priv;
1332 GtkTreeModel *model, *filter_model;
1334 /* Ignore transport account insertions, we're not showing them
1335 in the folder view */
1336 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1339 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1342 /* If we're adding a new account, and there is no previous
1343 one, we need to select the visible server account */
1344 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1345 !priv->visible_account_id)
1346 modest_widget_memory_restore (modest_runtime_get_conf(),
1347 G_OBJECT (user_data),
1348 MODEST_CONF_FOLDER_VIEW_KEY);
1352 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1353 &filter_model, NULL, &model))
1356 /* Insert the account in the model */
1357 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1359 /* When the model is a list store (plain representation) the
1360 outbox is not a child of any account so we have to manually
1361 delete it because removing the local folders account won't
1362 delete it (because tny_folder_get_account() is not defined
1363 for a merge folder */
1364 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1365 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1367 priv->outbox_deleted_handler =
1368 g_signal_connect (account,
1370 G_CALLBACK (on_outbox_deleted_cb),
1374 /* Refilter the model */
1375 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1380 same_account_selected (ModestFolderView *self,
1381 TnyAccount *account)
1383 ModestFolderViewPrivate *priv;
1384 gboolean same_account = FALSE;
1386 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1388 if (priv->cur_folder_store) {
1389 TnyAccount *selected_folder_account = NULL;
1391 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1392 selected_folder_account =
1393 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1395 selected_folder_account =
1396 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1399 if (selected_folder_account == account)
1400 same_account = TRUE;
1402 g_object_unref (selected_folder_account);
1404 return same_account;
1409 * Selects the first inbox or the local account in an idle
1412 on_idle_select_first_inbox_or_local (gpointer user_data)
1414 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1416 gdk_threads_enter ();
1417 modest_folder_view_select_first_inbox_or_local (self);
1418 gdk_threads_leave ();
1424 on_account_changed (TnyAccountStore *account_store,
1425 TnyAccount *tny_account,
1428 ModestFolderView *self;
1429 ModestFolderViewPrivate *priv;
1430 GtkTreeModel *model, *filter_model;
1431 GtkTreeSelection *sel;
1432 gboolean same_account;
1434 /* Ignore transport account insertions, we're not showing them
1435 in the folder view */
1436 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1439 self = MODEST_FOLDER_VIEW (user_data);
1440 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1442 /* Get the inner model */
1443 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1444 &filter_model, NULL, &model))
1447 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1449 /* Invalidate the cur_folder_store only if the selected folder
1450 belongs to the account that is being removed */
1451 same_account = same_account_selected (self, tny_account);
1453 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1454 gtk_tree_selection_unselect_all (sel);
1457 /* Remove the account from the model */
1458 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1460 /* Insert the account in the model */
1461 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1463 /* Refilter the model */
1464 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1466 /* Select the first INBOX if the currently selected folder
1467 belongs to the account that is being deleted */
1468 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1469 g_idle_add (on_idle_select_first_inbox_or_local, self);
1473 on_account_removed (TnyAccountStore *account_store,
1474 TnyAccount *account,
1477 ModestFolderView *self = NULL;
1478 ModestFolderViewPrivate *priv;
1479 GtkTreeModel *model, *filter_model;
1480 GtkTreeSelection *sel = NULL;
1481 gboolean same_account = FALSE;
1483 /* Ignore transport account removals, we're not showing them
1484 in the folder view */
1485 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1488 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1489 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1493 self = MODEST_FOLDER_VIEW (user_data);
1494 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1496 /* Invalidate the cur_folder_store only if the selected folder
1497 belongs to the account that is being removed */
1498 same_account = same_account_selected (self, account);
1500 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1501 gtk_tree_selection_unselect_all (sel);
1504 /* Invalidate row to select only if the folder to select
1505 belongs to the account that is being removed*/
1506 if (priv->folder_to_select) {
1507 TnyAccount *folder_to_select_account = NULL;
1509 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1510 if (folder_to_select_account == account) {
1511 modest_folder_view_disable_next_folder_selection (self);
1512 g_object_unref (priv->folder_to_select);
1513 priv->folder_to_select = NULL;
1515 g_object_unref (folder_to_select_account);
1518 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1519 &filter_model, NULL, &model))
1522 /* Disconnect the signal handler */
1523 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1524 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1525 if (g_signal_handler_is_connected (account,
1526 priv->outbox_deleted_handler))
1527 g_signal_handler_disconnect (account,
1528 priv->outbox_deleted_handler);
1531 /* Remove the account from the model */
1532 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1534 /* If the removed account is the currently viewed one then
1535 clear the configuration value. The new visible account will be the default account */
1536 if (priv->visible_account_id &&
1537 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1539 /* Clear the current visible account_id */
1540 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1542 /* Call the restore method, this will set the new visible account */
1543 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1544 MODEST_CONF_FOLDER_VIEW_KEY);
1547 /* Refilter the model */
1548 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1550 /* Select the first INBOX if the currently selected folder
1551 belongs to the account that is being deleted */
1553 g_idle_add (on_idle_select_first_inbox_or_local, self);
1557 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1559 GtkTreeViewColumn *col;
1561 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1563 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1565 g_printerr ("modest: failed get column for title\n");
1569 gtk_tree_view_column_set_title (col, title);
1570 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1575 modest_folder_view_on_map (ModestFolderView *self,
1576 GdkEventExpose *event,
1579 ModestFolderViewPrivate *priv;
1581 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1583 /* This won't happen often */
1584 if (G_UNLIKELY (priv->reselect)) {
1585 /* Select the first inbox or the local account if not found */
1587 /* TODO: this could cause a lock at startup, so we
1588 comment it for the moment. We know that this will
1589 be a bug, because the INBOX is not selected, but we
1590 need to rewrite some parts of Modest to avoid the
1591 deathlock situation */
1592 /* TODO: check if this is still the case */
1593 priv->reselect = FALSE;
1594 modest_folder_view_select_first_inbox_or_local (self);
1595 /* Notify the display name observers */
1596 g_signal_emit (G_OBJECT(self),
1597 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1601 if (priv->reexpand) {
1602 expand_root_items (self);
1603 priv->reexpand = FALSE;
1610 modest_folder_view_new (TnyFolderStoreQuery *query)
1613 ModestFolderViewPrivate *priv;
1614 GtkTreeSelection *sel;
1616 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1617 #ifdef MODEST_TOOLKIT_HILDON2
1618 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1621 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1624 priv->query = g_object_ref (query);
1626 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1627 priv->changed_signal = g_signal_connect (sel, "changed",
1628 G_CALLBACK (on_selection_changed), self);
1630 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1632 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1634 return GTK_WIDGET(self);
1637 /* this feels dirty; any other way to expand all the root items? */
1639 expand_root_items (ModestFolderView *self)
1642 GtkTreeModel *model;
1645 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1646 path = gtk_tree_path_new_first ();
1648 /* all folders should have child items, so.. */
1650 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1651 gtk_tree_path_next (path);
1652 } while (gtk_tree_model_get_iter (model, &iter, path));
1654 gtk_tree_path_free (path);
1658 * We use this function to implement the
1659 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1660 * account in this case, and the local folders.
1663 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1665 ModestFolderViewPrivate *priv;
1666 gboolean retval = TRUE;
1667 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1668 GObject *instance = NULL;
1669 const gchar *id = NULL;
1671 gboolean found = FALSE;
1672 gboolean cleared = FALSE;
1673 ModestTnyFolderRules rules = 0;
1675 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1676 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1678 gtk_tree_model_get (model, iter,
1680 INSTANCE_COLUMN, &instance,
1683 /* Do not show if there is no instance, this could indeed
1684 happen when the model is being modified while it's being
1685 drawn. This could occur for example when moving folders
1690 if (TNY_IS_ACCOUNT (instance)) {
1691 TnyAccount *acc = TNY_ACCOUNT (instance);
1692 const gchar *account_id = tny_account_get_id (acc);
1694 /* If it isn't a special folder,
1695 * don't show it unless it is the visible account: */
1696 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1697 !modest_tny_account_is_virtual_local_folders (acc) &&
1698 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1700 /* Show only the visible account id */
1701 if (priv->visible_account_id) {
1702 if (strcmp (account_id, priv->visible_account_id))
1709 /* Never show these to the user. They are merged into one folder
1710 * in the local-folders account instead: */
1711 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1714 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1715 /* Only show special folders for current account if needed */
1716 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1717 TnyAccount *account;
1719 account = tny_folder_get_account (TNY_FOLDER (instance));
1721 if (TNY_IS_ACCOUNT (account)) {
1722 const gchar *account_id = tny_account_get_id (account);
1724 if (!modest_tny_account_is_virtual_local_folders (account) &&
1725 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1726 /* Show only the visible account id */
1727 if (priv->visible_account_id) {
1728 if (strcmp (account_id, priv->visible_account_id))
1732 g_object_unref (account);
1739 /* Check hiding (if necessary) */
1740 cleared = modest_email_clipboard_cleared (priv->clipboard);
1741 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1742 id = tny_folder_get_id (TNY_FOLDER(instance));
1743 if (priv->hidding_ids != NULL)
1744 for (i=0; i < priv->n_selected && !found; i++)
1745 if (priv->hidding_ids[i] != NULL && id != NULL)
1746 found = (!strcmp (priv->hidding_ids[i], id));
1751 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1752 folder as no message can be move there according to UI specs */
1753 if (!priv->show_non_move) {
1754 if (TNY_IS_FOLDER (instance) &&
1755 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1757 case TNY_FOLDER_TYPE_OUTBOX:
1758 case TNY_FOLDER_TYPE_SENT:
1759 case TNY_FOLDER_TYPE_DRAFTS:
1762 case TNY_FOLDER_TYPE_UNKNOWN:
1763 case TNY_FOLDER_TYPE_NORMAL:
1764 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1765 if (type == TNY_FOLDER_TYPE_INVALID)
1766 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1768 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1769 type == TNY_FOLDER_TYPE_SENT
1770 || type == TNY_FOLDER_TYPE_DRAFTS)
1779 /* apply special filters */
1780 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1781 if (TNY_IS_ACCOUNT (instance))
1785 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1786 if (TNY_IS_FOLDER (instance)) {
1787 /* Check folder rules */
1788 ModestTnyFolderRules rules;
1790 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1791 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
1792 } else if (TNY_IS_ACCOUNT (instance)) {
1793 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1801 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1802 if (TNY_IS_FOLDER (instance)) {
1803 TnyFolderType guess_type;
1805 if (TNY_FOLDER_TYPE_NORMAL) {
1806 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1812 case TNY_FOLDER_TYPE_OUTBOX:
1813 case TNY_FOLDER_TYPE_SENT:
1814 case TNY_FOLDER_TYPE_DRAFTS:
1815 case TNY_FOLDER_TYPE_ARCHIVE:
1816 case TNY_FOLDER_TYPE_INBOX:
1819 case TNY_FOLDER_TYPE_UNKNOWN:
1820 case TNY_FOLDER_TYPE_NORMAL:
1826 } else if (TNY_IS_ACCOUNT (instance)) {
1831 if (retval && TNY_IS_FOLDER (instance)) {
1832 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1835 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1836 if (TNY_IS_FOLDER (instance)) {
1837 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1838 } else if (TNY_IS_ACCOUNT (instance)) {
1843 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1844 if (TNY_IS_FOLDER (instance)) {
1845 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1846 } else if (TNY_IS_ACCOUNT (instance)) {
1851 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1852 if (TNY_IS_FOLDER (instance)) {
1853 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1854 } else if (TNY_IS_ACCOUNT (instance)) {
1860 g_object_unref (instance);
1867 modest_folder_view_update_model (ModestFolderView *self,
1868 TnyAccountStore *account_store)
1870 ModestFolderViewPrivate *priv;
1871 GtkTreeModel *model /* , *old_model */;
1872 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1874 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1875 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1878 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1880 /* Notify that there is no folder selected */
1881 g_signal_emit (G_OBJECT(self),
1882 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1884 if (priv->cur_folder_store) {
1885 g_object_unref (priv->cur_folder_store);
1886 priv->cur_folder_store = NULL;
1889 /* FIXME: the local accounts are not shown when the query
1890 selects only the subscribed folders */
1891 #ifdef MODEST_TOOLKIT_HILDON2
1892 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1893 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1894 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1895 MODEST_FOLDER_PATH_SEPARATOR);
1897 model = tny_gtk_folder_store_tree_model_new (NULL);
1900 /* When the model is a list store (plain representation) the
1901 outbox is not a child of any account so we have to manually
1902 delete it because removing the local folders account won't
1903 delete it (because tny_folder_get_account() is not defined
1904 for a merge folder */
1905 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1906 TnyAccount *account;
1907 ModestTnyAccountStore *acc_store;
1909 acc_store = modest_runtime_get_account_store ();
1910 account = modest_tny_account_store_get_local_folders_account (acc_store);
1912 if (g_signal_handler_is_connected (account,
1913 priv->outbox_deleted_handler))
1914 g_signal_handler_disconnect (account,
1915 priv->outbox_deleted_handler);
1917 priv->outbox_deleted_handler =
1918 g_signal_connect (account,
1920 G_CALLBACK (on_outbox_deleted_cb),
1922 g_object_unref (account);
1925 /* Get the accounts: */
1926 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1928 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1930 sortable = gtk_tree_model_sort_new_with_model (model);
1931 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1933 GTK_SORT_ASCENDING);
1934 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1936 cmp_rows, NULL, NULL);
1938 /* Create filter model */
1939 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1940 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1946 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1947 #ifndef MODEST_TOOLKIT_HILDON2
1948 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1949 (GCallback) on_row_inserted_maybe_select_folder, self);
1952 g_object_unref (model);
1953 g_object_unref (filter_model);
1954 g_object_unref (sortable);
1956 /* Force a reselection of the INBOX next time the widget is shown */
1957 priv->reselect = TRUE;
1964 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1966 GtkTreeModel *model = NULL;
1967 TnyFolderStore *folder = NULL;
1969 ModestFolderView *tree_view = NULL;
1970 ModestFolderViewPrivate *priv = NULL;
1971 gboolean selected = FALSE;
1973 g_return_if_fail (sel);
1974 g_return_if_fail (user_data);
1976 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1978 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1980 tree_view = MODEST_FOLDER_VIEW (user_data);
1983 gtk_tree_model_get (model, &iter,
1984 INSTANCE_COLUMN, &folder,
1987 /* If the folder is the same do not notify */
1988 if (folder && priv->cur_folder_store == folder) {
1989 g_object_unref (folder);
1994 /* Current folder was unselected */
1995 if (priv->cur_folder_store) {
1996 /* We must do this firstly because a libtinymail-camel
1997 implementation detail. If we issue the signal
1998 before doing the sync_async, then that signal could
1999 cause (and it actually does it) a free of the
2000 summary of the folder (because the main window will
2001 clear the headers view */
2002 if (TNY_IS_FOLDER(priv->cur_folder_store))
2003 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2004 FALSE, NULL, NULL, NULL);
2006 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2007 priv->cur_folder_store, FALSE);
2009 g_object_unref (priv->cur_folder_store);
2010 priv->cur_folder_store = NULL;
2013 /* New current references */
2014 priv->cur_folder_store = folder;
2016 /* New folder has been selected. Do not notify if there is
2017 nothing new selected */
2019 g_signal_emit (G_OBJECT(tree_view),
2020 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2021 0, priv->cur_folder_store, TRUE);
2026 on_row_activated (GtkTreeView *treeview,
2027 GtkTreePath *treepath,
2028 GtkTreeViewColumn *column,
2031 GtkTreeModel *model = NULL;
2032 TnyFolderStore *folder = NULL;
2034 ModestFolderView *self = NULL;
2035 ModestFolderViewPrivate *priv = NULL;
2037 g_return_if_fail (treeview);
2038 g_return_if_fail (user_data);
2040 self = MODEST_FOLDER_VIEW (user_data);
2041 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2043 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2045 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2048 gtk_tree_model_get (model, &iter,
2049 INSTANCE_COLUMN, &folder,
2052 g_signal_emit (G_OBJECT(self),
2053 signals[FOLDER_ACTIVATED_SIGNAL],
2056 #ifdef MODEST_TOOLKIT_HILDON2
2057 HildonUIMode ui_mode;
2058 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2059 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2060 if (priv->cur_folder_store)
2061 g_object_unref (priv->cur_folder_store);
2062 priv->cur_folder_store = g_object_ref (folder);
2066 g_object_unref (folder);
2070 modest_folder_view_get_selected (ModestFolderView *self)
2072 ModestFolderViewPrivate *priv;
2074 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2076 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2077 if (priv->cur_folder_store)
2078 g_object_ref (priv->cur_folder_store);
2080 return priv->cur_folder_store;
2084 get_cmp_rows_type_pos (GObject *folder)
2086 /* Remote accounts -> Local account -> MMC account .*/
2089 if (TNY_IS_ACCOUNT (folder) &&
2090 modest_tny_account_is_virtual_local_folders (
2091 TNY_ACCOUNT (folder))) {
2093 } else if (TNY_IS_ACCOUNT (folder)) {
2094 TnyAccount *account = TNY_ACCOUNT (folder);
2095 const gchar *account_id = tny_account_get_id (account);
2096 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2102 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2103 return -1; /* Should never happen */
2108 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2110 TnyAccount *account;
2111 gboolean is_special;
2112 /* Inbox, Outbox, Drafts, Sent, User */
2115 if (!TNY_IS_FOLDER (folder_store))
2118 case TNY_FOLDER_TYPE_INBOX:
2120 account = tny_folder_get_account (folder_store);
2121 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2122 g_object_unref (account);
2123 return is_special?0:4;
2126 case TNY_FOLDER_TYPE_OUTBOX:
2129 case TNY_FOLDER_TYPE_DRAFTS:
2131 account = tny_folder_get_account (folder_store);
2132 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2133 g_object_unref (account);
2134 return is_special?1:4;
2137 case TNY_FOLDER_TYPE_SENT:
2139 account = tny_folder_get_account (folder_store);
2140 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2141 g_object_unref (account);
2142 return is_special?3:4;
2151 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2153 const gchar *a1_name, *a2_name;
2155 a1_name = tny_account_get_name (a1);
2156 a2_name = tny_account_get_name (a2);
2158 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2162 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2164 TnyAccount *a1 = NULL, *a2 = NULL;
2167 if (TNY_IS_ACCOUNT (s1)) {
2168 a1 = TNY_ACCOUNT (g_object_ref (s1));
2169 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2170 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2173 if (TNY_IS_ACCOUNT (s2)) {
2174 a2 = TNY_ACCOUNT (g_object_ref (s2));
2175 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2176 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2193 /* First we sort with the type of account */
2194 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2198 cmp = compare_account_names (a1, a2);
2202 g_object_unref (a1);
2204 g_object_unref (a2);
2210 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2212 gint is_account1, is_account2;
2214 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2215 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2217 return is_account2 - is_account1;
2221 * This function orders the mail accounts according to these rules:
2222 * 1st - remote accounts
2223 * 2nd - local account
2227 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2231 gchar *name1 = NULL;
2232 gchar *name2 = NULL;
2233 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2234 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2235 GObject *folder1 = NULL;
2236 GObject *folder2 = NULL;
2238 gtk_tree_model_get (tree_model, iter1,
2239 NAME_COLUMN, &name1,
2241 INSTANCE_COLUMN, &folder1,
2243 gtk_tree_model_get (tree_model, iter2,
2244 NAME_COLUMN, &name2,
2245 TYPE_COLUMN, &type2,
2246 INSTANCE_COLUMN, &folder2,
2249 /* Return if we get no folder. This could happen when folder
2250 operations are happening. The model is updated after the
2251 folder copy/move actually occurs, so there could be
2252 situations where the model to be drawn is not correct */
2253 if (!folder1 || !folder2)
2256 /* Sort by type. First the special folders, then the archives */
2257 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2261 /* Now we sort using the account of each folder */
2262 if (TNY_IS_FOLDER_STORE (folder1) &&
2263 TNY_IS_FOLDER_STORE (folder2)) {
2264 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2268 /* Each group is preceeded by its account */
2269 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2274 /* Pure sort by name */
2275 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2278 g_object_unref(G_OBJECT(folder1));
2280 g_object_unref(G_OBJECT(folder2));
2288 /*****************************************************************************/
2289 /* DRAG and DROP stuff */
2290 /*****************************************************************************/
2292 * This function fills the #GtkSelectionData with the row and the
2293 * model that has been dragged. It's called when this widget is a
2294 * source for dnd after the event drop happened
2297 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2298 guint info, guint time, gpointer data)
2300 GtkTreeSelection *selection;
2301 GtkTreeModel *model;
2303 GtkTreePath *source_row;
2305 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2306 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2308 source_row = gtk_tree_model_get_path (model, &iter);
2309 gtk_tree_set_row_drag_data (selection_data,
2313 gtk_tree_path_free (source_row);
2317 typedef struct _DndHelper {
2318 ModestFolderView *folder_view;
2319 gboolean delete_source;
2320 GtkTreePath *source_row;
2324 dnd_helper_destroyer (DndHelper *helper)
2326 /* Free the helper */
2327 gtk_tree_path_free (helper->source_row);
2328 g_slice_free (DndHelper, helper);
2332 xfer_folder_cb (ModestMailOperation *mail_op,
2333 TnyFolder *new_folder,
2337 /* Select the folder */
2338 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2344 /* get the folder for the row the treepath refers to. */
2345 /* folder must be unref'd */
2346 static TnyFolderStore *
2347 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2350 TnyFolderStore *folder = NULL;
2352 if (gtk_tree_model_get_iter (model,&iter, path))
2353 gtk_tree_model_get (model, &iter,
2354 INSTANCE_COLUMN, &folder,
2361 * This function is used by drag_data_received_cb to manage drag and
2362 * drop of a header, i.e, and drag from the header view to the folder
2366 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2367 GtkTreeModel *dest_model,
2368 GtkTreePath *dest_row,
2369 GtkSelectionData *selection_data)
2371 TnyList *headers = NULL;
2372 TnyFolder *folder = NULL, *src_folder = NULL;
2373 TnyFolderType folder_type;
2374 GtkTreeIter source_iter, dest_iter;
2375 ModestWindowMgr *mgr = NULL;
2376 ModestWindow *main_win = NULL;
2377 gchar **uris, **tmp;
2379 /* Build the list of headers */
2380 mgr = modest_runtime_get_window_mgr ();
2381 headers = tny_simple_list_new ();
2382 uris = modest_dnd_selection_data_get_paths (selection_data);
2385 while (*tmp != NULL) {
2388 gboolean first = TRUE;
2391 path = gtk_tree_path_new_from_string (*tmp);
2392 gtk_tree_model_get_iter (source_model, &source_iter, path);
2393 gtk_tree_model_get (source_model, &source_iter,
2394 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2397 /* Do not enable d&d of headers already opened */
2398 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2399 tny_list_append (headers, G_OBJECT (header));
2401 if (G_UNLIKELY (first)) {
2402 src_folder = tny_header_get_folder (header);
2406 /* Free and go on */
2407 gtk_tree_path_free (path);
2408 g_object_unref (header);
2413 /* This could happen ig we perform a d&d very quickly over the
2414 same row that row could dissapear because message is
2416 if (!TNY_IS_FOLDER (src_folder))
2419 /* Get the target folder */
2420 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2421 gtk_tree_model_get (dest_model, &dest_iter,
2425 if (!folder || !TNY_IS_FOLDER(folder)) {
2426 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2430 folder_type = modest_tny_folder_guess_folder_type (folder);
2431 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2432 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2433 goto cleanup; /* cannot move messages there */
2436 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2437 /* g_warning ("folder not writable"); */
2438 goto cleanup; /* verboten! */
2441 /* Ask for confirmation to move */
2442 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2444 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2448 /* Transfer messages */
2449 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2454 if (G_IS_OBJECT (src_folder))
2455 g_object_unref (src_folder);
2456 if (G_IS_OBJECT(folder))
2457 g_object_unref (G_OBJECT (folder));
2458 if (G_IS_OBJECT(headers))
2459 g_object_unref (headers);
2463 TnyFolderStore *src_folder;
2464 TnyFolderStore *dst_folder;
2465 ModestFolderView *folder_view;
2470 dnd_folder_info_destroyer (DndFolderInfo *info)
2472 if (info->src_folder)
2473 g_object_unref (info->src_folder);
2474 if (info->dst_folder)
2475 g_object_unref (info->dst_folder);
2476 g_slice_free (DndFolderInfo, info);
2480 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2481 GtkWindow *parent_window,
2482 TnyAccount *account)
2485 modest_ui_actions_on_account_connection_error (parent_window, account);
2487 /* Free the helper & info */
2488 dnd_helper_destroyer (info->helper);
2489 dnd_folder_info_destroyer (info);
2493 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2495 GtkWindow *parent_window,
2496 TnyAccount *account,
2499 DndFolderInfo *info = NULL;
2500 ModestMailOperation *mail_op;
2502 info = (DndFolderInfo *) user_data;
2504 if (err || canceled) {
2505 dnd_on_connection_failed_destroyer (info, parent_window, account);
2509 /* Do the mail operation */
2510 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2511 modest_ui_actions_move_folder_error_handler,
2512 info->src_folder, NULL);
2514 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2517 /* Transfer the folder */
2518 modest_mail_operation_xfer_folder (mail_op,
2519 TNY_FOLDER (info->src_folder),
2521 info->helper->delete_source,
2523 info->helper->folder_view);
2526 g_object_unref (G_OBJECT (mail_op));
2527 dnd_helper_destroyer (info->helper);
2528 dnd_folder_info_destroyer (info);
2533 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2535 GtkWindow *parent_window,
2536 TnyAccount *account,
2539 DndFolderInfo *info = NULL;
2541 info = (DndFolderInfo *) user_data;
2543 if (err || canceled) {
2544 dnd_on_connection_failed_destroyer (info, parent_window, account);
2548 /* Connect to source folder and perform the copy/move */
2549 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2551 drag_and_drop_from_folder_view_src_folder_performer,
2556 * This function is used by drag_data_received_cb to manage drag and
2557 * drop of a folder, i.e, and drag from the folder view to the same
2561 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2562 GtkTreeModel *dest_model,
2563 GtkTreePath *dest_row,
2564 GtkSelectionData *selection_data,
2567 GtkTreeIter dest_iter, iter;
2568 TnyFolderStore *dest_folder = NULL;
2569 TnyFolderStore *folder = NULL;
2570 gboolean forbidden = FALSE;
2572 DndFolderInfo *info = NULL;
2574 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2576 g_warning ("%s: BUG: no main window", __FUNCTION__);
2577 dnd_helper_destroyer (helper);
2582 /* check the folder rules for the destination */
2583 folder = tree_path_to_folder (dest_model, dest_row);
2584 if (TNY_IS_FOLDER(folder)) {
2585 ModestTnyFolderRules rules =
2586 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2587 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2588 } else if (TNY_IS_FOLDER_STORE(folder)) {
2589 /* enable local root as destination for folders */
2590 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2591 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2594 g_object_unref (folder);
2597 /* check the folder rules for the source */
2598 folder = tree_path_to_folder (source_model, helper->source_row);
2599 if (TNY_IS_FOLDER(folder)) {
2600 ModestTnyFolderRules rules =
2601 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2602 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2605 g_object_unref (folder);
2609 /* Check if the drag is possible */
2610 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2612 modest_platform_run_information_dialog ((GtkWindow *) win,
2613 _("mail_in_ui_folder_move_target_error"),
2615 /* Restore the previous selection */
2616 folder = tree_path_to_folder (source_model, helper->source_row);
2618 if (TNY_IS_FOLDER (folder))
2619 modest_folder_view_select_folder (helper->folder_view,
2620 TNY_FOLDER (folder), FALSE);
2621 g_object_unref (folder);
2623 dnd_helper_destroyer (helper);
2628 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2629 gtk_tree_model_get (dest_model, &dest_iter,
2632 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2633 gtk_tree_model_get (source_model, &iter,
2637 /* Create the info for the performer */
2638 info = g_slice_new0 (DndFolderInfo);
2639 info->src_folder = g_object_ref (folder);
2640 info->dst_folder = g_object_ref (dest_folder);
2641 info->helper = helper;
2643 /* Connect to the destination folder and perform the copy/move */
2644 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2646 drag_and_drop_from_folder_view_dst_folder_performer,
2650 g_object_unref (dest_folder);
2651 g_object_unref (folder);
2655 * This function receives the data set by the "drag-data-get" signal
2656 * handler. This information comes within the #GtkSelectionData. This
2657 * function will manage both the drags of folders of the treeview and
2658 * drags of headers of the header view widget.
2661 on_drag_data_received (GtkWidget *widget,
2662 GdkDragContext *context,
2665 GtkSelectionData *selection_data,
2670 GtkWidget *source_widget;
2671 GtkTreeModel *dest_model, *source_model;
2672 GtkTreePath *source_row, *dest_row;
2673 GtkTreeViewDropPosition pos;
2674 gboolean delete_source = FALSE;
2675 gboolean success = FALSE;
2677 /* Do not allow further process */
2678 g_signal_stop_emission_by_name (widget, "drag-data-received");
2679 source_widget = gtk_drag_get_source_widget (context);
2681 /* Get the action */
2682 if (context->action == GDK_ACTION_MOVE) {
2683 delete_source = TRUE;
2685 /* Notify that there is no folder selected. We need to
2686 do this in order to update the headers view (and
2687 its monitors, because when moving, the old folder
2688 won't longer exist. We can not wait for the end of
2689 the operation, because the operation won't start if
2690 the folder is in use */
2691 if (source_widget == widget) {
2692 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2693 gtk_tree_selection_unselect_all (sel);
2697 /* Check if the get_data failed */
2698 if (selection_data == NULL || selection_data->length < 0)
2701 /* Select the destination model */
2702 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2704 /* Get the path to the destination row. Can not call
2705 gtk_tree_view_get_drag_dest_row() because the source row
2706 is not selected anymore */
2707 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2710 /* Only allow drops IN other rows */
2712 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2713 pos == GTK_TREE_VIEW_DROP_AFTER)
2717 /* Drags from the header view */
2718 if (source_widget != widget) {
2719 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2721 drag_and_drop_from_header_view (source_model,
2726 DndHelper *helper = NULL;
2728 /* Get the source model and row */
2729 gtk_tree_get_row_drag_data (selection_data,
2733 /* Create the helper */
2734 helper = g_slice_new0 (DndHelper);
2735 helper->delete_source = delete_source;
2736 helper->source_row = gtk_tree_path_copy (source_row);
2737 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2739 drag_and_drop_from_folder_view (source_model,
2745 gtk_tree_path_free (source_row);
2749 gtk_tree_path_free (dest_row);
2752 /* Finish the drag and drop */
2753 gtk_drag_finish (context, success, FALSE, time);
2757 * We define a "drag-drop" signal handler because we do not want to
2758 * use the default one, because the default one always calls
2759 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2760 * signal handler, because there we have all the information available
2761 * to know if the dnd was a success or not.
2764 drag_drop_cb (GtkWidget *widget,
2765 GdkDragContext *context,
2773 if (!context->targets)
2776 /* Check if we're dragging a folder row */
2777 target = gtk_drag_dest_find_target (widget, context, NULL);
2779 /* Request the data from the source. */
2780 gtk_drag_get_data(widget, context, target, time);
2786 * This function expands a node of a tree view if it's not expanded
2787 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2788 * does that, so that's why they're here.
2791 expand_row_timeout (gpointer data)
2793 GtkTreeView *tree_view = data;
2794 GtkTreePath *dest_path = NULL;
2795 GtkTreeViewDropPosition pos;
2796 gboolean result = FALSE;
2798 gdk_threads_enter ();
2800 gtk_tree_view_get_drag_dest_row (tree_view,
2805 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2806 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2807 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2808 gtk_tree_path_free (dest_path);
2812 gtk_tree_path_free (dest_path);
2817 gdk_threads_leave ();
2823 * This function is called whenever the pointer is moved over a widget
2824 * while dragging some data. It installs a timeout that will expand a
2825 * node of the treeview if not expanded yet. This function also calls
2826 * gdk_drag_status in order to set the suggested action that will be
2827 * used by the "drag-data-received" signal handler to know if we
2828 * should do a move or just a copy of the data.
2831 on_drag_motion (GtkWidget *widget,
2832 GdkDragContext *context,
2838 GtkTreeViewDropPosition pos;
2839 GtkTreePath *dest_row;
2840 GtkTreeModel *dest_model;
2841 ModestFolderViewPrivate *priv;
2842 GdkDragAction suggested_action;
2843 gboolean valid_location = FALSE;
2844 TnyFolderStore *folder = NULL;
2846 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2848 if (priv->timer_expander != 0) {
2849 g_source_remove (priv->timer_expander);
2850 priv->timer_expander = 0;
2853 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2858 /* Do not allow drops between folders */
2860 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2861 pos == GTK_TREE_VIEW_DROP_AFTER) {
2862 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2863 gdk_drag_status(context, 0, time);
2864 valid_location = FALSE;
2867 valid_location = TRUE;
2870 /* Check that the destination folder is writable */
2871 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2872 folder = tree_path_to_folder (dest_model, dest_row);
2873 if (folder && TNY_IS_FOLDER (folder)) {
2874 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2876 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2877 valid_location = FALSE;
2882 /* Expand the selected row after 1/2 second */
2883 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2884 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2886 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2888 /* Select the desired action. By default we pick MOVE */
2889 suggested_action = GDK_ACTION_MOVE;
2891 if (context->actions == GDK_ACTION_COPY)
2892 gdk_drag_status(context, GDK_ACTION_COPY, time);
2893 else if (context->actions == GDK_ACTION_MOVE)
2894 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2895 else if (context->actions & suggested_action)
2896 gdk_drag_status(context, suggested_action, time);
2898 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2902 g_object_unref (folder);
2904 gtk_tree_path_free (dest_row);
2906 g_signal_stop_emission_by_name (widget, "drag-motion");
2908 return valid_location;
2912 * This function sets the treeview as a source and a target for dnd
2913 * events. It also connects all the requirede signals.
2916 setup_drag_and_drop (GtkTreeView *self)
2918 /* Set up the folder view as a dnd destination. Set only the
2919 highlight flag, otherwise gtk will have a different
2921 #ifdef MODEST_TOOLKIT_HILDON2
2924 gtk_drag_dest_set (GTK_WIDGET (self),
2925 GTK_DEST_DEFAULT_HIGHLIGHT,
2926 folder_view_drag_types,
2927 G_N_ELEMENTS (folder_view_drag_types),
2928 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2930 g_signal_connect (G_OBJECT (self),
2931 "drag_data_received",
2932 G_CALLBACK (on_drag_data_received),
2936 /* Set up the treeview as a dnd source */
2937 gtk_drag_source_set (GTK_WIDGET (self),
2939 folder_view_drag_types,
2940 G_N_ELEMENTS (folder_view_drag_types),
2941 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2943 g_signal_connect (G_OBJECT (self),
2945 G_CALLBACK (on_drag_motion),
2948 g_signal_connect (G_OBJECT (self),
2950 G_CALLBACK (on_drag_data_get),
2953 g_signal_connect (G_OBJECT (self),
2955 G_CALLBACK (drag_drop_cb),
2960 * This function manages the navigation through the folders using the
2961 * keyboard or the hardware keys in the device
2964 on_key_pressed (GtkWidget *self,
2968 GtkTreeSelection *selection;
2970 GtkTreeModel *model;
2971 gboolean retval = FALSE;
2973 /* Up and Down are automatically managed by the treeview */
2974 if (event->keyval == GDK_Return) {
2975 /* Expand/Collapse the selected row */
2976 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2977 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2980 path = gtk_tree_model_get_path (model, &iter);
2982 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2983 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2985 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2986 gtk_tree_path_free (path);
2988 /* No further processing */
2996 * We listen to the changes in the local folder account name key,
2997 * because we want to show the right name in the view. The local
2998 * folder account name corresponds to the device name in the Maemo
2999 * version. We do this because we do not want to query gconf on each
3000 * tree view refresh. It's better to cache it and change whenever
3004 on_configuration_key_changed (ModestConf* conf,
3006 ModestConfEvent event,
3007 ModestConfNotificationId id,
3008 ModestFolderView *self)
3010 ModestFolderViewPrivate *priv;
3013 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3014 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3016 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3017 g_free (priv->local_account_name);
3019 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3020 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3022 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3023 MODEST_CONF_DEVICE_NAME, NULL);
3025 /* Force a redraw */
3026 #if GTK_CHECK_VERSION(2, 8, 0)
3027 GtkTreeViewColumn * tree_column;
3029 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3031 gtk_tree_view_column_queue_resize (tree_column);
3033 gtk_widget_queue_draw (GTK_WIDGET (self));
3039 modest_folder_view_set_style (ModestFolderView *self,
3040 ModestFolderViewStyle style)
3042 ModestFolderViewPrivate *priv;
3044 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3045 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3046 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3048 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3051 priv->style = style;
3055 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3056 const gchar *account_id)
3058 ModestFolderViewPrivate *priv;
3059 GtkTreeModel *model;
3061 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3063 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3065 /* This will be used by the filter_row callback,
3066 * to decided which rows to show: */
3067 if (priv->visible_account_id) {
3068 g_free (priv->visible_account_id);
3069 priv->visible_account_id = NULL;
3072 priv->visible_account_id = g_strdup (account_id);
3075 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3076 if (GTK_IS_TREE_MODEL_FILTER (model))
3077 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3079 /* Save settings to gconf */
3080 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3081 MODEST_CONF_FOLDER_VIEW_KEY);
3085 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3087 ModestFolderViewPrivate *priv;
3089 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3091 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3093 return (const gchar *) priv->visible_account_id;
3097 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3101 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3103 gtk_tree_model_get (model, iter,
3107 gboolean result = FALSE;
3108 if (type == TNY_FOLDER_TYPE_INBOX) {
3112 *inbox_iter = *iter;
3116 if (gtk_tree_model_iter_children (model, &child, iter)) {
3117 if (find_inbox_iter (model, &child, inbox_iter))
3121 } while (gtk_tree_model_iter_next (model, iter));
3130 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3132 GtkTreeModel *model;
3133 GtkTreeIter iter, inbox_iter;
3134 GtkTreeSelection *sel;
3135 GtkTreePath *path = NULL;
3137 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3139 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3143 expand_root_items (self);
3144 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3146 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3147 g_warning ("%s: model is empty", __FUNCTION__);
3151 if (find_inbox_iter (model, &iter, &inbox_iter))
3152 path = gtk_tree_model_get_path (model, &inbox_iter);
3154 path = gtk_tree_path_new_first ();
3156 /* Select the row and free */
3157 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3158 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3159 gtk_tree_path_free (path);
3162 gtk_widget_grab_focus (GTK_WIDGET(self));
3168 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3173 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3174 TnyFolder* a_folder;
3177 gtk_tree_model_get (model, iter,
3178 INSTANCE_COLUMN, &a_folder,
3184 if (folder == a_folder) {
3185 g_object_unref (a_folder);
3186 *folder_iter = *iter;
3189 g_object_unref (a_folder);
3191 if (gtk_tree_model_iter_children (model, &child, iter)) {
3192 if (find_folder_iter (model, &child, folder_iter, folder))
3196 } while (gtk_tree_model_iter_next (model, iter));
3201 #ifndef MODEST_TOOLKIT_HILDON2
3203 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3206 ModestFolderView *self)
3208 ModestFolderViewPrivate *priv = NULL;
3209 GtkTreeSelection *sel;
3210 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3211 GObject *instance = NULL;
3213 if (!MODEST_IS_FOLDER_VIEW(self))
3216 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3218 priv->reexpand = TRUE;
3220 gtk_tree_model_get (tree_model, iter,
3222 INSTANCE_COLUMN, &instance,
3228 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3229 priv->folder_to_select = g_object_ref (instance);
3231 g_object_unref (instance);
3233 if (priv->folder_to_select) {
3235 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3238 path = gtk_tree_model_get_path (tree_model, iter);
3239 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3241 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3243 gtk_tree_selection_select_iter (sel, iter);
3244 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3246 gtk_tree_path_free (path);
3250 modest_folder_view_disable_next_folder_selection (self);
3252 /* Refilter the model */
3253 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3259 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3261 ModestFolderViewPrivate *priv;
3263 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3265 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3267 if (priv->folder_to_select)
3268 g_object_unref(priv->folder_to_select);
3270 priv->folder_to_select = NULL;
3274 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3275 gboolean after_change)
3277 GtkTreeModel *model;
3278 GtkTreeIter iter, folder_iter;
3279 GtkTreeSelection *sel;
3280 ModestFolderViewPrivate *priv = NULL;
3282 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3283 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3285 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3288 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3289 gtk_tree_selection_unselect_all (sel);
3291 if (priv->folder_to_select)
3292 g_object_unref(priv->folder_to_select);
3293 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3297 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3302 /* Refilter the model, before selecting the folder */
3303 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3305 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3306 g_warning ("%s: model is empty", __FUNCTION__);
3310 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3313 path = gtk_tree_model_get_path (model, &folder_iter);
3314 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3316 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3317 gtk_tree_selection_select_iter (sel, &folder_iter);
3318 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3320 gtk_tree_path_free (path);
3328 modest_folder_view_copy_selection (ModestFolderView *self)
3330 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3332 /* Copy selection */
3333 _clipboard_set_selected_data (self, FALSE);
3337 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3339 ModestFolderViewPrivate *priv = NULL;
3340 GtkTreeModel *model = NULL;
3341 const gchar **hidding = NULL;
3342 guint i, n_selected;
3344 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3345 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3347 /* Copy selection */
3348 if (!_clipboard_set_selected_data (folder_view, TRUE))
3351 /* Get hidding ids */
3352 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3354 /* Clear hidding array created by previous cut operation */
3355 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3357 /* Copy hidding array */
3358 priv->n_selected = n_selected;
3359 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3360 for (i=0; i < n_selected; i++)
3361 priv->hidding_ids[i] = g_strdup(hidding[i]);
3363 /* Hide cut folders */
3364 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3365 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3369 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3370 ModestFolderView *folder_view_dst)
3372 GtkTreeModel *filter_model = NULL;
3373 GtkTreeModel *model = NULL;
3374 GtkTreeModel *new_filter_model = NULL;
3376 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3377 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3380 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3381 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3383 /* Build new filter model */
3384 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3385 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3389 /* Set copied model */
3390 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3391 #ifndef MODEST_TOOLKIT_HILDON2
3392 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3393 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3397 g_object_unref (new_filter_model);
3401 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3404 GtkTreeModel *model = NULL;
3405 ModestFolderViewPrivate* priv;
3407 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3409 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3410 priv->show_non_move = show;
3411 /* modest_folder_view_update_model(folder_view, */
3412 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3414 /* Hide special folders */
3415 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3416 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3417 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3421 /* Returns FALSE if it did not selected anything */
3423 _clipboard_set_selected_data (ModestFolderView *folder_view,
3426 ModestFolderViewPrivate *priv = NULL;
3427 TnyFolderStore *folder = NULL;
3428 gboolean retval = FALSE;
3430 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3431 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3433 /* Set selected data on clipboard */
3434 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3435 folder = modest_folder_view_get_selected (folder_view);
3437 /* Do not allow to select an account */
3438 if (TNY_IS_FOLDER (folder)) {
3439 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3444 g_object_unref (folder);
3450 _clear_hidding_filter (ModestFolderView *folder_view)
3452 ModestFolderViewPrivate *priv;
3455 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3456 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3458 if (priv->hidding_ids != NULL) {
3459 for (i=0; i < priv->n_selected; i++)
3460 g_free (priv->hidding_ids[i]);
3461 g_free(priv->hidding_ids);
3467 on_display_name_changed (ModestAccountMgr *mgr,
3468 const gchar *account,
3471 ModestFolderView *self;
3473 self = MODEST_FOLDER_VIEW (user_data);
3475 /* Force a redraw */
3476 #if GTK_CHECK_VERSION(2, 8, 0)
3477 GtkTreeViewColumn * tree_column;
3479 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3481 gtk_tree_view_column_queue_resize (tree_column);
3483 gtk_widget_queue_draw (GTK_WIDGET (self));
3488 modest_folder_view_set_cell_style (ModestFolderView *self,
3489 ModestFolderViewCellStyle cell_style)
3491 ModestFolderViewPrivate *priv = NULL;
3493 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3494 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3496 priv->cell_style = cell_style;
3498 g_object_set (G_OBJECT (priv->messages_renderer),
3499 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3502 gtk_widget_queue_draw (GTK_WIDGET (self));
3506 update_style (ModestFolderView *self)
3508 ModestFolderViewPrivate *priv;
3509 GdkColor style_color;
3511 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3512 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3514 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3515 gdk_color_parse ("grey", &style_color);
3518 g_object_set (G_OBJECT (priv->messages_renderer),
3519 "foreground-gdk", &style_color,
3520 "foreground-set", TRUE,
3525 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3527 if (strcmp ("style", spec->name) == 0) {
3528 update_style (MODEST_FOLDER_VIEW (obj));
3529 gtk_widget_queue_draw (GTK_WIDGET (obj));
3534 modest_folder_view_set_filter (ModestFolderView *self,
3535 ModestFolderViewFilter filter)
3537 ModestFolderViewPrivate *priv;
3538 GtkTreeModel *filter_model;
3540 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3541 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3543 priv->filter |= filter;
3545 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3546 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3547 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3552 modest_folder_view_unset_filter (ModestFolderView *self,
3553 ModestFolderViewFilter filter)
3555 ModestFolderViewPrivate *priv;
3556 GtkTreeModel *filter_model;
3558 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3559 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3561 priv->filter &= ~filter;
3563 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3564 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3565 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));