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 ((ModestTnyAccountStore *)priv->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 #ifdef MODEST_TOOLKIT_HILDON2
1692 /* In hildon 2.2 we don't show the account rows */
1695 TnyAccount *acc = TNY_ACCOUNT (instance);
1696 const gchar *account_id = tny_account_get_id (acc);
1698 /* If it isn't a special folder,
1699 * don't show it unless it is the visible account: */
1700 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1701 !modest_tny_account_is_virtual_local_folders (acc) &&
1702 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1704 /* Show only the visible account id */
1705 if (priv->visible_account_id) {
1706 if (strcmp (account_id, priv->visible_account_id))
1713 /* Never show these to the user. They are merged into one folder
1714 * in the local-folders account instead: */
1715 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1719 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1720 /* Only show special folders for current account if needed */
1721 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1722 TnyAccount *account;
1724 account = tny_folder_get_account (TNY_FOLDER (instance));
1726 if (TNY_IS_ACCOUNT (account)) {
1727 const gchar *account_id = tny_account_get_id (account);
1729 if (!modest_tny_account_is_virtual_local_folders (account) &&
1730 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1731 /* Show only the visible account id */
1732 if (priv->visible_account_id) {
1733 if (strcmp (account_id, priv->visible_account_id))
1737 g_object_unref (account);
1744 /* Check hiding (if necessary) */
1745 cleared = modest_email_clipboard_cleared (priv->clipboard);
1746 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1747 id = tny_folder_get_id (TNY_FOLDER(instance));
1748 if (priv->hidding_ids != NULL)
1749 for (i=0; i < priv->n_selected && !found; i++)
1750 if (priv->hidding_ids[i] != NULL && id != NULL)
1751 found = (!strcmp (priv->hidding_ids[i], id));
1756 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1757 folder as no message can be move there according to UI specs */
1758 if (!priv->show_non_move) {
1760 case TNY_FOLDER_TYPE_OUTBOX:
1761 case TNY_FOLDER_TYPE_SENT:
1762 case TNY_FOLDER_TYPE_DRAFTS:
1765 case TNY_FOLDER_TYPE_UNKNOWN:
1766 case TNY_FOLDER_TYPE_NORMAL:
1767 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1768 if (type == TNY_FOLDER_TYPE_INVALID)
1769 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1771 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1772 type == TNY_FOLDER_TYPE_SENT
1773 || type == TNY_FOLDER_TYPE_DRAFTS)
1781 /* apply special filters */
1782 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1783 if (TNY_IS_FOLDER (instance)) {
1784 TnyFolderCaps capabilities;
1786 capabilities = tny_folder_get_caps (TNY_FOLDER (instance));
1787 retval = !(capabilities & TNY_FOLDER_CAPS_NOCHILDREN);
1790 retval = ((type != TNY_FOLDER_TYPE_DRAFTS) &&
1791 (type != TNY_FOLDER_TYPE_OUTBOX) &&
1792 (type != TNY_FOLDER_TYPE_SENT));
1794 } else if (TNY_IS_ACCOUNT (instance)) {
1799 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1800 if (TNY_IS_FOLDER (instance)) {
1801 TnyFolderType guess_type;
1803 if (TNY_FOLDER_TYPE_NORMAL) {
1804 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1810 case TNY_FOLDER_TYPE_OUTBOX:
1811 case TNY_FOLDER_TYPE_SENT:
1812 case TNY_FOLDER_TYPE_DRAFTS:
1813 case TNY_FOLDER_TYPE_ARCHIVE:
1814 case TNY_FOLDER_TYPE_INBOX:
1817 case TNY_FOLDER_TYPE_UNKNOWN:
1818 case TNY_FOLDER_TYPE_NORMAL:
1824 } else if (TNY_IS_ACCOUNT (instance)) {
1829 if (retval && TNY_IS_FOLDER (instance)) {
1830 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1833 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1834 if (TNY_IS_FOLDER (instance)) {
1835 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1836 } else if (TNY_IS_ACCOUNT (instance)) {
1841 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1842 if (TNY_IS_FOLDER (instance)) {
1843 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1844 } else if (TNY_IS_ACCOUNT (instance)) {
1849 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1850 if (TNY_IS_FOLDER (instance)) {
1851 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1852 } else if (TNY_IS_ACCOUNT (instance)) {
1858 g_object_unref (instance);
1865 modest_folder_view_update_model (ModestFolderView *self,
1866 TnyAccountStore *account_store)
1868 ModestFolderViewPrivate *priv;
1869 GtkTreeModel *model /* , *old_model */;
1870 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1872 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1873 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1876 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1878 /* Notify that there is no folder selected */
1879 g_signal_emit (G_OBJECT(self),
1880 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1882 if (priv->cur_folder_store) {
1883 g_object_unref (priv->cur_folder_store);
1884 priv->cur_folder_store = NULL;
1887 /* FIXME: the local accounts are not shown when the query
1888 selects only the subscribed folders */
1889 #ifdef MODEST_TOOLKIT_HILDON2
1890 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1891 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1892 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1893 MODEST_FOLDER_PATH_SEPARATOR);
1895 model = tny_gtk_folder_store_tree_model_new (NULL);
1898 /* When the model is a list store (plain representation) the
1899 outbox is not a child of any account so we have to manually
1900 delete it because removing the local folders account won't
1901 delete it (because tny_folder_get_account() is not defined
1902 for a merge folder */
1903 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1904 TnyAccount *account;
1905 ModestTnyAccountStore *acc_store;
1907 acc_store = modest_runtime_get_account_store ();
1908 account = modest_tny_account_store_get_local_folders_account (acc_store);
1910 if (g_signal_handler_is_connected (account,
1911 priv->outbox_deleted_handler))
1912 g_signal_handler_disconnect (account,
1913 priv->outbox_deleted_handler);
1915 priv->outbox_deleted_handler =
1916 g_signal_connect (account,
1918 G_CALLBACK (on_outbox_deleted_cb),
1920 g_object_unref (account);
1923 /* Get the accounts: */
1924 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1926 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1928 sortable = gtk_tree_model_sort_new_with_model (model);
1929 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1931 GTK_SORT_ASCENDING);
1932 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1934 cmp_rows, NULL, NULL);
1936 /* Create filter model */
1937 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1938 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1944 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1945 #ifndef MODEST_TOOLKIT_HILDON2
1946 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1947 (GCallback) on_row_inserted_maybe_select_folder, self);
1950 g_object_unref (model);
1951 g_object_unref (filter_model);
1952 g_object_unref (sortable);
1954 /* Force a reselection of the INBOX next time the widget is shown */
1955 priv->reselect = TRUE;
1962 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1964 GtkTreeModel *model = NULL;
1965 TnyFolderStore *folder = NULL;
1967 ModestFolderView *tree_view = NULL;
1968 ModestFolderViewPrivate *priv = NULL;
1969 gboolean selected = FALSE;
1971 g_return_if_fail (sel);
1972 g_return_if_fail (user_data);
1974 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1976 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1978 tree_view = MODEST_FOLDER_VIEW (user_data);
1981 gtk_tree_model_get (model, &iter,
1982 INSTANCE_COLUMN, &folder,
1985 /* If the folder is the same do not notify */
1986 if (folder && priv->cur_folder_store == folder) {
1987 g_object_unref (folder);
1992 /* Current folder was unselected */
1993 if (priv->cur_folder_store) {
1994 /* We must do this firstly because a libtinymail-camel
1995 implementation detail. If we issue the signal
1996 before doing the sync_async, then that signal could
1997 cause (and it actually does it) a free of the
1998 summary of the folder (because the main window will
1999 clear the headers view */
2000 if (TNY_IS_FOLDER(priv->cur_folder_store))
2001 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2002 FALSE, NULL, NULL, NULL);
2004 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2005 priv->cur_folder_store, FALSE);
2007 g_object_unref (priv->cur_folder_store);
2008 priv->cur_folder_store = NULL;
2011 /* New current references */
2012 priv->cur_folder_store = folder;
2014 /* New folder has been selected. Do not notify if there is
2015 nothing new selected */
2017 g_signal_emit (G_OBJECT(tree_view),
2018 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2019 0, priv->cur_folder_store, TRUE);
2024 on_row_activated (GtkTreeView *treeview,
2025 GtkTreePath *treepath,
2026 GtkTreeViewColumn *column,
2029 GtkTreeModel *model = NULL;
2030 TnyFolderStore *folder = NULL;
2032 ModestFolderView *self = NULL;
2033 ModestFolderViewPrivate *priv = NULL;
2035 g_return_if_fail (treeview);
2036 g_return_if_fail (user_data);
2038 self = MODEST_FOLDER_VIEW (user_data);
2039 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2041 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2043 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2046 gtk_tree_model_get (model, &iter,
2047 INSTANCE_COLUMN, &folder,
2050 g_signal_emit (G_OBJECT(self),
2051 signals[FOLDER_ACTIVATED_SIGNAL],
2054 #ifdef MODEST_TOOLKIT_HILDON2
2055 HildonUIMode ui_mode;
2056 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2057 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2058 if (priv->cur_folder_store)
2059 g_object_unref (priv->cur_folder_store);
2060 priv->cur_folder_store = g_object_ref (folder);
2064 g_object_unref (folder);
2068 modest_folder_view_get_selected (ModestFolderView *self)
2070 ModestFolderViewPrivate *priv;
2072 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2074 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2075 if (priv->cur_folder_store)
2076 g_object_ref (priv->cur_folder_store);
2078 return priv->cur_folder_store;
2082 get_cmp_rows_type_pos (GObject *folder)
2084 /* Remote accounts -> Local account -> MMC account .*/
2087 if (TNY_IS_ACCOUNT (folder) &&
2088 modest_tny_account_is_virtual_local_folders (
2089 TNY_ACCOUNT (folder))) {
2091 } else if (TNY_IS_ACCOUNT (folder)) {
2092 TnyAccount *account = TNY_ACCOUNT (folder);
2093 const gchar *account_id = tny_account_get_id (account);
2094 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2100 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2101 return -1; /* Should never happen */
2106 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2108 TnyAccount *account;
2109 gboolean is_special;
2110 /* Inbox, Outbox, Drafts, Sent, User */
2113 if (!TNY_IS_FOLDER (folder_store))
2116 case TNY_FOLDER_TYPE_INBOX:
2118 account = tny_folder_get_account (folder_store);
2119 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2120 g_object_unref (account);
2121 return is_special?0:4;
2124 case TNY_FOLDER_TYPE_OUTBOX:
2127 case TNY_FOLDER_TYPE_DRAFTS:
2129 account = tny_folder_get_account (folder_store);
2130 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2131 g_object_unref (account);
2132 return is_special?1:4;
2135 case TNY_FOLDER_TYPE_SENT:
2137 account = tny_folder_get_account (folder_store);
2138 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2139 g_object_unref (account);
2140 return is_special?3:4;
2149 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2151 const gchar *a1_name, *a2_name;
2153 a1_name = tny_account_get_name (a1);
2154 a2_name = tny_account_get_name (a2);
2156 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2160 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2162 TnyAccount *a1, *a2;
2165 if (TNY_IS_ACCOUNT (s1)) {
2166 a1 = TNY_ACCOUNT (g_object_ref (s1));
2168 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2171 if (TNY_IS_ACCOUNT (s2)) {
2172 a2 = TNY_ACCOUNT (g_object_ref (s2));
2174 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2181 /* First we sort with the type of account */
2182 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2186 cmp = compare_account_names (a1, a2);
2189 g_object_unref (a1);
2190 g_object_unref (a2);
2196 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2198 gint is_account1, is_account2;
2200 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2201 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2203 return is_account2 - is_account1;
2207 * This function orders the mail accounts according to these rules:
2208 * 1st - remote accounts
2209 * 2nd - local account
2213 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2217 gchar *name1 = NULL;
2218 gchar *name2 = NULL;
2219 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2220 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2221 GObject *folder1 = NULL;
2222 GObject *folder2 = NULL;
2224 gtk_tree_model_get (tree_model, iter1,
2225 NAME_COLUMN, &name1,
2227 INSTANCE_COLUMN, &folder1,
2229 gtk_tree_model_get (tree_model, iter2,
2230 NAME_COLUMN, &name2,
2231 TYPE_COLUMN, &type2,
2232 INSTANCE_COLUMN, &folder2,
2235 /* Return if we get no folder. This could happen when folder
2236 operations are happening. The model is updated after the
2237 folder copy/move actually occurs, so there could be
2238 situations where the model to be drawn is not correct */
2239 if (!folder1 || !folder2)
2242 /* Sort by type. First the special folders, then the archives */
2243 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2247 /* Now we sort using the account of each folder */
2248 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2252 /* Each group is preceeded by its account */
2253 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2257 /* Pure sort by name */
2258 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2261 g_object_unref(G_OBJECT(folder1));
2263 g_object_unref(G_OBJECT(folder2));
2271 /*****************************************************************************/
2272 /* DRAG and DROP stuff */
2273 /*****************************************************************************/
2275 * This function fills the #GtkSelectionData with the row and the
2276 * model that has been dragged. It's called when this widget is a
2277 * source for dnd after the event drop happened
2280 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2281 guint info, guint time, gpointer data)
2283 GtkTreeSelection *selection;
2284 GtkTreeModel *model;
2286 GtkTreePath *source_row;
2288 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2289 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2291 source_row = gtk_tree_model_get_path (model, &iter);
2292 gtk_tree_set_row_drag_data (selection_data,
2296 gtk_tree_path_free (source_row);
2300 typedef struct _DndHelper {
2301 ModestFolderView *folder_view;
2302 gboolean delete_source;
2303 GtkTreePath *source_row;
2307 dnd_helper_destroyer (DndHelper *helper)
2309 /* Free the helper */
2310 gtk_tree_path_free (helper->source_row);
2311 g_slice_free (DndHelper, helper);
2315 xfer_folder_cb (ModestMailOperation *mail_op,
2316 TnyFolder *new_folder,
2320 /* Select the folder */
2321 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2327 /* get the folder for the row the treepath refers to. */
2328 /* folder must be unref'd */
2329 static TnyFolderStore *
2330 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2333 TnyFolderStore *folder = NULL;
2335 if (gtk_tree_model_get_iter (model,&iter, path))
2336 gtk_tree_model_get (model, &iter,
2337 INSTANCE_COLUMN, &folder,
2344 * This function is used by drag_data_received_cb to manage drag and
2345 * drop of a header, i.e, and drag from the header view to the folder
2349 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2350 GtkTreeModel *dest_model,
2351 GtkTreePath *dest_row,
2352 GtkSelectionData *selection_data)
2354 TnyList *headers = NULL;
2355 TnyFolder *folder = NULL, *src_folder = NULL;
2356 TnyFolderType folder_type;
2357 GtkTreeIter source_iter, dest_iter;
2358 ModestWindowMgr *mgr = NULL;
2359 ModestWindow *main_win = NULL;
2360 gchar **uris, **tmp;
2362 /* Build the list of headers */
2363 mgr = modest_runtime_get_window_mgr ();
2364 headers = tny_simple_list_new ();
2365 uris = modest_dnd_selection_data_get_paths (selection_data);
2368 while (*tmp != NULL) {
2371 gboolean first = TRUE;
2374 path = gtk_tree_path_new_from_string (*tmp);
2375 gtk_tree_model_get_iter (source_model, &source_iter, path);
2376 gtk_tree_model_get (source_model, &source_iter,
2377 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2380 /* Do not enable d&d of headers already opened */
2381 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2382 tny_list_append (headers, G_OBJECT (header));
2384 if (G_UNLIKELY (first)) {
2385 src_folder = tny_header_get_folder (header);
2389 /* Free and go on */
2390 gtk_tree_path_free (path);
2391 g_object_unref (header);
2396 /* This could happen ig we perform a d&d very quickly over the
2397 same row that row could dissapear because message is
2399 if (!TNY_IS_FOLDER (src_folder))
2402 /* Get the target folder */
2403 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2404 gtk_tree_model_get (dest_model, &dest_iter,
2408 if (!folder || !TNY_IS_FOLDER(folder)) {
2409 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2413 folder_type = modest_tny_folder_guess_folder_type (folder);
2414 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2415 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2416 goto cleanup; /* cannot move messages there */
2419 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2420 /* g_warning ("folder not writable"); */
2421 goto cleanup; /* verboten! */
2424 /* Ask for confirmation to move */
2425 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2427 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2431 /* Transfer messages */
2432 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2437 if (G_IS_OBJECT (src_folder))
2438 g_object_unref (src_folder);
2439 if (G_IS_OBJECT(folder))
2440 g_object_unref (G_OBJECT (folder));
2441 if (G_IS_OBJECT(headers))
2442 g_object_unref (headers);
2446 TnyFolderStore *src_folder;
2447 TnyFolderStore *dst_folder;
2448 ModestFolderView *folder_view;
2453 dnd_folder_info_destroyer (DndFolderInfo *info)
2455 if (info->src_folder)
2456 g_object_unref (info->src_folder);
2457 if (info->dst_folder)
2458 g_object_unref (info->dst_folder);
2459 g_slice_free (DndFolderInfo, info);
2463 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2464 GtkWindow *parent_window,
2465 TnyAccount *account)
2468 modest_ui_actions_on_account_connection_error (parent_window, account);
2470 /* Free the helper & info */
2471 dnd_helper_destroyer (info->helper);
2472 dnd_folder_info_destroyer (info);
2476 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2478 GtkWindow *parent_window,
2479 TnyAccount *account,
2482 DndFolderInfo *info = NULL;
2483 ModestMailOperation *mail_op;
2485 info = (DndFolderInfo *) user_data;
2487 if (err || canceled) {
2488 dnd_on_connection_failed_destroyer (info, parent_window, account);
2492 /* Do the mail operation */
2493 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2494 modest_ui_actions_move_folder_error_handler,
2495 info->src_folder, NULL);
2497 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2500 /* Transfer the folder */
2501 modest_mail_operation_xfer_folder (mail_op,
2502 TNY_FOLDER (info->src_folder),
2504 info->helper->delete_source,
2506 info->helper->folder_view);
2509 g_object_unref (G_OBJECT (mail_op));
2510 dnd_helper_destroyer (info->helper);
2511 dnd_folder_info_destroyer (info);
2516 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2518 GtkWindow *parent_window,
2519 TnyAccount *account,
2522 DndFolderInfo *info = NULL;
2524 info = (DndFolderInfo *) user_data;
2526 if (err || canceled) {
2527 dnd_on_connection_failed_destroyer (info, parent_window, account);
2531 /* Connect to source folder and perform the copy/move */
2532 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2534 drag_and_drop_from_folder_view_src_folder_performer,
2539 * This function is used by drag_data_received_cb to manage drag and
2540 * drop of a folder, i.e, and drag from the folder view to the same
2544 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2545 GtkTreeModel *dest_model,
2546 GtkTreePath *dest_row,
2547 GtkSelectionData *selection_data,
2550 GtkTreeIter dest_iter, iter;
2551 TnyFolderStore *dest_folder = NULL;
2552 TnyFolderStore *folder = NULL;
2553 gboolean forbidden = FALSE;
2555 DndFolderInfo *info = NULL;
2557 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2559 g_warning ("%s: BUG: no main window", __FUNCTION__);
2560 dnd_helper_destroyer (helper);
2565 /* check the folder rules for the destination */
2566 folder = tree_path_to_folder (dest_model, dest_row);
2567 if (TNY_IS_FOLDER(folder)) {
2568 ModestTnyFolderRules rules =
2569 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2570 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2571 } else if (TNY_IS_FOLDER_STORE(folder)) {
2572 /* enable local root as destination for folders */
2573 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2574 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2577 g_object_unref (folder);
2580 /* check the folder rules for the source */
2581 folder = tree_path_to_folder (source_model, helper->source_row);
2582 if (TNY_IS_FOLDER(folder)) {
2583 ModestTnyFolderRules rules =
2584 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2585 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2588 g_object_unref (folder);
2592 /* Check if the drag is possible */
2593 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2595 modest_platform_run_information_dialog ((GtkWindow *) win,
2596 _("mail_in_ui_folder_move_target_error"),
2598 /* Restore the previous selection */
2599 folder = tree_path_to_folder (source_model, helper->source_row);
2601 if (TNY_IS_FOLDER (folder))
2602 modest_folder_view_select_folder (helper->folder_view,
2603 TNY_FOLDER (folder), FALSE);
2604 g_object_unref (folder);
2606 dnd_helper_destroyer (helper);
2611 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2612 gtk_tree_model_get (dest_model, &dest_iter,
2615 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2616 gtk_tree_model_get (source_model, &iter,
2620 /* Create the info for the performer */
2621 info = g_slice_new0 (DndFolderInfo);
2622 info->src_folder = g_object_ref (folder);
2623 info->dst_folder = g_object_ref (dest_folder);
2624 info->helper = helper;
2626 /* Connect to the destination folder and perform the copy/move */
2627 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2629 drag_and_drop_from_folder_view_dst_folder_performer,
2633 g_object_unref (dest_folder);
2634 g_object_unref (folder);
2638 * This function receives the data set by the "drag-data-get" signal
2639 * handler. This information comes within the #GtkSelectionData. This
2640 * function will manage both the drags of folders of the treeview and
2641 * drags of headers of the header view widget.
2644 on_drag_data_received (GtkWidget *widget,
2645 GdkDragContext *context,
2648 GtkSelectionData *selection_data,
2653 GtkWidget *source_widget;
2654 GtkTreeModel *dest_model, *source_model;
2655 GtkTreePath *source_row, *dest_row;
2656 GtkTreeViewDropPosition pos;
2657 gboolean delete_source = FALSE;
2658 gboolean success = FALSE;
2660 /* Do not allow further process */
2661 g_signal_stop_emission_by_name (widget, "drag-data-received");
2662 source_widget = gtk_drag_get_source_widget (context);
2664 /* Get the action */
2665 if (context->action == GDK_ACTION_MOVE) {
2666 delete_source = TRUE;
2668 /* Notify that there is no folder selected. We need to
2669 do this in order to update the headers view (and
2670 its monitors, because when moving, the old folder
2671 won't longer exist. We can not wait for the end of
2672 the operation, because the operation won't start if
2673 the folder is in use */
2674 if (source_widget == widget) {
2675 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2676 gtk_tree_selection_unselect_all (sel);
2680 /* Check if the get_data failed */
2681 if (selection_data == NULL || selection_data->length < 0)
2684 /* Select the destination model */
2685 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2687 /* Get the path to the destination row. Can not call
2688 gtk_tree_view_get_drag_dest_row() because the source row
2689 is not selected anymore */
2690 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2693 /* Only allow drops IN other rows */
2695 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2696 pos == GTK_TREE_VIEW_DROP_AFTER)
2700 /* Drags from the header view */
2701 if (source_widget != widget) {
2702 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2704 drag_and_drop_from_header_view (source_model,
2709 DndHelper *helper = NULL;
2711 /* Get the source model and row */
2712 gtk_tree_get_row_drag_data (selection_data,
2716 /* Create the helper */
2717 helper = g_slice_new0 (DndHelper);
2718 helper->delete_source = delete_source;
2719 helper->source_row = gtk_tree_path_copy (source_row);
2720 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2722 drag_and_drop_from_folder_view (source_model,
2728 gtk_tree_path_free (source_row);
2732 gtk_tree_path_free (dest_row);
2735 /* Finish the drag and drop */
2736 gtk_drag_finish (context, success, FALSE, time);
2740 * We define a "drag-drop" signal handler because we do not want to
2741 * use the default one, because the default one always calls
2742 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2743 * signal handler, because there we have all the information available
2744 * to know if the dnd was a success or not.
2747 drag_drop_cb (GtkWidget *widget,
2748 GdkDragContext *context,
2756 if (!context->targets)
2759 /* Check if we're dragging a folder row */
2760 target = gtk_drag_dest_find_target (widget, context, NULL);
2762 /* Request the data from the source. */
2763 gtk_drag_get_data(widget, context, target, time);
2769 * This function expands a node of a tree view if it's not expanded
2770 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2771 * does that, so that's why they're here.
2774 expand_row_timeout (gpointer data)
2776 GtkTreeView *tree_view = data;
2777 GtkTreePath *dest_path = NULL;
2778 GtkTreeViewDropPosition pos;
2779 gboolean result = FALSE;
2781 gdk_threads_enter ();
2783 gtk_tree_view_get_drag_dest_row (tree_view,
2788 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2789 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2790 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2791 gtk_tree_path_free (dest_path);
2795 gtk_tree_path_free (dest_path);
2800 gdk_threads_leave ();
2806 * This function is called whenever the pointer is moved over a widget
2807 * while dragging some data. It installs a timeout that will expand a
2808 * node of the treeview if not expanded yet. This function also calls
2809 * gdk_drag_status in order to set the suggested action that will be
2810 * used by the "drag-data-received" signal handler to know if we
2811 * should do a move or just a copy of the data.
2814 on_drag_motion (GtkWidget *widget,
2815 GdkDragContext *context,
2821 GtkTreeViewDropPosition pos;
2822 GtkTreePath *dest_row;
2823 GtkTreeModel *dest_model;
2824 ModestFolderViewPrivate *priv;
2825 GdkDragAction suggested_action;
2826 gboolean valid_location = FALSE;
2827 TnyFolderStore *folder = NULL;
2829 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2831 if (priv->timer_expander != 0) {
2832 g_source_remove (priv->timer_expander);
2833 priv->timer_expander = 0;
2836 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2841 /* Do not allow drops between folders */
2843 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2844 pos == GTK_TREE_VIEW_DROP_AFTER) {
2845 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2846 gdk_drag_status(context, 0, time);
2847 valid_location = FALSE;
2850 valid_location = TRUE;
2853 /* Check that the destination folder is writable */
2854 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2855 folder = tree_path_to_folder (dest_model, dest_row);
2856 if (folder && TNY_IS_FOLDER (folder)) {
2857 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2859 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2860 valid_location = FALSE;
2865 /* Expand the selected row after 1/2 second */
2866 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2867 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2869 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2871 /* Select the desired action. By default we pick MOVE */
2872 suggested_action = GDK_ACTION_MOVE;
2874 if (context->actions == GDK_ACTION_COPY)
2875 gdk_drag_status(context, GDK_ACTION_COPY, time);
2876 else if (context->actions == GDK_ACTION_MOVE)
2877 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2878 else if (context->actions & suggested_action)
2879 gdk_drag_status(context, suggested_action, time);
2881 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2885 g_object_unref (folder);
2887 gtk_tree_path_free (dest_row);
2889 g_signal_stop_emission_by_name (widget, "drag-motion");
2891 return valid_location;
2895 * This function sets the treeview as a source and a target for dnd
2896 * events. It also connects all the requirede signals.
2899 setup_drag_and_drop (GtkTreeView *self)
2901 /* Set up the folder view as a dnd destination. Set only the
2902 highlight flag, otherwise gtk will have a different
2904 #ifdef MODEST_TOOLKIT_HILDON2
2907 gtk_drag_dest_set (GTK_WIDGET (self),
2908 GTK_DEST_DEFAULT_HIGHLIGHT,
2909 folder_view_drag_types,
2910 G_N_ELEMENTS (folder_view_drag_types),
2911 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2913 g_signal_connect (G_OBJECT (self),
2914 "drag_data_received",
2915 G_CALLBACK (on_drag_data_received),
2919 /* Set up the treeview as a dnd source */
2920 gtk_drag_source_set (GTK_WIDGET (self),
2922 folder_view_drag_types,
2923 G_N_ELEMENTS (folder_view_drag_types),
2924 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2926 g_signal_connect (G_OBJECT (self),
2928 G_CALLBACK (on_drag_motion),
2931 g_signal_connect (G_OBJECT (self),
2933 G_CALLBACK (on_drag_data_get),
2936 g_signal_connect (G_OBJECT (self),
2938 G_CALLBACK (drag_drop_cb),
2943 * This function manages the navigation through the folders using the
2944 * keyboard or the hardware keys in the device
2947 on_key_pressed (GtkWidget *self,
2951 GtkTreeSelection *selection;
2953 GtkTreeModel *model;
2954 gboolean retval = FALSE;
2956 /* Up and Down are automatically managed by the treeview */
2957 if (event->keyval == GDK_Return) {
2958 /* Expand/Collapse the selected row */
2959 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2960 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2963 path = gtk_tree_model_get_path (model, &iter);
2965 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2966 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2968 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2969 gtk_tree_path_free (path);
2971 /* No further processing */
2979 * We listen to the changes in the local folder account name key,
2980 * because we want to show the right name in the view. The local
2981 * folder account name corresponds to the device name in the Maemo
2982 * version. We do this because we do not want to query gconf on each
2983 * tree view refresh. It's better to cache it and change whenever
2987 on_configuration_key_changed (ModestConf* conf,
2989 ModestConfEvent event,
2990 ModestConfNotificationId id,
2991 ModestFolderView *self)
2993 ModestFolderViewPrivate *priv;
2996 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2997 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2999 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3000 g_free (priv->local_account_name);
3002 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3003 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3005 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3006 MODEST_CONF_DEVICE_NAME, NULL);
3008 /* Force a redraw */
3009 #if GTK_CHECK_VERSION(2, 8, 0)
3010 GtkTreeViewColumn * tree_column;
3012 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3014 gtk_tree_view_column_queue_resize (tree_column);
3016 gtk_widget_queue_draw (GTK_WIDGET (self));
3022 modest_folder_view_set_style (ModestFolderView *self,
3023 ModestFolderViewStyle style)
3025 ModestFolderViewPrivate *priv;
3027 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3028 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3029 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3031 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3034 priv->style = style;
3038 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3039 const gchar *account_id)
3041 ModestFolderViewPrivate *priv;
3042 GtkTreeModel *model;
3044 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3046 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3048 /* This will be used by the filter_row callback,
3049 * to decided which rows to show: */
3050 if (priv->visible_account_id) {
3051 g_free (priv->visible_account_id);
3052 priv->visible_account_id = NULL;
3055 priv->visible_account_id = g_strdup (account_id);
3058 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3059 if (GTK_IS_TREE_MODEL_FILTER (model))
3060 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3062 /* Save settings to gconf */
3063 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3064 MODEST_CONF_FOLDER_VIEW_KEY);
3068 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3070 ModestFolderViewPrivate *priv;
3072 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3074 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3076 return (const gchar *) priv->visible_account_id;
3080 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3084 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3086 gtk_tree_model_get (model, iter,
3090 gboolean result = FALSE;
3091 if (type == TNY_FOLDER_TYPE_INBOX) {
3095 *inbox_iter = *iter;
3099 if (gtk_tree_model_iter_children (model, &child, iter)) {
3100 if (find_inbox_iter (model, &child, inbox_iter))
3104 } while (gtk_tree_model_iter_next (model, iter));
3113 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3115 GtkTreeModel *model;
3116 GtkTreeIter iter, inbox_iter;
3117 GtkTreeSelection *sel;
3118 GtkTreePath *path = NULL;
3120 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3122 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3126 expand_root_items (self);
3127 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3129 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3130 g_warning ("%s: model is empty", __FUNCTION__);
3134 if (find_inbox_iter (model, &iter, &inbox_iter))
3135 path = gtk_tree_model_get_path (model, &inbox_iter);
3137 path = gtk_tree_path_new_first ();
3139 /* Select the row and free */
3140 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3141 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3142 gtk_tree_path_free (path);
3145 gtk_widget_grab_focus (GTK_WIDGET(self));
3151 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3156 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3157 TnyFolder* a_folder;
3160 gtk_tree_model_get (model, iter,
3161 INSTANCE_COLUMN, &a_folder,
3167 if (folder == a_folder) {
3168 g_object_unref (a_folder);
3169 *folder_iter = *iter;
3172 g_object_unref (a_folder);
3174 if (gtk_tree_model_iter_children (model, &child, iter)) {
3175 if (find_folder_iter (model, &child, folder_iter, folder))
3179 } while (gtk_tree_model_iter_next (model, iter));
3184 #ifndef MODEST_TOOLKIT_HILDON2
3186 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3189 ModestFolderView *self)
3191 ModestFolderViewPrivate *priv = NULL;
3192 GtkTreeSelection *sel;
3193 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3194 GObject *instance = NULL;
3196 if (!MODEST_IS_FOLDER_VIEW(self))
3199 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3201 priv->reexpand = TRUE;
3203 gtk_tree_model_get (tree_model, iter,
3205 INSTANCE_COLUMN, &instance,
3211 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3212 priv->folder_to_select = g_object_ref (instance);
3214 g_object_unref (instance);
3216 if (priv->folder_to_select) {
3218 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3221 path = gtk_tree_model_get_path (tree_model, iter);
3222 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3224 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3226 gtk_tree_selection_select_iter (sel, iter);
3227 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3229 gtk_tree_path_free (path);
3233 modest_folder_view_disable_next_folder_selection (self);
3235 /* Refilter the model */
3236 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3242 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3244 ModestFolderViewPrivate *priv;
3246 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3248 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3250 if (priv->folder_to_select)
3251 g_object_unref(priv->folder_to_select);
3253 priv->folder_to_select = NULL;
3257 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3258 gboolean after_change)
3260 GtkTreeModel *model;
3261 GtkTreeIter iter, folder_iter;
3262 GtkTreeSelection *sel;
3263 ModestFolderViewPrivate *priv = NULL;
3265 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3266 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3268 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3271 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3272 gtk_tree_selection_unselect_all (sel);
3274 if (priv->folder_to_select)
3275 g_object_unref(priv->folder_to_select);
3276 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3280 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3285 /* Refilter the model, before selecting the folder */
3286 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3288 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3289 g_warning ("%s: model is empty", __FUNCTION__);
3293 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3296 path = gtk_tree_model_get_path (model, &folder_iter);
3297 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3299 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3300 gtk_tree_selection_select_iter (sel, &folder_iter);
3301 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3303 gtk_tree_path_free (path);
3311 modest_folder_view_copy_selection (ModestFolderView *self)
3313 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3315 /* Copy selection */
3316 _clipboard_set_selected_data (self, FALSE);
3320 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3322 ModestFolderViewPrivate *priv = NULL;
3323 GtkTreeModel *model = NULL;
3324 const gchar **hidding = NULL;
3325 guint i, n_selected;
3327 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3328 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3330 /* Copy selection */
3331 if (!_clipboard_set_selected_data (folder_view, TRUE))
3334 /* Get hidding ids */
3335 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3337 /* Clear hidding array created by previous cut operation */
3338 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3340 /* Copy hidding array */
3341 priv->n_selected = n_selected;
3342 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3343 for (i=0; i < n_selected; i++)
3344 priv->hidding_ids[i] = g_strdup(hidding[i]);
3346 /* Hide cut folders */
3347 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3348 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3352 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3353 ModestFolderView *folder_view_dst)
3355 GtkTreeModel *filter_model = NULL;
3356 GtkTreeModel *model = NULL;
3357 GtkTreeModel *new_filter_model = NULL;
3359 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3360 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3363 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3364 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3366 /* Build new filter model */
3367 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3368 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3372 /* Set copied model */
3373 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3374 #ifndef MODEST_TOOLKIT_HILDON2
3375 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3376 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3380 g_object_unref (new_filter_model);
3384 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3387 GtkTreeModel *model = NULL;
3388 ModestFolderViewPrivate* priv;
3390 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3392 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3393 priv->show_non_move = show;
3394 /* modest_folder_view_update_model(folder_view, */
3395 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3397 /* Hide special folders */
3398 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3399 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3400 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3404 /* Returns FALSE if it did not selected anything */
3406 _clipboard_set_selected_data (ModestFolderView *folder_view,
3409 ModestFolderViewPrivate *priv = NULL;
3410 TnyFolderStore *folder = NULL;
3411 gboolean retval = FALSE;
3413 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3414 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3416 /* Set selected data on clipboard */
3417 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3418 folder = modest_folder_view_get_selected (folder_view);
3420 /* Do not allow to select an account */
3421 if (TNY_IS_FOLDER (folder)) {
3422 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3427 g_object_unref (folder);
3433 _clear_hidding_filter (ModestFolderView *folder_view)
3435 ModestFolderViewPrivate *priv;
3438 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3439 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3441 if (priv->hidding_ids != NULL) {
3442 for (i=0; i < priv->n_selected; i++)
3443 g_free (priv->hidding_ids[i]);
3444 g_free(priv->hidding_ids);
3450 on_display_name_changed (ModestAccountMgr *mgr,
3451 const gchar *account,
3454 ModestFolderView *self;
3456 self = MODEST_FOLDER_VIEW (user_data);
3458 /* Force a redraw */
3459 #if GTK_CHECK_VERSION(2, 8, 0)
3460 GtkTreeViewColumn * tree_column;
3462 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3464 gtk_tree_view_column_queue_resize (tree_column);
3466 gtk_widget_queue_draw (GTK_WIDGET (self));
3471 modest_folder_view_set_cell_style (ModestFolderView *self,
3472 ModestFolderViewCellStyle cell_style)
3474 ModestFolderViewPrivate *priv = NULL;
3476 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3477 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3479 priv->cell_style = cell_style;
3481 g_object_set (G_OBJECT (priv->messages_renderer),
3482 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3485 gtk_widget_queue_draw (GTK_WIDGET (self));
3489 update_style (ModestFolderView *self)
3491 ModestFolderViewPrivate *priv;
3492 GdkColor style_color;
3494 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3495 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3497 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3498 gdk_color_parse ("grey", &style_color);
3501 g_object_set (G_OBJECT (priv->messages_renderer),
3502 "foreground-gdk", &style_color,
3503 "foreground-set", TRUE,
3508 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3510 if (strcmp ("style", spec->name) == 0) {
3511 update_style (MODEST_FOLDER_VIEW (obj));
3512 gtk_widget_queue_draw (GTK_WIDGET (obj));
3517 modest_folder_view_set_filter (ModestFolderView *self,
3518 ModestFolderViewFilter filter)
3520 ModestFolderViewPrivate *priv;
3521 GtkTreeModel *filter_model;
3523 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3524 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3526 priv->filter = filter;
3528 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3529 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3530 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));