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);
192 static gboolean inbox_is_special (TnyFolderStore *folder_store);
194 static gboolean get_inner_models (ModestFolderView *self,
195 GtkTreeModel **filter_model,
196 GtkTreeModel **sort_model,
197 GtkTreeModel **tny_model);
200 FOLDER_SELECTION_CHANGED_SIGNAL,
201 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
202 FOLDER_ACTIVATED_SIGNAL,
206 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
207 struct _ModestFolderViewPrivate {
208 TnyAccountStore *account_store;
209 TnyFolderStore *cur_folder_store;
211 TnyFolder *folder_to_select; /* folder to select after the next update */
213 gulong changed_signal;
214 gulong account_inserted_signal;
215 gulong account_removed_signal;
216 gulong account_changed_signal;
217 gulong conf_key_signal;
218 gulong display_name_changed_signal;
220 /* not unref this object, its a singlenton */
221 ModestEmailClipboard *clipboard;
223 /* Filter tree model */
226 ModestFolderViewFilter filter;
228 TnyFolderStoreQuery *query;
229 guint timer_expander;
231 gchar *local_account_name;
232 gchar *visible_account_id;
233 ModestFolderViewStyle style;
234 ModestFolderViewCellStyle cell_style;
236 gboolean reselect; /* we use this to force a reselection of the INBOX */
237 gboolean show_non_move;
238 gboolean reexpand; /* next time we expose, we'll expand all root folders */
240 GtkCellRenderer *messages_renderer;
242 gulong outbox_deleted_handler;
244 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
245 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
246 MODEST_TYPE_FOLDER_VIEW, \
247 ModestFolderViewPrivate))
249 static GObjectClass *parent_class = NULL;
251 static guint signals[LAST_SIGNAL] = {0};
254 modest_folder_view_get_type (void)
256 static GType my_type = 0;
258 static const GTypeInfo my_info = {
259 sizeof(ModestFolderViewClass),
260 NULL, /* base init */
261 NULL, /* base finalize */
262 (GClassInitFunc) modest_folder_view_class_init,
263 NULL, /* class finalize */
264 NULL, /* class data */
265 sizeof(ModestFolderView),
267 (GInstanceInitFunc) modest_folder_view_init,
271 static const GInterfaceInfo tny_account_store_view_info = {
272 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
273 NULL, /* interface_finalize */
274 NULL /* interface_data */
278 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
282 g_type_add_interface_static (my_type,
283 TNY_TYPE_ACCOUNT_STORE_VIEW,
284 &tny_account_store_view_info);
290 modest_folder_view_class_init (ModestFolderViewClass *klass)
292 GObjectClass *gobject_class;
293 GtkTreeViewClass *treeview_class;
294 gobject_class = (GObjectClass*) klass;
295 treeview_class = (GtkTreeViewClass*) klass;
297 parent_class = g_type_class_peek_parent (klass);
298 gobject_class->finalize = modest_folder_view_finalize;
300 g_type_class_add_private (gobject_class,
301 sizeof(ModestFolderViewPrivate));
303 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
304 g_signal_new ("folder_selection_changed",
305 G_TYPE_FROM_CLASS (gobject_class),
307 G_STRUCT_OFFSET (ModestFolderViewClass,
308 folder_selection_changed),
310 modest_marshal_VOID__POINTER_BOOLEAN,
311 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
314 * This signal is emitted whenever the currently selected
315 * folder display name is computed. Note that the name could
316 * be different to the folder name, because we could append
317 * the unread messages count to the folder name to build the
318 * folder display name
320 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
321 g_signal_new ("folder-display-name-changed",
322 G_TYPE_FROM_CLASS (gobject_class),
324 G_STRUCT_OFFSET (ModestFolderViewClass,
325 folder_display_name_changed),
327 g_cclosure_marshal_VOID__STRING,
328 G_TYPE_NONE, 1, G_TYPE_STRING);
330 signals[FOLDER_ACTIVATED_SIGNAL] =
331 g_signal_new ("folder_activated",
332 G_TYPE_FROM_CLASS (gobject_class),
334 G_STRUCT_OFFSET (ModestFolderViewClass,
337 g_cclosure_marshal_VOID__POINTER,
338 G_TYPE_NONE, 1, G_TYPE_POINTER);
340 treeview_class->select_cursor_parent = NULL;
342 #ifdef MODEST_TOOLKIT_HILDON2
343 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
349 /* Retrieves the filter, sort and tny models of the folder view. If
350 any of these does not exist then it returns FALSE */
352 get_inner_models (ModestFolderView *self,
353 GtkTreeModel **filter_model,
354 GtkTreeModel **sort_model,
355 GtkTreeModel **tny_model)
357 GtkTreeModel *s_model, *f_model, *t_model;
359 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
360 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
361 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
365 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
366 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
367 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
371 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
375 *filter_model = f_model;
377 *sort_model = s_model;
379 *tny_model = t_model;
384 /* Simplify checks for NULLs: */
386 strings_are_equal (const gchar *a, const gchar *b)
392 return (strcmp (a, b) == 0);
399 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
401 GObject *instance = NULL;
403 gtk_tree_model_get (model, iter,
404 INSTANCE_COLUMN, &instance,
408 return FALSE; /* keep walking */
410 if (!TNY_IS_ACCOUNT (instance)) {
411 g_object_unref (instance);
412 return FALSE; /* keep walking */
415 /* Check if this is the looked-for account: */
416 TnyAccount *this_account = TNY_ACCOUNT (instance);
417 TnyAccount *account = TNY_ACCOUNT (data);
419 const gchar *this_account_id = tny_account_get_id(this_account);
420 const gchar *account_id = tny_account_get_id(account);
421 g_object_unref (instance);
424 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
425 if (strings_are_equal(this_account_id, account_id)) {
426 /* Tell the model that the data has changed, so that
427 * it calls the cell_data_func callbacks again: */
428 /* TODO: This does not seem to actually cause the new string to be shown: */
429 gtk_tree_model_row_changed (model, path, iter);
431 return TRUE; /* stop walking */
434 return FALSE; /* keep walking */
439 ModestFolderView *self;
440 gchar *previous_name;
441 } GetMmcAccountNameData;
444 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
446 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
448 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
450 if (!strings_are_equal (
451 tny_account_get_name(TNY_ACCOUNT(account)),
452 data->previous_name)) {
454 /* Tell the model that the data has changed, so that
455 * it calls the cell_data_func callbacks again: */
456 ModestFolderView *self = data->self;
457 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
459 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
462 g_free (data->previous_name);
463 g_slice_free (GetMmcAccountNameData, data);
467 convert_parent_folders_to_dots (gchar **item_name)
471 gchar *last_separator;
473 if (item_name == NULL)
476 for (c = *item_name; *c != '\0'; c++) {
477 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
482 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
483 if (last_separator != NULL) {
484 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
491 buffer = g_string_new ("");
492 for (i = 0; i < n_parents; i++) {
493 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
495 buffer = g_string_append (buffer, last_separator);
497 *item_name = g_string_free (buffer, FALSE);
503 format_compact_style (gchar **item_name,
506 gboolean multiaccount,
507 gboolean *use_markup)
511 TnyFolderType folder_type;
513 if (!TNY_IS_FOLDER (instance))
516 folder = (TnyFolder *) instance;
518 folder_type = tny_folder_get_folder_type (folder);
519 is_special = (get_cmp_pos (folder_type, folder)!= 4);
521 if (!is_special || multiaccount) {
522 TnyAccount *account = tny_folder_get_account (folder);
523 const gchar *folder_name;
524 gboolean concat_folder_name = FALSE;
527 /* Should not happen */
531 /* convert parent folders to dots */
532 convert_parent_folders_to_dots (item_name);
534 folder_name = tny_folder_get_name (folder);
535 if (g_str_has_suffix (*item_name, folder_name)) {
536 gchar *offset = g_strrstr (*item_name, folder_name);
538 concat_folder_name = TRUE;
541 buffer = g_string_new ("");
543 buffer = g_string_append (buffer, *item_name);
544 if (concat_folder_name) {
545 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
546 buffer = g_string_append (buffer, folder_name);
547 if (bold) buffer = g_string_append (buffer, "</span>");
550 g_object_unref (account);
552 *item_name = g_string_free (buffer, FALSE);
560 text_cell_data (GtkTreeViewColumn *column,
561 GtkCellRenderer *renderer,
562 GtkTreeModel *tree_model,
566 ModestFolderViewPrivate *priv;
567 GObject *rendobj = (GObject *) renderer;
569 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
570 GObject *instance = NULL;
571 gboolean use_markup = FALSE;
573 gtk_tree_model_get (tree_model, iter,
576 INSTANCE_COLUMN, &instance,
578 if (!fname || !instance)
581 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
582 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
584 gchar *item_name = NULL;
585 gint item_weight = 400;
587 if (type != TNY_FOLDER_TYPE_ROOT) {
591 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
592 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
593 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
594 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
596 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
599 /* Sometimes an special folder is reported by the server as
600 NORMAL, like some versions of Dovecot */
601 if (type == TNY_FOLDER_TYPE_NORMAL ||
602 type == TNY_FOLDER_TYPE_UNKNOWN) {
603 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
607 if (type == TNY_FOLDER_TYPE_INBOX) {
608 if (inbox_is_special (TNY_FOLDER_STORE (instance))) {
610 fname = g_strdup (_("mcen_me_folder_inbox"));
614 /* note: we cannot reliably get the counts from the tree model, we need
615 * to use explicit calls on tny_folder for some reason.
617 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
618 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
619 (type == TNY_FOLDER_TYPE_OUTBOX) ||
620 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
621 number = tny_folder_get_all_count (TNY_FOLDER(instance));
624 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
628 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
629 item_name = g_strdup (fname);
636 /* Use bold font style if there are unread or unset messages */
638 item_name = g_strdup_printf ("%s (%d)", fname, number);
641 item_name = g_strdup (fname);
646 } else if (TNY_IS_ACCOUNT (instance)) {
647 /* If it's a server account */
648 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
649 item_name = g_strdup (priv->local_account_name);
651 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
652 /* fname is only correct when the items are first
653 * added to the model, not when the account is
654 * changed later, so get the name from the account
656 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
659 item_name = g_strdup (fname);
665 item_name = g_strdup ("unknown");
667 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
668 gboolean multiaccount;
670 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
671 /* Convert item_name to markup */
672 format_compact_style (&item_name, instance,
674 multiaccount, &use_markup);
677 if (item_name && item_weight) {
678 /* Set the name in the treeview cell: */
680 g_object_set (rendobj, "markup", item_name, NULL);
682 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
684 /* Notify display name observers */
685 /* TODO: What listens for this signal, and how can it use only the new name? */
686 if (((GObject *) priv->cur_folder_store) == instance) {
687 g_signal_emit (G_OBJECT(self),
688 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
695 /* If it is a Memory card account, make sure that we have the correct name.
696 * This function will be trigerred again when the name has been retrieved: */
697 if (TNY_IS_STORE_ACCOUNT (instance) &&
698 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
700 /* Get the account name asynchronously: */
701 GetMmcAccountNameData *callback_data =
702 g_slice_new0(GetMmcAccountNameData);
703 callback_data->self = self;
705 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
707 callback_data->previous_name = g_strdup (name);
709 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
710 on_get_mmc_account_name, callback_data);
714 g_object_unref (G_OBJECT (instance));
720 messages_cell_data (GtkTreeViewColumn *column,
721 GtkCellRenderer *renderer,
722 GtkTreeModel *tree_model,
726 ModestFolderView *self;
727 ModestFolderViewPrivate *priv;
728 GObject *rendobj = (GObject *) renderer;
729 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
730 GObject *instance = NULL;
731 gchar *item_name = NULL;
733 gtk_tree_model_get (tree_model, iter,
735 INSTANCE_COLUMN, &instance,
740 self = MODEST_FOLDER_VIEW (data);
741 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
744 if (type != TNY_FOLDER_TYPE_ROOT) {
748 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
749 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
750 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
752 /* Sometimes an special folder is reported by the server as
753 NORMAL, like some versions of Dovecot */
754 if (type == TNY_FOLDER_TYPE_NORMAL ||
755 type == TNY_FOLDER_TYPE_UNKNOWN) {
756 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
760 /* note: we cannot reliably get the counts from the tree model, we need
761 * to use explicit calls on tny_folder for some reason.
763 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
764 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
765 (type == TNY_FOLDER_TYPE_OUTBOX) ||
766 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
767 number = tny_folder_get_all_count (TNY_FOLDER(instance));
770 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
774 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
776 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
784 item_name = g_strdup ("");
787 /* Set the name in the treeview cell: */
788 g_object_set (rendobj,"text", item_name, NULL);
796 g_object_unref (G_OBJECT (instance));
802 GdkPixbuf *pixbuf_open;
803 GdkPixbuf *pixbuf_close;
807 static inline GdkPixbuf *
808 get_composite_pixbuf (const gchar *icon_name,
810 GdkPixbuf *base_pixbuf)
812 GdkPixbuf *emblem, *retval = NULL;
814 emblem = modest_platform_get_icon (icon_name, size);
816 retval = gdk_pixbuf_copy (base_pixbuf);
817 gdk_pixbuf_composite (emblem, retval, 0, 0,
818 MIN (gdk_pixbuf_get_width (emblem),
819 gdk_pixbuf_get_width (retval)),
820 MIN (gdk_pixbuf_get_height (emblem),
821 gdk_pixbuf_get_height (retval)),
822 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
823 g_object_unref (emblem);
828 static inline ThreePixbufs *
829 get_composite_icons (const gchar *icon_code,
831 GdkPixbuf **pixbuf_open,
832 GdkPixbuf **pixbuf_close)
834 ThreePixbufs *retval;
837 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
840 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
845 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
849 retval = g_slice_new0 (ThreePixbufs);
851 retval->pixbuf = g_object_ref (*pixbuf);
853 retval->pixbuf_open = g_object_ref (*pixbuf_open);
855 retval->pixbuf_close = g_object_ref (*pixbuf_close);
860 static inline ThreePixbufs*
861 get_folder_icons (TnyFolderType type, GObject *instance)
863 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
864 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
865 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
866 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
867 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
869 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
870 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
871 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
872 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
873 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
875 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
876 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
877 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
878 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
879 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
881 ThreePixbufs *retval = NULL;
883 /* Sometimes an special folder is reported by the server as
884 NORMAL, like some versions of Dovecot */
885 if (type == TNY_FOLDER_TYPE_NORMAL ||
886 type == TNY_FOLDER_TYPE_UNKNOWN) {
887 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
890 /* Remote folders should not be treated as special folders */
891 if (TNY_IS_FOLDER_STORE (instance) &&
892 !TNY_IS_ACCOUNT (instance) &&
893 type != TNY_FOLDER_TYPE_INBOX &&
894 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
895 #ifdef MODEST_TOOLKIT_HILDON2
896 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
899 &anorm_pixbuf_close);
901 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
904 &normal_pixbuf_close);
910 case TNY_FOLDER_TYPE_INVALID:
911 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
914 case TNY_FOLDER_TYPE_ROOT:
915 if (TNY_IS_ACCOUNT (instance)) {
917 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
918 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
921 &avirt_pixbuf_close);
923 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
925 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
926 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
931 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
934 &anorm_pixbuf_close);
939 case TNY_FOLDER_TYPE_INBOX:
940 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
943 &inbox_pixbuf_close);
945 case TNY_FOLDER_TYPE_OUTBOX:
946 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
949 &outbox_pixbuf_close);
951 case TNY_FOLDER_TYPE_JUNK:
952 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
957 case TNY_FOLDER_TYPE_SENT:
958 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
963 case TNY_FOLDER_TYPE_TRASH:
964 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
967 &trash_pixbuf_close);
969 case TNY_FOLDER_TYPE_DRAFTS:
970 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
973 &draft_pixbuf_close);
975 case TNY_FOLDER_TYPE_ARCHIVE:
976 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
981 case TNY_FOLDER_TYPE_NORMAL:
983 /* Memory card folders could have an special icon */
984 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
985 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
990 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
993 &normal_pixbuf_close);
1002 free_pixbufs (ThreePixbufs *pixbufs)
1004 if (pixbufs->pixbuf)
1005 g_object_unref (pixbufs->pixbuf);
1006 if (pixbufs->pixbuf_open)
1007 g_object_unref (pixbufs->pixbuf_open);
1008 if (pixbufs->pixbuf_close)
1009 g_object_unref (pixbufs->pixbuf_close);
1010 g_slice_free (ThreePixbufs, pixbufs);
1014 icon_cell_data (GtkTreeViewColumn *column,
1015 GtkCellRenderer *renderer,
1016 GtkTreeModel *tree_model,
1020 GObject *rendobj = NULL, *instance = NULL;
1021 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1022 gboolean has_children;
1023 ThreePixbufs *pixbufs;
1025 rendobj = (GObject *) renderer;
1027 gtk_tree_model_get (tree_model, iter,
1029 INSTANCE_COLUMN, &instance,
1035 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1036 pixbufs = get_folder_icons (type, instance);
1037 g_object_unref (instance);
1040 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1043 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1044 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1047 free_pixbufs (pixbufs);
1051 add_columns (GtkWidget *treeview)
1053 GtkTreeViewColumn *column;
1054 GtkCellRenderer *renderer;
1055 GtkTreeSelection *sel;
1056 ModestFolderViewPrivate *priv;
1058 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1061 column = gtk_tree_view_column_new ();
1063 /* Set icon and text render function */
1064 renderer = gtk_cell_renderer_pixbuf_new();
1065 #ifdef MODEST_TOOLKIT_HILDON2
1066 g_object_set (renderer,
1067 "xpad", MODEST_MARGIN_DEFAULT,
1068 "ypad", MODEST_MARGIN_DEFAULT,
1071 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1072 gtk_tree_view_column_set_cell_data_func(column, renderer,
1073 icon_cell_data, treeview, NULL);
1075 renderer = gtk_cell_renderer_text_new();
1076 g_object_set (renderer,
1077 #ifdef MODEST_TOOLKIT_HILDON2
1078 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1079 "ypad", MODEST_MARGIN_DEFAULT,
1081 "ellipsize", PANGO_ELLIPSIZE_END,
1083 "ellipsize-set", TRUE, NULL);
1084 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1085 gtk_tree_view_column_set_cell_data_func(column, renderer,
1086 text_cell_data, treeview, NULL);
1088 priv->messages_renderer = gtk_cell_renderer_text_new ();
1089 g_object_set (priv->messages_renderer,
1090 #ifdef MODEST_TOOLKIT_HILDON2
1092 "ypad", MODEST_MARGIN_DEFAULT,
1093 "xpad", MODEST_MARGIN_DOUBLE,
1095 "scale", PANGO_SCALE_X_SMALL,
1098 "alignment", PANGO_ALIGN_RIGHT,
1102 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1103 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1104 messages_cell_data, treeview, NULL);
1106 /* Set selection mode */
1107 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1108 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1110 /* Set treeview appearance */
1111 gtk_tree_view_column_set_spacing (column, 2);
1112 gtk_tree_view_column_set_resizable (column, TRUE);
1113 gtk_tree_view_column_set_fixed_width (column, TRUE);
1114 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1115 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1118 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1122 modest_folder_view_init (ModestFolderView *obj)
1124 ModestFolderViewPrivate *priv;
1127 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1129 priv->timer_expander = 0;
1130 priv->account_store = NULL;
1132 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1133 priv->cur_folder_store = NULL;
1134 priv->visible_account_id = NULL;
1135 priv->folder_to_select = NULL;
1136 priv->outbox_deleted_handler = 0;
1137 priv->reexpand = TRUE;
1139 /* Initialize the local account name */
1140 conf = modest_runtime_get_conf();
1141 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1143 /* Init email clipboard */
1144 priv->clipboard = modest_runtime_get_email_clipboard ();
1145 priv->hidding_ids = NULL;
1146 priv->n_selected = 0;
1147 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1148 priv->reselect = FALSE;
1149 priv->show_non_move = TRUE;
1151 /* Build treeview */
1152 add_columns (GTK_WIDGET (obj));
1154 /* Setup drag and drop */
1155 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1157 /* Connect signals */
1158 g_signal_connect (G_OBJECT (obj),
1160 G_CALLBACK (on_key_pressed), NULL);
1162 priv->display_name_changed_signal =
1163 g_signal_connect (modest_runtime_get_account_mgr (),
1164 "display_name_changed",
1165 G_CALLBACK (on_display_name_changed),
1169 * Track changes in the local account name (in the device it
1170 * will be the device name)
1172 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1174 G_CALLBACK(on_configuration_key_changed),
1178 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1184 tny_account_store_view_init (gpointer g, gpointer iface_data)
1186 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1188 klass->set_account_store = modest_folder_view_set_account_store;
1192 modest_folder_view_finalize (GObject *obj)
1194 ModestFolderViewPrivate *priv;
1195 GtkTreeSelection *sel;
1196 TnyAccount *local_account;
1198 g_return_if_fail (obj);
1200 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1202 if (priv->timer_expander != 0) {
1203 g_source_remove (priv->timer_expander);
1204 priv->timer_expander = 0;
1207 local_account = (TnyAccount *)
1208 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1209 if (local_account) {
1210 if (g_signal_handler_is_connected (local_account,
1211 priv->outbox_deleted_handler))
1212 g_signal_handler_disconnect (local_account,
1213 priv->outbox_deleted_handler);
1214 g_object_unref (local_account);
1217 if (priv->account_store) {
1218 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1219 priv->account_inserted_signal);
1220 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1221 priv->account_removed_signal);
1222 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1223 priv->account_changed_signal);
1224 g_object_unref (G_OBJECT(priv->account_store));
1225 priv->account_store = NULL;
1228 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1229 priv->display_name_changed_signal)) {
1230 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1231 priv->display_name_changed_signal);
1232 priv->display_name_changed_signal = 0;
1236 g_object_unref (G_OBJECT (priv->query));
1240 if (priv->folder_to_select) {
1241 g_object_unref (G_OBJECT(priv->folder_to_select));
1242 priv->folder_to_select = NULL;
1245 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1247 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1249 g_free (priv->local_account_name);
1250 g_free (priv->visible_account_id);
1252 if (priv->conf_key_signal) {
1253 g_signal_handler_disconnect (modest_runtime_get_conf (),
1254 priv->conf_key_signal);
1255 priv->conf_key_signal = 0;
1258 if (priv->cur_folder_store) {
1259 g_object_unref (priv->cur_folder_store);
1260 priv->cur_folder_store = NULL;
1263 /* Clear hidding array created by cut operation */
1264 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1266 G_OBJECT_CLASS(parent_class)->finalize (obj);
1271 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1273 ModestFolderViewPrivate *priv;
1276 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1277 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1279 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1280 device = tny_account_store_get_device (account_store);
1282 if (G_UNLIKELY (priv->account_store)) {
1284 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1285 priv->account_inserted_signal))
1286 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1287 priv->account_inserted_signal);
1288 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1289 priv->account_removed_signal))
1290 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1291 priv->account_removed_signal);
1292 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1293 priv->account_changed_signal))
1294 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1295 priv->account_changed_signal);
1296 g_object_unref (G_OBJECT (priv->account_store));
1299 priv->account_store = g_object_ref (G_OBJECT (account_store));
1301 priv->account_removed_signal =
1302 g_signal_connect (G_OBJECT(account_store), "account_removed",
1303 G_CALLBACK (on_account_removed), self);
1305 priv->account_inserted_signal =
1306 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1307 G_CALLBACK (on_account_inserted), self);
1309 priv->account_changed_signal =
1310 g_signal_connect (G_OBJECT(account_store), "account_changed",
1311 G_CALLBACK (on_account_changed), self);
1313 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1314 priv->reselect = FALSE;
1315 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1317 g_object_unref (G_OBJECT (device));
1321 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1324 ModestFolderView *self;
1325 GtkTreeModel *model, *filter_model;
1328 self = MODEST_FOLDER_VIEW (user_data);
1330 if (!get_inner_models (self, &filter_model, NULL, &model))
1333 /* Remove outbox from model */
1334 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1335 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1336 g_object_unref (outbox);
1339 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1343 on_account_inserted (TnyAccountStore *account_store,
1344 TnyAccount *account,
1347 ModestFolderViewPrivate *priv;
1348 GtkTreeModel *model, *filter_model;
1350 /* Ignore transport account insertions, we're not showing them
1351 in the folder view */
1352 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1355 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1358 /* If we're adding a new account, and there is no previous
1359 one, we need to select the visible server account */
1360 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1361 !priv->visible_account_id)
1362 modest_widget_memory_restore (modest_runtime_get_conf(),
1363 G_OBJECT (user_data),
1364 MODEST_CONF_FOLDER_VIEW_KEY);
1368 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1369 &filter_model, NULL, &model))
1372 /* Insert the account in the model */
1373 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1375 /* When the model is a list store (plain representation) the
1376 outbox is not a child of any account so we have to manually
1377 delete it because removing the local folders account won't
1378 delete it (because tny_folder_get_account() is not defined
1379 for a merge folder */
1380 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1381 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1383 priv->outbox_deleted_handler =
1384 g_signal_connect (account,
1386 G_CALLBACK (on_outbox_deleted_cb),
1390 /* Refilter the model */
1391 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1396 same_account_selected (ModestFolderView *self,
1397 TnyAccount *account)
1399 ModestFolderViewPrivate *priv;
1400 gboolean same_account = FALSE;
1402 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1404 if (priv->cur_folder_store) {
1405 TnyAccount *selected_folder_account = NULL;
1407 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1408 selected_folder_account =
1409 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1411 selected_folder_account =
1412 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1415 if (selected_folder_account == account)
1416 same_account = TRUE;
1418 g_object_unref (selected_folder_account);
1420 return same_account;
1425 * Selects the first inbox or the local account in an idle
1428 on_idle_select_first_inbox_or_local (gpointer user_data)
1430 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1432 gdk_threads_enter ();
1433 modest_folder_view_select_first_inbox_or_local (self);
1434 gdk_threads_leave ();
1440 on_account_changed (TnyAccountStore *account_store,
1441 TnyAccount *tny_account,
1444 ModestFolderView *self;
1445 ModestFolderViewPrivate *priv;
1446 GtkTreeModel *model, *filter_model;
1447 GtkTreeSelection *sel;
1448 gboolean same_account;
1450 /* Ignore transport account insertions, we're not showing them
1451 in the folder view */
1452 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1455 self = MODEST_FOLDER_VIEW (user_data);
1456 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1458 /* Get the inner model */
1459 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1460 &filter_model, NULL, &model))
1463 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1465 /* Invalidate the cur_folder_store only if the selected folder
1466 belongs to the account that is being removed */
1467 same_account = same_account_selected (self, tny_account);
1469 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1470 gtk_tree_selection_unselect_all (sel);
1473 /* Remove the account from the model */
1474 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1476 /* Insert the account in the model */
1477 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1479 /* Refilter the model */
1480 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1482 /* Select the first INBOX if the currently selected folder
1483 belongs to the account that is being deleted */
1484 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1485 g_idle_add (on_idle_select_first_inbox_or_local, self);
1489 on_account_removed (TnyAccountStore *account_store,
1490 TnyAccount *account,
1493 ModestFolderView *self = NULL;
1494 ModestFolderViewPrivate *priv;
1495 GtkTreeModel *model, *filter_model;
1496 GtkTreeSelection *sel = NULL;
1497 gboolean same_account = FALSE;
1499 /* Ignore transport account removals, we're not showing them
1500 in the folder view */
1501 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1504 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1505 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1509 self = MODEST_FOLDER_VIEW (user_data);
1510 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1512 /* Invalidate the cur_folder_store only if the selected folder
1513 belongs to the account that is being removed */
1514 same_account = same_account_selected (self, account);
1516 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1517 gtk_tree_selection_unselect_all (sel);
1520 /* Invalidate row to select only if the folder to select
1521 belongs to the account that is being removed*/
1522 if (priv->folder_to_select) {
1523 TnyAccount *folder_to_select_account = NULL;
1525 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1526 if (folder_to_select_account == account) {
1527 modest_folder_view_disable_next_folder_selection (self);
1528 g_object_unref (priv->folder_to_select);
1529 priv->folder_to_select = NULL;
1531 g_object_unref (folder_to_select_account);
1534 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1535 &filter_model, NULL, &model))
1538 /* Disconnect the signal handler */
1539 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1540 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1541 if (g_signal_handler_is_connected (account,
1542 priv->outbox_deleted_handler))
1543 g_signal_handler_disconnect (account,
1544 priv->outbox_deleted_handler);
1547 /* Remove the account from the model */
1548 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1550 /* If the removed account is the currently viewed one then
1551 clear the configuration value. The new visible account will be the default account */
1552 if (priv->visible_account_id &&
1553 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1555 /* Clear the current visible account_id */
1556 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1558 /* Call the restore method, this will set the new visible account */
1559 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1560 MODEST_CONF_FOLDER_VIEW_KEY);
1563 /* Refilter the model */
1564 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1566 /* Select the first INBOX if the currently selected folder
1567 belongs to the account that is being deleted */
1569 g_idle_add (on_idle_select_first_inbox_or_local, self);
1573 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1575 GtkTreeViewColumn *col;
1577 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1579 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1581 g_printerr ("modest: failed get column for title\n");
1585 gtk_tree_view_column_set_title (col, title);
1586 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1591 modest_folder_view_on_map (ModestFolderView *self,
1592 GdkEventExpose *event,
1595 ModestFolderViewPrivate *priv;
1597 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1599 /* This won't happen often */
1600 if (G_UNLIKELY (priv->reselect)) {
1601 /* Select the first inbox or the local account if not found */
1603 /* TODO: this could cause a lock at startup, so we
1604 comment it for the moment. We know that this will
1605 be a bug, because the INBOX is not selected, but we
1606 need to rewrite some parts of Modest to avoid the
1607 deathlock situation */
1608 /* TODO: check if this is still the case */
1609 priv->reselect = FALSE;
1610 modest_folder_view_select_first_inbox_or_local (self);
1611 /* Notify the display name observers */
1612 g_signal_emit (G_OBJECT(self),
1613 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1617 if (priv->reexpand) {
1618 expand_root_items (self);
1619 priv->reexpand = FALSE;
1626 modest_folder_view_new (TnyFolderStoreQuery *query)
1629 ModestFolderViewPrivate *priv;
1630 GtkTreeSelection *sel;
1632 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1633 #ifdef MODEST_TOOLKIT_HILDON2
1634 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1637 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1640 priv->query = g_object_ref (query);
1642 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1643 priv->changed_signal = g_signal_connect (sel, "changed",
1644 G_CALLBACK (on_selection_changed), self);
1646 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1648 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1650 return GTK_WIDGET(self);
1653 /* this feels dirty; any other way to expand all the root items? */
1655 expand_root_items (ModestFolderView *self)
1658 GtkTreeModel *model;
1661 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1662 path = gtk_tree_path_new_first ();
1664 /* all folders should have child items, so.. */
1666 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1667 gtk_tree_path_next (path);
1668 } while (gtk_tree_model_get_iter (model, &iter, path));
1670 gtk_tree_path_free (path);
1674 * We use this function to implement the
1675 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1676 * account in this case, and the local folders.
1679 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1681 ModestFolderViewPrivate *priv;
1682 gboolean retval = TRUE;
1683 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1684 GObject *instance = NULL;
1685 const gchar *id = NULL;
1687 gboolean found = FALSE;
1688 gboolean cleared = FALSE;
1689 ModestTnyFolderRules rules = 0;
1691 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1692 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1694 gtk_tree_model_get (model, iter,
1696 INSTANCE_COLUMN, &instance,
1699 /* Do not show if there is no instance, this could indeed
1700 happen when the model is being modified while it's being
1701 drawn. This could occur for example when moving folders
1706 if (TNY_IS_ACCOUNT (instance)) {
1707 TnyAccount *acc = TNY_ACCOUNT (instance);
1708 const gchar *account_id = tny_account_get_id (acc);
1710 /* If it isn't a special folder,
1711 * don't show it unless it is the visible account: */
1712 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1713 !modest_tny_account_is_virtual_local_folders (acc) &&
1714 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1716 /* Show only the visible account id */
1717 if (priv->visible_account_id) {
1718 if (strcmp (account_id, priv->visible_account_id))
1725 /* Never show these to the user. They are merged into one folder
1726 * in the local-folders account instead: */
1727 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1730 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1731 /* Only show special folders for current account if needed */
1732 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1733 TnyAccount *account;
1735 account = tny_folder_get_account (TNY_FOLDER (instance));
1737 if (TNY_IS_ACCOUNT (account)) {
1738 const gchar *account_id = tny_account_get_id (account);
1740 if (!modest_tny_account_is_virtual_local_folders (account) &&
1741 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1742 /* Show only the visible account id */
1743 if (priv->visible_account_id) {
1744 if (strcmp (account_id, priv->visible_account_id))
1748 g_object_unref (account);
1755 /* Check hiding (if necessary) */
1756 cleared = modest_email_clipboard_cleared (priv->clipboard);
1757 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1758 id = tny_folder_get_id (TNY_FOLDER(instance));
1759 if (priv->hidding_ids != NULL)
1760 for (i=0; i < priv->n_selected && !found; i++)
1761 if (priv->hidding_ids[i] != NULL && id != NULL)
1762 found = (!strcmp (priv->hidding_ids[i], id));
1767 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1768 folder as no message can be move there according to UI specs */
1769 if (!priv->show_non_move) {
1770 if (TNY_IS_FOLDER (instance) &&
1771 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1773 case TNY_FOLDER_TYPE_OUTBOX:
1774 case TNY_FOLDER_TYPE_SENT:
1775 case TNY_FOLDER_TYPE_DRAFTS:
1778 case TNY_FOLDER_TYPE_UNKNOWN:
1779 case TNY_FOLDER_TYPE_NORMAL:
1780 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1781 if (type == TNY_FOLDER_TYPE_INVALID)
1782 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1784 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1785 type == TNY_FOLDER_TYPE_SENT
1786 || type == TNY_FOLDER_TYPE_DRAFTS)
1795 /* apply special filters */
1796 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1797 if (TNY_IS_ACCOUNT (instance))
1801 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1802 if (TNY_IS_FOLDER (instance)) {
1803 /* Check folder rules */
1804 ModestTnyFolderRules rules;
1806 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1807 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
1808 } else if (TNY_IS_ACCOUNT (instance)) {
1809 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1817 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1818 if (TNY_IS_FOLDER (instance)) {
1819 TnyFolderType guess_type;
1821 if (TNY_FOLDER_TYPE_NORMAL) {
1822 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1828 case TNY_FOLDER_TYPE_OUTBOX:
1829 case TNY_FOLDER_TYPE_SENT:
1830 case TNY_FOLDER_TYPE_DRAFTS:
1831 case TNY_FOLDER_TYPE_ARCHIVE:
1832 case TNY_FOLDER_TYPE_INBOX:
1835 case TNY_FOLDER_TYPE_UNKNOWN:
1836 case TNY_FOLDER_TYPE_NORMAL:
1842 } else if (TNY_IS_ACCOUNT (instance)) {
1847 if (retval && TNY_IS_FOLDER (instance)) {
1848 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1851 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1852 if (TNY_IS_FOLDER (instance)) {
1853 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1854 } else if (TNY_IS_ACCOUNT (instance)) {
1859 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1860 if (TNY_IS_FOLDER (instance)) {
1861 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1862 } else if (TNY_IS_ACCOUNT (instance)) {
1867 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1868 if (TNY_IS_FOLDER (instance)) {
1869 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1870 } else if (TNY_IS_ACCOUNT (instance)) {
1876 g_object_unref (instance);
1883 modest_folder_view_update_model (ModestFolderView *self,
1884 TnyAccountStore *account_store)
1886 ModestFolderViewPrivate *priv;
1887 GtkTreeModel *model /* , *old_model */;
1888 GtkTreeModel *filter_model = NULL, *sortable = NULL;
1890 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1891 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1894 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1896 /* Notify that there is no folder selected */
1897 g_signal_emit (G_OBJECT(self),
1898 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1900 if (priv->cur_folder_store) {
1901 g_object_unref (priv->cur_folder_store);
1902 priv->cur_folder_store = NULL;
1905 /* FIXME: the local accounts are not shown when the query
1906 selects only the subscribed folders */
1907 #ifdef MODEST_TOOLKIT_HILDON2
1908 model = tny_gtk_folder_list_store_new_with_flags (NULL,
1909 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1910 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1911 MODEST_FOLDER_PATH_SEPARATOR);
1913 model = tny_gtk_folder_store_tree_model_new (NULL);
1916 /* When the model is a list store (plain representation) the
1917 outbox is not a child of any account so we have to manually
1918 delete it because removing the local folders account won't
1919 delete it (because tny_folder_get_account() is not defined
1920 for a merge folder */
1921 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1922 TnyAccount *account;
1923 ModestTnyAccountStore *acc_store;
1925 acc_store = modest_runtime_get_account_store ();
1926 account = modest_tny_account_store_get_local_folders_account (acc_store);
1928 if (g_signal_handler_is_connected (account,
1929 priv->outbox_deleted_handler))
1930 g_signal_handler_disconnect (account,
1931 priv->outbox_deleted_handler);
1933 priv->outbox_deleted_handler =
1934 g_signal_connect (account,
1936 G_CALLBACK (on_outbox_deleted_cb),
1938 g_object_unref (account);
1941 /* Get the accounts: */
1942 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1944 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1946 sortable = gtk_tree_model_sort_new_with_model (model);
1947 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1949 GTK_SORT_ASCENDING);
1950 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1952 cmp_rows, NULL, NULL);
1954 /* Create filter model */
1955 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1956 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1962 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1963 #ifndef MODEST_TOOLKIT_HILDON2
1964 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1965 (GCallback) on_row_inserted_maybe_select_folder, self);
1968 g_object_unref (model);
1969 g_object_unref (filter_model);
1970 g_object_unref (sortable);
1972 /* Force a reselection of the INBOX next time the widget is shown */
1973 priv->reselect = TRUE;
1980 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1982 GtkTreeModel *model = NULL;
1983 TnyFolderStore *folder = NULL;
1985 ModestFolderView *tree_view = NULL;
1986 ModestFolderViewPrivate *priv = NULL;
1987 gboolean selected = FALSE;
1989 g_return_if_fail (sel);
1990 g_return_if_fail (user_data);
1992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1994 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1996 tree_view = MODEST_FOLDER_VIEW (user_data);
1999 gtk_tree_model_get (model, &iter,
2000 INSTANCE_COLUMN, &folder,
2003 /* If the folder is the same do not notify */
2004 if (folder && priv->cur_folder_store == folder) {
2005 g_object_unref (folder);
2010 /* Current folder was unselected */
2011 if (priv->cur_folder_store) {
2012 /* We must do this firstly because a libtinymail-camel
2013 implementation detail. If we issue the signal
2014 before doing the sync_async, then that signal could
2015 cause (and it actually does it) a free of the
2016 summary of the folder (because the main window will
2017 clear the headers view */
2018 if (TNY_IS_FOLDER(priv->cur_folder_store))
2019 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2020 FALSE, NULL, NULL, NULL);
2022 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2023 priv->cur_folder_store, FALSE);
2025 g_object_unref (priv->cur_folder_store);
2026 priv->cur_folder_store = NULL;
2029 /* New current references */
2030 priv->cur_folder_store = folder;
2032 /* New folder has been selected. Do not notify if there is
2033 nothing new selected */
2035 g_signal_emit (G_OBJECT(tree_view),
2036 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2037 0, priv->cur_folder_store, TRUE);
2042 on_row_activated (GtkTreeView *treeview,
2043 GtkTreePath *treepath,
2044 GtkTreeViewColumn *column,
2047 GtkTreeModel *model = NULL;
2048 TnyFolderStore *folder = NULL;
2050 ModestFolderView *self = NULL;
2051 ModestFolderViewPrivate *priv = NULL;
2053 g_return_if_fail (treeview);
2054 g_return_if_fail (user_data);
2056 self = MODEST_FOLDER_VIEW (user_data);
2057 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2059 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2061 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2064 gtk_tree_model_get (model, &iter,
2065 INSTANCE_COLUMN, &folder,
2068 g_signal_emit (G_OBJECT(self),
2069 signals[FOLDER_ACTIVATED_SIGNAL],
2072 #ifdef MODEST_TOOLKIT_HILDON2
2073 HildonUIMode ui_mode;
2074 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2075 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2076 if (priv->cur_folder_store)
2077 g_object_unref (priv->cur_folder_store);
2078 priv->cur_folder_store = g_object_ref (folder);
2082 g_object_unref (folder);
2086 modest_folder_view_get_selected (ModestFolderView *self)
2088 ModestFolderViewPrivate *priv;
2090 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2092 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2093 if (priv->cur_folder_store)
2094 g_object_ref (priv->cur_folder_store);
2096 return priv->cur_folder_store;
2100 get_cmp_rows_type_pos (GObject *folder)
2102 /* Remote accounts -> Local account -> MMC account .*/
2105 if (TNY_IS_ACCOUNT (folder) &&
2106 modest_tny_account_is_virtual_local_folders (
2107 TNY_ACCOUNT (folder))) {
2109 } else if (TNY_IS_ACCOUNT (folder)) {
2110 TnyAccount *account = TNY_ACCOUNT (folder);
2111 const gchar *account_id = tny_account_get_id (account);
2112 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2118 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2119 return -1; /* Should never happen */
2124 inbox_is_special (TnyFolderStore *folder_store)
2126 gboolean is_special = TRUE;
2128 if (TNY_IS_FOLDER (folder_store)) {
2132 gchar *last_inbox_bar;
2134 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2135 downcase = g_utf8_strdown (id, -1);
2136 last_bar = g_strrstr (downcase, "/");
2138 last_inbox_bar = g_strrstr (downcase, "inbox/");
2139 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2148 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2150 TnyAccount *account;
2151 gboolean is_special;
2152 /* Inbox, Outbox, Drafts, Sent, User */
2155 if (!TNY_IS_FOLDER (folder_store))
2158 case TNY_FOLDER_TYPE_INBOX:
2160 account = tny_folder_get_account (folder_store);
2161 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2163 /* In inbox case we need to know if the inbox is really the top
2164 * inbox of the account, or if it's a submailbox inbox. To do
2165 * this we'll apply an heuristic rule: Find last "/" and check
2166 * if it's preceeded by another Inbox */
2167 is_special = is_special && inbox_is_special (TNY_FOLDER_STORE (folder_store));
2168 g_object_unref (account);
2169 return is_special?0:4;
2172 case TNY_FOLDER_TYPE_OUTBOX:
2173 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2175 case TNY_FOLDER_TYPE_DRAFTS:
2177 account = tny_folder_get_account (folder_store);
2178 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2179 g_object_unref (account);
2180 return is_special?1:4;
2183 case TNY_FOLDER_TYPE_SENT:
2185 account = tny_folder_get_account (folder_store);
2186 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2187 g_object_unref (account);
2188 return is_special?3:4;
2197 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2199 const gchar *a1_name, *a2_name;
2201 a1_name = tny_account_get_name (a1);
2202 a2_name = tny_account_get_name (a2);
2204 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2208 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2210 TnyAccount *a1 = NULL, *a2 = NULL;
2213 if (TNY_IS_ACCOUNT (s1)) {
2214 a1 = TNY_ACCOUNT (g_object_ref (s1));
2215 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2216 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2219 if (TNY_IS_ACCOUNT (s2)) {
2220 a2 = TNY_ACCOUNT (g_object_ref (s2));
2221 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2222 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2239 /* First we sort with the type of account */
2240 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2244 cmp = compare_account_names (a1, a2);
2248 g_object_unref (a1);
2250 g_object_unref (a2);
2256 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2258 gint is_account1, is_account2;
2260 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2261 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2263 return is_account2 - is_account1;
2267 * This function orders the mail accounts according to these rules:
2268 * 1st - remote accounts
2269 * 2nd - local account
2273 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2277 gchar *name1 = NULL;
2278 gchar *name2 = NULL;
2279 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2280 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2281 GObject *folder1 = NULL;
2282 GObject *folder2 = NULL;
2284 gtk_tree_model_get (tree_model, iter1,
2285 NAME_COLUMN, &name1,
2287 INSTANCE_COLUMN, &folder1,
2289 gtk_tree_model_get (tree_model, iter2,
2290 NAME_COLUMN, &name2,
2291 TYPE_COLUMN, &type2,
2292 INSTANCE_COLUMN, &folder2,
2295 /* Return if we get no folder. This could happen when folder
2296 operations are happening. The model is updated after the
2297 folder copy/move actually occurs, so there could be
2298 situations where the model to be drawn is not correct */
2299 if (!folder1 || !folder2)
2302 /* Sort by type. First the special folders, then the archives */
2303 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2307 /* Now we sort using the account of each folder */
2308 if (TNY_IS_FOLDER_STORE (folder1) &&
2309 TNY_IS_FOLDER_STORE (folder2)) {
2310 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2314 /* Each group is preceeded by its account */
2315 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2320 /* Pure sort by name */
2321 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2324 g_object_unref(G_OBJECT(folder1));
2326 g_object_unref(G_OBJECT(folder2));
2334 /*****************************************************************************/
2335 /* DRAG and DROP stuff */
2336 /*****************************************************************************/
2338 * This function fills the #GtkSelectionData with the row and the
2339 * model that has been dragged. It's called when this widget is a
2340 * source for dnd after the event drop happened
2343 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2344 guint info, guint time, gpointer data)
2346 GtkTreeSelection *selection;
2347 GtkTreeModel *model;
2349 GtkTreePath *source_row;
2351 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2352 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2354 source_row = gtk_tree_model_get_path (model, &iter);
2355 gtk_tree_set_row_drag_data (selection_data,
2359 gtk_tree_path_free (source_row);
2363 typedef struct _DndHelper {
2364 ModestFolderView *folder_view;
2365 gboolean delete_source;
2366 GtkTreePath *source_row;
2370 dnd_helper_destroyer (DndHelper *helper)
2372 /* Free the helper */
2373 gtk_tree_path_free (helper->source_row);
2374 g_slice_free (DndHelper, helper);
2378 xfer_folder_cb (ModestMailOperation *mail_op,
2379 TnyFolder *new_folder,
2383 /* Select the folder */
2384 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2390 /* get the folder for the row the treepath refers to. */
2391 /* folder must be unref'd */
2392 static TnyFolderStore *
2393 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2396 TnyFolderStore *folder = NULL;
2398 if (gtk_tree_model_get_iter (model,&iter, path))
2399 gtk_tree_model_get (model, &iter,
2400 INSTANCE_COLUMN, &folder,
2407 * This function is used by drag_data_received_cb to manage drag and
2408 * drop of a header, i.e, and drag from the header view to the folder
2412 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2413 GtkTreeModel *dest_model,
2414 GtkTreePath *dest_row,
2415 GtkSelectionData *selection_data)
2417 TnyList *headers = NULL;
2418 TnyFolder *folder = NULL, *src_folder = NULL;
2419 TnyFolderType folder_type;
2420 GtkTreeIter source_iter, dest_iter;
2421 ModestWindowMgr *mgr = NULL;
2422 ModestWindow *main_win = NULL;
2423 gchar **uris, **tmp;
2425 /* Build the list of headers */
2426 mgr = modest_runtime_get_window_mgr ();
2427 headers = tny_simple_list_new ();
2428 uris = modest_dnd_selection_data_get_paths (selection_data);
2431 while (*tmp != NULL) {
2434 gboolean first = TRUE;
2437 path = gtk_tree_path_new_from_string (*tmp);
2438 gtk_tree_model_get_iter (source_model, &source_iter, path);
2439 gtk_tree_model_get (source_model, &source_iter,
2440 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2443 /* Do not enable d&d of headers already opened */
2444 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2445 tny_list_append (headers, G_OBJECT (header));
2447 if (G_UNLIKELY (first)) {
2448 src_folder = tny_header_get_folder (header);
2452 /* Free and go on */
2453 gtk_tree_path_free (path);
2454 g_object_unref (header);
2459 /* This could happen ig we perform a d&d very quickly over the
2460 same row that row could dissapear because message is
2462 if (!TNY_IS_FOLDER (src_folder))
2465 /* Get the target folder */
2466 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2467 gtk_tree_model_get (dest_model, &dest_iter,
2471 if (!folder || !TNY_IS_FOLDER(folder)) {
2472 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2476 folder_type = modest_tny_folder_guess_folder_type (folder);
2477 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2478 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2479 goto cleanup; /* cannot move messages there */
2482 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2483 /* g_warning ("folder not writable"); */
2484 goto cleanup; /* verboten! */
2487 /* Ask for confirmation to move */
2488 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2490 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2494 /* Transfer messages */
2495 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2500 if (G_IS_OBJECT (src_folder))
2501 g_object_unref (src_folder);
2502 if (G_IS_OBJECT(folder))
2503 g_object_unref (G_OBJECT (folder));
2504 if (G_IS_OBJECT(headers))
2505 g_object_unref (headers);
2509 TnyFolderStore *src_folder;
2510 TnyFolderStore *dst_folder;
2511 ModestFolderView *folder_view;
2516 dnd_folder_info_destroyer (DndFolderInfo *info)
2518 if (info->src_folder)
2519 g_object_unref (info->src_folder);
2520 if (info->dst_folder)
2521 g_object_unref (info->dst_folder);
2522 g_slice_free (DndFolderInfo, info);
2526 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2527 GtkWindow *parent_window,
2528 TnyAccount *account)
2531 modest_ui_actions_on_account_connection_error (parent_window, account);
2533 /* Free the helper & info */
2534 dnd_helper_destroyer (info->helper);
2535 dnd_folder_info_destroyer (info);
2539 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2541 GtkWindow *parent_window,
2542 TnyAccount *account,
2545 DndFolderInfo *info = NULL;
2546 ModestMailOperation *mail_op;
2548 info = (DndFolderInfo *) user_data;
2550 if (err || canceled) {
2551 dnd_on_connection_failed_destroyer (info, parent_window, account);
2555 /* Do the mail operation */
2556 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2557 modest_ui_actions_move_folder_error_handler,
2558 info->src_folder, NULL);
2560 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2563 /* Transfer the folder */
2564 modest_mail_operation_xfer_folder (mail_op,
2565 TNY_FOLDER (info->src_folder),
2567 info->helper->delete_source,
2569 info->helper->folder_view);
2572 g_object_unref (G_OBJECT (mail_op));
2573 dnd_helper_destroyer (info->helper);
2574 dnd_folder_info_destroyer (info);
2579 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2581 GtkWindow *parent_window,
2582 TnyAccount *account,
2585 DndFolderInfo *info = NULL;
2587 info = (DndFolderInfo *) user_data;
2589 if (err || canceled) {
2590 dnd_on_connection_failed_destroyer (info, parent_window, account);
2594 /* Connect to source folder and perform the copy/move */
2595 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2597 drag_and_drop_from_folder_view_src_folder_performer,
2602 * This function is used by drag_data_received_cb to manage drag and
2603 * drop of a folder, i.e, and drag from the folder view to the same
2607 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2608 GtkTreeModel *dest_model,
2609 GtkTreePath *dest_row,
2610 GtkSelectionData *selection_data,
2613 GtkTreeIter dest_iter, iter;
2614 TnyFolderStore *dest_folder = NULL;
2615 TnyFolderStore *folder = NULL;
2616 gboolean forbidden = FALSE;
2618 DndFolderInfo *info = NULL;
2620 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2622 g_warning ("%s: BUG: no main window", __FUNCTION__);
2623 dnd_helper_destroyer (helper);
2628 /* check the folder rules for the destination */
2629 folder = tree_path_to_folder (dest_model, dest_row);
2630 if (TNY_IS_FOLDER(folder)) {
2631 ModestTnyFolderRules rules =
2632 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2633 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2634 } else if (TNY_IS_FOLDER_STORE(folder)) {
2635 /* enable local root as destination for folders */
2636 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2637 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2640 g_object_unref (folder);
2643 /* check the folder rules for the source */
2644 folder = tree_path_to_folder (source_model, helper->source_row);
2645 if (TNY_IS_FOLDER(folder)) {
2646 ModestTnyFolderRules rules =
2647 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2648 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2651 g_object_unref (folder);
2655 /* Check if the drag is possible */
2656 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2658 modest_platform_run_information_dialog ((GtkWindow *) win,
2659 _("mail_in_ui_folder_move_target_error"),
2661 /* Restore the previous selection */
2662 folder = tree_path_to_folder (source_model, helper->source_row);
2664 if (TNY_IS_FOLDER (folder))
2665 modest_folder_view_select_folder (helper->folder_view,
2666 TNY_FOLDER (folder), FALSE);
2667 g_object_unref (folder);
2669 dnd_helper_destroyer (helper);
2674 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2675 gtk_tree_model_get (dest_model, &dest_iter,
2678 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2679 gtk_tree_model_get (source_model, &iter,
2683 /* Create the info for the performer */
2684 info = g_slice_new0 (DndFolderInfo);
2685 info->src_folder = g_object_ref (folder);
2686 info->dst_folder = g_object_ref (dest_folder);
2687 info->helper = helper;
2689 /* Connect to the destination folder and perform the copy/move */
2690 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2692 drag_and_drop_from_folder_view_dst_folder_performer,
2696 g_object_unref (dest_folder);
2697 g_object_unref (folder);
2701 * This function receives the data set by the "drag-data-get" signal
2702 * handler. This information comes within the #GtkSelectionData. This
2703 * function will manage both the drags of folders of the treeview and
2704 * drags of headers of the header view widget.
2707 on_drag_data_received (GtkWidget *widget,
2708 GdkDragContext *context,
2711 GtkSelectionData *selection_data,
2716 GtkWidget *source_widget;
2717 GtkTreeModel *dest_model, *source_model;
2718 GtkTreePath *source_row, *dest_row;
2719 GtkTreeViewDropPosition pos;
2720 gboolean delete_source = FALSE;
2721 gboolean success = FALSE;
2723 /* Do not allow further process */
2724 g_signal_stop_emission_by_name (widget, "drag-data-received");
2725 source_widget = gtk_drag_get_source_widget (context);
2727 /* Get the action */
2728 if (context->action == GDK_ACTION_MOVE) {
2729 delete_source = TRUE;
2731 /* Notify that there is no folder selected. We need to
2732 do this in order to update the headers view (and
2733 its monitors, because when moving, the old folder
2734 won't longer exist. We can not wait for the end of
2735 the operation, because the operation won't start if
2736 the folder is in use */
2737 if (source_widget == widget) {
2738 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2739 gtk_tree_selection_unselect_all (sel);
2743 /* Check if the get_data failed */
2744 if (selection_data == NULL || selection_data->length < 0)
2747 /* Select the destination model */
2748 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2750 /* Get the path to the destination row. Can not call
2751 gtk_tree_view_get_drag_dest_row() because the source row
2752 is not selected anymore */
2753 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2756 /* Only allow drops IN other rows */
2758 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2759 pos == GTK_TREE_VIEW_DROP_AFTER)
2763 /* Drags from the header view */
2764 if (source_widget != widget) {
2765 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2767 drag_and_drop_from_header_view (source_model,
2772 DndHelper *helper = NULL;
2774 /* Get the source model and row */
2775 gtk_tree_get_row_drag_data (selection_data,
2779 /* Create the helper */
2780 helper = g_slice_new0 (DndHelper);
2781 helper->delete_source = delete_source;
2782 helper->source_row = gtk_tree_path_copy (source_row);
2783 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2785 drag_and_drop_from_folder_view (source_model,
2791 gtk_tree_path_free (source_row);
2795 gtk_tree_path_free (dest_row);
2798 /* Finish the drag and drop */
2799 gtk_drag_finish (context, success, FALSE, time);
2803 * We define a "drag-drop" signal handler because we do not want to
2804 * use the default one, because the default one always calls
2805 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2806 * signal handler, because there we have all the information available
2807 * to know if the dnd was a success or not.
2810 drag_drop_cb (GtkWidget *widget,
2811 GdkDragContext *context,
2819 if (!context->targets)
2822 /* Check if we're dragging a folder row */
2823 target = gtk_drag_dest_find_target (widget, context, NULL);
2825 /* Request the data from the source. */
2826 gtk_drag_get_data(widget, context, target, time);
2832 * This function expands a node of a tree view if it's not expanded
2833 * yet. Not sure why it needs the threads stuff, but gtk+`example code
2834 * does that, so that's why they're here.
2837 expand_row_timeout (gpointer data)
2839 GtkTreeView *tree_view = data;
2840 GtkTreePath *dest_path = NULL;
2841 GtkTreeViewDropPosition pos;
2842 gboolean result = FALSE;
2844 gdk_threads_enter ();
2846 gtk_tree_view_get_drag_dest_row (tree_view,
2851 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2852 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2853 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2854 gtk_tree_path_free (dest_path);
2858 gtk_tree_path_free (dest_path);
2863 gdk_threads_leave ();
2869 * This function is called whenever the pointer is moved over a widget
2870 * while dragging some data. It installs a timeout that will expand a
2871 * node of the treeview if not expanded yet. This function also calls
2872 * gdk_drag_status in order to set the suggested action that will be
2873 * used by the "drag-data-received" signal handler to know if we
2874 * should do a move or just a copy of the data.
2877 on_drag_motion (GtkWidget *widget,
2878 GdkDragContext *context,
2884 GtkTreeViewDropPosition pos;
2885 GtkTreePath *dest_row;
2886 GtkTreeModel *dest_model;
2887 ModestFolderViewPrivate *priv;
2888 GdkDragAction suggested_action;
2889 gboolean valid_location = FALSE;
2890 TnyFolderStore *folder = NULL;
2892 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2894 if (priv->timer_expander != 0) {
2895 g_source_remove (priv->timer_expander);
2896 priv->timer_expander = 0;
2899 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2904 /* Do not allow drops between folders */
2906 pos == GTK_TREE_VIEW_DROP_BEFORE ||
2907 pos == GTK_TREE_VIEW_DROP_AFTER) {
2908 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2909 gdk_drag_status(context, 0, time);
2910 valid_location = FALSE;
2913 valid_location = TRUE;
2916 /* Check that the destination folder is writable */
2917 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2918 folder = tree_path_to_folder (dest_model, dest_row);
2919 if (folder && TNY_IS_FOLDER (folder)) {
2920 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2922 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2923 valid_location = FALSE;
2928 /* Expand the selected row after 1/2 second */
2929 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2930 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2932 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2934 /* Select the desired action. By default we pick MOVE */
2935 suggested_action = GDK_ACTION_MOVE;
2937 if (context->actions == GDK_ACTION_COPY)
2938 gdk_drag_status(context, GDK_ACTION_COPY, time);
2939 else if (context->actions == GDK_ACTION_MOVE)
2940 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2941 else if (context->actions & suggested_action)
2942 gdk_drag_status(context, suggested_action, time);
2944 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2948 g_object_unref (folder);
2950 gtk_tree_path_free (dest_row);
2952 g_signal_stop_emission_by_name (widget, "drag-motion");
2954 return valid_location;
2958 * This function sets the treeview as a source and a target for dnd
2959 * events. It also connects all the requirede signals.
2962 setup_drag_and_drop (GtkTreeView *self)
2964 /* Set up the folder view as a dnd destination. Set only the
2965 highlight flag, otherwise gtk will have a different
2967 #ifdef MODEST_TOOLKIT_HILDON2
2970 gtk_drag_dest_set (GTK_WIDGET (self),
2971 GTK_DEST_DEFAULT_HIGHLIGHT,
2972 folder_view_drag_types,
2973 G_N_ELEMENTS (folder_view_drag_types),
2974 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2976 g_signal_connect (G_OBJECT (self),
2977 "drag_data_received",
2978 G_CALLBACK (on_drag_data_received),
2982 /* Set up the treeview as a dnd source */
2983 gtk_drag_source_set (GTK_WIDGET (self),
2985 folder_view_drag_types,
2986 G_N_ELEMENTS (folder_view_drag_types),
2987 GDK_ACTION_MOVE | GDK_ACTION_COPY);
2989 g_signal_connect (G_OBJECT (self),
2991 G_CALLBACK (on_drag_motion),
2994 g_signal_connect (G_OBJECT (self),
2996 G_CALLBACK (on_drag_data_get),
2999 g_signal_connect (G_OBJECT (self),
3001 G_CALLBACK (drag_drop_cb),
3006 * This function manages the navigation through the folders using the
3007 * keyboard or the hardware keys in the device
3010 on_key_pressed (GtkWidget *self,
3014 GtkTreeSelection *selection;
3016 GtkTreeModel *model;
3017 gboolean retval = FALSE;
3019 /* Up and Down are automatically managed by the treeview */
3020 if (event->keyval == GDK_Return) {
3021 /* Expand/Collapse the selected row */
3022 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3023 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3026 path = gtk_tree_model_get_path (model, &iter);
3028 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3029 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3031 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3032 gtk_tree_path_free (path);
3034 /* No further processing */
3042 * We listen to the changes in the local folder account name key,
3043 * because we want to show the right name in the view. The local
3044 * folder account name corresponds to the device name in the Maemo
3045 * version. We do this because we do not want to query gconf on each
3046 * tree view refresh. It's better to cache it and change whenever
3050 on_configuration_key_changed (ModestConf* conf,
3052 ModestConfEvent event,
3053 ModestConfNotificationId id,
3054 ModestFolderView *self)
3056 ModestFolderViewPrivate *priv;
3059 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3060 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3062 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3063 g_free (priv->local_account_name);
3065 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3066 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3068 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3069 MODEST_CONF_DEVICE_NAME, NULL);
3071 /* Force a redraw */
3072 #if GTK_CHECK_VERSION(2, 8, 0)
3073 GtkTreeViewColumn * tree_column;
3075 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3077 gtk_tree_view_column_queue_resize (tree_column);
3079 gtk_widget_queue_draw (GTK_WIDGET (self));
3085 modest_folder_view_set_style (ModestFolderView *self,
3086 ModestFolderViewStyle style)
3088 ModestFolderViewPrivate *priv;
3090 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3091 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3092 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3094 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3097 priv->style = style;
3101 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3102 const gchar *account_id)
3104 ModestFolderViewPrivate *priv;
3105 GtkTreeModel *model;
3107 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3109 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3111 /* This will be used by the filter_row callback,
3112 * to decided which rows to show: */
3113 if (priv->visible_account_id) {
3114 g_free (priv->visible_account_id);
3115 priv->visible_account_id = NULL;
3118 priv->visible_account_id = g_strdup (account_id);
3121 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3122 if (GTK_IS_TREE_MODEL_FILTER (model))
3123 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3125 /* Save settings to gconf */
3126 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3127 MODEST_CONF_FOLDER_VIEW_KEY);
3131 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3133 ModestFolderViewPrivate *priv;
3135 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3137 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3139 return (const gchar *) priv->visible_account_id;
3143 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3147 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3149 gtk_tree_model_get (model, iter,
3153 gboolean result = FALSE;
3154 if (type == TNY_FOLDER_TYPE_INBOX) {
3158 *inbox_iter = *iter;
3162 if (gtk_tree_model_iter_children (model, &child, iter)) {
3163 if (find_inbox_iter (model, &child, inbox_iter))
3167 } while (gtk_tree_model_iter_next (model, iter));
3176 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3178 GtkTreeModel *model;
3179 GtkTreeIter iter, inbox_iter;
3180 GtkTreeSelection *sel;
3181 GtkTreePath *path = NULL;
3183 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3185 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3189 expand_root_items (self);
3190 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3192 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3193 g_warning ("%s: model is empty", __FUNCTION__);
3197 if (find_inbox_iter (model, &iter, &inbox_iter))
3198 path = gtk_tree_model_get_path (model, &inbox_iter);
3200 path = gtk_tree_path_new_first ();
3202 /* Select the row and free */
3203 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3204 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3205 gtk_tree_path_free (path);
3208 gtk_widget_grab_focus (GTK_WIDGET(self));
3214 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3219 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3220 TnyFolder* a_folder;
3223 gtk_tree_model_get (model, iter,
3224 INSTANCE_COLUMN, &a_folder,
3230 if (folder == a_folder) {
3231 g_object_unref (a_folder);
3232 *folder_iter = *iter;
3235 g_object_unref (a_folder);
3237 if (gtk_tree_model_iter_children (model, &child, iter)) {
3238 if (find_folder_iter (model, &child, folder_iter, folder))
3242 } while (gtk_tree_model_iter_next (model, iter));
3247 #ifndef MODEST_TOOLKIT_HILDON2
3249 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3252 ModestFolderView *self)
3254 ModestFolderViewPrivate *priv = NULL;
3255 GtkTreeSelection *sel;
3256 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3257 GObject *instance = NULL;
3259 if (!MODEST_IS_FOLDER_VIEW(self))
3262 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3264 priv->reexpand = TRUE;
3266 gtk_tree_model_get (tree_model, iter,
3268 INSTANCE_COLUMN, &instance,
3274 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3275 priv->folder_to_select = g_object_ref (instance);
3277 g_object_unref (instance);
3279 if (priv->folder_to_select) {
3281 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3284 path = gtk_tree_model_get_path (tree_model, iter);
3285 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3287 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3289 gtk_tree_selection_select_iter (sel, iter);
3290 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3292 gtk_tree_path_free (path);
3296 modest_folder_view_disable_next_folder_selection (self);
3298 /* Refilter the model */
3299 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3305 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3307 ModestFolderViewPrivate *priv;
3309 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3311 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3313 if (priv->folder_to_select)
3314 g_object_unref(priv->folder_to_select);
3316 priv->folder_to_select = NULL;
3320 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3321 gboolean after_change)
3323 GtkTreeModel *model;
3324 GtkTreeIter iter, folder_iter;
3325 GtkTreeSelection *sel;
3326 ModestFolderViewPrivate *priv = NULL;
3328 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3329 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3331 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3334 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3335 gtk_tree_selection_unselect_all (sel);
3337 if (priv->folder_to_select)
3338 g_object_unref(priv->folder_to_select);
3339 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3343 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3348 /* Refilter the model, before selecting the folder */
3349 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3351 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3352 g_warning ("%s: model is empty", __FUNCTION__);
3356 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3359 path = gtk_tree_model_get_path (model, &folder_iter);
3360 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3362 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3363 gtk_tree_selection_select_iter (sel, &folder_iter);
3364 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3366 gtk_tree_path_free (path);
3374 modest_folder_view_copy_selection (ModestFolderView *self)
3376 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3378 /* Copy selection */
3379 _clipboard_set_selected_data (self, FALSE);
3383 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3385 ModestFolderViewPrivate *priv = NULL;
3386 GtkTreeModel *model = NULL;
3387 const gchar **hidding = NULL;
3388 guint i, n_selected;
3390 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3391 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3393 /* Copy selection */
3394 if (!_clipboard_set_selected_data (folder_view, TRUE))
3397 /* Get hidding ids */
3398 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3400 /* Clear hidding array created by previous cut operation */
3401 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3403 /* Copy hidding array */
3404 priv->n_selected = n_selected;
3405 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3406 for (i=0; i < n_selected; i++)
3407 priv->hidding_ids[i] = g_strdup(hidding[i]);
3409 /* Hide cut folders */
3410 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3411 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3415 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3416 ModestFolderView *folder_view_dst)
3418 GtkTreeModel *filter_model = NULL;
3419 GtkTreeModel *model = NULL;
3420 GtkTreeModel *new_filter_model = NULL;
3422 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3423 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3426 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3427 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3429 /* Build new filter model */
3430 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3431 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3435 /* Set copied model */
3436 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3437 #ifndef MODEST_TOOLKIT_HILDON2
3438 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3439 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3443 g_object_unref (new_filter_model);
3447 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3450 GtkTreeModel *model = NULL;
3451 ModestFolderViewPrivate* priv;
3453 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3455 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3456 priv->show_non_move = show;
3457 /* modest_folder_view_update_model(folder_view, */
3458 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3460 /* Hide special folders */
3461 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3462 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3463 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3467 /* Returns FALSE if it did not selected anything */
3469 _clipboard_set_selected_data (ModestFolderView *folder_view,
3472 ModestFolderViewPrivate *priv = NULL;
3473 TnyFolderStore *folder = NULL;
3474 gboolean retval = FALSE;
3476 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3477 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3479 /* Set selected data on clipboard */
3480 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3481 folder = modest_folder_view_get_selected (folder_view);
3483 /* Do not allow to select an account */
3484 if (TNY_IS_FOLDER (folder)) {
3485 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3490 g_object_unref (folder);
3496 _clear_hidding_filter (ModestFolderView *folder_view)
3498 ModestFolderViewPrivate *priv;
3501 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3502 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3504 if (priv->hidding_ids != NULL) {
3505 for (i=0; i < priv->n_selected; i++)
3506 g_free (priv->hidding_ids[i]);
3507 g_free(priv->hidding_ids);
3513 on_display_name_changed (ModestAccountMgr *mgr,
3514 const gchar *account,
3517 ModestFolderView *self;
3519 self = MODEST_FOLDER_VIEW (user_data);
3521 /* Force a redraw */
3522 #if GTK_CHECK_VERSION(2, 8, 0)
3523 GtkTreeViewColumn * tree_column;
3525 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3527 gtk_tree_view_column_queue_resize (tree_column);
3529 gtk_widget_queue_draw (GTK_WIDGET (self));
3534 modest_folder_view_set_cell_style (ModestFolderView *self,
3535 ModestFolderViewCellStyle cell_style)
3537 ModestFolderViewPrivate *priv = NULL;
3539 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3540 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3542 priv->cell_style = cell_style;
3544 g_object_set (G_OBJECT (priv->messages_renderer),
3545 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3548 gtk_widget_queue_draw (GTK_WIDGET (self));
3552 update_style (ModestFolderView *self)
3554 ModestFolderViewPrivate *priv;
3555 GdkColor style_color;
3556 PangoAttrList *attr_list;
3558 PangoAttribute *attr;
3560 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3561 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3565 attr_list = pango_attr_list_new ();
3566 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3567 gdk_color_parse ("grey", &style_color);
3569 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3570 pango_attr_list_insert (attr_list, attr);
3573 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3575 "SmallSystemFont", NULL,
3577 attr = pango_attr_font_desc_new (pango_font_description_copy
3578 (style->font_desc));
3579 pango_attr_list_insert (attr_list, attr);
3581 g_object_set (G_OBJECT (priv->messages_renderer),
3582 "foreground-gdk", &style_color,
3583 "foreground-set", TRUE,
3584 "attributes", attr_list,
3586 pango_attr_list_unref (attr_list);
3590 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3592 if (strcmp ("style", spec->name) == 0) {
3593 update_style (MODEST_FOLDER_VIEW (obj));
3594 gtk_widget_queue_draw (GTK_WIDGET (obj));
3599 modest_folder_view_set_filter (ModestFolderView *self,
3600 ModestFolderViewFilter filter)
3602 ModestFolderViewPrivate *priv;
3603 GtkTreeModel *filter_model;
3605 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3606 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3608 priv->filter |= filter;
3610 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3611 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3612 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3617 modest_folder_view_unset_filter (ModestFolderView *self,
3618 ModestFolderViewFilter filter)
3620 ModestFolderViewPrivate *priv;
3621 GtkTreeModel *filter_model;
3623 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3624 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3626 priv->filter &= ~filter;
3628 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3629 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3630 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));