1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-tny-account.h>
48 #include <modest-tny-folder.h>
49 #include <modest-tny-local-folders-account.h>
50 #include <modest-tny-outbox-account.h>
51 #include <modest-marshal.h>
52 #include <modest-icon-names.h>
53 #include <modest-tny-account-store.h>
54 #include <modest-tny-local-folders-account.h>
55 #include <modest-text-utils.h>
56 #include <modest-runtime.h>
57 #include "modest-folder-view.h"
58 #include <modest-platform.h>
59 #include <modest-widget-memory.h>
60 #include <modest-ui-actions.h>
61 #include "modest-dnd.h"
62 #include "modest-ui-constants.h"
63 #include "widgets/modest-window.h"
65 /* Folder view drag types */
66 const GtkTargetEntry folder_view_drag_types[] =
68 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
69 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
72 /* Default icon sizes for Fremantle style are different */
73 #ifdef MODEST_TOOLKIT_HILDON2
74 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
79 /* Column names depending on we use list store or tree store */
80 #ifdef MODEST_TOOLKIT_HILDON2
81 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
82 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
83 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
84 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
85 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
87 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
88 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
89 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
90 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
91 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
94 /* 'private'/'protected' functions */
95 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
96 static void modest_folder_view_init (ModestFolderView *obj);
97 static void modest_folder_view_finalize (GObject *obj);
99 static void tny_account_store_view_init (gpointer g,
100 gpointer iface_data);
102 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
103 TnyAccountStore *account_store);
105 static void on_selection_changed (GtkTreeSelection *sel,
108 static void on_row_activated (GtkTreeView *treeview,
110 GtkTreeViewColumn *column,
113 static void on_account_removed (TnyAccountStore *self,
117 static void on_account_inserted (TnyAccountStore *self,
121 static void on_account_changed (TnyAccountStore *self,
125 static gint cmp_rows (GtkTreeModel *tree_model,
130 static gboolean filter_row (GtkTreeModel *model,
134 static gboolean on_key_pressed (GtkWidget *self,
138 static void on_configuration_key_changed (ModestConf* conf,
140 ModestConfEvent event,
141 ModestConfNotificationId notification_id,
142 ModestFolderView *self);
145 static void on_drag_data_get (GtkWidget *widget,
146 GdkDragContext *context,
147 GtkSelectionData *selection_data,
152 static void on_drag_data_received (GtkWidget *widget,
153 GdkDragContext *context,
156 GtkSelectionData *selection_data,
161 static gboolean on_drag_motion (GtkWidget *widget,
162 GdkDragContext *context,
168 static void expand_root_items (ModestFolderView *self);
170 static gint expand_row_timeout (gpointer data);
172 static void setup_drag_and_drop (GtkTreeView *self);
174 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
177 static void _clear_hidding_filter (ModestFolderView *folder_view);
179 #ifndef MODEST_TOOLKIT_HILDON2
180 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
183 ModestFolderView *self);
186 static void on_display_name_changed (ModestAccountMgr *self,
187 const gchar *account,
189 static void update_style (ModestFolderView *self);
190 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
191 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
193 static gboolean get_inner_models (ModestFolderView *self,
194 GtkTreeModel **filter_model,
195 GtkTreeModel **sort_model,
196 GtkTreeModel **tny_model);
199 FOLDER_SELECTION_CHANGED_SIGNAL,
200 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
201 FOLDER_ACTIVATED_SIGNAL,
205 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
206 struct _ModestFolderViewPrivate {
207 TnyAccountStore *account_store;
208 TnyFolderStore *cur_folder_store;
210 TnyFolder *folder_to_select; /* folder to select after the next update */
212 gulong changed_signal;
213 gulong account_inserted_signal;
214 gulong account_removed_signal;
215 gulong account_changed_signal;
216 gulong conf_key_signal;
217 gulong display_name_changed_signal;
219 /* not unref this object, its a singlenton */
220 ModestEmailClipboard *clipboard;
222 /* Filter tree model */
225 ModestFolderViewFilter filter;
227 TnyFolderStoreQuery *query;
228 guint timer_expander;
230 gchar *local_account_name;
231 gchar *visible_account_id;
232 ModestFolderViewStyle style;
233 ModestFolderViewCellStyle cell_style;
235 gboolean reselect; /* we use this to force a reselection of the INBOX */
236 gboolean show_non_move;
237 gboolean reexpand; /* next time we expose, we'll expand all root folders */
239 GtkCellRenderer *messages_renderer;
241 gulong outbox_deleted_handler;
243 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
244 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
245 MODEST_TYPE_FOLDER_VIEW, \
246 ModestFolderViewPrivate))
248 static GObjectClass *parent_class = NULL;
250 static guint signals[LAST_SIGNAL] = {0};
253 modest_folder_view_get_type (void)
255 static GType my_type = 0;
257 static const GTypeInfo my_info = {
258 sizeof(ModestFolderViewClass),
259 NULL, /* base init */
260 NULL, /* base finalize */
261 (GClassInitFunc) modest_folder_view_class_init,
262 NULL, /* class finalize */
263 NULL, /* class data */
264 sizeof(ModestFolderView),
266 (GInstanceInitFunc) modest_folder_view_init,
270 static const GInterfaceInfo tny_account_store_view_info = {
271 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
272 NULL, /* interface_finalize */
273 NULL /* interface_data */
277 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
281 g_type_add_interface_static (my_type,
282 TNY_TYPE_ACCOUNT_STORE_VIEW,
283 &tny_account_store_view_info);
289 modest_folder_view_class_init (ModestFolderViewClass *klass)
291 GObjectClass *gobject_class;
292 GtkTreeViewClass *treeview_class;
293 gobject_class = (GObjectClass*) klass;
294 treeview_class = (GtkTreeViewClass*) klass;
296 parent_class = g_type_class_peek_parent (klass);
297 gobject_class->finalize = modest_folder_view_finalize;
299 g_type_class_add_private (gobject_class,
300 sizeof(ModestFolderViewPrivate));
302 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
303 g_signal_new ("folder_selection_changed",
304 G_TYPE_FROM_CLASS (gobject_class),
306 G_STRUCT_OFFSET (ModestFolderViewClass,
307 folder_selection_changed),
309 modest_marshal_VOID__POINTER_BOOLEAN,
310 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
313 * This signal is emitted whenever the currently selected
314 * folder display name is computed. Note that the name could
315 * be different to the folder name, because we could append
316 * the unread messages count to the folder name to build the
317 * folder display name
319 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
320 g_signal_new ("folder-display-name-changed",
321 G_TYPE_FROM_CLASS (gobject_class),
323 G_STRUCT_OFFSET (ModestFolderViewClass,
324 folder_display_name_changed),
326 g_cclosure_marshal_VOID__STRING,
327 G_TYPE_NONE, 1, G_TYPE_STRING);
329 signals[FOLDER_ACTIVATED_SIGNAL] =
330 g_signal_new ("folder_activated",
331 G_TYPE_FROM_CLASS (gobject_class),
333 G_STRUCT_OFFSET (ModestFolderViewClass,
336 g_cclosure_marshal_VOID__POINTER,
337 G_TYPE_NONE, 1, G_TYPE_POINTER);
339 treeview_class->select_cursor_parent = NULL;
341 #ifdef MODEST_TOOLKIT_HILDON2
342 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
348 /* Retrieves the filter, sort and tny models of the folder view. If
349 any of these does not exist then it returns FALSE */
351 get_inner_models (ModestFolderView *self,
352 GtkTreeModel **filter_model,
353 GtkTreeModel **sort_model,
354 GtkTreeModel **tny_model)
356 GtkTreeModel *s_model, *f_model, *t_model;
358 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
359 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
360 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
364 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
365 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
366 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
370 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
374 *filter_model = f_model;
376 *sort_model = s_model;
378 *tny_model = t_model;
383 /* Simplify checks for NULLs: */
385 strings_are_equal (const gchar *a, const gchar *b)
391 return (strcmp (a, b) == 0);
398 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
400 GObject *instance = NULL;
402 gtk_tree_model_get (model, iter,
403 INSTANCE_COLUMN, &instance,
407 return FALSE; /* keep walking */
409 if (!TNY_IS_ACCOUNT (instance)) {
410 g_object_unref (instance);
411 return FALSE; /* keep walking */
414 /* Check if this is the looked-for account: */
415 TnyAccount *this_account = TNY_ACCOUNT (instance);
416 TnyAccount *account = TNY_ACCOUNT (data);
418 const gchar *this_account_id = tny_account_get_id(this_account);
419 const gchar *account_id = tny_account_get_id(account);
420 g_object_unref (instance);
423 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
424 if (strings_are_equal(this_account_id, account_id)) {
425 /* Tell the model that the data has changed, so that
426 * it calls the cell_data_func callbacks again: */
427 /* TODO: This does not seem to actually cause the new string to be shown: */
428 gtk_tree_model_row_changed (model, path, iter);
430 return TRUE; /* stop walking */
433 return FALSE; /* keep walking */
438 ModestFolderView *self;
439 gchar *previous_name;
440 } GetMmcAccountNameData;
443 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
445 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
447 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
449 if (!strings_are_equal (
450 tny_account_get_name(TNY_ACCOUNT(account)),
451 data->previous_name)) {
453 /* Tell the model that the data has changed, so that
454 * it calls the cell_data_func callbacks again: */
455 ModestFolderView *self = data->self;
456 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
458 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
461 g_free (data->previous_name);
462 g_slice_free (GetMmcAccountNameData, data);
466 convert_parent_folders_to_dots (gchar **item_name)
470 gchar *last_separator;
472 if (item_name == NULL)
475 for (c = *item_name; *c != '\0'; c++) {
476 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
481 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
482 if (last_separator != NULL) {
483 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
490 buffer = g_string_new ("");
491 for (i = 0; i < n_parents; i++) {
492 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
494 buffer = g_string_append (buffer, last_separator);
496 *item_name = g_string_free (buffer, FALSE);
502 format_compact_style (gchar **item_name,
505 gboolean multiaccount,
506 gboolean *use_markup)
510 TnyFolderType folder_type;
512 if (!TNY_IS_FOLDER (instance))
515 folder = (TnyFolder *) instance;
517 folder_type = tny_folder_get_folder_type (folder);
518 is_special = (get_cmp_pos (folder_type, folder)!= 4);
520 if (!is_special || multiaccount) {
521 TnyAccount *account = tny_folder_get_account (folder);
522 const gchar *folder_name;
523 gboolean concat_folder_name = FALSE;
526 /* Should not happen */
530 /* convert parent folders to dots */
531 convert_parent_folders_to_dots (item_name);
533 folder_name = tny_folder_get_name (folder);
534 if (g_str_has_suffix (*item_name, folder_name)) {
535 gchar *offset = g_strrstr (*item_name, folder_name);
537 concat_folder_name = TRUE;
540 buffer = g_string_new ("");
542 buffer = g_string_append (buffer, *item_name);
543 if (concat_folder_name) {
544 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
545 buffer = g_string_append (buffer, folder_name);
546 if (bold) buffer = g_string_append (buffer, "</span>");
549 g_object_unref (account);
551 *item_name = g_string_free (buffer, FALSE);
559 text_cell_data (GtkTreeViewColumn *column,
560 GtkCellRenderer *renderer,
561 GtkTreeModel *tree_model,
565 ModestFolderViewPrivate *priv;
566 GObject *rendobj = (GObject *) renderer;
568 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
569 GObject *instance = NULL;
570 gboolean use_markup = FALSE;
572 gtk_tree_model_get (tree_model, iter,
575 INSTANCE_COLUMN, &instance,
577 if (!fname || !instance)
580 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
581 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
583 gchar *item_name = NULL;
584 gint item_weight = 400;
586 if (type != TNY_FOLDER_TYPE_ROOT) {
590 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
591 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
592 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
593 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
595 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
598 /* Sometimes an special folder is reported by the server as
599 NORMAL, like some versions of Dovecot */
600 if (type == TNY_FOLDER_TYPE_NORMAL ||
601 type == TNY_FOLDER_TYPE_UNKNOWN) {
602 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
606 if (type == TNY_FOLDER_TYPE_INBOX) {
608 fname = g_strdup (_("mcen_me_folder_inbox"));
611 /* note: we cannot reliably get the counts from the tree model, we need
612 * to use explicit calls on tny_folder for some reason.
614 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
615 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
616 (type == TNY_FOLDER_TYPE_OUTBOX) ||
617 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
618 number = tny_folder_get_all_count (TNY_FOLDER(instance));
621 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
625 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
626 item_name = g_strdup (fname);
633 /* Use bold font style if there are unread or unset messages */
635 item_name = g_strdup_printf ("%s (%d)", fname, number);
638 item_name = g_strdup (fname);
643 } else if (TNY_IS_ACCOUNT (instance)) {
644 /* If it's a server account */
645 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
646 item_name = g_strdup (priv->local_account_name);
648 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
649 /* fname is only correct when the items are first
650 * added to the model, not when the account is
651 * changed later, so get the name from the account
653 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
656 item_name = g_strdup (fname);
662 item_name = g_strdup ("unknown");
664 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
665 gboolean multiaccount;
667 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
668 /* Convert item_name to markup */
669 format_compact_style (&item_name, instance,
671 multiaccount, &use_markup);
674 if (item_name && item_weight) {
675 /* Set the name in the treeview cell: */
677 g_object_set (rendobj, "markup", item_name, NULL);
679 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
681 /* Notify display name observers */
682 /* TODO: What listens for this signal, and how can it use only the new name? */
683 if (((GObject *) priv->cur_folder_store) == instance) {
684 g_signal_emit (G_OBJECT(self),
685 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
692 /* If it is a Memory card account, make sure that we have the correct name.
693 * This function will be trigerred again when the name has been retrieved: */
694 if (TNY_IS_STORE_ACCOUNT (instance) &&
695 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
697 /* Get the account name asynchronously: */
698 GetMmcAccountNameData *callback_data =
699 g_slice_new0(GetMmcAccountNameData);
700 callback_data->self = self;
702 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
704 callback_data->previous_name = g_strdup (name);
706 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
707 on_get_mmc_account_name, callback_data);
711 g_object_unref (G_OBJECT (instance));
717 messages_cell_data (GtkTreeViewColumn *column,
718 GtkCellRenderer *renderer,
719 GtkTreeModel *tree_model,
723 ModestFolderView *self;
724 ModestFolderViewPrivate *priv;
725 GObject *rendobj = (GObject *) renderer;
726 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
727 GObject *instance = NULL;
728 gchar *item_name = NULL;
730 gtk_tree_model_get (tree_model, iter,
732 INSTANCE_COLUMN, &instance,
737 self = MODEST_FOLDER_VIEW (data);
738 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
741 if (type != TNY_FOLDER_TYPE_ROOT) {
745 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
746 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
747 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
749 /* Sometimes an special folder is reported by the server as
750 NORMAL, like some versions of Dovecot */
751 if (type == TNY_FOLDER_TYPE_NORMAL ||
752 type == TNY_FOLDER_TYPE_UNKNOWN) {
753 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
757 /* note: we cannot reliably get the counts from the tree model, we need
758 * to use explicit calls on tny_folder for some reason.
760 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
761 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
762 (type == TNY_FOLDER_TYPE_OUTBOX) ||
763 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
764 number = tny_folder_get_all_count (TNY_FOLDER(instance));
767 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
771 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
773 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
781 item_name = g_strdup ("");
784 /* Set the name in the treeview cell: */
785 g_object_set (rendobj,"text", item_name, NULL);
793 g_object_unref (G_OBJECT (instance));
799 GdkPixbuf *pixbuf_open;
800 GdkPixbuf *pixbuf_close;
804 static inline GdkPixbuf *
805 get_composite_pixbuf (const gchar *icon_name,
807 GdkPixbuf *base_pixbuf)
809 GdkPixbuf *emblem, *retval = NULL;
811 emblem = modest_platform_get_icon (icon_name, size);
813 retval = gdk_pixbuf_copy (base_pixbuf);
814 gdk_pixbuf_composite (emblem, retval, 0, 0,
815 MIN (gdk_pixbuf_get_width (emblem),
816 gdk_pixbuf_get_width (retval)),
817 MIN (gdk_pixbuf_get_height (emblem),
818 gdk_pixbuf_get_height (retval)),
819 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
820 g_object_unref (emblem);
825 static inline ThreePixbufs *
826 get_composite_icons (const gchar *icon_code,
828 GdkPixbuf **pixbuf_open,
829 GdkPixbuf **pixbuf_close)
831 ThreePixbufs *retval;
834 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
837 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
842 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
846 retval = g_slice_new0 (ThreePixbufs);
848 retval->pixbuf = g_object_ref (*pixbuf);
850 retval->pixbuf_open = g_object_ref (*pixbuf_open);
852 retval->pixbuf_close = g_object_ref (*pixbuf_close);
857 static inline ThreePixbufs*
858 get_folder_icons (TnyFolderType type, GObject *instance)
860 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
861 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
862 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
863 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
864 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
866 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
867 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
868 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
869 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
870 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
872 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
873 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
874 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
875 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
876 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
878 ThreePixbufs *retval = NULL;
880 /* Sometimes an special folder is reported by the server as
881 NORMAL, like some versions of Dovecot */
882 if (type == TNY_FOLDER_TYPE_NORMAL ||
883 type == TNY_FOLDER_TYPE_UNKNOWN) {
884 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
887 /* Remote folders should not be treated as special folders */
888 if (TNY_IS_FOLDER_STORE (instance) &&
889 !TNY_IS_ACCOUNT (instance) &&
890 type != TNY_FOLDER_TYPE_INBOX &&
891 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
892 #ifdef MODEST_TOOLKIT_HILDON2
893 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
896 &anorm_pixbuf_close);
898 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
901 &normal_pixbuf_close);
907 case TNY_FOLDER_TYPE_INVALID:
908 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
911 case TNY_FOLDER_TYPE_ROOT:
912 if (TNY_IS_ACCOUNT (instance)) {
914 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
915 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
918 &avirt_pixbuf_close);
920 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
922 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
923 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
928 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
931 &anorm_pixbuf_close);
936 case TNY_FOLDER_TYPE_INBOX:
937 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
940 &inbox_pixbuf_close);
942 case TNY_FOLDER_TYPE_OUTBOX:
943 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
946 &outbox_pixbuf_close);
948 case TNY_FOLDER_TYPE_JUNK:
949 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
954 case TNY_FOLDER_TYPE_SENT:
955 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
960 case TNY_FOLDER_TYPE_TRASH:
961 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
964 &trash_pixbuf_close);
966 case TNY_FOLDER_TYPE_DRAFTS:
967 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
970 &draft_pixbuf_close);
972 case TNY_FOLDER_TYPE_ARCHIVE:
973 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
978 case TNY_FOLDER_TYPE_NORMAL:
980 /* Memory card folders could have an special icon */
981 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
982 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
987 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
990 &normal_pixbuf_close);
999 free_pixbufs (ThreePixbufs *pixbufs)
1001 if (pixbufs->pixbuf)
1002 g_object_unref (pixbufs->pixbuf);
1003 if (pixbufs->pixbuf_open)
1004 g_object_unref (pixbufs->pixbuf_open);
1005 if (pixbufs->pixbuf_close)
1006 g_object_unref (pixbufs->pixbuf_close);
1007 g_slice_free (ThreePixbufs, pixbufs);
1011 icon_cell_data (GtkTreeViewColumn *column,
1012 GtkCellRenderer *renderer,
1013 GtkTreeModel *tree_model,
1017 GObject *rendobj = NULL, *instance = NULL;
1018 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1019 gboolean has_children;
1020 ThreePixbufs *pixbufs;
1022 rendobj = (GObject *) renderer;
1024 gtk_tree_model_get (tree_model, iter,
1026 INSTANCE_COLUMN, &instance,
1032 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1033 pixbufs = get_folder_icons (type, instance);
1034 g_object_unref (instance);
1037 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1040 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1041 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1044 free_pixbufs (pixbufs);
1048 add_columns (GtkWidget *treeview)
1050 GtkTreeViewColumn *column;
1051 GtkCellRenderer *renderer;
1052 GtkTreeSelection *sel;
1053 ModestFolderViewPrivate *priv;
1055 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1058 column = gtk_tree_view_column_new ();
1060 /* Set icon and text render function */
1061 renderer = gtk_cell_renderer_pixbuf_new();
1062 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1063 gtk_tree_view_column_set_cell_data_func(column, renderer,
1064 icon_cell_data, treeview, NULL);
1066 renderer = gtk_cell_renderer_text_new();
1067 g_object_set (renderer,
1068 #ifdef MODEST_TOOLKIT_HILDON2
1069 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1071 "ellipsize", PANGO_ELLIPSIZE_END,
1073 "ellipsize-set", TRUE, NULL);
1074 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1075 gtk_tree_view_column_set_cell_data_func(column, renderer,
1076 text_cell_data, treeview, NULL);
1078 priv->messages_renderer = gtk_cell_renderer_text_new ();
1079 g_object_set (priv->messages_renderer,
1080 "scale", PANGO_SCALE_X_SMALL,
1082 "alignment", PANGO_ALIGN_RIGHT,
1086 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1087 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1088 messages_cell_data, treeview, NULL);
1090 /* Set selection mode */
1091 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1092 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1094 /* Set treeview appearance */
1095 gtk_tree_view_column_set_spacing (column, 2);
1096 gtk_tree_view_column_set_resizable (column, TRUE);
1097 gtk_tree_view_column_set_fixed_width (column, TRUE);
1098 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1099 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1102 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1106 modest_folder_view_init (ModestFolderView *obj)
1108 ModestFolderViewPrivate *priv;
1111 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1113 priv->timer_expander = 0;
1114 priv->account_store = NULL;
1116 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1117 priv->cur_folder_store = NULL;
1118 priv->visible_account_id = NULL;
1119 priv->folder_to_select = NULL;
1120 priv->outbox_deleted_handler = 0;
1121 priv->reexpand = TRUE;
1123 /* Initialize the local account name */
1124 conf = modest_runtime_get_conf();
1125 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1127 /* Init email clipboard */
1128 priv->clipboard = modest_runtime_get_email_clipboard ();
1129 priv->hidding_ids = NULL;
1130 priv->n_selected = 0;
1131 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1132 priv->reselect = FALSE;
1133 priv->show_non_move = TRUE;
1135 /* Build treeview */
1136 add_columns (GTK_WIDGET (obj));
1138 /* Setup drag and drop */
1139 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1141 /* Connect signals */
1142 g_signal_connect (G_OBJECT (obj),
1144 G_CALLBACK (on_key_pressed), NULL);
1146 priv->display_name_changed_signal =
1147 g_signal_connect (modest_runtime_get_account_mgr (),
1148 "display_name_changed",
1149 G_CALLBACK (on_display_name_changed),
1153 * Track changes in the local account name (in the device it
1154 * will be the device name)
1156 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1158 G_CALLBACK(on_configuration_key_changed),
1162 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1168 tny_account_store_view_init (gpointer g, gpointer iface_data)
1170 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1172 klass->set_account_store = modest_folder_view_set_account_store;
1176 modest_folder_view_finalize (GObject *obj)
1178 ModestFolderViewPrivate *priv;
1179 GtkTreeSelection *sel;
1180 TnyAccount *local_account;
1182 g_return_if_fail (obj);
1184 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1186 if (priv->timer_expander != 0) {
1187 g_source_remove (priv->timer_expander);
1188 priv->timer_expander = 0;
1191 local_account = (TnyAccount *)
1192 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1193 if (local_account) {
1194 if (g_signal_handler_is_connected (local_account,
1195 priv->outbox_deleted_handler))
1196 g_signal_handler_disconnect (local_account,
1197 priv->outbox_deleted_handler);
1198 g_object_unref (local_account);
1201 if (priv->account_store) {
1202 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1203 priv->account_inserted_signal);
1204 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1205 priv->account_removed_signal);
1206 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1207 priv->account_changed_signal);
1208 g_object_unref (G_OBJECT(priv->account_store));
1209 priv->account_store = NULL;
1212 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1213 priv->display_name_changed_signal)) {
1214 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1215 priv->display_name_changed_signal);
1216 priv->display_name_changed_signal = 0;
1220 g_object_unref (G_OBJECT (priv->query));
1224 if (priv->folder_to_select) {
1225 g_object_unref (G_OBJECT(priv->folder_to_select));
1226 priv->folder_to_select = NULL;
1229 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1231 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1233 g_free (priv->local_account_name);
1234 g_free (priv->visible_account_id);
1236 if (priv->conf_key_signal) {
1237 g_signal_handler_disconnect (modest_runtime_get_conf (),
1238 priv->conf_key_signal);
1239 priv->conf_key_signal = 0;
1242 if (priv->cur_folder_store) {
1243 g_object_unref (priv->cur_folder_store);
1244 priv->cur_folder_store = NULL;
1247 /* Clear hidding array created by cut operation */
1248 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1250 G_OBJECT_CLASS(parent_class)->finalize (obj);
1255 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1257 ModestFolderViewPrivate *priv;
1260 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1261 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1263 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1264 device = tny_account_store_get_device (account_store);
1266 if (G_UNLIKELY (priv->account_store)) {
1268 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1269 priv->account_inserted_signal))
1270 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1271 priv->account_inserted_signal);
1272 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1273 priv->account_removed_signal))
1274 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1275 priv->account_removed_signal);
1276 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1277 priv->account_changed_signal))
1278 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1279 priv->account_changed_signal);
1280 g_object_unref (G_OBJECT (priv->account_store));
1283 priv->account_store = g_object_ref (G_OBJECT (account_store));
1285 priv->account_removed_signal =
1286 g_signal_connect (G_OBJECT(account_store), "account_removed",
1287 G_CALLBACK (on_account_removed), self);
1289 priv->account_inserted_signal =
1290 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1291 G_CALLBACK (on_account_inserted), self);
1293 priv->account_changed_signal =
1294 g_signal_connect (G_OBJECT(account_store), "account_changed",
1295 G_CALLBACK (on_account_changed), self);
1297 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1298 priv->reselect = FALSE;
1299 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1301 g_object_unref (G_OBJECT (device));
1305 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1308 ModestFolderView *self;
1309 GtkTreeModel *model, *filter_model;
1312 self = MODEST_FOLDER_VIEW (user_data);
1314 if (!get_inner_models (self, &filter_model, NULL, &model))
1317 /* Remove outbox from model */
1318 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1319 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1320 g_object_unref (outbox);
1323 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1327 on_account_inserted (TnyAccountStore *account_store,
1328 TnyAccount *account,
1331 ModestFolderViewPrivate *priv;
1332 GtkTreeModel *model, *filter_model;
1334 /* Ignore transport account insertions, we're not showing them
1335 in the folder view */
1336 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1339 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1342 /* If we're adding a new account, and there is no previous
1343 one, we need to select the visible server account */
1344 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1345 !priv->visible_account_id)
1346 modest_widget_memory_restore (modest_runtime_get_conf(),
1347 G_OBJECT (user_data),
1348 MODEST_CONF_FOLDER_VIEW_KEY);
1352 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1353 &filter_model, NULL, &model))
1356 /* Insert the account in the model */
1357 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1359 /* When the model is a list store (plain representation) the
1360 outbox is not a child of any account so we have to manually
1361 delete it because removing the local folders account won't
1362 delete it (because tny_folder_get_account() is not defined
1363 for a merge folder */
1364 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1365 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1367 priv->outbox_deleted_handler =
1368 g_signal_connect (account,
1370 G_CALLBACK (on_outbox_deleted_cb),
1374 /* Refilter the model */
1375 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1380 same_account_selected (ModestFolderView *self,
1381 TnyAccount *account)
1383 ModestFolderViewPrivate *priv;
1384 gboolean same_account = FALSE;
1386 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1388 if (priv->cur_folder_store) {
1389 TnyAccount *selected_folder_account = NULL;
1391 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1392 selected_folder_account =
1393 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1395 selected_folder_account =
1396 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1399 if (selected_folder_account == account)
1400 same_account = TRUE;
1402 g_object_unref (selected_folder_account);
1404 return same_account;
1409 * Selects the first inbox or the local account in an idle
1412 on_idle_select_first_inbox_or_local (gpointer user_data)
1414 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1416 gdk_threads_enter ();
1417 modest_folder_view_select_first_inbox_or_local (self);
1418 gdk_threads_leave ();
1424 on_account_changed (TnyAccountStore *account_store,
1425 TnyAccount *tny_account,
1428 ModestFolderView *self;
1429 ModestFolderViewPrivate *priv;
1430 GtkTreeModel *model, *filter_model;
1431 GtkTreeSelection *sel;
1432 gboolean same_account;
1434 /* Ignore transport account insertions, we're not showing them
1435 in the folder view */
1436 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1439 self = MODEST_FOLDER_VIEW (user_data);
1440 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1442 /* Get the inner model */
1443 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1444 &filter_model, NULL, &model))
1447 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1449 /* Invalidate the cur_folder_store only if the selected folder
1450 belongs to the account that is being removed */
1451 same_account = same_account_selected (self, tny_account);
1453 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1454 gtk_tree_selection_unselect_all (sel);
1457 /* Remove the account from the model */
1458 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1460 /* Insert the account in the model */
1461 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1463 /* Refilter the model */
1464 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1466 /* Select the first INBOX if the currently selected folder
1467 belongs to the account that is being deleted */
1468 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1469 g_idle_add (on_idle_select_first_inbox_or_local, self);
1473 on_account_removed (TnyAccountStore *account_store,
1474 TnyAccount *account,
1477 ModestFolderView *self = NULL;
1478 ModestFolderViewPrivate *priv;
1479 GtkTreeModel *model, *filter_model;
1480 GtkTreeSelection *sel = NULL;
1481 gboolean same_account = FALSE;
1483 /* Ignore transport account removals, we're not showing them
1484 in the folder view */
1485 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1488 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1489 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1493 self = MODEST_FOLDER_VIEW (user_data);
1494 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1496 /* Invalidate the cur_folder_store only if the selected folder
1497 belongs to the account that is being removed */
1498 same_account = same_account_selected (self, account);
1500 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1501 gtk_tree_selection_unselect_all (sel);
1504 /* Invalidate row to select only if the folder to select
1505 belongs to the account that is being removed*/
1506 if (priv->folder_to_select) {
1507 TnyAccount *folder_to_select_account = NULL;
1509 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1510 if (folder_to_select_account == account) {
1511 modest_folder_view_disable_next_folder_selection (self);
1512 g_object_unref (priv->folder_to_select);
1513 priv->folder_to_select = NULL;
1515 g_object_unref (folder_to_select_account);
1518 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1519 &filter_model, NULL, &model))
1522 /* Disconnect the signal handler */
1523 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1524 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1525 if (g_signal_handler_is_connected (account,
1526 priv->outbox_deleted_handler))
1527 g_signal_handler_disconnect (account,
1528 priv->outbox_deleted_handler);
1531 /* Remove the account from the model */
1532 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1534 /* If the removed account is the currently viewed one then
1535 clear the configuration value. The new visible account will be the default account */
1536 if (priv->visible_account_id &&
1537 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1539 /* Clear the current visible account_id */
1540 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1542 /* Call the restore method, this will set the new visible account */
1543 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1544 MODEST_CONF_FOLDER_VIEW_KEY);
1547 /* Refilter the model */
1548 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1550 /* Select the first INBOX if the currently selected folder
1551 belongs to the account that is being deleted */
1553 g_idle_add (on_idle_select_first_inbox_or_local, self);
1557 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1559 GtkTreeViewColumn *col;
1561 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1563 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1565 g_printerr ("modest: failed get column for title\n");
1569 gtk_tree_view_column_set_title (col, title);
1570 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1575 modest_folder_view_on_map (ModestFolderView *self,
1576 GdkEventExpose *event,
1579 ModestFolderViewPrivate *priv;
1581 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1583 /* This won't happen often */
1584 if (G_UNLIKELY (priv->reselect)) {
1585 /* Select the first inbox or the local account if not found */
1587 /* TODO: this could cause a lock at startup, so we
1588 comment it for the moment. We know that this will
1589 be a bug, because the INBOX is not selected, but we
1590 need to rewrite some parts of Modest to avoid the
1591 deathlock situation */
1592 /* TODO: check if this is still the case */
1593 priv->reselect = FALSE;
1594 modest_folder_view_select_first_inbox_or_local (self);
1595 /* Notify the display name observers */
1596 g_signal_emit (G_OBJECT(self),
1597 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1601 if (priv->reexpand) {
1602 expand_root_items (self);
1603 priv->reexpand = FALSE;
1610 modest_folder_view_new (TnyFolderStoreQuery *query)
1613 ModestFolderViewPrivate *priv;
1614 GtkTreeSelection *sel;
1616 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1617 #ifdef MODEST_TOOLKIT_HILDON2
1618 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1621 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1624 priv->query = g_object_ref (query);
1626 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1627 priv->changed_signal = g_signal_connect (sel, "changed",
1628 G_CALLBACK (on_selection_changed), self);
1630 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1632 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1634 return GTK_WIDGET(self);
1637 /* this feels dirty; any other way to expand all the root items? */
1639 expand_root_items (ModestFolderView *self)
1642 GtkTreeModel *model;
1645 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1646 path = gtk_tree_path_new_first ();
1648 /* all folders should have child items, so.. */
1650 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1651 gtk_tree_path_next (path);
1652 } while (gtk_tree_model_get_iter (model, &iter, path));
1654 gtk_tree_path_free (path);
1658 * We use this function to implement the
1659 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1660 * account in this case, and the local folders.
1663 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1665 ModestFolderViewPrivate *priv;
1666 gboolean retval = TRUE;
1667 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1668 GObject *instance = NULL;
1669 const gchar *id = NULL;
1671 gboolean found = FALSE;
1672 gboolean cleared = FALSE;
1673 ModestTnyFolderRules rules = 0;
1675 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1676 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1678 gtk_tree_model_get (model, iter,
1680 INSTANCE_COLUMN, &instance,
1683 /* Do not show if there is no instance, this could indeed
1684 happen when the model is being modified while it's being
1685 drawn. This could occur for example when moving folders
1690 if (TNY_IS_ACCOUNT (instance)) {
1691 TnyAccount *acc = TNY_ACCOUNT (instance);
1692 const gchar *account_id = tny_account_get_id (acc);
1694 /* If it isn't a special folder,
1695 * don't show it unless it is the visible account: */
1696 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1697 !modest_tny_account_is_virtual_local_folders (acc) &&
1698 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1700 /* Show only the visible account id */
1701 if (priv->visible_account_id) {
1702 if (strcmp (account_id, priv->visible_account_id))
1709 /* Never show these to the user. They are merged into one folder
1710 * in the local-folders account instead: */
1711 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1714 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1715 /* Only show special folders for current account if needed */
1716 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1717 TnyAccount *account;
1719 account = tny_folder_get_account (TNY_FOLDER (instance));
1721 if (TNY_IS_ACCOUNT (account)) {
1722 const gchar *account_id = tny_account_get_id (account);
1724 if (!modest_tny_account_is_virtual_local_folders (account) &&
1725 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1726 /* Show only the visible account id */
1727 if (priv->visible_account_id) {
1728 if (strcmp (account_id, priv->visible_account_id))
1732 g_object_unref (account);
1739 /* Check hiding (if necessary) */
1740 cleared = modest_email_clipboard_cleared (priv->clipboard);
1741 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1742 id = tny_folder_get_id (TNY_FOLDER(instance));
1743 if (priv->hidding_ids != NULL)
1744 for (i=0; i < priv->n_selected && !found; i++)
1745 if (priv->hidding_ids[i] != NULL && id != NULL)
1746 found = (!strcmp (priv->hidding_ids[i], id));
1751 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1752 folder as no message can be move there according to UI specs */
1753 if (!priv->show_non_move) {
1754 if (TNY_IS_FOLDER (instance) &&
1755 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1757 case TNY_FOLDER_TYPE_OUTBOX:
1758 case TNY_FOLDER_TYPE_SENT:
1759 case TNY_FOLDER_TYPE_DRAFTS:
1762 case TNY_FOLDER_TYPE_UNKNOWN:
1763 case TNY_FOLDER_TYPE_NORMAL:
1764 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1765 if (type == TNY_FOLDER_TYPE_INVALID)
1766 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1768 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1769 type == TNY_FOLDER_TYPE_SENT
1770 || type == TNY_FOLDER_TYPE_DRAFTS)
1779 /* apply special filters */
1780 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1781 if (TNY_IS_ACCOUNT (instance))
1785 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1786 if (TNY_IS_FOLDER (instance)) {
1787 /* Check folder rules */
1788 ModestTnyFolderRules rules;
1790 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1791 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
1792 } else if (TNY_IS_ACCOUNT (instance)) {
1793 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1801 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1802 if (TNY_IS_FOLDER (instance)) {
1803 TnyFolderType guess_type;
1805 if (TNY_FOLDER_TYPE_NORMAL) {
1806 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1812 case TNY_FOLDER_TYPE_OUTBOX:
1813 case TNY_FOLDER_TYPE_SENT:
1814 case TNY_FOLDER_TYPE_DRAFTS:
1815 case TNY_FOLDER_TYPE_ARCHIVE:
1816 case TNY_FOLDER_TYPE_INBOX:
1819 case TNY_FOLDER_TYPE_UNKNOWN:
1820 case TNY_FOLDER_TYPE_NORMAL:
1826 } else if (TNY_IS_ACCOUNT (instance)) {
1831 if (retval && TNY_IS_FOLDER (instance)) {
1832 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1835 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1836 if (TNY_IS_FOLDER (instance)) {
1837 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1838 } else if (TNY_IS_ACCOUNT (instance)) {
1843 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1844 if (TNY_IS_FOLDER (instance)) {
1845 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1846 } else if (TNY_IS_ACCOUNT (instance)) {
1851 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1852 if (TNY_IS_FOLDER (instance)) {
1853 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1854 } else if (TNY_IS_ACCOUNT (instance)) {
1860 g_object_unref (instance);
1867 modest_folder_view_update_model (ModestFolderView *self,
1868 TnyAccountStore *account_store)
1870 ModestFolderViewPrivate *priv;
1871 GtkTreeModel *model /* , *old_model */;
1872 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1874 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1875 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1878 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1880 /* Notify that there is no folder selected */
1881 g_signal_emit (G_OBJECT(self),
1882 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1884 if (priv->cur_folder_store) {
1885 g_object_unref (priv->cur_folder_store);
1886 priv->cur_folder_store = NULL;
1889 /* FIXME: the local accounts are not shown when the query
1890 selects only the subscribed folders */
1891 #ifdef MODEST_TOOLKIT_HILDON2
1892 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1893 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1894 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1895 MODEST_FOLDER_PATH_SEPARATOR);
1897 model = tny_gtk_folder_store_tree_model_new (NULL);
1900 /* When the model is a list store (plain representation) the
1901 outbox is not a child of any account so we have to manually
1902 delete it because removing the local folders account won't
1903 delete it (because tny_folder_get_account() is not defined
1904 for a merge folder */
1905 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1906 TnyAccount *account;
1907 ModestTnyAccountStore *acc_store;
1909 acc_store = modest_runtime_get_account_store ();
1910 account = modest_tny_account_store_get_local_folders_account (acc_store);
1912 if (g_signal_handler_is_connected (account,
1913 priv->outbox_deleted_handler))
1914 g_signal_handler_disconnect (account,
1915 priv->outbox_deleted_handler);
1917 priv->outbox_deleted_handler =
1918 g_signal_connect (account,
1920 G_CALLBACK (on_outbox_deleted_cb),
1922 g_object_unref (account);
1925 /* Get the accounts: */
1926 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1928 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1930 sortable = gtk_tree_model_sort_new_with_model (model);
1931 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1933 GTK_SORT_ASCENDING);
1934 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1936 cmp_rows, NULL, NULL);
1938 /* Create filter model */
1939 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1940 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1946 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1947 #ifndef MODEST_TOOLKIT_HILDON2
1948 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1949 (GCallback) on_row_inserted_maybe_select_folder, self);
1952 g_object_unref (model);
1953 g_object_unref (filter_model);
1954 g_object_unref (sortable);
1956 /* Force a reselection of the INBOX next time the widget is shown */
1957 priv->reselect = TRUE;
1964 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1966 GtkTreeModel *model = NULL;
1967 TnyFolderStore *folder = NULL;
1969 ModestFolderView *tree_view = NULL;
1970 ModestFolderViewPrivate *priv = NULL;
1971 gboolean selected = FALSE;
1973 g_return_if_fail (sel);
1974 g_return_if_fail (user_data);
1976 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1978 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1980 tree_view = MODEST_FOLDER_VIEW (user_data);
1983 gtk_tree_model_get (model, &iter,
1984 INSTANCE_COLUMN, &folder,
1987 /* If the folder is the same do not notify */
1988 if (folder && priv->cur_folder_store == folder) {
1989 g_object_unref (folder);
1994 /* Current folder was unselected */
1995 if (priv->cur_folder_store) {
1996 /* We must do this firstly because a libtinymail-camel
1997 implementation detail. If we issue the signal
1998 before doing the sync_async, then that signal could
1999 cause (and it actually does it) a free of the
2000 summary of the folder (because the main window will
2001 clear the headers view */
2002 if (TNY_IS_FOLDER(priv->cur_folder_store))
2003 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2004 FALSE, NULL, NULL, NULL);
2006 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2007 priv->cur_folder_store, FALSE);
2009 g_object_unref (priv->cur_folder_store);
2010 priv->cur_folder_store = NULL;
2013 /* New current references */
2014 priv->cur_folder_store = folder;
2016 /* New folder has been selected. Do not notify if there is
2017 nothing new selected */
2019 g_signal_emit (G_OBJECT(tree_view),
2020 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2021 0, priv->cur_folder_store, TRUE);
2026 on_row_activated (GtkTreeView *treeview,
2027 GtkTreePath *treepath,
2028 GtkTreeViewColumn *column,
2031 GtkTreeModel *model = NULL;
2032 TnyFolderStore *folder = NULL;
2034 ModestFolderView *self = NULL;
2035 ModestFolderViewPrivate *priv = NULL;
2037 g_return_if_fail (treeview);
2038 g_return_if_fail (user_data);
2040 self = MODEST_FOLDER_VIEW (user_data);
2041 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2043 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2045 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2048 gtk_tree_model_get (model, &iter,
2049 INSTANCE_COLUMN, &folder,
2052 g_signal_emit (G_OBJECT(self),
2053 signals[FOLDER_ACTIVATED_SIGNAL],
2056 #ifdef MODEST_TOOLKIT_HILDON2
2057 HildonUIMode ui_mode;
2058 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2059 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2060 if (priv->cur_folder_store)
2061 g_object_unref (priv->cur_folder_store);
2062 priv->cur_folder_store = g_object_ref (folder);
2066 g_object_unref (folder);
2070 modest_folder_view_get_selected (ModestFolderView *self)
2072 ModestFolderViewPrivate *priv;
2074 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2076 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2077 if (priv->cur_folder_store)
2078 g_object_ref (priv->cur_folder_store);
2080 return priv->cur_folder_store;
2084 get_cmp_rows_type_pos (GObject *folder)
2086 /* Remote accounts -> Local account -> MMC account .*/
2089 if (TNY_IS_ACCOUNT (folder) &&
2090 modest_tny_account_is_virtual_local_folders (
2091 TNY_ACCOUNT (folder))) {
2093 } else if (TNY_IS_ACCOUNT (folder)) {
2094 TnyAccount *account = TNY_ACCOUNT (folder);
2095 const gchar *account_id = tny_account_get_id (account);
2096 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2102 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2103 return -1; /* Should never happen */
2108 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2110 TnyAccount *account;
2111 gboolean is_special;
2112 /* Inbox, Outbox, Drafts, Sent, User */
2115 if (!TNY_IS_FOLDER (folder_store))
2118 case TNY_FOLDER_TYPE_INBOX:
2120 account = tny_folder_get_account (folder_store);
2121 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2122 g_object_unref (account);
2123 return is_special?0:4;
2126 case TNY_FOLDER_TYPE_OUTBOX:
2129 case TNY_FOLDER_TYPE_DRAFTS:
2131 account = tny_folder_get_account (folder_store);
2132 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2133 g_object_unref (account);
2134 return is_special?1:4;
2137 case TNY_FOLDER_TYPE_SENT:
2139 account = tny_folder_get_account (folder_store);
2140 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2141 g_object_unref (account);
2142 return is_special?3:4;
2151 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2153 const gchar *a1_name, *a2_name;
2155 a1_name = tny_account_get_name (a1);
2156 a2_name = tny_account_get_name (a2);
2158 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2162 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2164 TnyAccount *a1, *a2;
2167 if (TNY_IS_ACCOUNT (s1)) {
2168 a1 = TNY_ACCOUNT (g_object_ref (s1));
2170 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2173 if (TNY_IS_ACCOUNT (s2)) {
2174 a2 = TNY_ACCOUNT (g_object_ref (s2));
2176 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2183 /* First we sort with the type of account */
2184 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2188 cmp = compare_account_names (a1, a2);
2191 g_object_unref (a1);
2192 g_object_unref (a2);
2198 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2200 gint is_account1, is_account2;
2202 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2203 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2205 return is_account2 - is_account1;
2209 * This function orders the mail accounts according to these rules:
2210 * 1st - remote accounts
2211 * 2nd - local account
2215 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2219 gchar *name1 = NULL;
2220 gchar *name2 = NULL;
2221 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2222 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2223 GObject *folder1 = NULL;
2224 GObject *folder2 = NULL;
2226 gtk_tree_model_get (tree_model, iter1,
2227 NAME_COLUMN, &name1,
2229 INSTANCE_COLUMN, &folder1,
2231 gtk_tree_model_get (tree_model, iter2,
2232 NAME_COLUMN, &name2,
2233 TYPE_COLUMN, &type2,
2234 INSTANCE_COLUMN, &folder2,
2237 /* Return if we get no folder. This could happen when folder
2238 operations are happening. The model is updated after the
2239 folder copy/move actually occurs, so there could be
2240 situations where the model to be drawn is not correct */
2241 if (!folder1 || !folder2)
2244 /* Sort by type. First the special folders, then the archives */
2245 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2249 /* Now we sort using the account of each folder */
2250 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2254 /* Each group is preceeded by its account */
2255 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2259 /* Pure sort by name */
2260 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2263 g_object_unref(G_OBJECT(folder1));
2265 g_object_unref(G_OBJECT(folder2));
2273 /*****************************************************************************/
2274 /* DRAG and DROP stuff */
2275 /*****************************************************************************/
2277 * This function fills the #GtkSelectionData with the row and the
2278 * model that has been dragged. It's called when this widget is a
2279 * source for dnd after the event drop happened
2282 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2283 guint info, guint time, gpointer data)
2285 GtkTreeSelection *selection;
2286 GtkTreeModel *model;
2288 GtkTreePath *source_row;
2290 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2291 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2293 source_row = gtk_tree_model_get_path (model, &iter);
2294 gtk_tree_set_row_drag_data (selection_data,
2298 gtk_tree_path_free (source_row);
2302 typedef struct _DndHelper {
2303 ModestFolderView *folder_view;
2304 gboolean delete_source;
2305 GtkTreePath *source_row;
2309 dnd_helper_destroyer (DndHelper *helper)
2311 /* Free the helper */
2312 gtk_tree_path_free (helper->source_row);
2313 g_slice_free (DndHelper, helper);
2317 xfer_folder_cb (ModestMailOperation *mail_op,
2318 TnyFolder *new_folder,
2322 /* Select the folder */
2323 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2329 /* get the folder for the row the treepath refers to. */
2330 /* folder must be unref'd */
2331 static TnyFolderStore *
2332 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2335 TnyFolderStore *folder = NULL;
2337 if (gtk_tree_model_get_iter (model,&iter, path))
2338 gtk_tree_model_get (model, &iter,
2339 INSTANCE_COLUMN, &folder,
2346 * This function is used by drag_data_received_cb to manage drag and
2347 * drop of a header, i.e, and drag from the header view to the folder
2351 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2352 GtkTreeModel *dest_model,
2353 GtkTreePath *dest_row,
2354 GtkSelectionData *selection_data)
2356 TnyList *headers = NULL;
2357 TnyFolder *folder = NULL, *src_folder = NULL;
2358 TnyFolderType folder_type;
2359 GtkTreeIter source_iter, dest_iter;
2360 ModestWindowMgr *mgr = NULL;
2361 ModestWindow *main_win = NULL;
2362 gchar **uris, **tmp;
2364 /* Build the list of headers */
2365 mgr = modest_runtime_get_window_mgr ();
2366 headers = tny_simple_list_new ();
2367 uris = modest_dnd_selection_data_get_paths (selection_data);
2370 while (*tmp != NULL) {
2373 gboolean first = TRUE;
2376 path = gtk_tree_path_new_from_string (*tmp);
2377 gtk_tree_model_get_iter (source_model, &source_iter, path);
2378 gtk_tree_model_get (source_model, &source_iter,
2379 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2382 /* Do not enable d&d of headers already opened */
2383 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2384 tny_list_append (headers, G_OBJECT (header));
2386 if (G_UNLIKELY (first)) {
2387 src_folder = tny_header_get_folder (header);
2391 /* Free and go on */
2392 gtk_tree_path_free (path);
2393 g_object_unref (header);
2398 /* This could happen ig we perform a d&d very quickly over the
2399 same row that row could dissapear because message is
2401 if (!TNY_IS_FOLDER (src_folder))
2404 /* Get the target folder */
2405 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2406 gtk_tree_model_get (dest_model, &dest_iter,
2410 if (!folder || !TNY_IS_FOLDER(folder)) {
2411 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2415 folder_type = modest_tny_folder_guess_folder_type (folder);
2416 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2417 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2418 goto cleanup; /* cannot move messages there */
2421 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2422 /* g_warning ("folder not writable"); */
2423 goto cleanup; /* verboten! */
2426 /* Ask for confirmation to move */
2427 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2429 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2433 /* Transfer messages */
2434 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2439 if (G_IS_OBJECT (src_folder))
2440 g_object_unref (src_folder);
2441 if (G_IS_OBJECT(folder))
2442 g_object_unref (G_OBJECT (folder));
2443 if (G_IS_OBJECT(headers))
2444 g_object_unref (headers);
2448 TnyFolderStore *src_folder;
2449 TnyFolderStore *dst_folder;
2450 ModestFolderView *folder_view;
2455 dnd_folder_info_destroyer (DndFolderInfo *info)
2457 if (info->src_folder)
2458 g_object_unref (info->src_folder);
2459 if (info->dst_folder)
2460 g_object_unref (info->dst_folder);
2461 g_slice_free (DndFolderInfo, info);
2465 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2466 GtkWindow *parent_window,
2467 TnyAccount *account)
2470 modest_ui_actions_on_account_connection_error (parent_window, account);
2472 /* Free the helper & info */
2473 dnd_helper_destroyer (info->helper);
2474 dnd_folder_info_destroyer (info);
2478 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2480 GtkWindow *parent_window,
2481 TnyAccount *account,
2484 DndFolderInfo *info = NULL;
2485 ModestMailOperation *mail_op;
2487 info = (DndFolderInfo *) user_data;
2489 if (err || canceled) {
2490 dnd_on_connection_failed_destroyer (info, parent_window, account);
2494 /* Do the mail operation */
2495 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2496 modest_ui_actions_move_folder_error_handler,
2497 info->src_folder, NULL);
2499 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2502 /* Transfer the folder */
2503 modest_mail_operation_xfer_folder (mail_op,
2504 TNY_FOLDER (info->src_folder),
2506 info->helper->delete_source,
2508 info->helper->folder_view);
2511 g_object_unref (G_OBJECT (mail_op));
2512 dnd_helper_destroyer (info->helper);
2513 dnd_folder_info_destroyer (info);
2518 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2520 GtkWindow *parent_window,
2521 TnyAccount *account,
2524 DndFolderInfo *info = NULL;
2526 info = (DndFolderInfo *) user_data;
2528 if (err || canceled) {
2529 dnd_on_connection_failed_destroyer (info, parent_window, account);
2533 /* Connect to source folder and perform the copy/move */
2534 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2536 drag_and_drop_from_folder_view_src_folder_performer,
2541 * This function is used by drag_data_received_cb to manage drag and
2542 * drop of a folder, i.e, and drag from the folder view to the same
2546 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2547 GtkTreeModel *dest_model,
2548 GtkTreePath *dest_row,
2549 GtkSelectionData *selection_data,
2552 GtkTreeIter dest_iter, iter;
2553 TnyFolderStore *dest_folder = NULL;
2554 TnyFolderStore *folder = NULL;
2555 gboolean forbidden = FALSE;
2557 DndFolderInfo *info = NULL;
2559 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2561 g_warning ("%s: BUG: no main window", __FUNCTION__);
2562 dnd_helper_destroyer (helper);
2567 /* check the folder rules for the destination */
2568 folder = tree_path_to_folder (dest_model, dest_row);
2569 if (TNY_IS_FOLDER(folder)) {
2570 ModestTnyFolderRules rules =
2571 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2572 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2573 } else if (TNY_IS_FOLDER_STORE(folder)) {
2574 /* enable local root as destination for folders */
2575 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2576 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2579 g_object_unref (folder);
2582 /* check the folder rules for the source */
2583 folder = tree_path_to_folder (source_model, helper->source_row);
2584 if (TNY_IS_FOLDER(folder)) {
2585 ModestTnyFolderRules rules =
2586 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2587 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2590 g_object_unref (folder);
2594 /* Check if the drag is possible */
2595 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2597 modest_platform_run_information_dialog ((GtkWindow *) win,
2598 _("mail_in_ui_folder_move_target_error"),
2600 /* Restore the previous selection */
2601 folder = tree_path_to_folder (source_model, helper->source_row);
2603 if (TNY_IS_FOLDER (folder))
2604 modest_folder_view_select_folder (helper->folder_view,
2605 TNY_FOLDER (folder), FALSE);
2606 g_object_unref (folder);
2608 dnd_helper_destroyer (helper);
2613 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2614 gtk_tree_model_get (dest_model, &dest_iter,
2617 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2618 gtk_tree_model_get (source_model, &iter,
2622 /* Create the info for the performer */
2623 info = g_slice_new0 (DndFolderInfo);
2624 info->src_folder = g_object_ref (folder);
2625 info->dst_folder = g_object_ref (dest_folder);
2626 info->helper = helper;
2628 /* Connect to the destination folder and perform the copy/move */
2629 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2631 drag_and_drop_from_folder_view_dst_folder_performer,
2635 g_object_unref (dest_folder);
2636 g_object_unref (folder);
2640 * This function receives the data set by the "drag-data-get" signal
2641 * handler. This information comes within the #GtkSelectionData. This
2642 * function will manage both the drags of folders of the treeview and
2643 * drags of headers of the header view widget.
2646 on_drag_data_received (GtkWidget *widget,
2647 GdkDragContext *context,
2650 GtkSelectionData *selection_data,
2655 GtkWidget *source_widget;
2656 GtkTreeModel *dest_model, *source_model;
2657 GtkTreePath *source_row, *dest_row;
2658 GtkTreeViewDropPosition pos;
2659 gboolean delete_source = FALSE;
2660 gboolean success = FALSE;
2662 /* Do not allow further process */
2663 g_signal_stop_emission_by_name (widget, "drag-data-received");
2664 source_widget = gtk_drag_get_source_widget (context);
2666 /* Get the action */
2667 if (context->action == GDK_ACTION_MOVE) {
2668 delete_source = TRUE;
2670 /* Notify that there is no folder selected. We need to
2671 do this in order to update the headers view (and
2672 its monitors, because when moving, the old folder
2673 won't longer exist. We can not wait for the end of
2674 the operation, because the operation won't start if
2675 the folder is in use */
2676 if (source_widget == widget) {
2677 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2678 gtk_tree_selection_unselect_all (sel);
2682 /* Check if the get_data failed */
2683 if (selection_data == NULL || selection_data->length < 0)
2686 /* Select the destination model */
2687 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2689 /* Get the path to the destination row. Can not call
2690 gtk_tree_view_get_drag_dest_row() because the source row
2691 is not selected anymore */
2692 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2695 /* Only allow drops IN other rows */
2697 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2698 pos == GTK_TREE_VIEW_DROP_AFTER)
2702 /* Drags from the header view */
2703 if (source_widget != widget) {
2704 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2706 drag_and_drop_from_header_view (source_model,
2711 DndHelper *helper = NULL;
2713 /* Get the source model and row */
2714 gtk_tree_get_row_drag_data (selection_data,
2718 /* Create the helper */
2719 helper = g_slice_new0 (DndHelper);
2720 helper->delete_source = delete_source;
2721 helper->source_row = gtk_tree_path_copy (source_row);
2722 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2724 drag_and_drop_from_folder_view (source_model,
2730 gtk_tree_path_free (source_row);
2734 gtk_tree_path_free (dest_row);
2737 /* Finish the drag and drop */
2738 gtk_drag_finish (context, success, FALSE, time);
2742 * We define a "drag-drop" signal handler because we do not want to
2743 * use the default one, because the default one always calls
2744 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2745 * signal handler, because there we have all the information available
2746 * to know if the dnd was a success or not.
2749 drag_drop_cb (GtkWidget *widget,
2750 GdkDragContext *context,
2758 if (!context->targets)
2761 /* Check if we're dragging a folder row */
2762 target = gtk_drag_dest_find_target (widget, context, NULL);
2764 /* Request the data from the source. */
2765 gtk_drag_get_data(widget, context, target, time);
2771 * This function expands a node of a tree view if it's not expanded
2772 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2773 * does that, so that's why they're here.
2776 expand_row_timeout (gpointer data)
2778 GtkTreeView *tree_view = data;
2779 GtkTreePath *dest_path = NULL;
2780 GtkTreeViewDropPosition pos;
2781 gboolean result = FALSE;
2783 gdk_threads_enter ();
2785 gtk_tree_view_get_drag_dest_row (tree_view,
2790 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2791 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2792 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2793 gtk_tree_path_free (dest_path);
2797 gtk_tree_path_free (dest_path);
2802 gdk_threads_leave ();
2808 * This function is called whenever the pointer is moved over a widget
2809 * while dragging some data. It installs a timeout that will expand a
2810 * node of the treeview if not expanded yet. This function also calls
2811 * gdk_drag_status in order to set the suggested action that will be
2812 * used by the "drag-data-received" signal handler to know if we
2813 * should do a move or just a copy of the data.
2816 on_drag_motion (GtkWidget *widget,
2817 GdkDragContext *context,
2823 GtkTreeViewDropPosition pos;
2824 GtkTreePath *dest_row;
2825 GtkTreeModel *dest_model;
2826 ModestFolderViewPrivate *priv;
2827 GdkDragAction suggested_action;
2828 gboolean valid_location = FALSE;
2829 TnyFolderStore *folder = NULL;
2831 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2833 if (priv->timer_expander != 0) {
2834 g_source_remove (priv->timer_expander);
2835 priv->timer_expander = 0;
2838 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2843 /* Do not allow drops between folders */
2845 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2846 pos == GTK_TREE_VIEW_DROP_AFTER) {
2847 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2848 gdk_drag_status(context, 0, time);
2849 valid_location = FALSE;
2852 valid_location = TRUE;
2855 /* Check that the destination folder is writable */
2856 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2857 folder = tree_path_to_folder (dest_model, dest_row);
2858 if (folder && TNY_IS_FOLDER (folder)) {
2859 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2861 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2862 valid_location = FALSE;
2867 /* Expand the selected row after 1/2 second */
2868 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2869 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2871 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2873 /* Select the desired action. By default we pick MOVE */
2874 suggested_action = GDK_ACTION_MOVE;
2876 if (context->actions == GDK_ACTION_COPY)
2877 gdk_drag_status(context, GDK_ACTION_COPY, time);
2878 else if (context->actions == GDK_ACTION_MOVE)
2879 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2880 else if (context->actions & suggested_action)
2881 gdk_drag_status(context, suggested_action, time);
2883 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2887 g_object_unref (folder);
2889 gtk_tree_path_free (dest_row);
2891 g_signal_stop_emission_by_name (widget, "drag-motion");
2893 return valid_location;
2897 * This function sets the treeview as a source and a target for dnd
2898 * events. It also connects all the requirede signals.
2901 setup_drag_and_drop (GtkTreeView *self)
2903 /* Set up the folder view as a dnd destination. Set only the
2904 highlight flag, otherwise gtk will have a different
2906 #ifdef MODEST_TOOLKIT_HILDON2
2909 gtk_drag_dest_set (GTK_WIDGET (self),
2910 GTK_DEST_DEFAULT_HIGHLIGHT,
2911 folder_view_drag_types,
2912 G_N_ELEMENTS (folder_view_drag_types),
2913 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2915 g_signal_connect (G_OBJECT (self),
2916 "drag_data_received",
2917 G_CALLBACK (on_drag_data_received),
2921 /* Set up the treeview as a dnd source */
2922 gtk_drag_source_set (GTK_WIDGET (self),
2924 folder_view_drag_types,
2925 G_N_ELEMENTS (folder_view_drag_types),
2926 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2928 g_signal_connect (G_OBJECT (self),
2930 G_CALLBACK (on_drag_motion),
2933 g_signal_connect (G_OBJECT (self),
2935 G_CALLBACK (on_drag_data_get),
2938 g_signal_connect (G_OBJECT (self),
2940 G_CALLBACK (drag_drop_cb),
2945 * This function manages the navigation through the folders using the
2946 * keyboard or the hardware keys in the device
2949 on_key_pressed (GtkWidget *self,
2953 GtkTreeSelection *selection;
2955 GtkTreeModel *model;
2956 gboolean retval = FALSE;
2958 /* Up and Down are automatically managed by the treeview */
2959 if (event->keyval == GDK_Return) {
2960 /* Expand/Collapse the selected row */
2961 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2962 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2965 path = gtk_tree_model_get_path (model, &iter);
2967 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2968 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2970 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2971 gtk_tree_path_free (path);
2973 /* No further processing */
2981 * We listen to the changes in the local folder account name key,
2982 * because we want to show the right name in the view. The local
2983 * folder account name corresponds to the device name in the Maemo
2984 * version. We do this because we do not want to query gconf on each
2985 * tree view refresh. It's better to cache it and change whenever
2989 on_configuration_key_changed (ModestConf* conf,
2991 ModestConfEvent event,
2992 ModestConfNotificationId id,
2993 ModestFolderView *self)
2995 ModestFolderViewPrivate *priv;
2998 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2999 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3001 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3002 g_free (priv->local_account_name);
3004 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3005 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3007 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3008 MODEST_CONF_DEVICE_NAME, NULL);
3010 /* Force a redraw */
3011 #if GTK_CHECK_VERSION(2, 8, 0)
3012 GtkTreeViewColumn * tree_column;
3014 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3016 gtk_tree_view_column_queue_resize (tree_column);
3018 gtk_widget_queue_draw (GTK_WIDGET (self));
3024 modest_folder_view_set_style (ModestFolderView *self,
3025 ModestFolderViewStyle style)
3027 ModestFolderViewPrivate *priv;
3029 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3030 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3031 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3033 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3036 priv->style = style;
3040 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3041 const gchar *account_id)
3043 ModestFolderViewPrivate *priv;
3044 GtkTreeModel *model;
3046 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3048 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3050 /* This will be used by the filter_row callback,
3051 * to decided which rows to show: */
3052 if (priv->visible_account_id) {
3053 g_free (priv->visible_account_id);
3054 priv->visible_account_id = NULL;
3057 priv->visible_account_id = g_strdup (account_id);
3060 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3061 if (GTK_IS_TREE_MODEL_FILTER (model))
3062 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3064 /* Save settings to gconf */
3065 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3066 MODEST_CONF_FOLDER_VIEW_KEY);
3070 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3072 ModestFolderViewPrivate *priv;
3074 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3076 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3078 return (const gchar *) priv->visible_account_id;
3082 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3086 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3088 gtk_tree_model_get (model, iter,
3092 gboolean result = FALSE;
3093 if (type == TNY_FOLDER_TYPE_INBOX) {
3097 *inbox_iter = *iter;
3101 if (gtk_tree_model_iter_children (model, &child, iter)) {
3102 if (find_inbox_iter (model, &child, inbox_iter))
3106 } while (gtk_tree_model_iter_next (model, iter));
3115 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3117 GtkTreeModel *model;
3118 GtkTreeIter iter, inbox_iter;
3119 GtkTreeSelection *sel;
3120 GtkTreePath *path = NULL;
3122 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3124 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3128 expand_root_items (self);
3129 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3131 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3132 g_warning ("%s: model is empty", __FUNCTION__);
3136 if (find_inbox_iter (model, &iter, &inbox_iter))
3137 path = gtk_tree_model_get_path (model, &inbox_iter);
3139 path = gtk_tree_path_new_first ();
3141 /* Select the row and free */
3142 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3143 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3144 gtk_tree_path_free (path);
3147 gtk_widget_grab_focus (GTK_WIDGET(self));
3153 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3158 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3159 TnyFolder* a_folder;
3162 gtk_tree_model_get (model, iter,
3163 INSTANCE_COLUMN, &a_folder,
3169 if (folder == a_folder) {
3170 g_object_unref (a_folder);
3171 *folder_iter = *iter;
3174 g_object_unref (a_folder);
3176 if (gtk_tree_model_iter_children (model, &child, iter)) {
3177 if (find_folder_iter (model, &child, folder_iter, folder))
3181 } while (gtk_tree_model_iter_next (model, iter));
3186 #ifndef MODEST_TOOLKIT_HILDON2
3188 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3191 ModestFolderView *self)
3193 ModestFolderViewPrivate *priv = NULL;
3194 GtkTreeSelection *sel;
3195 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3196 GObject *instance = NULL;
3198 if (!MODEST_IS_FOLDER_VIEW(self))
3201 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3203 priv->reexpand = TRUE;
3205 gtk_tree_model_get (tree_model, iter,
3207 INSTANCE_COLUMN, &instance,
3213 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3214 priv->folder_to_select = g_object_ref (instance);
3216 g_object_unref (instance);
3218 if (priv->folder_to_select) {
3220 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3223 path = gtk_tree_model_get_path (tree_model, iter);
3224 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3226 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3228 gtk_tree_selection_select_iter (sel, iter);
3229 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3231 gtk_tree_path_free (path);
3235 modest_folder_view_disable_next_folder_selection (self);
3237 /* Refilter the model */
3238 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3244 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3246 ModestFolderViewPrivate *priv;
3248 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3250 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3252 if (priv->folder_to_select)
3253 g_object_unref(priv->folder_to_select);
3255 priv->folder_to_select = NULL;
3259 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3260 gboolean after_change)
3262 GtkTreeModel *model;
3263 GtkTreeIter iter, folder_iter;
3264 GtkTreeSelection *sel;
3265 ModestFolderViewPrivate *priv = NULL;
3267 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3268 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3270 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3273 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3274 gtk_tree_selection_unselect_all (sel);
3276 if (priv->folder_to_select)
3277 g_object_unref(priv->folder_to_select);
3278 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3282 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3287 /* Refilter the model, before selecting the folder */
3288 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3290 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3291 g_warning ("%s: model is empty", __FUNCTION__);
3295 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3298 path = gtk_tree_model_get_path (model, &folder_iter);
3299 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3301 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3302 gtk_tree_selection_select_iter (sel, &folder_iter);
3303 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3305 gtk_tree_path_free (path);
3313 modest_folder_view_copy_selection (ModestFolderView *self)
3315 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3317 /* Copy selection */
3318 _clipboard_set_selected_data (self, FALSE);
3322 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3324 ModestFolderViewPrivate *priv = NULL;
3325 GtkTreeModel *model = NULL;
3326 const gchar **hidding = NULL;
3327 guint i, n_selected;
3329 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3330 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3332 /* Copy selection */
3333 if (!_clipboard_set_selected_data (folder_view, TRUE))
3336 /* Get hidding ids */
3337 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3339 /* Clear hidding array created by previous cut operation */
3340 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3342 /* Copy hidding array */
3343 priv->n_selected = n_selected;
3344 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3345 for (i=0; i < n_selected; i++)
3346 priv->hidding_ids[i] = g_strdup(hidding[i]);
3348 /* Hide cut folders */
3349 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3350 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3354 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3355 ModestFolderView *folder_view_dst)
3357 GtkTreeModel *filter_model = NULL;
3358 GtkTreeModel *model = NULL;
3359 GtkTreeModel *new_filter_model = NULL;
3361 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3362 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3365 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3366 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3368 /* Build new filter model */
3369 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3370 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3374 /* Set copied model */
3375 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3376 #ifndef MODEST_TOOLKIT_HILDON2
3377 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3378 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3382 g_object_unref (new_filter_model);
3386 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3389 GtkTreeModel *model = NULL;
3390 ModestFolderViewPrivate* priv;
3392 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3394 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3395 priv->show_non_move = show;
3396 /* modest_folder_view_update_model(folder_view, */
3397 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3399 /* Hide special folders */
3400 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3401 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3402 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3406 /* Returns FALSE if it did not selected anything */
3408 _clipboard_set_selected_data (ModestFolderView *folder_view,
3411 ModestFolderViewPrivate *priv = NULL;
3412 TnyFolderStore *folder = NULL;
3413 gboolean retval = FALSE;
3415 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3416 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3418 /* Set selected data on clipboard */
3419 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3420 folder = modest_folder_view_get_selected (folder_view);
3422 /* Do not allow to select an account */
3423 if (TNY_IS_FOLDER (folder)) {
3424 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3429 g_object_unref (folder);
3435 _clear_hidding_filter (ModestFolderView *folder_view)
3437 ModestFolderViewPrivate *priv;
3440 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3441 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3443 if (priv->hidding_ids != NULL) {
3444 for (i=0; i < priv->n_selected; i++)
3445 g_free (priv->hidding_ids[i]);
3446 g_free(priv->hidding_ids);
3452 on_display_name_changed (ModestAccountMgr *mgr,
3453 const gchar *account,
3456 ModestFolderView *self;
3458 self = MODEST_FOLDER_VIEW (user_data);
3460 /* Force a redraw */
3461 #if GTK_CHECK_VERSION(2, 8, 0)
3462 GtkTreeViewColumn * tree_column;
3464 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3466 gtk_tree_view_column_queue_resize (tree_column);
3468 gtk_widget_queue_draw (GTK_WIDGET (self));
3473 modest_folder_view_set_cell_style (ModestFolderView *self,
3474 ModestFolderViewCellStyle cell_style)
3476 ModestFolderViewPrivate *priv = NULL;
3478 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3479 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3481 priv->cell_style = cell_style;
3483 g_object_set (G_OBJECT (priv->messages_renderer),
3484 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3487 gtk_widget_queue_draw (GTK_WIDGET (self));
3491 update_style (ModestFolderView *self)
3493 ModestFolderViewPrivate *priv;
3494 GdkColor style_color;
3496 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3497 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3499 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3500 gdk_color_parse ("grey", &style_color);
3503 g_object_set (G_OBJECT (priv->messages_renderer),
3504 "foreground-gdk", &style_color,
3505 "foreground-set", TRUE,
3510 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3512 if (strcmp ("style", spec->name) == 0) {
3513 update_style (MODEST_FOLDER_VIEW (obj));
3514 gtk_widget_queue_draw (GTK_WIDGET (obj));
3519 modest_folder_view_set_filter (ModestFolderView *self,
3520 ModestFolderViewFilter filter)
3522 ModestFolderViewPrivate *priv;
3523 GtkTreeModel *filter_model;
3525 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3526 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3528 priv->filter |= filter;
3530 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3531 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3532 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3537 modest_folder_view_unset_filter (ModestFolderView *self,
3538 ModestFolderViewFilter filter)
3540 ModestFolderViewPrivate *priv;
3541 GtkTreeModel *filter_model;
3543 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3544 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3546 priv->filter &= ~filter;
3548 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3549 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3550 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));