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-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
66 /* Folder view drag types */
67 const GtkTargetEntry folder_view_drag_types[] =
69 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
70 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
73 /* Default icon sizes for Fremantle style are different */
74 #ifdef MODEST_TOOLKIT_HILDON2
75 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
77 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
80 /* Column names depending on we use list store or tree store */
81 #ifdef MODEST_TOOLKIT_HILDON2
82 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
83 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
84 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
85 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
86 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
88 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
89 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
90 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
91 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
92 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
95 /* 'private'/'protected' functions */
96 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
97 static void modest_folder_view_init (ModestFolderView *obj);
98 static void modest_folder_view_finalize (GObject *obj);
100 static void tny_account_store_view_init (gpointer g,
101 gpointer iface_data);
103 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
104 TnyAccountStore *account_store);
106 static void on_selection_changed (GtkTreeSelection *sel,
109 static void on_row_activated (GtkTreeView *treeview,
111 GtkTreeViewColumn *column,
114 static void on_account_removed (TnyAccountStore *self,
118 static void on_account_inserted (TnyAccountStore *self,
122 static void on_account_changed (TnyAccountStore *self,
126 static gint cmp_rows (GtkTreeModel *tree_model,
131 static gboolean filter_row (GtkTreeModel *model,
135 static gboolean on_key_pressed (GtkWidget *self,
139 static void on_configuration_key_changed (ModestConf* conf,
141 ModestConfEvent event,
142 ModestConfNotificationId notification_id,
143 ModestFolderView *self);
146 static void on_drag_data_get (GtkWidget *widget,
147 GdkDragContext *context,
148 GtkSelectionData *selection_data,
153 static void on_drag_data_received (GtkWidget *widget,
154 GdkDragContext *context,
157 GtkSelectionData *selection_data,
162 static gboolean on_drag_motion (GtkWidget *widget,
163 GdkDragContext *context,
169 static void expand_root_items (ModestFolderView *self);
171 static gint expand_row_timeout (gpointer data);
173 static void setup_drag_and_drop (GtkTreeView *self);
175 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
178 static void _clear_hidding_filter (ModestFolderView *folder_view);
180 #ifndef MODEST_TOOLKIT_HILDON2
181 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
184 ModestFolderView *self);
187 static void on_display_name_changed (ModestAccountMgr *self,
188 const gchar *account,
190 static void update_style (ModestFolderView *self);
191 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
192 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
193 static gboolean inbox_is_special (TnyFolderStore *folder_store);
195 static gboolean get_inner_models (ModestFolderView *self,
196 GtkTreeModel **filter_model,
197 GtkTreeModel **sort_model,
198 GtkTreeModel **tny_model);
201 FOLDER_SELECTION_CHANGED_SIGNAL,
202 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
203 FOLDER_ACTIVATED_SIGNAL,
204 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
208 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
209 struct _ModestFolderViewPrivate {
210 TnyAccountStore *account_store;
211 TnyFolderStore *cur_folder_store;
213 TnyFolder *folder_to_select; /* folder to select after the next update */
215 gulong changed_signal;
216 gulong account_inserted_signal;
217 gulong account_removed_signal;
218 gulong account_changed_signal;
219 gulong conf_key_signal;
220 gulong display_name_changed_signal;
222 /* not unref this object, its a singlenton */
223 ModestEmailClipboard *clipboard;
225 /* Filter tree model */
228 ModestFolderViewFilter filter;
230 TnyFolderStoreQuery *query;
231 guint timer_expander;
233 gchar *local_account_name;
234 gchar *visible_account_id;
236 ModestFolderViewStyle style;
237 ModestFolderViewCellStyle cell_style;
238 gboolean show_message_count;
240 gboolean reselect; /* we use this to force a reselection of the INBOX */
241 gboolean show_non_move;
242 TnyList *list_to_move;
243 gboolean reexpand; /* next time we expose, we'll expand all root folders */
245 GtkCellRenderer *messages_renderer;
247 gulong outbox_deleted_handler;
249 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
250 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
251 MODEST_TYPE_FOLDER_VIEW, \
252 ModestFolderViewPrivate))
254 static GObjectClass *parent_class = NULL;
256 static guint signals[LAST_SIGNAL] = {0};
259 modest_folder_view_get_type (void)
261 static GType my_type = 0;
263 static const GTypeInfo my_info = {
264 sizeof(ModestFolderViewClass),
265 NULL, /* base init */
266 NULL, /* base finalize */
267 (GClassInitFunc) modest_folder_view_class_init,
268 NULL, /* class finalize */
269 NULL, /* class data */
270 sizeof(ModestFolderView),
272 (GInstanceInitFunc) modest_folder_view_init,
276 static const GInterfaceInfo tny_account_store_view_info = {
277 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
278 NULL, /* interface_finalize */
279 NULL /* interface_data */
283 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
287 g_type_add_interface_static (my_type,
288 TNY_TYPE_ACCOUNT_STORE_VIEW,
289 &tny_account_store_view_info);
295 modest_folder_view_class_init (ModestFolderViewClass *klass)
297 GObjectClass *gobject_class;
298 GtkTreeViewClass *treeview_class;
299 gobject_class = (GObjectClass*) klass;
300 treeview_class = (GtkTreeViewClass*) klass;
302 parent_class = g_type_class_peek_parent (klass);
303 gobject_class->finalize = modest_folder_view_finalize;
305 g_type_class_add_private (gobject_class,
306 sizeof(ModestFolderViewPrivate));
308 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
309 g_signal_new ("folder_selection_changed",
310 G_TYPE_FROM_CLASS (gobject_class),
312 G_STRUCT_OFFSET (ModestFolderViewClass,
313 folder_selection_changed),
315 modest_marshal_VOID__POINTER_BOOLEAN,
316 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
319 * This signal is emitted whenever the currently selected
320 * folder display name is computed. Note that the name could
321 * be different to the folder name, because we could append
322 * the unread messages count to the folder name to build the
323 * folder display name
325 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
326 g_signal_new ("folder-display-name-changed",
327 G_TYPE_FROM_CLASS (gobject_class),
329 G_STRUCT_OFFSET (ModestFolderViewClass,
330 folder_display_name_changed),
332 g_cclosure_marshal_VOID__STRING,
333 G_TYPE_NONE, 1, G_TYPE_STRING);
335 signals[FOLDER_ACTIVATED_SIGNAL] =
336 g_signal_new ("folder_activated",
337 G_TYPE_FROM_CLASS (gobject_class),
339 G_STRUCT_OFFSET (ModestFolderViewClass,
342 g_cclosure_marshal_VOID__POINTER,
343 G_TYPE_NONE, 1, G_TYPE_POINTER);
346 * Emitted whenever the visible account changes
348 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
349 g_signal_new ("visible-account-changed",
350 G_TYPE_FROM_CLASS (gobject_class),
352 G_STRUCT_OFFSET (ModestFolderViewClass,
353 visible_account_changed),
355 g_cclosure_marshal_VOID__STRING,
356 G_TYPE_NONE, 1, G_TYPE_STRING);
358 treeview_class->select_cursor_parent = NULL;
360 #ifdef MODEST_TOOLKIT_HILDON2
361 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
367 /* Retrieves the filter, sort and tny models of the folder view. If
368 any of these does not exist then it returns FALSE */
370 get_inner_models (ModestFolderView *self,
371 GtkTreeModel **filter_model,
372 GtkTreeModel **sort_model,
373 GtkTreeModel **tny_model)
375 GtkTreeModel *s_model, *f_model, *t_model;
377 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
378 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
379 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
383 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
384 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
385 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
389 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
393 *filter_model = f_model;
395 *sort_model = s_model;
397 *tny_model = t_model;
402 /* Simplify checks for NULLs: */
404 strings_are_equal (const gchar *a, const gchar *b)
410 return (strcmp (a, b) == 0);
417 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
419 GObject *instance = NULL;
421 gtk_tree_model_get (model, iter,
422 INSTANCE_COLUMN, &instance,
426 return FALSE; /* keep walking */
428 if (!TNY_IS_ACCOUNT (instance)) {
429 g_object_unref (instance);
430 return FALSE; /* keep walking */
433 /* Check if this is the looked-for account: */
434 TnyAccount *this_account = TNY_ACCOUNT (instance);
435 TnyAccount *account = TNY_ACCOUNT (data);
437 const gchar *this_account_id = tny_account_get_id(this_account);
438 const gchar *account_id = tny_account_get_id(account);
439 g_object_unref (instance);
442 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
443 if (strings_are_equal(this_account_id, account_id)) {
444 /* Tell the model that the data has changed, so that
445 * it calls the cell_data_func callbacks again: */
446 /* TODO: This does not seem to actually cause the new string to be shown: */
447 gtk_tree_model_row_changed (model, path, iter);
449 return TRUE; /* stop walking */
452 return FALSE; /* keep walking */
457 ModestFolderView *self;
458 gchar *previous_name;
459 } GetMmcAccountNameData;
462 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
464 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
466 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
468 if (!strings_are_equal (
469 tny_account_get_name(TNY_ACCOUNT(account)),
470 data->previous_name)) {
472 /* Tell the model that the data has changed, so that
473 * it calls the cell_data_func callbacks again: */
474 ModestFolderView *self = data->self;
475 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
477 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
480 g_free (data->previous_name);
481 g_slice_free (GetMmcAccountNameData, data);
485 convert_parent_folders_to_dots (gchar **item_name)
489 gchar *last_separator;
491 if (item_name == NULL)
494 for (c = *item_name; *c != '\0'; c++) {
495 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
500 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
501 if (last_separator != NULL) {
502 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
509 buffer = g_string_new ("");
510 for (i = 0; i < n_parents; i++) {
511 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
513 buffer = g_string_append (buffer, last_separator);
515 *item_name = g_string_free (buffer, FALSE);
521 format_compact_style (gchar **item_name,
523 const gchar *mailbox,
525 gboolean multiaccount,
526 gboolean *use_markup)
530 TnyFolderType folder_type;
532 if (!TNY_IS_FOLDER (instance))
535 folder = (TnyFolder *) instance;
537 folder_type = tny_folder_get_folder_type (folder);
538 is_special = (get_cmp_pos (folder_type, folder)!= 4);
541 /* Remove mailbox prefix if any */
542 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
543 if (g_str_has_prefix (*item_name, prefix)) {
544 gchar *new_item_name;
546 new_item_name = g_strdup (*item_name + strlen (prefix));
547 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
548 g_free (new_item_name);
549 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
552 *item_name = new_item_name;
554 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
557 *item_name = g_strdup (_("mcen_me_folder_inbox"));
560 if (!is_special || multiaccount) {
561 TnyAccount *account = tny_folder_get_account (folder);
562 const gchar *folder_name;
563 gboolean concat_folder_name = FALSE;
566 /* Should not happen */
570 /* convert parent folders to dots */
571 convert_parent_folders_to_dots (item_name);
573 folder_name = tny_folder_get_name (folder);
574 if (g_str_has_suffix (*item_name, folder_name)) {
575 gchar *offset = g_strrstr (*item_name, folder_name);
577 concat_folder_name = TRUE;
580 buffer = g_string_new ("");
582 buffer = g_string_append (buffer, *item_name);
583 if (concat_folder_name) {
584 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
585 buffer = g_string_append (buffer, folder_name);
586 if (bold) buffer = g_string_append (buffer, "</span>");
589 g_object_unref (account);
591 *item_name = g_string_free (buffer, FALSE);
599 text_cell_data (GtkTreeViewColumn *column,
600 GtkCellRenderer *renderer,
601 GtkTreeModel *tree_model,
605 ModestFolderViewPrivate *priv;
606 GObject *rendobj = (GObject *) renderer;
608 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
609 GObject *instance = NULL;
610 gboolean use_markup = FALSE;
612 gtk_tree_model_get (tree_model, iter,
615 INSTANCE_COLUMN, &instance,
617 if (!fname || !instance)
620 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
621 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
623 gchar *item_name = NULL;
624 gint item_weight = 400;
626 if (type != TNY_FOLDER_TYPE_ROOT) {
630 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
631 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
632 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
633 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
635 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
638 /* Sometimes an special folder is reported by the server as
639 NORMAL, like some versions of Dovecot */
640 if (type == TNY_FOLDER_TYPE_NORMAL ||
641 type == TNY_FOLDER_TYPE_UNKNOWN) {
642 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
646 /* note: we cannot reliably get the counts from the
647 * tree model, we need to use explicit calls on
648 * tny_folder for some reason. Select the number to
649 * show: the unread or unsent messages. in case of
650 * outbox/drafts, show all */
651 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
652 (type == TNY_FOLDER_TYPE_OUTBOX) ||
653 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
654 number = tny_folder_get_all_count (TNY_FOLDER(instance));
657 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
661 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
662 item_name = g_strdup (fname);
669 /* Use bold font style if there are unread or unset messages */
671 if (priv->show_message_count) {
672 item_name = g_strdup_printf ("%s (%d)", fname, number);
674 item_name = g_strdup (fname);
678 item_name = g_strdup (fname);
683 } else if (TNY_IS_ACCOUNT (instance)) {
684 /* If it's a server account */
685 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
686 item_name = g_strdup (priv->local_account_name);
688 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
689 /* fname is only correct when the items are first
690 * added to the model, not when the account is
691 * changed later, so get the name from the account
693 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
696 item_name = g_strdup (fname);
702 item_name = g_strdup ("unknown");
704 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
705 gboolean multiaccount;
707 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
708 /* Convert item_name to markup */
709 format_compact_style (&item_name, instance, priv->mailbox,
711 multiaccount, &use_markup);
714 if (item_name && item_weight) {
715 /* Set the name in the treeview cell: */
717 g_object_set (rendobj, "markup", item_name, NULL);
719 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
721 /* Notify display name observers */
722 /* TODO: What listens for this signal, and how can it use only the new name? */
723 if (((GObject *) priv->cur_folder_store) == instance) {
724 g_signal_emit (G_OBJECT(self),
725 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
732 /* If it is a Memory card account, make sure that we have the correct name.
733 * This function will be trigerred again when the name has been retrieved: */
734 if (TNY_IS_STORE_ACCOUNT (instance) &&
735 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
737 /* Get the account name asynchronously: */
738 GetMmcAccountNameData *callback_data =
739 g_slice_new0(GetMmcAccountNameData);
740 callback_data->self = self;
742 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
744 callback_data->previous_name = g_strdup (name);
746 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
747 on_get_mmc_account_name, callback_data);
751 g_object_unref (G_OBJECT (instance));
757 messages_cell_data (GtkTreeViewColumn *column,
758 GtkCellRenderer *renderer,
759 GtkTreeModel *tree_model,
763 ModestFolderView *self;
764 ModestFolderViewPrivate *priv;
765 GObject *rendobj = (GObject *) renderer;
766 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
767 GObject *instance = NULL;
768 gchar *item_name = NULL;
770 gtk_tree_model_get (tree_model, iter,
772 INSTANCE_COLUMN, &instance,
777 self = MODEST_FOLDER_VIEW (data);
778 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
781 if (type != TNY_FOLDER_TYPE_ROOT) {
785 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
786 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
787 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
789 /* Sometimes an special folder is reported by the server as
790 NORMAL, like some versions of Dovecot */
791 if (type == TNY_FOLDER_TYPE_NORMAL ||
792 type == TNY_FOLDER_TYPE_UNKNOWN) {
793 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
797 /* note: we cannot reliably get the counts from the tree model, we need
798 * to use explicit calls on tny_folder for some reason.
800 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
801 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
802 (type == TNY_FOLDER_TYPE_OUTBOX) ||
803 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
804 number = tny_folder_get_all_count (TNY_FOLDER(instance));
807 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
811 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
813 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
821 item_name = g_strdup ("");
824 /* Set the name in the treeview cell: */
825 g_object_set (rendobj,"text", item_name, NULL);
833 g_object_unref (G_OBJECT (instance));
839 GdkPixbuf *pixbuf_open;
840 GdkPixbuf *pixbuf_close;
844 static inline GdkPixbuf *
845 get_composite_pixbuf (const gchar *icon_name,
847 GdkPixbuf *base_pixbuf)
849 GdkPixbuf *emblem, *retval = NULL;
851 emblem = modest_platform_get_icon (icon_name, size);
853 retval = gdk_pixbuf_copy (base_pixbuf);
854 gdk_pixbuf_composite (emblem, retval, 0, 0,
855 MIN (gdk_pixbuf_get_width (emblem),
856 gdk_pixbuf_get_width (retval)),
857 MIN (gdk_pixbuf_get_height (emblem),
858 gdk_pixbuf_get_height (retval)),
859 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
860 g_object_unref (emblem);
865 static inline ThreePixbufs *
866 get_composite_icons (const gchar *icon_code,
868 GdkPixbuf **pixbuf_open,
869 GdkPixbuf **pixbuf_close)
871 ThreePixbufs *retval;
874 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
877 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
882 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
886 retval = g_slice_new0 (ThreePixbufs);
888 retval->pixbuf = g_object_ref (*pixbuf);
890 retval->pixbuf_open = g_object_ref (*pixbuf_open);
892 retval->pixbuf_close = g_object_ref (*pixbuf_close);
897 static inline ThreePixbufs*
898 get_folder_icons (TnyFolderType type, GObject *instance)
900 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
901 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
902 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
903 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
904 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
906 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
907 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
908 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
909 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
910 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
912 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
913 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
914 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
915 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
916 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
918 ThreePixbufs *retval = NULL;
920 /* Sometimes an special folder is reported by the server as
921 NORMAL, like some versions of Dovecot */
922 if (type == TNY_FOLDER_TYPE_NORMAL ||
923 type == TNY_FOLDER_TYPE_UNKNOWN) {
924 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
927 /* It's not enough with check the folder type. We need to
928 ensure that we're not giving a special folder icon to a
929 normal folder with the same name than a special folder */
930 if (TNY_IS_FOLDER (instance) &&
931 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
932 type = TNY_FOLDER_TYPE_NORMAL;
934 /* Remote folders should not be treated as special folders */
935 if (TNY_IS_FOLDER_STORE (instance) &&
936 !TNY_IS_ACCOUNT (instance) &&
937 type != TNY_FOLDER_TYPE_INBOX &&
938 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
939 #ifdef MODEST_TOOLKIT_HILDON2
940 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
943 &anorm_pixbuf_close);
945 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
948 &normal_pixbuf_close);
954 case TNY_FOLDER_TYPE_INVALID:
955 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
958 case TNY_FOLDER_TYPE_ROOT:
959 if (TNY_IS_ACCOUNT (instance)) {
961 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
962 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
965 &avirt_pixbuf_close);
967 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
969 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
970 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
975 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
978 &anorm_pixbuf_close);
983 case TNY_FOLDER_TYPE_INBOX:
984 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
987 &inbox_pixbuf_close);
989 case TNY_FOLDER_TYPE_OUTBOX:
990 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
993 &outbox_pixbuf_close);
995 case TNY_FOLDER_TYPE_JUNK:
996 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1001 case TNY_FOLDER_TYPE_SENT:
1002 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1005 &sent_pixbuf_close);
1007 case TNY_FOLDER_TYPE_TRASH:
1008 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1011 &trash_pixbuf_close);
1013 case TNY_FOLDER_TYPE_DRAFTS:
1014 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1017 &draft_pixbuf_close);
1019 case TNY_FOLDER_TYPE_ARCHIVE:
1020 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1025 case TNY_FOLDER_TYPE_NORMAL:
1027 /* Memory card folders could have an special icon */
1028 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1029 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1034 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1036 &normal_pixbuf_open,
1037 &normal_pixbuf_close);
1046 free_pixbufs (ThreePixbufs *pixbufs)
1048 if (pixbufs->pixbuf)
1049 g_object_unref (pixbufs->pixbuf);
1050 if (pixbufs->pixbuf_open)
1051 g_object_unref (pixbufs->pixbuf_open);
1052 if (pixbufs->pixbuf_close)
1053 g_object_unref (pixbufs->pixbuf_close);
1054 g_slice_free (ThreePixbufs, pixbufs);
1058 icon_cell_data (GtkTreeViewColumn *column,
1059 GtkCellRenderer *renderer,
1060 GtkTreeModel *tree_model,
1064 GObject *rendobj = NULL, *instance = NULL;
1065 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1066 gboolean has_children;
1067 ThreePixbufs *pixbufs;
1069 rendobj = (GObject *) renderer;
1071 gtk_tree_model_get (tree_model, iter,
1073 INSTANCE_COLUMN, &instance,
1079 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1080 pixbufs = get_folder_icons (type, instance);
1081 g_object_unref (instance);
1084 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1087 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1088 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1091 free_pixbufs (pixbufs);
1095 add_columns (GtkWidget *treeview)
1097 GtkTreeViewColumn *column;
1098 GtkCellRenderer *renderer;
1099 GtkTreeSelection *sel;
1100 ModestFolderViewPrivate *priv;
1102 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1105 column = gtk_tree_view_column_new ();
1107 /* Set icon and text render function */
1108 renderer = gtk_cell_renderer_pixbuf_new();
1109 #ifdef MODEST_TOOLKIT_HILDON2
1110 g_object_set (renderer,
1111 "xpad", MODEST_MARGIN_DEFAULT,
1112 "ypad", MODEST_MARGIN_DEFAULT,
1115 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1116 gtk_tree_view_column_set_cell_data_func(column, renderer,
1117 icon_cell_data, treeview, NULL);
1119 renderer = gtk_cell_renderer_text_new();
1120 g_object_set (renderer,
1121 #ifdef MODEST_TOOLKIT_HILDON2
1122 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1123 "ypad", MODEST_MARGIN_DEFAULT,
1124 "xpad", MODEST_MARGIN_DEFAULT,
1126 "ellipsize", PANGO_ELLIPSIZE_END,
1128 "ellipsize-set", TRUE, NULL);
1129 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1130 gtk_tree_view_column_set_cell_data_func(column, renderer,
1131 text_cell_data, treeview, NULL);
1133 priv->messages_renderer = gtk_cell_renderer_text_new ();
1134 g_object_set (priv->messages_renderer,
1135 #ifdef MODEST_TOOLKIT_HILDON2
1137 "ypad", MODEST_MARGIN_DEFAULT,
1138 "xpad", MODEST_MARGIN_DOUBLE,
1140 "scale", PANGO_SCALE_X_SMALL,
1143 "alignment", PANGO_ALIGN_RIGHT,
1147 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1148 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1149 messages_cell_data, treeview, NULL);
1151 /* Set selection mode */
1152 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1153 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1155 /* Set treeview appearance */
1156 gtk_tree_view_column_set_spacing (column, 2);
1157 gtk_tree_view_column_set_resizable (column, TRUE);
1158 gtk_tree_view_column_set_fixed_width (column, TRUE);
1159 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1160 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1163 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1167 modest_folder_view_init (ModestFolderView *obj)
1169 ModestFolderViewPrivate *priv;
1172 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1174 priv->timer_expander = 0;
1175 priv->account_store = NULL;
1177 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1178 priv->cur_folder_store = NULL;
1179 priv->visible_account_id = NULL;
1180 priv->mailbox = NULL;
1181 priv->folder_to_select = NULL;
1182 priv->outbox_deleted_handler = 0;
1183 priv->reexpand = TRUE;
1185 /* Initialize the local account name */
1186 conf = modest_runtime_get_conf();
1187 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1189 /* Init email clipboard */
1190 priv->clipboard = modest_runtime_get_email_clipboard ();
1191 priv->hidding_ids = NULL;
1192 priv->n_selected = 0;
1193 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1194 priv->reselect = FALSE;
1195 priv->show_non_move = TRUE;
1196 priv->list_to_move = NULL;
1197 priv->show_message_count = TRUE;
1199 /* Build treeview */
1200 add_columns (GTK_WIDGET (obj));
1202 /* Setup drag and drop */
1203 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1205 /* Connect signals */
1206 g_signal_connect (G_OBJECT (obj),
1208 G_CALLBACK (on_key_pressed), NULL);
1210 priv->display_name_changed_signal =
1211 g_signal_connect (modest_runtime_get_account_mgr (),
1212 "display_name_changed",
1213 G_CALLBACK (on_display_name_changed),
1217 * Track changes in the local account name (in the device it
1218 * will be the device name)
1220 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1222 G_CALLBACK(on_configuration_key_changed),
1226 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1232 tny_account_store_view_init (gpointer g, gpointer iface_data)
1234 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1236 klass->set_account_store = modest_folder_view_set_account_store;
1240 modest_folder_view_finalize (GObject *obj)
1242 ModestFolderViewPrivate *priv;
1243 GtkTreeSelection *sel;
1244 TnyAccount *local_account;
1246 g_return_if_fail (obj);
1248 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1250 if (priv->timer_expander != 0) {
1251 g_source_remove (priv->timer_expander);
1252 priv->timer_expander = 0;
1255 local_account = (TnyAccount *)
1256 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1257 if (local_account) {
1258 if (g_signal_handler_is_connected (local_account,
1259 priv->outbox_deleted_handler))
1260 g_signal_handler_disconnect (local_account,
1261 priv->outbox_deleted_handler);
1262 g_object_unref (local_account);
1265 if (priv->account_store) {
1266 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1267 priv->account_inserted_signal);
1268 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1269 priv->account_removed_signal);
1270 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1271 priv->account_changed_signal);
1272 g_object_unref (G_OBJECT(priv->account_store));
1273 priv->account_store = NULL;
1276 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1277 priv->display_name_changed_signal)) {
1278 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1279 priv->display_name_changed_signal);
1280 priv->display_name_changed_signal = 0;
1284 g_object_unref (G_OBJECT (priv->query));
1288 if (priv->folder_to_select) {
1289 g_object_unref (G_OBJECT(priv->folder_to_select));
1290 priv->folder_to_select = NULL;
1293 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1295 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1297 g_free (priv->local_account_name);
1298 g_free (priv->visible_account_id);
1299 g_free (priv->mailbox);
1301 if (priv->conf_key_signal) {
1302 g_signal_handler_disconnect (modest_runtime_get_conf (),
1303 priv->conf_key_signal);
1304 priv->conf_key_signal = 0;
1307 if (priv->cur_folder_store) {
1308 g_object_unref (priv->cur_folder_store);
1309 priv->cur_folder_store = NULL;
1312 if (priv->list_to_move) {
1313 g_object_unref (priv->list_to_move);
1314 priv->list_to_move = NULL;
1317 /* Clear hidding array created by cut operation */
1318 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1320 G_OBJECT_CLASS(parent_class)->finalize (obj);
1325 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1327 ModestFolderViewPrivate *priv;
1330 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1331 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1333 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1334 device = tny_account_store_get_device (account_store);
1336 if (G_UNLIKELY (priv->account_store)) {
1338 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1339 priv->account_inserted_signal))
1340 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1341 priv->account_inserted_signal);
1342 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1343 priv->account_removed_signal))
1344 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1345 priv->account_removed_signal);
1346 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1347 priv->account_changed_signal))
1348 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1349 priv->account_changed_signal);
1350 g_object_unref (G_OBJECT (priv->account_store));
1353 priv->account_store = g_object_ref (G_OBJECT (account_store));
1355 priv->account_removed_signal =
1356 g_signal_connect (G_OBJECT(account_store), "account_removed",
1357 G_CALLBACK (on_account_removed), self);
1359 priv->account_inserted_signal =
1360 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1361 G_CALLBACK (on_account_inserted), self);
1363 priv->account_changed_signal =
1364 g_signal_connect (G_OBJECT(account_store), "account_changed",
1365 G_CALLBACK (on_account_changed), self);
1367 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1368 priv->reselect = FALSE;
1369 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1371 g_object_unref (G_OBJECT (device));
1375 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1378 ModestFolderView *self;
1379 GtkTreeModel *model, *filter_model;
1382 self = MODEST_FOLDER_VIEW (user_data);
1384 if (!get_inner_models (self, &filter_model, NULL, &model))
1387 /* Remove outbox from model */
1388 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1389 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1390 g_object_unref (outbox);
1393 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1397 on_account_inserted (TnyAccountStore *account_store,
1398 TnyAccount *account,
1401 ModestFolderViewPrivate *priv;
1402 GtkTreeModel *model, *filter_model;
1404 /* Ignore transport account insertions, we're not showing them
1405 in the folder view */
1406 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1409 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1412 /* If we're adding a new account, and there is no previous
1413 one, we need to select the visible server account */
1414 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1415 !priv->visible_account_id)
1416 modest_widget_memory_restore (modest_runtime_get_conf(),
1417 G_OBJECT (user_data),
1418 MODEST_CONF_FOLDER_VIEW_KEY);
1422 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1423 &filter_model, NULL, &model))
1426 /* Insert the account in the model */
1427 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1429 /* When the model is a list store (plain representation) the
1430 outbox is not a child of any account so we have to manually
1431 delete it because removing the local folders account won't
1432 delete it (because tny_folder_get_account() is not defined
1433 for a merge folder */
1434 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1435 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1437 priv->outbox_deleted_handler =
1438 g_signal_connect (account,
1440 G_CALLBACK (on_outbox_deleted_cb),
1444 /* Refilter the model */
1445 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1450 same_account_selected (ModestFolderView *self,
1451 TnyAccount *account)
1453 ModestFolderViewPrivate *priv;
1454 gboolean same_account = FALSE;
1456 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1458 if (priv->cur_folder_store) {
1459 TnyAccount *selected_folder_account = NULL;
1461 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1462 selected_folder_account =
1463 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1465 selected_folder_account =
1466 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1469 if (selected_folder_account == account)
1470 same_account = TRUE;
1472 g_object_unref (selected_folder_account);
1474 return same_account;
1479 * Selects the first inbox or the local account in an idle
1482 on_idle_select_first_inbox_or_local (gpointer user_data)
1484 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1486 gdk_threads_enter ();
1487 modest_folder_view_select_first_inbox_or_local (self);
1488 gdk_threads_leave ();
1494 on_account_changed (TnyAccountStore *account_store,
1495 TnyAccount *tny_account,
1498 ModestFolderView *self;
1499 ModestFolderViewPrivate *priv;
1500 GtkTreeModel *model, *filter_model;
1501 GtkTreeSelection *sel;
1502 gboolean same_account;
1504 /* Ignore transport account insertions, we're not showing them
1505 in the folder view */
1506 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1509 self = MODEST_FOLDER_VIEW (user_data);
1510 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1512 /* Get the inner model */
1513 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1514 &filter_model, NULL, &model))
1517 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1519 /* Invalidate the cur_folder_store only if the selected folder
1520 belongs to the account that is being removed */
1521 same_account = same_account_selected (self, tny_account);
1523 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1524 gtk_tree_selection_unselect_all (sel);
1527 /* Remove the account from the model */
1528 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1530 /* Insert the account in the model */
1531 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1533 /* Refilter the model */
1534 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1536 /* Select the first INBOX if the currently selected folder
1537 belongs to the account that is being deleted */
1538 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1539 g_idle_add (on_idle_select_first_inbox_or_local, self);
1543 on_account_removed (TnyAccountStore *account_store,
1544 TnyAccount *account,
1547 ModestFolderView *self = NULL;
1548 ModestFolderViewPrivate *priv;
1549 GtkTreeModel *model, *filter_model;
1550 GtkTreeSelection *sel = NULL;
1551 gboolean same_account = FALSE;
1553 /* Ignore transport account removals, we're not showing them
1554 in the folder view */
1555 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1558 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1559 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1563 self = MODEST_FOLDER_VIEW (user_data);
1564 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1566 /* Invalidate the cur_folder_store only if the selected folder
1567 belongs to the account that is being removed */
1568 same_account = same_account_selected (self, account);
1570 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1571 gtk_tree_selection_unselect_all (sel);
1574 /* Invalidate row to select only if the folder to select
1575 belongs to the account that is being removed*/
1576 if (priv->folder_to_select) {
1577 TnyAccount *folder_to_select_account = NULL;
1579 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1580 if (folder_to_select_account == account) {
1581 modest_folder_view_disable_next_folder_selection (self);
1582 g_object_unref (priv->folder_to_select);
1583 priv->folder_to_select = NULL;
1585 g_object_unref (folder_to_select_account);
1588 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1589 &filter_model, NULL, &model))
1592 /* Disconnect the signal handler */
1593 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1594 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1595 if (g_signal_handler_is_connected (account,
1596 priv->outbox_deleted_handler))
1597 g_signal_handler_disconnect (account,
1598 priv->outbox_deleted_handler);
1601 /* Remove the account from the model */
1602 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1604 /* If the removed account is the currently viewed one then
1605 clear the configuration value. The new visible account will be the default account */
1606 if (priv->visible_account_id &&
1607 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1609 /* Clear the current visible account_id */
1610 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1611 modest_folder_view_set_mailbox (self, NULL);
1613 /* Call the restore method, this will set the new visible account */
1614 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1615 MODEST_CONF_FOLDER_VIEW_KEY);
1618 /* Refilter the model */
1619 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1621 /* Select the first INBOX if the currently selected folder
1622 belongs to the account that is being deleted */
1624 g_idle_add (on_idle_select_first_inbox_or_local, self);
1628 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1630 GtkTreeViewColumn *col;
1632 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1634 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1636 g_printerr ("modest: failed get column for title\n");
1640 gtk_tree_view_column_set_title (col, title);
1641 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1646 modest_folder_view_on_map (ModestFolderView *self,
1647 GdkEventExpose *event,
1650 ModestFolderViewPrivate *priv;
1652 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1654 /* This won't happen often */
1655 if (G_UNLIKELY (priv->reselect)) {
1656 /* Select the first inbox or the local account if not found */
1658 /* TODO: this could cause a lock at startup, so we
1659 comment it for the moment. We know that this will
1660 be a bug, because the INBOX is not selected, but we
1661 need to rewrite some parts of Modest to avoid the
1662 deathlock situation */
1663 /* TODO: check if this is still the case */
1664 priv->reselect = FALSE;
1665 modest_folder_view_select_first_inbox_or_local (self);
1666 /* Notify the display name observers */
1667 g_signal_emit (G_OBJECT(self),
1668 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1672 if (priv->reexpand) {
1673 expand_root_items (self);
1674 priv->reexpand = FALSE;
1681 modest_folder_view_new (TnyFolderStoreQuery *query)
1684 ModestFolderViewPrivate *priv;
1685 GtkTreeSelection *sel;
1687 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1688 #ifdef MODEST_TOOLKIT_HILDON2
1689 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1692 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1695 priv->query = g_object_ref (query);
1697 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1698 priv->changed_signal = g_signal_connect (sel, "changed",
1699 G_CALLBACK (on_selection_changed), self);
1701 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1703 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1705 return GTK_WIDGET(self);
1708 /* this feels dirty; any other way to expand all the root items? */
1710 expand_root_items (ModestFolderView *self)
1713 GtkTreeModel *model;
1716 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1717 path = gtk_tree_path_new_first ();
1719 /* all folders should have child items, so.. */
1721 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1722 gtk_tree_path_next (path);
1723 } while (gtk_tree_model_get_iter (model, &iter, path));
1725 gtk_tree_path_free (path);
1729 is_parent_of (TnyFolder *a, TnyFolder *b)
1732 gboolean retval = FALSE;
1734 a_id = tny_folder_get_id (a);
1736 gchar *string_to_match;
1739 string_to_match = g_strconcat (a_id, "/", NULL);
1740 b_id = tny_folder_get_id (b);
1741 retval = g_str_has_prefix (b_id, string_to_match);
1742 g_free (string_to_match);
1748 typedef struct _ForeachFolderInfo {
1751 } ForeachFolderInfo;
1754 foreach_folder_with_id (GtkTreeModel *model,
1759 ForeachFolderInfo *info;
1762 info = (ForeachFolderInfo *) data;
1763 gtk_tree_model_get (model, iter,
1764 INSTANCE_COLUMN, &instance,
1767 if (TNY_IS_FOLDER (instance)) {
1770 id = tny_folder_get_id (TNY_FOLDER (instance));
1772 collate = g_utf8_collate_key (id, -1);
1773 info->found = !strcmp (info->needle, collate);
1779 g_object_unref (instance);
1787 has_folder_with_id (ModestFolderView *self, const gchar *id)
1789 GtkTreeModel *model;
1790 ForeachFolderInfo info = {NULL, FALSE};
1792 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1793 info.needle = g_utf8_collate_key (id, -1);
1795 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1796 g_free (info.needle);
1802 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1805 gboolean retval = FALSE;
1807 a_id = tny_folder_get_id (a);
1810 b_id = tny_folder_get_id (b);
1813 const gchar *last_bar;
1814 gchar *string_to_match;
1815 last_bar = g_strrstr (b_id, "/");
1820 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1821 retval = has_folder_with_id (self, string_to_match);
1822 g_free (string_to_match);
1830 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1832 ModestFolderViewPrivate *priv;
1833 TnyIterator *iterator;
1834 gboolean retval = TRUE;
1836 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1837 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1839 for (iterator = tny_list_create_iterator (priv->list_to_move);
1840 retval && !tny_iterator_is_done (iterator);
1841 tny_iterator_next (iterator)) {
1843 instance = tny_iterator_get_current (iterator);
1844 if (instance == (GObject *) folder) {
1846 } else if (TNY_IS_FOLDER (instance)) {
1847 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1849 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1852 g_object_unref (instance);
1854 g_object_unref (iterator);
1861 * We use this function to implement the
1862 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1863 * account in this case, and the local folders.
1866 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1868 ModestFolderViewPrivate *priv;
1869 gboolean retval = TRUE;
1870 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1871 GObject *instance = NULL;
1872 const gchar *id = NULL;
1874 gboolean found = FALSE;
1875 gboolean cleared = FALSE;
1876 ModestTnyFolderRules rules = 0;
1879 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1880 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1882 gtk_tree_model_get (model, iter,
1883 NAME_COLUMN, &fname,
1885 INSTANCE_COLUMN, &instance,
1888 /* Do not show if there is no instance, this could indeed
1889 happen when the model is being modified while it's being
1890 drawn. This could occur for example when moving folders
1897 if (TNY_IS_ACCOUNT (instance)) {
1898 TnyAccount *acc = TNY_ACCOUNT (instance);
1899 const gchar *account_id = tny_account_get_id (acc);
1901 /* If it isn't a special folder,
1902 * don't show it unless it is the visible account: */
1903 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1904 !modest_tny_account_is_virtual_local_folders (acc) &&
1905 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1907 /* Show only the visible account id */
1908 if (priv->visible_account_id) {
1909 if (strcmp (account_id, priv->visible_account_id))
1916 /* Never show these to the user. They are merged into one folder
1917 * in the local-folders account instead: */
1918 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1921 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1922 /* Only show special folders for current account if needed */
1923 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1924 TnyAccount *account;
1926 account = tny_folder_get_account (TNY_FOLDER (instance));
1928 if (TNY_IS_ACCOUNT (account)) {
1929 const gchar *account_id = tny_account_get_id (account);
1931 if (!modest_tny_account_is_virtual_local_folders (account) &&
1932 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1933 /* Show only the visible account id */
1934 if (priv->visible_account_id) {
1935 if (strcmp (account_id, priv->visible_account_id)) {
1937 } else if (priv->mailbox) {
1938 /* Filter mailboxes */
1939 if (!g_str_has_prefix (fname, priv->mailbox)) {
1941 } else if (!strcmp (fname, priv->mailbox)) {
1942 /* Hide mailbox parent */
1948 g_object_unref (account);
1955 /* Check hiding (if necessary) */
1956 cleared = modest_email_clipboard_cleared (priv->clipboard);
1957 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1958 id = tny_folder_get_id (TNY_FOLDER(instance));
1959 if (priv->hidding_ids != NULL)
1960 for (i=0; i < priv->n_selected && !found; i++)
1961 if (priv->hidding_ids[i] != NULL && id != NULL)
1962 found = (!strcmp (priv->hidding_ids[i], id));
1967 /* If this is a move to dialog, hide Sent, Outbox and Drafts
1968 folder as no message can be move there according to UI specs */
1969 if (retval && !priv->show_non_move) {
1970 if (priv->list_to_move &&
1971 tny_list_get_length (priv->list_to_move) > 0 &&
1972 TNY_IS_FOLDER (instance)) {
1973 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1975 if (retval && TNY_IS_FOLDER (instance) &&
1976 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1978 case TNY_FOLDER_TYPE_OUTBOX:
1979 case TNY_FOLDER_TYPE_SENT:
1980 case TNY_FOLDER_TYPE_DRAFTS:
1983 case TNY_FOLDER_TYPE_UNKNOWN:
1984 case TNY_FOLDER_TYPE_NORMAL:
1985 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1986 if (type == TNY_FOLDER_TYPE_INVALID)
1987 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1989 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1990 type == TNY_FOLDER_TYPE_SENT
1991 || type == TNY_FOLDER_TYPE_DRAFTS)
1998 if (retval && TNY_IS_ACCOUNT (instance) &&
1999 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2000 ModestProtocolType protocol_type;
2002 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2003 retval = !modest_protocol_registry_protocol_type_has_tag
2004 (modest_runtime_get_protocol_registry (),
2006 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2010 /* apply special filters */
2011 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2012 if (TNY_IS_ACCOUNT (instance))
2016 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2017 if (TNY_IS_FOLDER (instance))
2021 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2022 if (TNY_IS_ACCOUNT (instance)) {
2023 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2025 } else if (TNY_IS_FOLDER (instance)) {
2026 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2031 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2032 if (TNY_IS_ACCOUNT (instance)) {
2033 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2035 } else if (TNY_IS_FOLDER (instance)) {
2036 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2041 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2042 /* A mailbox is a fake folder with an @ in the middle of the name */
2043 if (!TNY_IS_FOLDER (instance) ||
2044 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2047 const gchar *folder_name;
2048 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2049 if (!folder_name || strchr (folder_name, '@') == NULL)
2055 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2056 if (TNY_IS_FOLDER (instance)) {
2057 /* Check folder rules */
2058 ModestTnyFolderRules rules;
2060 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2061 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2062 } else if (TNY_IS_ACCOUNT (instance)) {
2063 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2071 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2072 if (TNY_IS_FOLDER (instance)) {
2073 TnyFolderType guess_type;
2075 if (TNY_FOLDER_TYPE_NORMAL) {
2076 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2082 case TNY_FOLDER_TYPE_OUTBOX:
2083 case TNY_FOLDER_TYPE_SENT:
2084 case TNY_FOLDER_TYPE_DRAFTS:
2085 case TNY_FOLDER_TYPE_ARCHIVE:
2086 case TNY_FOLDER_TYPE_INBOX:
2089 case TNY_FOLDER_TYPE_UNKNOWN:
2090 case TNY_FOLDER_TYPE_NORMAL:
2096 } else if (TNY_IS_ACCOUNT (instance)) {
2101 if (retval && TNY_IS_FOLDER (instance)) {
2102 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2105 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2106 if (TNY_IS_FOLDER (instance)) {
2107 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2108 } else if (TNY_IS_ACCOUNT (instance)) {
2113 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2114 if (TNY_IS_FOLDER (instance)) {
2115 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2116 } else if (TNY_IS_ACCOUNT (instance)) {
2121 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2122 if (TNY_IS_FOLDER (instance)) {
2123 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2124 } else if (TNY_IS_ACCOUNT (instance)) {
2130 g_object_unref (instance);
2138 modest_folder_view_update_model (ModestFolderView *self,
2139 TnyAccountStore *account_store)
2141 ModestFolderViewPrivate *priv;
2142 GtkTreeModel *model /* , *old_model */;
2143 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2145 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2146 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2149 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2151 /* Notify that there is no folder selected */
2152 g_signal_emit (G_OBJECT(self),
2153 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2155 if (priv->cur_folder_store) {
2156 g_object_unref (priv->cur_folder_store);
2157 priv->cur_folder_store = NULL;
2160 /* FIXME: the local accounts are not shown when the query
2161 selects only the subscribed folders */
2162 #ifdef MODEST_TOOLKIT_HILDON2
2163 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2164 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2165 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2166 MODEST_FOLDER_PATH_SEPARATOR);
2168 model = tny_gtk_folder_store_tree_model_new (NULL);
2171 /* When the model is a list store (plain representation) the
2172 outbox is not a child of any account so we have to manually
2173 delete it because removing the local folders account won't
2174 delete it (because tny_folder_get_account() is not defined
2175 for a merge folder */
2176 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2177 TnyAccount *account;
2178 ModestTnyAccountStore *acc_store;
2180 acc_store = modest_runtime_get_account_store ();
2181 account = modest_tny_account_store_get_local_folders_account (acc_store);
2183 if (g_signal_handler_is_connected (account,
2184 priv->outbox_deleted_handler))
2185 g_signal_handler_disconnect (account,
2186 priv->outbox_deleted_handler);
2188 priv->outbox_deleted_handler =
2189 g_signal_connect (account,
2191 G_CALLBACK (on_outbox_deleted_cb),
2193 g_object_unref (account);
2196 /* Get the accounts: */
2197 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2199 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2201 sortable = gtk_tree_model_sort_new_with_model (model);
2202 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2204 GTK_SORT_ASCENDING);
2205 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2207 cmp_rows, NULL, NULL);
2209 /* Create filter model */
2210 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2211 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2217 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2218 #ifndef MODEST_TOOLKIT_HILDON2
2219 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2220 (GCallback) on_row_inserted_maybe_select_folder, self);
2223 g_object_unref (model);
2224 g_object_unref (filter_model);
2225 g_object_unref (sortable);
2227 /* Force a reselection of the INBOX next time the widget is shown */
2228 priv->reselect = TRUE;
2235 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2237 GtkTreeModel *model = NULL;
2238 TnyFolderStore *folder = NULL;
2240 ModestFolderView *tree_view = NULL;
2241 ModestFolderViewPrivate *priv = NULL;
2242 gboolean selected = FALSE;
2244 g_return_if_fail (sel);
2245 g_return_if_fail (user_data);
2247 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2249 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2251 tree_view = MODEST_FOLDER_VIEW (user_data);
2254 gtk_tree_model_get (model, &iter,
2255 INSTANCE_COLUMN, &folder,
2258 /* If the folder is the same do not notify */
2259 if (folder && priv->cur_folder_store == folder) {
2260 g_object_unref (folder);
2265 /* Current folder was unselected */
2266 if (priv->cur_folder_store) {
2267 /* We must do this firstly because a libtinymail-camel
2268 implementation detail. If we issue the signal
2269 before doing the sync_async, then that signal could
2270 cause (and it actually does it) a free of the
2271 summary of the folder (because the main window will
2272 clear the headers view */
2273 if (TNY_IS_FOLDER(priv->cur_folder_store))
2274 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2275 FALSE, NULL, NULL, NULL);
2277 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2278 priv->cur_folder_store, FALSE);
2280 g_object_unref (priv->cur_folder_store);
2281 priv->cur_folder_store = NULL;
2284 /* New current references */
2285 priv->cur_folder_store = folder;
2287 /* New folder has been selected. Do not notify if there is
2288 nothing new selected */
2290 g_signal_emit (G_OBJECT(tree_view),
2291 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2292 0, priv->cur_folder_store, TRUE);
2297 on_row_activated (GtkTreeView *treeview,
2298 GtkTreePath *treepath,
2299 GtkTreeViewColumn *column,
2302 GtkTreeModel *model = NULL;
2303 TnyFolderStore *folder = NULL;
2305 ModestFolderView *self = NULL;
2306 ModestFolderViewPrivate *priv = NULL;
2308 g_return_if_fail (treeview);
2309 g_return_if_fail (user_data);
2311 self = MODEST_FOLDER_VIEW (user_data);
2312 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2314 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2316 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2319 gtk_tree_model_get (model, &iter,
2320 INSTANCE_COLUMN, &folder,
2323 g_signal_emit (G_OBJECT(self),
2324 signals[FOLDER_ACTIVATED_SIGNAL],
2327 #ifdef MODEST_TOOLKIT_HILDON2
2328 HildonUIMode ui_mode;
2329 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2330 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2331 if (priv->cur_folder_store)
2332 g_object_unref (priv->cur_folder_store);
2333 priv->cur_folder_store = g_object_ref (folder);
2337 g_object_unref (folder);
2341 modest_folder_view_get_selected (ModestFolderView *self)
2343 ModestFolderViewPrivate *priv;
2345 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2347 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2348 if (priv->cur_folder_store)
2349 g_object_ref (priv->cur_folder_store);
2351 return priv->cur_folder_store;
2355 get_cmp_rows_type_pos (GObject *folder)
2357 /* Remote accounts -> Local account -> MMC account .*/
2360 if (TNY_IS_ACCOUNT (folder) &&
2361 modest_tny_account_is_virtual_local_folders (
2362 TNY_ACCOUNT (folder))) {
2364 } else if (TNY_IS_ACCOUNT (folder)) {
2365 TnyAccount *account = TNY_ACCOUNT (folder);
2366 const gchar *account_id = tny_account_get_id (account);
2367 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2373 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2374 return -1; /* Should never happen */
2379 inbox_is_special (TnyFolderStore *folder_store)
2381 gboolean is_special = TRUE;
2383 if (TNY_IS_FOLDER (folder_store)) {
2387 gchar *last_inbox_bar;
2389 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2390 downcase = g_utf8_strdown (id, -1);
2391 last_bar = g_strrstr (downcase, "/");
2393 last_inbox_bar = g_strrstr (downcase, "inbox/");
2394 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2405 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2407 TnyAccount *account;
2408 gboolean is_special;
2409 /* Inbox, Outbox, Drafts, Sent, User */
2412 if (!TNY_IS_FOLDER (folder_store))
2415 case TNY_FOLDER_TYPE_INBOX:
2417 account = tny_folder_get_account (folder_store);
2418 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2420 /* In inbox case we need to know if the inbox is really the top
2421 * inbox of the account, or if it's a submailbox inbox. To do
2422 * this we'll apply an heuristic rule: Find last "/" and check
2423 * if it's preceeded by another Inbox */
2424 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2425 g_object_unref (account);
2426 return is_special?0:4;
2429 case TNY_FOLDER_TYPE_OUTBOX:
2430 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2432 case TNY_FOLDER_TYPE_DRAFTS:
2434 account = tny_folder_get_account (folder_store);
2435 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2436 g_object_unref (account);
2437 return is_special?1:4;
2440 case TNY_FOLDER_TYPE_SENT:
2442 account = tny_folder_get_account (folder_store);
2443 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2444 g_object_unref (account);
2445 return is_special?3:4;
2454 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2456 const gchar *a1_name, *a2_name;
2458 a1_name = tny_account_get_name (a1);
2459 a2_name = tny_account_get_name (a2);
2461 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2465 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2467 TnyAccount *a1 = NULL, *a2 = NULL;
2470 if (TNY_IS_ACCOUNT (s1)) {
2471 a1 = TNY_ACCOUNT (g_object_ref (s1));
2472 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2473 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2476 if (TNY_IS_ACCOUNT (s2)) {
2477 a2 = TNY_ACCOUNT (g_object_ref (s2));
2478 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2479 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2496 /* First we sort with the type of account */
2497 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2501 cmp = compare_account_names (a1, a2);
2505 g_object_unref (a1);
2507 g_object_unref (a2);
2513 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2515 gint is_account1, is_account2;
2517 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2518 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2520 return is_account2 - is_account1;
2524 * This function orders the mail accounts according to these rules:
2525 * 1st - remote accounts
2526 * 2nd - local account
2530 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2534 gchar *name1 = NULL;
2535 gchar *name2 = NULL;
2536 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2537 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2538 GObject *folder1 = NULL;
2539 GObject *folder2 = NULL;
2541 gtk_tree_model_get (tree_model, iter1,
2542 NAME_COLUMN, &name1,
2544 INSTANCE_COLUMN, &folder1,
2546 gtk_tree_model_get (tree_model, iter2,
2547 NAME_COLUMN, &name2,
2548 TYPE_COLUMN, &type2,
2549 INSTANCE_COLUMN, &folder2,
2552 /* Return if we get no folder. This could happen when folder
2553 operations are happening. The model is updated after the
2554 folder copy/move actually occurs, so there could be
2555 situations where the model to be drawn is not correct */
2556 if (!folder1 || !folder2)
2559 /* Sort by type. First the special folders, then the archives */
2560 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2564 /* Now we sort using the account of each folder */
2565 if (TNY_IS_FOLDER_STORE (folder1) &&
2566 TNY_IS_FOLDER_STORE (folder2)) {
2567 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2571 /* Each group is preceeded by its account */
2572 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2577 /* Pure sort by name */
2578 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2581 g_object_unref(G_OBJECT(folder1));
2583 g_object_unref(G_OBJECT(folder2));
2591 /*****************************************************************************/
2592 /* DRAG and DROP stuff */
2593 /*****************************************************************************/
2595 * This function fills the #GtkSelectionData with the row and the
2596 * model that has been dragged. It's called when this widget is a
2597 * source for dnd after the event drop happened
2600 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2601 guint info, guint time, gpointer data)
2603 GtkTreeSelection *selection;
2604 GtkTreeModel *model;
2606 GtkTreePath *source_row;
2608 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2609 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2611 source_row = gtk_tree_model_get_path (model, &iter);
2612 gtk_tree_set_row_drag_data (selection_data,
2616 gtk_tree_path_free (source_row);
2620 typedef struct _DndHelper {
2621 ModestFolderView *folder_view;
2622 gboolean delete_source;
2623 GtkTreePath *source_row;
2627 dnd_helper_destroyer (DndHelper *helper)
2629 /* Free the helper */
2630 gtk_tree_path_free (helper->source_row);
2631 g_slice_free (DndHelper, helper);
2635 xfer_folder_cb (ModestMailOperation *mail_op,
2636 TnyFolder *new_folder,
2640 /* Select the folder */
2641 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2647 /* get the folder for the row the treepath refers to. */
2648 /* folder must be unref'd */
2649 static TnyFolderStore *
2650 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2653 TnyFolderStore *folder = NULL;
2655 if (gtk_tree_model_get_iter (model,&iter, path))
2656 gtk_tree_model_get (model, &iter,
2657 INSTANCE_COLUMN, &folder,
2664 * This function is used by drag_data_received_cb to manage drag and
2665 * drop of a header, i.e, and drag from the header view to the folder
2669 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2670 GtkTreeModel *dest_model,
2671 GtkTreePath *dest_row,
2672 GtkSelectionData *selection_data)
2674 TnyList *headers = NULL;
2675 TnyFolder *folder = NULL, *src_folder = NULL;
2676 TnyFolderType folder_type;
2677 GtkTreeIter source_iter, dest_iter;
2678 ModestWindowMgr *mgr = NULL;
2679 ModestWindow *main_win = NULL;
2680 gchar **uris, **tmp;
2682 /* Build the list of headers */
2683 mgr = modest_runtime_get_window_mgr ();
2684 headers = tny_simple_list_new ();
2685 uris = modest_dnd_selection_data_get_paths (selection_data);
2688 while (*tmp != NULL) {
2691 gboolean first = TRUE;
2694 path = gtk_tree_path_new_from_string (*tmp);
2695 gtk_tree_model_get_iter (source_model, &source_iter, path);
2696 gtk_tree_model_get (source_model, &source_iter,
2697 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2700 /* Do not enable d&d of headers already opened */
2701 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2702 tny_list_append (headers, G_OBJECT (header));
2704 if (G_UNLIKELY (first)) {
2705 src_folder = tny_header_get_folder (header);
2709 /* Free and go on */
2710 gtk_tree_path_free (path);
2711 g_object_unref (header);
2716 /* This could happen ig we perform a d&d very quickly over the
2717 same row that row could dissapear because message is
2719 if (!TNY_IS_FOLDER (src_folder))
2722 /* Get the target folder */
2723 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2724 gtk_tree_model_get (dest_model, &dest_iter,
2728 if (!folder || !TNY_IS_FOLDER(folder)) {
2729 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2733 folder_type = modest_tny_folder_guess_folder_type (folder);
2734 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2735 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2736 goto cleanup; /* cannot move messages there */
2739 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2740 /* g_warning ("folder not writable"); */
2741 goto cleanup; /* verboten! */
2744 /* Ask for confirmation to move */
2745 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2747 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2751 /* Transfer messages */
2752 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2757 if (G_IS_OBJECT (src_folder))
2758 g_object_unref (src_folder);
2759 if (G_IS_OBJECT(folder))
2760 g_object_unref (G_OBJECT (folder));
2761 if (G_IS_OBJECT(headers))
2762 g_object_unref (headers);
2766 TnyFolderStore *src_folder;
2767 TnyFolderStore *dst_folder;
2768 ModestFolderView *folder_view;
2773 dnd_folder_info_destroyer (DndFolderInfo *info)
2775 if (info->src_folder)
2776 g_object_unref (info->src_folder);
2777 if (info->dst_folder)
2778 g_object_unref (info->dst_folder);
2779 g_slice_free (DndFolderInfo, info);
2783 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2784 GtkWindow *parent_window,
2785 TnyAccount *account)
2788 modest_ui_actions_on_account_connection_error (parent_window, account);
2790 /* Free the helper & info */
2791 dnd_helper_destroyer (info->helper);
2792 dnd_folder_info_destroyer (info);
2796 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2798 GtkWindow *parent_window,
2799 TnyAccount *account,
2802 DndFolderInfo *info = NULL;
2803 ModestMailOperation *mail_op;
2805 info = (DndFolderInfo *) user_data;
2807 if (err || canceled) {
2808 dnd_on_connection_failed_destroyer (info, parent_window, account);
2812 /* Do the mail operation */
2813 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2814 modest_ui_actions_move_folder_error_handler,
2815 info->src_folder, NULL);
2817 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2820 /* Transfer the folder */
2821 modest_mail_operation_xfer_folder (mail_op,
2822 TNY_FOLDER (info->src_folder),
2824 info->helper->delete_source,
2826 info->helper->folder_view);
2829 g_object_unref (G_OBJECT (mail_op));
2830 dnd_helper_destroyer (info->helper);
2831 dnd_folder_info_destroyer (info);
2836 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2838 GtkWindow *parent_window,
2839 TnyAccount *account,
2842 DndFolderInfo *info = NULL;
2844 info = (DndFolderInfo *) user_data;
2846 if (err || canceled) {
2847 dnd_on_connection_failed_destroyer (info, parent_window, account);
2851 /* Connect to source folder and perform the copy/move */
2852 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2854 drag_and_drop_from_folder_view_src_folder_performer,
2859 * This function is used by drag_data_received_cb to manage drag and
2860 * drop of a folder, i.e, and drag from the folder view to the same
2864 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2865 GtkTreeModel *dest_model,
2866 GtkTreePath *dest_row,
2867 GtkSelectionData *selection_data,
2870 GtkTreeIter dest_iter, iter;
2871 TnyFolderStore *dest_folder = NULL;
2872 TnyFolderStore *folder = NULL;
2873 gboolean forbidden = FALSE;
2875 DndFolderInfo *info = NULL;
2877 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2879 g_warning ("%s: BUG: no main window", __FUNCTION__);
2880 dnd_helper_destroyer (helper);
2885 /* check the folder rules for the destination */
2886 folder = tree_path_to_folder (dest_model, dest_row);
2887 if (TNY_IS_FOLDER(folder)) {
2888 ModestTnyFolderRules rules =
2889 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2890 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2891 } else if (TNY_IS_FOLDER_STORE(folder)) {
2892 /* enable local root as destination for folders */
2893 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2894 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2897 g_object_unref (folder);
2900 /* check the folder rules for the source */
2901 folder = tree_path_to_folder (source_model, helper->source_row);
2902 if (TNY_IS_FOLDER(folder)) {
2903 ModestTnyFolderRules rules =
2904 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2905 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2908 g_object_unref (folder);
2912 /* Check if the drag is possible */
2913 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2915 modest_platform_run_information_dialog ((GtkWindow *) win,
2916 _("mail_in_ui_folder_move_target_error"),
2918 /* Restore the previous selection */
2919 folder = tree_path_to_folder (source_model, helper->source_row);
2921 if (TNY_IS_FOLDER (folder))
2922 modest_folder_view_select_folder (helper->folder_view,
2923 TNY_FOLDER (folder), FALSE);
2924 g_object_unref (folder);
2926 dnd_helper_destroyer (helper);
2931 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2932 gtk_tree_model_get (dest_model, &dest_iter,
2935 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2936 gtk_tree_model_get (source_model, &iter,
2940 /* Create the info for the performer */
2941 info = g_slice_new0 (DndFolderInfo);
2942 info->src_folder = g_object_ref (folder);
2943 info->dst_folder = g_object_ref (dest_folder);
2944 info->helper = helper;
2946 /* Connect to the destination folder and perform the copy/move */
2947 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2949 drag_and_drop_from_folder_view_dst_folder_performer,
2953 g_object_unref (dest_folder);
2954 g_object_unref (folder);
2958 * This function receives the data set by the "drag-data-get" signal
2959 * handler. This information comes within the #GtkSelectionData. This
2960 * function will manage both the drags of folders of the treeview and
2961 * drags of headers of the header view widget.
2964 on_drag_data_received (GtkWidget *widget,
2965 GdkDragContext *context,
2968 GtkSelectionData *selection_data,
2973 GtkWidget *source_widget;
2974 GtkTreeModel *dest_model, *source_model;
2975 GtkTreePath *source_row, *dest_row;
2976 GtkTreeViewDropPosition pos;
2977 gboolean delete_source = FALSE;
2978 gboolean success = FALSE;
2980 /* Do not allow further process */
2981 g_signal_stop_emission_by_name (widget, "drag-data-received");
2982 source_widget = gtk_drag_get_source_widget (context);
2984 /* Get the action */
2985 if (context->action == GDK_ACTION_MOVE) {
2986 delete_source = TRUE;
2988 /* Notify that there is no folder selected. We need to
2989 do this in order to update the headers view (and
2990 its monitors, because when moving, the old folder
2991 won't longer exist. We can not wait for the end of
2992 the operation, because the operation won't start if
2993 the folder is in use */
2994 if (source_widget == widget) {
2995 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2996 gtk_tree_selection_unselect_all (sel);
3000 /* Check if the get_data failed */
3001 if (selection_data == NULL || selection_data->length < 0)
3004 /* Select the destination model */
3005 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3007 /* Get the path to the destination row. Can not call
3008 gtk_tree_view_get_drag_dest_row() because the source row
3009 is not selected anymore */
3010 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3013 /* Only allow drops IN other rows */
3015 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3016 pos == GTK_TREE_VIEW_DROP_AFTER)
3020 /* Drags from the header view */
3021 if (source_widget != widget) {
3022 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3024 drag_and_drop_from_header_view (source_model,
3029 DndHelper *helper = NULL;
3031 /* Get the source model and row */
3032 gtk_tree_get_row_drag_data (selection_data,
3036 /* Create the helper */
3037 helper = g_slice_new0 (DndHelper);
3038 helper->delete_source = delete_source;
3039 helper->source_row = gtk_tree_path_copy (source_row);
3040 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3042 drag_and_drop_from_folder_view (source_model,
3048 gtk_tree_path_free (source_row);
3052 gtk_tree_path_free (dest_row);
3055 /* Finish the drag and drop */
3056 gtk_drag_finish (context, success, FALSE, time);
3060 * We define a "drag-drop" signal handler because we do not want to
3061 * use the default one, because the default one always calls
3062 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3063 * signal handler, because there we have all the information available
3064 * to know if the dnd was a success or not.
3067 drag_drop_cb (GtkWidget *widget,
3068 GdkDragContext *context,
3076 if (!context->targets)
3079 /* Check if we're dragging a folder row */
3080 target = gtk_drag_dest_find_target (widget, context, NULL);
3082 /* Request the data from the source. */
3083 gtk_drag_get_data(widget, context, target, time);
3089 * This function expands a node of a tree view if it's not expanded
3090 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3091 * does that, so that's why they're here.
3094 expand_row_timeout (gpointer data)
3096 GtkTreeView *tree_view = data;
3097 GtkTreePath *dest_path = NULL;
3098 GtkTreeViewDropPosition pos;
3099 gboolean result = FALSE;
3101 gdk_threads_enter ();
3103 gtk_tree_view_get_drag_dest_row (tree_view,
3108 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3109 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3110 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3111 gtk_tree_path_free (dest_path);
3115 gtk_tree_path_free (dest_path);
3120 gdk_threads_leave ();
3126 * This function is called whenever the pointer is moved over a widget
3127 * while dragging some data. It installs a timeout that will expand a
3128 * node of the treeview if not expanded yet. This function also calls
3129 * gdk_drag_status in order to set the suggested action that will be
3130 * used by the "drag-data-received" signal handler to know if we
3131 * should do a move or just a copy of the data.
3134 on_drag_motion (GtkWidget *widget,
3135 GdkDragContext *context,
3141 GtkTreeViewDropPosition pos;
3142 GtkTreePath *dest_row;
3143 GtkTreeModel *dest_model;
3144 ModestFolderViewPrivate *priv;
3145 GdkDragAction suggested_action;
3146 gboolean valid_location = FALSE;
3147 TnyFolderStore *folder = NULL;
3149 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3151 if (priv->timer_expander != 0) {
3152 g_source_remove (priv->timer_expander);
3153 priv->timer_expander = 0;
3156 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3161 /* Do not allow drops between folders */
3163 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3164 pos == GTK_TREE_VIEW_DROP_AFTER) {
3165 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3166 gdk_drag_status(context, 0, time);
3167 valid_location = FALSE;
3170 valid_location = TRUE;
3173 /* Check that the destination folder is writable */
3174 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3175 folder = tree_path_to_folder (dest_model, dest_row);
3176 if (folder && TNY_IS_FOLDER (folder)) {
3177 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3179 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3180 valid_location = FALSE;
3185 /* Expand the selected row after 1/2 second */
3186 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3187 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3189 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3191 /* Select the desired action. By default we pick MOVE */
3192 suggested_action = GDK_ACTION_MOVE;
3194 if (context->actions == GDK_ACTION_COPY)
3195 gdk_drag_status(context, GDK_ACTION_COPY, time);
3196 else if (context->actions == GDK_ACTION_MOVE)
3197 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3198 else if (context->actions & suggested_action)
3199 gdk_drag_status(context, suggested_action, time);
3201 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3205 g_object_unref (folder);
3207 gtk_tree_path_free (dest_row);
3209 g_signal_stop_emission_by_name (widget, "drag-motion");
3211 return valid_location;
3215 * This function sets the treeview as a source and a target for dnd
3216 * events. It also connects all the requirede signals.
3219 setup_drag_and_drop (GtkTreeView *self)
3221 /* Set up the folder view as a dnd destination. Set only the
3222 highlight flag, otherwise gtk will have a different
3224 #ifdef MODEST_TOOLKIT_HILDON2
3227 gtk_drag_dest_set (GTK_WIDGET (self),
3228 GTK_DEST_DEFAULT_HIGHLIGHT,
3229 folder_view_drag_types,
3230 G_N_ELEMENTS (folder_view_drag_types),
3231 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3233 g_signal_connect (G_OBJECT (self),
3234 "drag_data_received",
3235 G_CALLBACK (on_drag_data_received),
3239 /* Set up the treeview as a dnd source */
3240 gtk_drag_source_set (GTK_WIDGET (self),
3242 folder_view_drag_types,
3243 G_N_ELEMENTS (folder_view_drag_types),
3244 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3246 g_signal_connect (G_OBJECT (self),
3248 G_CALLBACK (on_drag_motion),
3251 g_signal_connect (G_OBJECT (self),
3253 G_CALLBACK (on_drag_data_get),
3256 g_signal_connect (G_OBJECT (self),
3258 G_CALLBACK (drag_drop_cb),
3263 * This function manages the navigation through the folders using the
3264 * keyboard or the hardware keys in the device
3267 on_key_pressed (GtkWidget *self,
3271 GtkTreeSelection *selection;
3273 GtkTreeModel *model;
3274 gboolean retval = FALSE;
3276 /* Up and Down are automatically managed by the treeview */
3277 if (event->keyval == GDK_Return) {
3278 /* Expand/Collapse the selected row */
3279 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3280 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3283 path = gtk_tree_model_get_path (model, &iter);
3285 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3286 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3288 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3289 gtk_tree_path_free (path);
3291 /* No further processing */
3299 * We listen to the changes in the local folder account name key,
3300 * because we want to show the right name in the view. The local
3301 * folder account name corresponds to the device name in the Maemo
3302 * version. We do this because we do not want to query gconf on each
3303 * tree view refresh. It's better to cache it and change whenever
3307 on_configuration_key_changed (ModestConf* conf,
3309 ModestConfEvent event,
3310 ModestConfNotificationId id,
3311 ModestFolderView *self)
3313 ModestFolderViewPrivate *priv;
3316 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3317 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3319 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3320 g_free (priv->local_account_name);
3322 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3323 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3325 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3326 MODEST_CONF_DEVICE_NAME, NULL);
3328 /* Force a redraw */
3329 #if GTK_CHECK_VERSION(2, 8, 0)
3330 GtkTreeViewColumn * tree_column;
3332 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3334 gtk_tree_view_column_queue_resize (tree_column);
3336 gtk_widget_queue_draw (GTK_WIDGET (self));
3342 modest_folder_view_set_style (ModestFolderView *self,
3343 ModestFolderViewStyle style)
3345 ModestFolderViewPrivate *priv;
3347 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3348 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3349 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3351 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3354 priv->style = style;
3358 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3359 const gchar *account_id)
3361 ModestFolderViewPrivate *priv;
3362 GtkTreeModel *model;
3364 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3366 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3368 /* This will be used by the filter_row callback,
3369 * to decided which rows to show: */
3370 if (priv->visible_account_id) {
3371 g_free (priv->visible_account_id);
3372 priv->visible_account_id = NULL;
3375 priv->visible_account_id = g_strdup (account_id);
3378 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3379 if (GTK_IS_TREE_MODEL_FILTER (model))
3380 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3382 /* Save settings to gconf */
3383 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3384 MODEST_CONF_FOLDER_VIEW_KEY);
3386 /* Notify observers */
3387 g_signal_emit (G_OBJECT(self),
3388 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3393 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3395 ModestFolderViewPrivate *priv;
3397 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3399 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3401 return (const gchar *) priv->visible_account_id;
3405 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3409 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3411 gtk_tree_model_get (model, iter,
3415 gboolean result = FALSE;
3416 if (type == TNY_FOLDER_TYPE_INBOX) {
3420 *inbox_iter = *iter;
3424 if (gtk_tree_model_iter_children (model, &child, iter)) {
3425 if (find_inbox_iter (model, &child, inbox_iter))
3429 } while (gtk_tree_model_iter_next (model, iter));
3438 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3440 #ifndef MODEST_TOOLKIT_HILDON2
3441 GtkTreeModel *model;
3442 GtkTreeIter iter, inbox_iter;
3443 GtkTreeSelection *sel;
3444 GtkTreePath *path = NULL;
3446 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3448 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3452 expand_root_items (self);
3453 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3455 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3456 g_warning ("%s: model is empty", __FUNCTION__);
3460 if (find_inbox_iter (model, &iter, &inbox_iter))
3461 path = gtk_tree_model_get_path (model, &inbox_iter);
3463 path = gtk_tree_path_new_first ();
3465 /* Select the row and free */
3466 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3467 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3468 gtk_tree_path_free (path);
3471 gtk_widget_grab_focus (GTK_WIDGET(self));
3478 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3483 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3484 TnyFolder* a_folder;
3487 gtk_tree_model_get (model, iter,
3488 INSTANCE_COLUMN, &a_folder,
3494 if (folder == a_folder) {
3495 g_object_unref (a_folder);
3496 *folder_iter = *iter;
3499 g_object_unref (a_folder);
3501 if (gtk_tree_model_iter_children (model, &child, iter)) {
3502 if (find_folder_iter (model, &child, folder_iter, folder))
3506 } while (gtk_tree_model_iter_next (model, iter));
3511 #ifndef MODEST_TOOLKIT_HILDON2
3513 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3516 ModestFolderView *self)
3518 ModestFolderViewPrivate *priv = NULL;
3519 GtkTreeSelection *sel;
3520 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3521 GObject *instance = NULL;
3523 if (!MODEST_IS_FOLDER_VIEW(self))
3526 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3528 priv->reexpand = TRUE;
3530 gtk_tree_model_get (tree_model, iter,
3532 INSTANCE_COLUMN, &instance,
3538 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3539 priv->folder_to_select = g_object_ref (instance);
3541 g_object_unref (instance);
3543 if (priv->folder_to_select) {
3545 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3548 path = gtk_tree_model_get_path (tree_model, iter);
3549 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3551 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3553 gtk_tree_selection_select_iter (sel, iter);
3554 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3556 gtk_tree_path_free (path);
3560 modest_folder_view_disable_next_folder_selection (self);
3562 /* Refilter the model */
3563 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3569 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3571 ModestFolderViewPrivate *priv;
3573 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3575 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3577 if (priv->folder_to_select)
3578 g_object_unref(priv->folder_to_select);
3580 priv->folder_to_select = NULL;
3584 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3585 gboolean after_change)
3587 GtkTreeModel *model;
3588 GtkTreeIter iter, folder_iter;
3589 GtkTreeSelection *sel;
3590 ModestFolderViewPrivate *priv = NULL;
3592 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3593 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3595 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3598 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3599 gtk_tree_selection_unselect_all (sel);
3601 if (priv->folder_to_select)
3602 g_object_unref(priv->folder_to_select);
3603 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3607 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3612 /* Refilter the model, before selecting the folder */
3613 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3615 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3616 g_warning ("%s: model is empty", __FUNCTION__);
3620 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3623 path = gtk_tree_model_get_path (model, &folder_iter);
3624 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3626 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3627 gtk_tree_selection_select_iter (sel, &folder_iter);
3628 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3630 gtk_tree_path_free (path);
3638 modest_folder_view_copy_selection (ModestFolderView *self)
3640 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3642 /* Copy selection */
3643 _clipboard_set_selected_data (self, FALSE);
3647 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3649 ModestFolderViewPrivate *priv = NULL;
3650 GtkTreeModel *model = NULL;
3651 const gchar **hidding = NULL;
3652 guint i, n_selected;
3654 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3655 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3657 /* Copy selection */
3658 if (!_clipboard_set_selected_data (folder_view, TRUE))
3661 /* Get hidding ids */
3662 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3664 /* Clear hidding array created by previous cut operation */
3665 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3667 /* Copy hidding array */
3668 priv->n_selected = n_selected;
3669 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3670 for (i=0; i < n_selected; i++)
3671 priv->hidding_ids[i] = g_strdup(hidding[i]);
3673 /* Hide cut folders */
3674 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3675 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3679 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3680 ModestFolderView *folder_view_dst)
3682 GtkTreeModel *filter_model = NULL;
3683 GtkTreeModel *model = NULL;
3684 GtkTreeModel *new_filter_model = NULL;
3686 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3687 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3690 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3691 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3693 /* Build new filter model */
3694 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3695 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3699 /* Set copied model */
3700 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3701 #ifndef MODEST_TOOLKIT_HILDON2
3702 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3703 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3707 g_object_unref (new_filter_model);
3711 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3714 GtkTreeModel *model = NULL;
3715 ModestFolderViewPrivate* priv;
3717 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3719 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3720 priv->show_non_move = show;
3721 /* modest_folder_view_update_model(folder_view, */
3722 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3724 /* Hide special folders */
3725 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3726 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3727 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3732 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3735 ModestFolderViewPrivate* priv;
3737 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3739 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3740 priv->show_message_count = show;
3742 g_object_set (G_OBJECT (priv->messages_renderer),
3743 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3747 /* Returns FALSE if it did not selected anything */
3749 _clipboard_set_selected_data (ModestFolderView *folder_view,
3752 ModestFolderViewPrivate *priv = NULL;
3753 TnyFolderStore *folder = NULL;
3754 gboolean retval = FALSE;
3756 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3757 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3759 /* Set selected data on clipboard */
3760 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3761 folder = modest_folder_view_get_selected (folder_view);
3763 /* Do not allow to select an account */
3764 if (TNY_IS_FOLDER (folder)) {
3765 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3770 g_object_unref (folder);
3776 _clear_hidding_filter (ModestFolderView *folder_view)
3778 ModestFolderViewPrivate *priv;
3781 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3782 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3784 if (priv->hidding_ids != NULL) {
3785 for (i=0; i < priv->n_selected; i++)
3786 g_free (priv->hidding_ids[i]);
3787 g_free(priv->hidding_ids);
3793 on_display_name_changed (ModestAccountMgr *mgr,
3794 const gchar *account,
3797 ModestFolderView *self;
3799 self = MODEST_FOLDER_VIEW (user_data);
3801 /* Force a redraw */
3802 #if GTK_CHECK_VERSION(2, 8, 0)
3803 GtkTreeViewColumn * tree_column;
3805 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3807 gtk_tree_view_column_queue_resize (tree_column);
3809 gtk_widget_queue_draw (GTK_WIDGET (self));
3814 modest_folder_view_set_cell_style (ModestFolderView *self,
3815 ModestFolderViewCellStyle cell_style)
3817 ModestFolderViewPrivate *priv = NULL;
3819 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3820 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3822 priv->cell_style = cell_style;
3824 g_object_set (G_OBJECT (priv->messages_renderer),
3825 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3828 gtk_widget_queue_draw (GTK_WIDGET (self));
3832 update_style (ModestFolderView *self)
3834 ModestFolderViewPrivate *priv;
3835 GdkColor style_color;
3836 PangoAttrList *attr_list;
3838 PangoAttribute *attr;
3840 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3841 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3845 attr_list = pango_attr_list_new ();
3846 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3847 gdk_color_parse ("grey", &style_color);
3849 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3850 pango_attr_list_insert (attr_list, attr);
3853 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3855 "SmallSystemFont", NULL,
3858 attr = pango_attr_font_desc_new (pango_font_description_copy
3859 (style->font_desc));
3860 pango_attr_list_insert (attr_list, attr);
3862 g_object_set (G_OBJECT (priv->messages_renderer),
3863 "foreground-gdk", &style_color,
3864 "foreground-set", TRUE,
3865 "attributes", attr_list,
3867 pango_attr_list_unref (attr_list);
3872 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3874 if (strcmp ("style", spec->name) == 0) {
3875 update_style (MODEST_FOLDER_VIEW (obj));
3876 gtk_widget_queue_draw (GTK_WIDGET (obj));
3881 modest_folder_view_set_filter (ModestFolderView *self,
3882 ModestFolderViewFilter filter)
3884 ModestFolderViewPrivate *priv;
3885 GtkTreeModel *filter_model;
3887 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3888 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3890 priv->filter |= filter;
3892 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3893 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3894 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3899 modest_folder_view_unset_filter (ModestFolderView *self,
3900 ModestFolderViewFilter filter)
3902 ModestFolderViewPrivate *priv;
3903 GtkTreeModel *filter_model;
3905 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3906 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3908 priv->filter &= ~filter;
3910 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3911 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3912 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3917 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3918 ModestTnyFolderRules rules)
3920 GtkTreeModel *filter_model;
3922 gboolean fulfil = FALSE;
3924 if (!get_inner_models (self, &filter_model, NULL, NULL))
3927 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3931 TnyFolderStore *folder;
3933 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3935 if (TNY_IS_FOLDER (folder)) {
3936 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3937 /* Folder rules are negative: non_writable, non_deletable... */
3938 if (!(folder_rules & rules))
3941 g_object_unref (folder);
3944 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3950 modest_folder_view_set_list_to_move (ModestFolderView *self,
3953 ModestFolderViewPrivate *priv;
3955 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3956 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3958 if (priv->list_to_move)
3959 g_object_unref (priv->list_to_move);
3962 g_object_ref (list);
3964 priv->list_to_move = list;
3968 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3970 ModestFolderViewPrivate *priv;
3972 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3973 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3976 g_free (priv->mailbox);
3978 priv->mailbox = g_strdup (mailbox);
3980 /* Notify observers */
3981 g_signal_emit (G_OBJECT(self),
3982 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3983 priv->visible_account_id);
3987 modest_folder_view_get_mailbox (ModestFolderView *self)
3989 ModestFolderViewPrivate *priv;
3991 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3994 return (const gchar *) priv->mailbox;