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) {
1755 case TNY_FOLDER_TYPE_OUTBOX:
1756 case TNY_FOLDER_TYPE_SENT:
1757 case TNY_FOLDER_TYPE_DRAFTS:
1760 case TNY_FOLDER_TYPE_UNKNOWN:
1761 case TNY_FOLDER_TYPE_NORMAL:
1762 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1763 if (type == TNY_FOLDER_TYPE_INVALID)
1764 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1766 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1767 type == TNY_FOLDER_TYPE_SENT
1768 || type == TNY_FOLDER_TYPE_DRAFTS)
1776 /* apply special filters */
1777 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1778 if (TNY_IS_ACCOUNT (instance))
1782 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1783 if (TNY_IS_FOLDER (instance)) {
1784 /* Check folder rules */
1785 ModestTnyFolderRules rules;
1787 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1788 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
1789 } else if (TNY_IS_ACCOUNT (instance)) {
1790 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1798 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1799 if (TNY_IS_FOLDER (instance)) {
1800 TnyFolderType guess_type;
1802 if (TNY_FOLDER_TYPE_NORMAL) {
1803 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1809 case TNY_FOLDER_TYPE_OUTBOX:
1810 case TNY_FOLDER_TYPE_SENT:
1811 case TNY_FOLDER_TYPE_DRAFTS:
1812 case TNY_FOLDER_TYPE_ARCHIVE:
1813 case TNY_FOLDER_TYPE_INBOX:
1816 case TNY_FOLDER_TYPE_UNKNOWN:
1817 case TNY_FOLDER_TYPE_NORMAL:
1823 } else if (TNY_IS_ACCOUNT (instance)) {
1828 if (retval && TNY_IS_FOLDER (instance)) {
1829 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1832 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1833 if (TNY_IS_FOLDER (instance)) {
1834 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1835 } else if (TNY_IS_ACCOUNT (instance)) {
1840 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1841 if (TNY_IS_FOLDER (instance)) {
1842 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1843 } else if (TNY_IS_ACCOUNT (instance)) {
1848 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1849 if (TNY_IS_FOLDER (instance)) {
1850 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1851 } else if (TNY_IS_ACCOUNT (instance)) {
1857 g_object_unref (instance);
1864 modest_folder_view_update_model (ModestFolderView *self,
1865 TnyAccountStore *account_store)
1867 ModestFolderViewPrivate *priv;
1868 GtkTreeModel *model /* , *old_model */;
1869 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1871 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1872 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1875 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1877 /* Notify that there is no folder selected */
1878 g_signal_emit (G_OBJECT(self),
1879 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1881 if (priv->cur_folder_store) {
1882 g_object_unref (priv->cur_folder_store);
1883 priv->cur_folder_store = NULL;
1886 /* FIXME: the local accounts are not shown when the query
1887 selects only the subscribed folders */
1888 #ifdef MODEST_TOOLKIT_HILDON2
1889 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1890 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1891 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1892 MODEST_FOLDER_PATH_SEPARATOR);
1894 model = tny_gtk_folder_store_tree_model_new (NULL);
1897 /* When the model is a list store (plain representation) the
1898 outbox is not a child of any account so we have to manually
1899 delete it because removing the local folders account won't
1900 delete it (because tny_folder_get_account() is not defined
1901 for a merge folder */
1902 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1903 TnyAccount *account;
1904 ModestTnyAccountStore *acc_store;
1906 acc_store = modest_runtime_get_account_store ();
1907 account = modest_tny_account_store_get_local_folders_account (acc_store);
1909 if (g_signal_handler_is_connected (account,
1910 priv->outbox_deleted_handler))
1911 g_signal_handler_disconnect (account,
1912 priv->outbox_deleted_handler);
1914 priv->outbox_deleted_handler =
1915 g_signal_connect (account,
1917 G_CALLBACK (on_outbox_deleted_cb),
1919 g_object_unref (account);
1922 /* Get the accounts: */
1923 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1925 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1927 sortable = gtk_tree_model_sort_new_with_model (model);
1928 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1930 GTK_SORT_ASCENDING);
1931 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1933 cmp_rows, NULL, NULL);
1935 /* Create filter model */
1936 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1937 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1943 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1944 #ifndef MODEST_TOOLKIT_HILDON2
1945 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1946 (GCallback) on_row_inserted_maybe_select_folder, self);
1949 g_object_unref (model);
1950 g_object_unref (filter_model);
1951 g_object_unref (sortable);
1953 /* Force a reselection of the INBOX next time the widget is shown */
1954 priv->reselect = TRUE;
1961 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1963 GtkTreeModel *model = NULL;
1964 TnyFolderStore *folder = NULL;
1966 ModestFolderView *tree_view = NULL;
1967 ModestFolderViewPrivate *priv = NULL;
1968 gboolean selected = FALSE;
1970 g_return_if_fail (sel);
1971 g_return_if_fail (user_data);
1973 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1975 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1977 tree_view = MODEST_FOLDER_VIEW (user_data);
1980 gtk_tree_model_get (model, &iter,
1981 INSTANCE_COLUMN, &folder,
1984 /* If the folder is the same do not notify */
1985 if (folder && priv->cur_folder_store == folder) {
1986 g_object_unref (folder);
1991 /* Current folder was unselected */
1992 if (priv->cur_folder_store) {
1993 /* We must do this firstly because a libtinymail-camel
1994 implementation detail. If we issue the signal
1995 before doing the sync_async, then that signal could
1996 cause (and it actually does it) a free of the
1997 summary of the folder (because the main window will
1998 clear the headers view */
1999 if (TNY_IS_FOLDER(priv->cur_folder_store))
2000 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2001 FALSE, NULL, NULL, NULL);
2003 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2004 priv->cur_folder_store, FALSE);
2006 g_object_unref (priv->cur_folder_store);
2007 priv->cur_folder_store = NULL;
2010 /* New current references */
2011 priv->cur_folder_store = folder;
2013 /* New folder has been selected. Do not notify if there is
2014 nothing new selected */
2016 g_signal_emit (G_OBJECT(tree_view),
2017 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2018 0, priv->cur_folder_store, TRUE);
2023 on_row_activated (GtkTreeView *treeview,
2024 GtkTreePath *treepath,
2025 GtkTreeViewColumn *column,
2028 GtkTreeModel *model = NULL;
2029 TnyFolderStore *folder = NULL;
2031 ModestFolderView *self = NULL;
2032 ModestFolderViewPrivate *priv = NULL;
2034 g_return_if_fail (treeview);
2035 g_return_if_fail (user_data);
2037 self = MODEST_FOLDER_VIEW (user_data);
2038 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2040 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2042 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2045 gtk_tree_model_get (model, &iter,
2046 INSTANCE_COLUMN, &folder,
2049 g_signal_emit (G_OBJECT(self),
2050 signals[FOLDER_ACTIVATED_SIGNAL],
2053 #ifdef MODEST_TOOLKIT_HILDON2
2054 HildonUIMode ui_mode;
2055 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2056 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2057 if (priv->cur_folder_store)
2058 g_object_unref (priv->cur_folder_store);
2059 priv->cur_folder_store = g_object_ref (folder);
2063 g_object_unref (folder);
2067 modest_folder_view_get_selected (ModestFolderView *self)
2069 ModestFolderViewPrivate *priv;
2071 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2073 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2074 if (priv->cur_folder_store)
2075 g_object_ref (priv->cur_folder_store);
2077 return priv->cur_folder_store;
2081 get_cmp_rows_type_pos (GObject *folder)
2083 /* Remote accounts -> Local account -> MMC account .*/
2086 if (TNY_IS_ACCOUNT (folder) &&
2087 modest_tny_account_is_virtual_local_folders (
2088 TNY_ACCOUNT (folder))) {
2090 } else if (TNY_IS_ACCOUNT (folder)) {
2091 TnyAccount *account = TNY_ACCOUNT (folder);
2092 const gchar *account_id = tny_account_get_id (account);
2093 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2099 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2100 return -1; /* Should never happen */
2105 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2107 TnyAccount *account;
2108 gboolean is_special;
2109 /* Inbox, Outbox, Drafts, Sent, User */
2112 if (!TNY_IS_FOLDER (folder_store))
2115 case TNY_FOLDER_TYPE_INBOX:
2117 account = tny_folder_get_account (folder_store);
2118 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2119 g_object_unref (account);
2120 return is_special?0:4;
2123 case TNY_FOLDER_TYPE_OUTBOX:
2126 case TNY_FOLDER_TYPE_DRAFTS:
2128 account = tny_folder_get_account (folder_store);
2129 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2130 g_object_unref (account);
2131 return is_special?1:4;
2134 case TNY_FOLDER_TYPE_SENT:
2136 account = tny_folder_get_account (folder_store);
2137 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2138 g_object_unref (account);
2139 return is_special?3:4;
2148 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2150 const gchar *a1_name, *a2_name;
2152 a1_name = tny_account_get_name (a1);
2153 a2_name = tny_account_get_name (a2);
2155 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2159 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2161 TnyAccount *a1, *a2;
2164 if (TNY_IS_ACCOUNT (s1)) {
2165 a1 = TNY_ACCOUNT (g_object_ref (s1));
2167 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2170 if (TNY_IS_ACCOUNT (s2)) {
2171 a2 = TNY_ACCOUNT (g_object_ref (s2));
2173 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2180 /* First we sort with the type of account */
2181 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2185 cmp = compare_account_names (a1, a2);
2188 g_object_unref (a1);
2189 g_object_unref (a2);
2195 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2197 gint is_account1, is_account2;
2199 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2200 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2202 return is_account2 - is_account1;
2206 * This function orders the mail accounts according to these rules:
2207 * 1st - remote accounts
2208 * 2nd - local account
2212 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2216 gchar *name1 = NULL;
2217 gchar *name2 = NULL;
2218 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2219 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2220 GObject *folder1 = NULL;
2221 GObject *folder2 = NULL;
2223 gtk_tree_model_get (tree_model, iter1,
2224 NAME_COLUMN, &name1,
2226 INSTANCE_COLUMN, &folder1,
2228 gtk_tree_model_get (tree_model, iter2,
2229 NAME_COLUMN, &name2,
2230 TYPE_COLUMN, &type2,
2231 INSTANCE_COLUMN, &folder2,
2234 /* Return if we get no folder. This could happen when folder
2235 operations are happening. The model is updated after the
2236 folder copy/move actually occurs, so there could be
2237 situations where the model to be drawn is not correct */
2238 if (!folder1 || !folder2)
2241 /* Sort by type. First the special folders, then the archives */
2242 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2246 /* Now we sort using the account of each folder */
2247 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2251 /* Each group is preceeded by its account */
2252 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2256 /* Pure sort by name */
2257 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2260 g_object_unref(G_OBJECT(folder1));
2262 g_object_unref(G_OBJECT(folder2));
2270 /*****************************************************************************/
2271 /* DRAG and DROP stuff */
2272 /*****************************************************************************/
2274 * This function fills the #GtkSelectionData with the row and the
2275 * model that has been dragged. It's called when this widget is a
2276 * source for dnd after the event drop happened
2279 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2280 guint info, guint time, gpointer data)
2282 GtkTreeSelection *selection;
2283 GtkTreeModel *model;
2285 GtkTreePath *source_row;
2287 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2288 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2290 source_row = gtk_tree_model_get_path (model, &iter);
2291 gtk_tree_set_row_drag_data (selection_data,
2295 gtk_tree_path_free (source_row);
2299 typedef struct _DndHelper {
2300 ModestFolderView *folder_view;
2301 gboolean delete_source;
2302 GtkTreePath *source_row;
2306 dnd_helper_destroyer (DndHelper *helper)
2308 /* Free the helper */
2309 gtk_tree_path_free (helper->source_row);
2310 g_slice_free (DndHelper, helper);
2314 xfer_folder_cb (ModestMailOperation *mail_op,
2315 TnyFolder *new_folder,
2319 /* Select the folder */
2320 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2326 /* get the folder for the row the treepath refers to. */
2327 /* folder must be unref'd */
2328 static TnyFolderStore *
2329 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2332 TnyFolderStore *folder = NULL;
2334 if (gtk_tree_model_get_iter (model,&iter, path))
2335 gtk_tree_model_get (model, &iter,
2336 INSTANCE_COLUMN, &folder,
2343 * This function is used by drag_data_received_cb to manage drag and
2344 * drop of a header, i.e, and drag from the header view to the folder
2348 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2349 GtkTreeModel *dest_model,
2350 GtkTreePath *dest_row,
2351 GtkSelectionData *selection_data)
2353 TnyList *headers = NULL;
2354 TnyFolder *folder = NULL, *src_folder = NULL;
2355 TnyFolderType folder_type;
2356 GtkTreeIter source_iter, dest_iter;
2357 ModestWindowMgr *mgr = NULL;
2358 ModestWindow *main_win = NULL;
2359 gchar **uris, **tmp;
2361 /* Build the list of headers */
2362 mgr = modest_runtime_get_window_mgr ();
2363 headers = tny_simple_list_new ();
2364 uris = modest_dnd_selection_data_get_paths (selection_data);
2367 while (*tmp != NULL) {
2370 gboolean first = TRUE;
2373 path = gtk_tree_path_new_from_string (*tmp);
2374 gtk_tree_model_get_iter (source_model, &source_iter, path);
2375 gtk_tree_model_get (source_model, &source_iter,
2376 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2379 /* Do not enable d&d of headers already opened */
2380 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2381 tny_list_append (headers, G_OBJECT (header));
2383 if (G_UNLIKELY (first)) {
2384 src_folder = tny_header_get_folder (header);
2388 /* Free and go on */
2389 gtk_tree_path_free (path);
2390 g_object_unref (header);
2395 /* This could happen ig we perform a d&d very quickly over the
2396 same row that row could dissapear because message is
2398 if (!TNY_IS_FOLDER (src_folder))
2401 /* Get the target folder */
2402 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2403 gtk_tree_model_get (dest_model, &dest_iter,
2407 if (!folder || !TNY_IS_FOLDER(folder)) {
2408 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2412 folder_type = modest_tny_folder_guess_folder_type (folder);
2413 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2414 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2415 goto cleanup; /* cannot move messages there */
2418 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2419 /* g_warning ("folder not writable"); */
2420 goto cleanup; /* verboten! */
2423 /* Ask for confirmation to move */
2424 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2426 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2430 /* Transfer messages */
2431 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2436 if (G_IS_OBJECT (src_folder))
2437 g_object_unref (src_folder);
2438 if (G_IS_OBJECT(folder))
2439 g_object_unref (G_OBJECT (folder));
2440 if (G_IS_OBJECT(headers))
2441 g_object_unref (headers);
2445 TnyFolderStore *src_folder;
2446 TnyFolderStore *dst_folder;
2447 ModestFolderView *folder_view;
2452 dnd_folder_info_destroyer (DndFolderInfo *info)
2454 if (info->src_folder)
2455 g_object_unref (info->src_folder);
2456 if (info->dst_folder)
2457 g_object_unref (info->dst_folder);
2458 g_slice_free (DndFolderInfo, info);
2462 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2463 GtkWindow *parent_window,
2464 TnyAccount *account)
2467 modest_ui_actions_on_account_connection_error (parent_window, account);
2469 /* Free the helper & info */
2470 dnd_helper_destroyer (info->helper);
2471 dnd_folder_info_destroyer (info);
2475 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2477 GtkWindow *parent_window,
2478 TnyAccount *account,
2481 DndFolderInfo *info = NULL;
2482 ModestMailOperation *mail_op;
2484 info = (DndFolderInfo *) user_data;
2486 if (err || canceled) {
2487 dnd_on_connection_failed_destroyer (info, parent_window, account);
2491 /* Do the mail operation */
2492 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2493 modest_ui_actions_move_folder_error_handler,
2494 info->src_folder, NULL);
2496 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2499 /* Transfer the folder */
2500 modest_mail_operation_xfer_folder (mail_op,
2501 TNY_FOLDER (info->src_folder),
2503 info->helper->delete_source,
2505 info->helper->folder_view);
2508 g_object_unref (G_OBJECT (mail_op));
2509 dnd_helper_destroyer (info->helper);
2510 dnd_folder_info_destroyer (info);
2515 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2517 GtkWindow *parent_window,
2518 TnyAccount *account,
2521 DndFolderInfo *info = NULL;
2523 info = (DndFolderInfo *) user_data;
2525 if (err || canceled) {
2526 dnd_on_connection_failed_destroyer (info, parent_window, account);
2530 /* Connect to source folder and perform the copy/move */
2531 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2533 drag_and_drop_from_folder_view_src_folder_performer,
2538 * This function is used by drag_data_received_cb to manage drag and
2539 * drop of a folder, i.e, and drag from the folder view to the same
2543 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2544 GtkTreeModel *dest_model,
2545 GtkTreePath *dest_row,
2546 GtkSelectionData *selection_data,
2549 GtkTreeIter dest_iter, iter;
2550 TnyFolderStore *dest_folder = NULL;
2551 TnyFolderStore *folder = NULL;
2552 gboolean forbidden = FALSE;
2554 DndFolderInfo *info = NULL;
2556 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2558 g_warning ("%s: BUG: no main window", __FUNCTION__);
2559 dnd_helper_destroyer (helper);
2564 /* check the folder rules for the destination */
2565 folder = tree_path_to_folder (dest_model, dest_row);
2566 if (TNY_IS_FOLDER(folder)) {
2567 ModestTnyFolderRules rules =
2568 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2569 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2570 } else if (TNY_IS_FOLDER_STORE(folder)) {
2571 /* enable local root as destination for folders */
2572 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2573 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2576 g_object_unref (folder);
2579 /* check the folder rules for the source */
2580 folder = tree_path_to_folder (source_model, helper->source_row);
2581 if (TNY_IS_FOLDER(folder)) {
2582 ModestTnyFolderRules rules =
2583 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2584 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2587 g_object_unref (folder);
2591 /* Check if the drag is possible */
2592 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2594 modest_platform_run_information_dialog ((GtkWindow *) win,
2595 _("mail_in_ui_folder_move_target_error"),
2597 /* Restore the previous selection */
2598 folder = tree_path_to_folder (source_model, helper->source_row);
2600 if (TNY_IS_FOLDER (folder))
2601 modest_folder_view_select_folder (helper->folder_view,
2602 TNY_FOLDER (folder), FALSE);
2603 g_object_unref (folder);
2605 dnd_helper_destroyer (helper);
2610 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2611 gtk_tree_model_get (dest_model, &dest_iter,
2614 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2615 gtk_tree_model_get (source_model, &iter,
2619 /* Create the info for the performer */
2620 info = g_slice_new0 (DndFolderInfo);
2621 info->src_folder = g_object_ref (folder);
2622 info->dst_folder = g_object_ref (dest_folder);
2623 info->helper = helper;
2625 /* Connect to the destination folder and perform the copy/move */
2626 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2628 drag_and_drop_from_folder_view_dst_folder_performer,
2632 g_object_unref (dest_folder);
2633 g_object_unref (folder);
2637 * This function receives the data set by the "drag-data-get" signal
2638 * handler. This information comes within the #GtkSelectionData. This
2639 * function will manage both the drags of folders of the treeview and
2640 * drags of headers of the header view widget.
2643 on_drag_data_received (GtkWidget *widget,
2644 GdkDragContext *context,
2647 GtkSelectionData *selection_data,
2652 GtkWidget *source_widget;
2653 GtkTreeModel *dest_model, *source_model;
2654 GtkTreePath *source_row, *dest_row;
2655 GtkTreeViewDropPosition pos;
2656 gboolean delete_source = FALSE;
2657 gboolean success = FALSE;
2659 /* Do not allow further process */
2660 g_signal_stop_emission_by_name (widget, "drag-data-received");
2661 source_widget = gtk_drag_get_source_widget (context);
2663 /* Get the action */
2664 if (context->action == GDK_ACTION_MOVE) {
2665 delete_source = TRUE;
2667 /* Notify that there is no folder selected. We need to
2668 do this in order to update the headers view (and
2669 its monitors, because when moving, the old folder
2670 won't longer exist. We can not wait for the end of
2671 the operation, because the operation won't start if
2672 the folder is in use */
2673 if (source_widget == widget) {
2674 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2675 gtk_tree_selection_unselect_all (sel);
2679 /* Check if the get_data failed */
2680 if (selection_data == NULL || selection_data->length < 0)
2683 /* Select the destination model */
2684 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2686 /* Get the path to the destination row. Can not call
2687 gtk_tree_view_get_drag_dest_row() because the source row
2688 is not selected anymore */
2689 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2692 /* Only allow drops IN other rows */
2694 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2695 pos == GTK_TREE_VIEW_DROP_AFTER)
2699 /* Drags from the header view */
2700 if (source_widget != widget) {
2701 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2703 drag_and_drop_from_header_view (source_model,
2708 DndHelper *helper = NULL;
2710 /* Get the source model and row */
2711 gtk_tree_get_row_drag_data (selection_data,
2715 /* Create the helper */
2716 helper = g_slice_new0 (DndHelper);
2717 helper->delete_source = delete_source;
2718 helper->source_row = gtk_tree_path_copy (source_row);
2719 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2721 drag_and_drop_from_folder_view (source_model,
2727 gtk_tree_path_free (source_row);
2731 gtk_tree_path_free (dest_row);
2734 /* Finish the drag and drop */
2735 gtk_drag_finish (context, success, FALSE, time);
2739 * We define a "drag-drop" signal handler because we do not want to
2740 * use the default one, because the default one always calls
2741 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2742 * signal handler, because there we have all the information available
2743 * to know if the dnd was a success or not.
2746 drag_drop_cb (GtkWidget *widget,
2747 GdkDragContext *context,
2755 if (!context->targets)
2758 /* Check if we're dragging a folder row */
2759 target = gtk_drag_dest_find_target (widget, context, NULL);
2761 /* Request the data from the source. */
2762 gtk_drag_get_data(widget, context, target, time);
2768 * This function expands a node of a tree view if it's not expanded
2769 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2770 * does that, so that's why they're here.
2773 expand_row_timeout (gpointer data)
2775 GtkTreeView *tree_view = data;
2776 GtkTreePath *dest_path = NULL;
2777 GtkTreeViewDropPosition pos;
2778 gboolean result = FALSE;
2780 gdk_threads_enter ();
2782 gtk_tree_view_get_drag_dest_row (tree_view,
2787 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2788 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2789 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2790 gtk_tree_path_free (dest_path);
2794 gtk_tree_path_free (dest_path);
2799 gdk_threads_leave ();
2805 * This function is called whenever the pointer is moved over a widget
2806 * while dragging some data. It installs a timeout that will expand a
2807 * node of the treeview if not expanded yet. This function also calls
2808 * gdk_drag_status in order to set the suggested action that will be
2809 * used by the "drag-data-received" signal handler to know if we
2810 * should do a move or just a copy of the data.
2813 on_drag_motion (GtkWidget *widget,
2814 GdkDragContext *context,
2820 GtkTreeViewDropPosition pos;
2821 GtkTreePath *dest_row;
2822 GtkTreeModel *dest_model;
2823 ModestFolderViewPrivate *priv;
2824 GdkDragAction suggested_action;
2825 gboolean valid_location = FALSE;
2826 TnyFolderStore *folder = NULL;
2828 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2830 if (priv->timer_expander != 0) {
2831 g_source_remove (priv->timer_expander);
2832 priv->timer_expander = 0;
2835 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2840 /* Do not allow drops between folders */
2842 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2843 pos == GTK_TREE_VIEW_DROP_AFTER) {
2844 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2845 gdk_drag_status(context, 0, time);
2846 valid_location = FALSE;
2849 valid_location = TRUE;
2852 /* Check that the destination folder is writable */
2853 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2854 folder = tree_path_to_folder (dest_model, dest_row);
2855 if (folder && TNY_IS_FOLDER (folder)) {
2856 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2858 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2859 valid_location = FALSE;
2864 /* Expand the selected row after 1/2 second */
2865 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2866 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2868 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2870 /* Select the desired action. By default we pick MOVE */
2871 suggested_action = GDK_ACTION_MOVE;
2873 if (context->actions == GDK_ACTION_COPY)
2874 gdk_drag_status(context, GDK_ACTION_COPY, time);
2875 else if (context->actions == GDK_ACTION_MOVE)
2876 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2877 else if (context->actions & suggested_action)
2878 gdk_drag_status(context, suggested_action, time);
2880 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2884 g_object_unref (folder);
2886 gtk_tree_path_free (dest_row);
2888 g_signal_stop_emission_by_name (widget, "drag-motion");
2890 return valid_location;
2894 * This function sets the treeview as a source and a target for dnd
2895 * events. It also connects all the requirede signals.
2898 setup_drag_and_drop (GtkTreeView *self)
2900 /* Set up the folder view as a dnd destination. Set only the
2901 highlight flag, otherwise gtk will have a different
2903 #ifdef MODEST_TOOLKIT_HILDON2
2906 gtk_drag_dest_set (GTK_WIDGET (self),
2907 GTK_DEST_DEFAULT_HIGHLIGHT,
2908 folder_view_drag_types,
2909 G_N_ELEMENTS (folder_view_drag_types),
2910 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2912 g_signal_connect (G_OBJECT (self),
2913 "drag_data_received",
2914 G_CALLBACK (on_drag_data_received),
2918 /* Set up the treeview as a dnd source */
2919 gtk_drag_source_set (GTK_WIDGET (self),
2921 folder_view_drag_types,
2922 G_N_ELEMENTS (folder_view_drag_types),
2923 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2925 g_signal_connect (G_OBJECT (self),
2927 G_CALLBACK (on_drag_motion),
2930 g_signal_connect (G_OBJECT (self),
2932 G_CALLBACK (on_drag_data_get),
2935 g_signal_connect (G_OBJECT (self),
2937 G_CALLBACK (drag_drop_cb),
2942 * This function manages the navigation through the folders using the
2943 * keyboard or the hardware keys in the device
2946 on_key_pressed (GtkWidget *self,
2950 GtkTreeSelection *selection;
2952 GtkTreeModel *model;
2953 gboolean retval = FALSE;
2955 /* Up and Down are automatically managed by the treeview */
2956 if (event->keyval == GDK_Return) {
2957 /* Expand/Collapse the selected row */
2958 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2959 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2962 path = gtk_tree_model_get_path (model, &iter);
2964 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2965 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2967 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2968 gtk_tree_path_free (path);
2970 /* No further processing */
2978 * We listen to the changes in the local folder account name key,
2979 * because we want to show the right name in the view. The local
2980 * folder account name corresponds to the device name in the Maemo
2981 * version. We do this because we do not want to query gconf on each
2982 * tree view refresh. It's better to cache it and change whenever
2986 on_configuration_key_changed (ModestConf* conf,
2988 ModestConfEvent event,
2989 ModestConfNotificationId id,
2990 ModestFolderView *self)
2992 ModestFolderViewPrivate *priv;
2995 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2996 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2998 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2999 g_free (priv->local_account_name);
3001 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3002 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3004 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3005 MODEST_CONF_DEVICE_NAME, NULL);
3007 /* Force a redraw */
3008 #if GTK_CHECK_VERSION(2, 8, 0)
3009 GtkTreeViewColumn * tree_column;
3011 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3013 gtk_tree_view_column_queue_resize (tree_column);
3015 gtk_widget_queue_draw (GTK_WIDGET (self));
3021 modest_folder_view_set_style (ModestFolderView *self,
3022 ModestFolderViewStyle style)
3024 ModestFolderViewPrivate *priv;
3026 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3027 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3028 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3030 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3033 priv->style = style;
3037 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3038 const gchar *account_id)
3040 ModestFolderViewPrivate *priv;
3041 GtkTreeModel *model;
3043 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3045 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3047 /* This will be used by the filter_row callback,
3048 * to decided which rows to show: */
3049 if (priv->visible_account_id) {
3050 g_free (priv->visible_account_id);
3051 priv->visible_account_id = NULL;
3054 priv->visible_account_id = g_strdup (account_id);
3057 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3058 if (GTK_IS_TREE_MODEL_FILTER (model))
3059 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3061 /* Save settings to gconf */
3062 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3063 MODEST_CONF_FOLDER_VIEW_KEY);
3067 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3069 ModestFolderViewPrivate *priv;
3071 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3073 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3075 return (const gchar *) priv->visible_account_id;
3079 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3083 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3085 gtk_tree_model_get (model, iter,
3089 gboolean result = FALSE;
3090 if (type == TNY_FOLDER_TYPE_INBOX) {
3094 *inbox_iter = *iter;
3098 if (gtk_tree_model_iter_children (model, &child, iter)) {
3099 if (find_inbox_iter (model, &child, inbox_iter))
3103 } while (gtk_tree_model_iter_next (model, iter));
3112 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3114 GtkTreeModel *model;
3115 GtkTreeIter iter, inbox_iter;
3116 GtkTreeSelection *sel;
3117 GtkTreePath *path = NULL;
3119 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3121 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3125 expand_root_items (self);
3126 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3128 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3129 g_warning ("%s: model is empty", __FUNCTION__);
3133 if (find_inbox_iter (model, &iter, &inbox_iter))
3134 path = gtk_tree_model_get_path (model, &inbox_iter);
3136 path = gtk_tree_path_new_first ();
3138 /* Select the row and free */
3139 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3140 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3141 gtk_tree_path_free (path);
3144 gtk_widget_grab_focus (GTK_WIDGET(self));
3150 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3155 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3156 TnyFolder* a_folder;
3159 gtk_tree_model_get (model, iter,
3160 INSTANCE_COLUMN, &a_folder,
3166 if (folder == a_folder) {
3167 g_object_unref (a_folder);
3168 *folder_iter = *iter;
3171 g_object_unref (a_folder);
3173 if (gtk_tree_model_iter_children (model, &child, iter)) {
3174 if (find_folder_iter (model, &child, folder_iter, folder))
3178 } while (gtk_tree_model_iter_next (model, iter));
3183 #ifndef MODEST_TOOLKIT_HILDON2
3185 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3188 ModestFolderView *self)
3190 ModestFolderViewPrivate *priv = NULL;
3191 GtkTreeSelection *sel;
3192 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3193 GObject *instance = NULL;
3195 if (!MODEST_IS_FOLDER_VIEW(self))
3198 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3200 priv->reexpand = TRUE;
3202 gtk_tree_model_get (tree_model, iter,
3204 INSTANCE_COLUMN, &instance,
3210 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3211 priv->folder_to_select = g_object_ref (instance);
3213 g_object_unref (instance);
3215 if (priv->folder_to_select) {
3217 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3220 path = gtk_tree_model_get_path (tree_model, iter);
3221 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3223 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3225 gtk_tree_selection_select_iter (sel, iter);
3226 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3228 gtk_tree_path_free (path);
3232 modest_folder_view_disable_next_folder_selection (self);
3234 /* Refilter the model */
3235 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3241 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3243 ModestFolderViewPrivate *priv;
3245 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3247 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3249 if (priv->folder_to_select)
3250 g_object_unref(priv->folder_to_select);
3252 priv->folder_to_select = NULL;
3256 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3257 gboolean after_change)
3259 GtkTreeModel *model;
3260 GtkTreeIter iter, folder_iter;
3261 GtkTreeSelection *sel;
3262 ModestFolderViewPrivate *priv = NULL;
3264 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3265 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3267 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3270 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3271 gtk_tree_selection_unselect_all (sel);
3273 if (priv->folder_to_select)
3274 g_object_unref(priv->folder_to_select);
3275 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3279 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3284 /* Refilter the model, before selecting the folder */
3285 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3287 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3288 g_warning ("%s: model is empty", __FUNCTION__);
3292 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3295 path = gtk_tree_model_get_path (model, &folder_iter);
3296 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3298 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3299 gtk_tree_selection_select_iter (sel, &folder_iter);
3300 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3302 gtk_tree_path_free (path);
3310 modest_folder_view_copy_selection (ModestFolderView *self)
3312 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3314 /* Copy selection */
3315 _clipboard_set_selected_data (self, FALSE);
3319 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3321 ModestFolderViewPrivate *priv = NULL;
3322 GtkTreeModel *model = NULL;
3323 const gchar **hidding = NULL;
3324 guint i, n_selected;
3326 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3327 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3329 /* Copy selection */
3330 if (!_clipboard_set_selected_data (folder_view, TRUE))
3333 /* Get hidding ids */
3334 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3336 /* Clear hidding array created by previous cut operation */
3337 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3339 /* Copy hidding array */
3340 priv->n_selected = n_selected;
3341 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3342 for (i=0; i < n_selected; i++)
3343 priv->hidding_ids[i] = g_strdup(hidding[i]);
3345 /* Hide cut folders */
3346 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3347 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3351 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3352 ModestFolderView *folder_view_dst)
3354 GtkTreeModel *filter_model = NULL;
3355 GtkTreeModel *model = NULL;
3356 GtkTreeModel *new_filter_model = NULL;
3358 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3359 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3362 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3363 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3365 /* Build new filter model */
3366 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3367 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3371 /* Set copied model */
3372 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3373 #ifndef MODEST_TOOLKIT_HILDON2
3374 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3375 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3379 g_object_unref (new_filter_model);
3383 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3386 GtkTreeModel *model = NULL;
3387 ModestFolderViewPrivate* priv;
3389 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3391 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3392 priv->show_non_move = show;
3393 /* modest_folder_view_update_model(folder_view, */
3394 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3396 /* Hide special folders */
3397 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3398 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3399 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3403 /* Returns FALSE if it did not selected anything */
3405 _clipboard_set_selected_data (ModestFolderView *folder_view,
3408 ModestFolderViewPrivate *priv = NULL;
3409 TnyFolderStore *folder = NULL;
3410 gboolean retval = FALSE;
3412 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3413 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3415 /* Set selected data on clipboard */
3416 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3417 folder = modest_folder_view_get_selected (folder_view);
3419 /* Do not allow to select an account */
3420 if (TNY_IS_FOLDER (folder)) {
3421 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3426 g_object_unref (folder);
3432 _clear_hidding_filter (ModestFolderView *folder_view)
3434 ModestFolderViewPrivate *priv;
3437 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3438 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3440 if (priv->hidding_ids != NULL) {
3441 for (i=0; i < priv->n_selected; i++)
3442 g_free (priv->hidding_ids[i]);
3443 g_free(priv->hidding_ids);
3449 on_display_name_changed (ModestAccountMgr *mgr,
3450 const gchar *account,
3453 ModestFolderView *self;
3455 self = MODEST_FOLDER_VIEW (user_data);
3457 /* Force a redraw */
3458 #if GTK_CHECK_VERSION(2, 8, 0)
3459 GtkTreeViewColumn * tree_column;
3461 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3463 gtk_tree_view_column_queue_resize (tree_column);
3465 gtk_widget_queue_draw (GTK_WIDGET (self));
3470 modest_folder_view_set_cell_style (ModestFolderView *self,
3471 ModestFolderViewCellStyle cell_style)
3473 ModestFolderViewPrivate *priv = NULL;
3475 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3476 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3478 priv->cell_style = cell_style;
3480 g_object_set (G_OBJECT (priv->messages_renderer),
3481 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3484 gtk_widget_queue_draw (GTK_WIDGET (self));
3488 update_style (ModestFolderView *self)
3490 ModestFolderViewPrivate *priv;
3491 GdkColor style_color;
3493 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3494 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3496 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3497 gdk_color_parse ("grey", &style_color);
3500 g_object_set (G_OBJECT (priv->messages_renderer),
3501 "foreground-gdk", &style_color,
3502 "foreground-set", TRUE,
3507 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3509 if (strcmp ("style", spec->name) == 0) {
3510 update_style (MODEST_FOLDER_VIEW (obj));
3511 gtk_widget_queue_draw (GTK_WIDGET (obj));
3516 modest_folder_view_set_filter (ModestFolderView *self,
3517 ModestFolderViewFilter filter)
3519 ModestFolderViewPrivate *priv;
3520 GtkTreeModel *filter_model;
3522 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3523 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3525 priv->filter = filter;
3527 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3528 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3529 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3534 modest_folder_view_unset_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));