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"
65 #include <modest-account-protocol.h>
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
98 static void modest_folder_view_init (ModestFolderView *obj);
99 static void modest_folder_view_finalize (GObject *obj);
101 static void tny_account_store_view_init (gpointer g,
102 gpointer iface_data);
104 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
105 TnyAccountStore *account_store);
107 static void on_selection_changed (GtkTreeSelection *sel,
110 static void on_row_activated (GtkTreeView *treeview,
112 GtkTreeViewColumn *column,
115 static void on_account_removed (TnyAccountStore *self,
119 static void on_account_inserted (TnyAccountStore *self,
123 static void on_account_changed (TnyAccountStore *self,
127 static gint cmp_rows (GtkTreeModel *tree_model,
132 static gboolean filter_row (GtkTreeModel *model,
136 static gboolean on_key_pressed (GtkWidget *self,
140 static void on_configuration_key_changed (ModestConf* conf,
142 ModestConfEvent event,
143 ModestConfNotificationId notification_id,
144 ModestFolderView *self);
147 static void on_drag_data_get (GtkWidget *widget,
148 GdkDragContext *context,
149 GtkSelectionData *selection_data,
154 static void on_drag_data_received (GtkWidget *widget,
155 GdkDragContext *context,
158 GtkSelectionData *selection_data,
163 static gboolean on_drag_motion (GtkWidget *widget,
164 GdkDragContext *context,
170 static void expand_root_items (ModestFolderView *self);
172 static gint expand_row_timeout (gpointer data);
174 static void setup_drag_and_drop (GtkTreeView *self);
176 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
179 static void _clear_hidding_filter (ModestFolderView *folder_view);
181 #ifndef MODEST_TOOLKIT_HILDON2
182 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
185 ModestFolderView *self);
188 static void on_display_name_changed (ModestAccountMgr *self,
189 const gchar *account,
191 static void update_style (ModestFolderView *self);
192 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
193 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
194 static gboolean inbox_is_special (TnyFolderStore *folder_store);
196 static gboolean get_inner_models (ModestFolderView *self,
197 GtkTreeModel **filter_model,
198 GtkTreeModel **sort_model,
199 GtkTreeModel **tny_model);
200 #ifdef MODEST_TOOLKIT_HILDON2
202 on_activity_changed (TnyGtkFolderListStore *store,
204 ModestFolderView *folder_view);
208 FOLDER_SELECTION_CHANGED_SIGNAL,
209 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
210 FOLDER_ACTIVATED_SIGNAL,
211 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
212 ACTIVITY_CHANGED_SIGNAL,
216 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
217 struct _ModestFolderViewPrivate {
218 TnyAccountStore *account_store;
219 TnyFolderStore *cur_folder_store;
221 TnyFolder *folder_to_select; /* folder to select after the next update */
223 gulong changed_signal;
224 gulong account_inserted_signal;
225 gulong account_removed_signal;
226 gulong account_changed_signal;
227 gulong conf_key_signal;
228 gulong display_name_changed_signal;
230 /* not unref this object, its a singlenton */
231 ModestEmailClipboard *clipboard;
233 /* Filter tree model */
236 ModestFolderViewFilter filter;
238 TnyFolderStoreQuery *query;
240 guint timer_expander;
242 gchar *local_account_name;
243 gchar *visible_account_id;
245 ModestFolderViewStyle style;
246 ModestFolderViewCellStyle cell_style;
247 gboolean show_message_count;
249 gboolean reselect; /* we use this to force a reselection of the INBOX */
250 gboolean show_non_move;
251 TnyList *list_to_move;
252 gboolean reexpand; /* next time we expose, we'll expand all root folders */
254 GtkCellRenderer *messages_renderer;
256 gulong outbox_deleted_handler;
258 guint activity_changed_handler;
259 GdkColor *active_color;
261 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
262 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
263 MODEST_TYPE_FOLDER_VIEW, \
264 ModestFolderViewPrivate))
266 static GObjectClass *parent_class = NULL;
268 static guint signals[LAST_SIGNAL] = {0};
271 modest_folder_view_get_type (void)
273 static GType my_type = 0;
275 static const GTypeInfo my_info = {
276 sizeof(ModestFolderViewClass),
277 NULL, /* base init */
278 NULL, /* base finalize */
279 (GClassInitFunc) modest_folder_view_class_init,
280 NULL, /* class finalize */
281 NULL, /* class data */
282 sizeof(ModestFolderView),
284 (GInstanceInitFunc) modest_folder_view_init,
288 static const GInterfaceInfo tny_account_store_view_info = {
289 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
290 NULL, /* interface_finalize */
291 NULL /* interface_data */
295 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
299 g_type_add_interface_static (my_type,
300 TNY_TYPE_ACCOUNT_STORE_VIEW,
301 &tny_account_store_view_info);
307 modest_folder_view_class_init (ModestFolderViewClass *klass)
309 GObjectClass *gobject_class;
310 GtkTreeViewClass *treeview_class;
311 gobject_class = (GObjectClass*) klass;
312 treeview_class = (GtkTreeViewClass*) klass;
314 parent_class = g_type_class_peek_parent (klass);
315 gobject_class->finalize = modest_folder_view_finalize;
317 g_type_class_add_private (gobject_class,
318 sizeof(ModestFolderViewPrivate));
320 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
321 g_signal_new ("folder_selection_changed",
322 G_TYPE_FROM_CLASS (gobject_class),
324 G_STRUCT_OFFSET (ModestFolderViewClass,
325 folder_selection_changed),
327 modest_marshal_VOID__POINTER_BOOLEAN,
328 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
331 * This signal is emitted whenever the currently selected
332 * folder display name is computed. Note that the name could
333 * be different to the folder name, because we could append
334 * the unread messages count to the folder name to build the
335 * folder display name
337 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
338 g_signal_new ("folder-display-name-changed",
339 G_TYPE_FROM_CLASS (gobject_class),
341 G_STRUCT_OFFSET (ModestFolderViewClass,
342 folder_display_name_changed),
344 g_cclosure_marshal_VOID__STRING,
345 G_TYPE_NONE, 1, G_TYPE_STRING);
347 signals[FOLDER_ACTIVATED_SIGNAL] =
348 g_signal_new ("folder_activated",
349 G_TYPE_FROM_CLASS (gobject_class),
351 G_STRUCT_OFFSET (ModestFolderViewClass,
354 g_cclosure_marshal_VOID__POINTER,
355 G_TYPE_NONE, 1, G_TYPE_POINTER);
358 * Emitted whenever the visible account changes
360 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
361 g_signal_new ("visible-account-changed",
362 G_TYPE_FROM_CLASS (gobject_class),
364 G_STRUCT_OFFSET (ModestFolderViewClass,
365 visible_account_changed),
367 g_cclosure_marshal_VOID__STRING,
368 G_TYPE_NONE, 1, G_TYPE_STRING);
371 * Emitted when the underlying GtkListStore is updating data
373 signals[ACTIVITY_CHANGED_SIGNAL] =
374 g_signal_new ("activity-changed",
375 G_TYPE_FROM_CLASS (gobject_class),
377 G_STRUCT_OFFSET (ModestFolderViewClass,
380 g_cclosure_marshal_VOID__BOOLEAN,
381 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
383 treeview_class->select_cursor_parent = NULL;
385 #ifdef MODEST_TOOLKIT_HILDON2
386 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
392 /* Retrieves the filter, sort and tny models of the folder view. If
393 any of these does not exist then it returns FALSE */
395 get_inner_models (ModestFolderView *self,
396 GtkTreeModel **filter_model,
397 GtkTreeModel **sort_model,
398 GtkTreeModel **tny_model)
400 GtkTreeModel *s_model, *f_model, *t_model;
402 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
403 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
404 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
408 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
409 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
410 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
414 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
418 *filter_model = f_model;
420 *sort_model = s_model;
422 *tny_model = t_model;
427 /* Simplify checks for NULLs: */
429 strings_are_equal (const gchar *a, const gchar *b)
435 return (strcmp (a, b) == 0);
442 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
444 GObject *instance = NULL;
446 gtk_tree_model_get (model, iter,
447 INSTANCE_COLUMN, &instance,
451 return FALSE; /* keep walking */
453 if (!TNY_IS_ACCOUNT (instance)) {
454 g_object_unref (instance);
455 return FALSE; /* keep walking */
458 /* Check if this is the looked-for account: */
459 TnyAccount *this_account = TNY_ACCOUNT (instance);
460 TnyAccount *account = TNY_ACCOUNT (data);
462 const gchar *this_account_id = tny_account_get_id(this_account);
463 const gchar *account_id = tny_account_get_id(account);
464 g_object_unref (instance);
467 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
468 if (strings_are_equal(this_account_id, account_id)) {
469 /* Tell the model that the data has changed, so that
470 * it calls the cell_data_func callbacks again: */
471 /* TODO: This does not seem to actually cause the new string to be shown: */
472 gtk_tree_model_row_changed (model, path, iter);
474 return TRUE; /* stop walking */
477 return FALSE; /* keep walking */
482 ModestFolderView *self;
483 gchar *previous_name;
484 } GetMmcAccountNameData;
487 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
489 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
491 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
493 if (!strings_are_equal (
494 tny_account_get_name(TNY_ACCOUNT(account)),
495 data->previous_name)) {
497 /* Tell the model that the data has changed, so that
498 * it calls the cell_data_func callbacks again: */
499 ModestFolderView *self = data->self;
500 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
502 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
505 g_free (data->previous_name);
506 g_slice_free (GetMmcAccountNameData, data);
510 convert_parent_folders_to_dots (gchar **item_name)
514 gchar *last_separator;
516 if (item_name == NULL)
519 for (c = *item_name; *c != '\0'; c++) {
520 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
525 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
526 if (last_separator != NULL) {
527 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
534 buffer = g_string_new ("");
535 for (i = 0; i < n_parents; i++) {
536 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
538 buffer = g_string_append (buffer, last_separator);
540 *item_name = g_string_free (buffer, FALSE);
546 format_compact_style (gchar **item_name,
548 const gchar *mailbox,
550 gboolean multiaccount,
551 gboolean *use_markup)
555 TnyFolderType folder_type;
557 if (!TNY_IS_FOLDER (instance))
560 folder = (TnyFolder *) instance;
562 folder_type = tny_folder_get_folder_type (folder);
563 is_special = (get_cmp_pos (folder_type, folder)!= 4);
566 /* Remove mailbox prefix if any */
567 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
568 if (g_str_has_prefix (*item_name, prefix)) {
569 gchar *new_item_name;
571 new_item_name = g_strdup (*item_name + strlen (prefix));
572 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
573 g_free (new_item_name);
574 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
577 *item_name = new_item_name;
579 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
582 *item_name = g_strdup (_("mcen_me_folder_inbox"));
585 if (!is_special || multiaccount) {
586 TnyAccount *account = tny_folder_get_account (folder);
587 const gchar *folder_name;
588 gboolean concat_folder_name = FALSE;
591 /* Should not happen */
595 /* convert parent folders to dots */
596 convert_parent_folders_to_dots (item_name);
598 folder_name = tny_folder_get_name (folder);
599 if (g_str_has_suffix (*item_name, folder_name)) {
600 gchar *offset = g_strrstr (*item_name, folder_name);
602 concat_folder_name = TRUE;
605 buffer = g_string_new ("");
607 buffer = g_string_append (buffer, *item_name);
608 if (concat_folder_name) {
609 buffer = g_string_append (buffer, folder_name);
612 g_object_unref (account);
614 *item_name = g_string_free (buffer, FALSE);
622 text_cell_data (GtkTreeViewColumn *column,
623 GtkCellRenderer *renderer,
624 GtkTreeModel *tree_model,
628 ModestFolderViewPrivate *priv;
629 GObject *rendobj = (GObject *) renderer;
631 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
632 GObject *instance = NULL;
633 gboolean use_markup = FALSE;
635 gtk_tree_model_get (tree_model, iter,
638 INSTANCE_COLUMN, &instance,
640 if (!fname || !instance)
643 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
644 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
646 gchar *item_name = NULL;
647 gint item_weight = 400;
649 if (type != TNY_FOLDER_TYPE_ROOT) {
654 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
655 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
658 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
659 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
661 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
664 /* Sometimes an special folder is reported by the server as
665 NORMAL, like some versions of Dovecot */
666 if (type == TNY_FOLDER_TYPE_NORMAL ||
667 type == TNY_FOLDER_TYPE_UNKNOWN) {
668 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
672 /* note: we cannot reliably get the counts from the
673 * tree model, we need to use explicit calls on
674 * tny_folder for some reason. Select the number to
675 * show: the unread or unsent messages. in case of
676 * outbox/drafts, show all */
677 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
678 (type == TNY_FOLDER_TYPE_OUTBOX) ||
679 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
680 number = tny_folder_get_all_count (TNY_FOLDER(instance));
683 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
687 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
688 item_name = g_strdup (fname);
695 /* Use bold font style if there are unread or unset messages */
697 if (priv->show_message_count) {
698 item_name = g_strdup_printf ("%s (%d)", fname, number);
700 item_name = g_strdup (fname);
704 item_name = g_strdup (fname);
709 } else if (TNY_IS_ACCOUNT (instance)) {
710 /* If it's a server account */
711 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
712 item_name = g_strdup (priv->local_account_name);
714 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
715 /* fname is only correct when the items are first
716 * added to the model, not when the account is
717 * changed later, so get the name from the account
719 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
722 item_name = g_strdup (fname);
728 item_name = g_strdup ("unknown");
730 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
731 gboolean multiaccount;
733 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
734 /* Convert item_name to markup */
735 format_compact_style (&item_name, instance, priv->mailbox,
737 multiaccount, &use_markup);
740 if (item_name && item_weight) {
741 /* Set the name in the treeview cell: */
742 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 && priv->active_color) {
743 g_object_set (rendobj,
746 "foreground-set", TRUE,
747 "foreground-gdk", priv->active_color,
750 g_object_set (rendobj,
752 "foreground-set", FALSE,
754 "weight", item_weight,
758 /* Notify display name observers */
759 /* TODO: What listens for this signal, and how can it use only the new name? */
760 if (((GObject *) priv->cur_folder_store) == instance) {
761 g_signal_emit (G_OBJECT(self),
762 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
769 /* If it is a Memory card account, make sure that we have the correct name.
770 * This function will be trigerred again when the name has been retrieved: */
771 if (TNY_IS_STORE_ACCOUNT (instance) &&
772 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
774 /* Get the account name asynchronously: */
775 GetMmcAccountNameData *callback_data =
776 g_slice_new0(GetMmcAccountNameData);
777 callback_data->self = self;
779 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
781 callback_data->previous_name = g_strdup (name);
783 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
784 on_get_mmc_account_name, callback_data);
788 g_object_unref (G_OBJECT (instance));
794 messages_cell_data (GtkTreeViewColumn *column,
795 GtkCellRenderer *renderer,
796 GtkTreeModel *tree_model,
800 ModestFolderView *self;
801 ModestFolderViewPrivate *priv;
802 GObject *rendobj = (GObject *) renderer;
803 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
804 GObject *instance = NULL;
805 gchar *item_name = NULL;
807 gtk_tree_model_get (tree_model, iter,
809 INSTANCE_COLUMN, &instance,
814 self = MODEST_FOLDER_VIEW (data);
815 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
818 if (type != TNY_FOLDER_TYPE_ROOT) {
823 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
824 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
827 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
829 /* Sometimes an special folder is reported by the server as
830 NORMAL, like some versions of Dovecot */
831 if (type == TNY_FOLDER_TYPE_NORMAL ||
832 type == TNY_FOLDER_TYPE_UNKNOWN) {
833 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
837 /* note: we cannot reliably get the counts from the tree model, we need
838 * to use explicit calls on tny_folder for some reason.
840 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
841 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
842 (type == TNY_FOLDER_TYPE_OUTBOX) ||
843 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
844 number = tny_folder_get_all_count (TNY_FOLDER(instance));
847 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
851 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
853 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
855 } else if (number == 1) {
856 item_name = g_strdup_printf (drafts?_("mcen_ti_message"):_("mcen_ti_new_message"),
864 item_name = g_strdup ("");
867 /* Set the name in the treeview cell: */
868 g_object_set (rendobj,"text", item_name, NULL);
876 g_object_unref (G_OBJECT (instance));
882 GdkPixbuf *pixbuf_open;
883 GdkPixbuf *pixbuf_close;
887 static inline GdkPixbuf *
888 get_composite_pixbuf (const gchar *icon_name,
890 GdkPixbuf *base_pixbuf)
892 GdkPixbuf *emblem, *retval = NULL;
894 emblem = modest_platform_get_icon (icon_name, size);
896 retval = gdk_pixbuf_copy (base_pixbuf);
897 gdk_pixbuf_composite (emblem, retval, 0, 0,
898 MIN (gdk_pixbuf_get_width (emblem),
899 gdk_pixbuf_get_width (retval)),
900 MIN (gdk_pixbuf_get_height (emblem),
901 gdk_pixbuf_get_height (retval)),
902 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
903 g_object_unref (emblem);
908 static inline ThreePixbufs *
909 get_composite_icons (const gchar *icon_code,
911 GdkPixbuf **pixbuf_open,
912 GdkPixbuf **pixbuf_close)
914 ThreePixbufs *retval;
917 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
920 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
925 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
929 retval = g_slice_new0 (ThreePixbufs);
931 retval->pixbuf = g_object_ref (*pixbuf);
933 retval->pixbuf_open = g_object_ref (*pixbuf_open);
935 retval->pixbuf_close = g_object_ref (*pixbuf_close);
940 static inline ThreePixbufs *
941 get_account_protocol_pixbufs (ModestFolderView *folder_view,
942 ModestProtocolType protocol_type,
945 ModestProtocol *protocol;
946 const GdkPixbuf *pixbuf = NULL;
947 ModestFolderViewPrivate *priv;
949 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
951 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
954 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
955 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
956 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
957 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
958 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
959 object, FOLDER_ICON_SIZE);
963 ThreePixbufs *retval;
964 retval = g_slice_new0 (ThreePixbufs);
965 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
966 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
967 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
974 static inline ThreePixbufs*
975 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
977 TnyAccount *account = NULL;
978 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
979 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
980 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
981 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
982 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
984 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
985 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
986 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
987 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
988 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
990 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
991 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
992 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
993 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
994 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
996 ThreePixbufs *retval = NULL;
998 if (TNY_IS_ACCOUNT (instance)) {
999 account = g_object_ref (instance);
1000 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1001 account = tny_folder_get_account (TNY_FOLDER (instance));
1005 ModestProtocolType account_store_protocol;
1007 account_store_protocol = modest_tny_account_get_protocol_type (account);
1008 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1009 g_object_unref (account);
1015 /* Sometimes an special folder is reported by the server as
1016 NORMAL, like some versions of Dovecot */
1017 if (type == TNY_FOLDER_TYPE_NORMAL ||
1018 type == TNY_FOLDER_TYPE_UNKNOWN) {
1019 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1022 /* It's not enough with check the folder type. We need to
1023 ensure that we're not giving a special folder icon to a
1024 normal folder with the same name than a special folder */
1025 if (TNY_IS_FOLDER (instance) &&
1026 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1027 type = TNY_FOLDER_TYPE_NORMAL;
1029 /* Remote folders should not be treated as special folders */
1030 if (TNY_IS_FOLDER_STORE (instance) &&
1031 !TNY_IS_ACCOUNT (instance) &&
1032 type != TNY_FOLDER_TYPE_INBOX &&
1033 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1034 #ifdef MODEST_TOOLKIT_HILDON2
1035 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1038 &anorm_pixbuf_close);
1040 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1042 &normal_pixbuf_open,
1043 &normal_pixbuf_close);
1049 case TNY_FOLDER_TYPE_INVALID:
1050 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1053 case TNY_FOLDER_TYPE_ROOT:
1054 if (TNY_IS_ACCOUNT (instance)) {
1056 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1057 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1060 &avirt_pixbuf_close);
1062 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1064 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1065 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1068 &ammc_pixbuf_close);
1070 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1073 &anorm_pixbuf_close);
1078 case TNY_FOLDER_TYPE_INBOX:
1079 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1082 &inbox_pixbuf_close);
1084 case TNY_FOLDER_TYPE_OUTBOX:
1085 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1087 &outbox_pixbuf_open,
1088 &outbox_pixbuf_close);
1090 case TNY_FOLDER_TYPE_JUNK:
1091 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1094 &junk_pixbuf_close);
1096 case TNY_FOLDER_TYPE_SENT:
1097 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1100 &sent_pixbuf_close);
1102 case TNY_FOLDER_TYPE_TRASH:
1103 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1106 &trash_pixbuf_close);
1108 case TNY_FOLDER_TYPE_DRAFTS:
1109 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1112 &draft_pixbuf_close);
1114 case TNY_FOLDER_TYPE_ARCHIVE:
1115 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1120 case TNY_FOLDER_TYPE_NORMAL:
1122 /* Memory card folders could have an special icon */
1123 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1124 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1129 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1131 &normal_pixbuf_open,
1132 &normal_pixbuf_close);
1141 free_pixbufs (ThreePixbufs *pixbufs)
1143 if (pixbufs->pixbuf)
1144 g_object_unref (pixbufs->pixbuf);
1145 if (pixbufs->pixbuf_open)
1146 g_object_unref (pixbufs->pixbuf_open);
1147 if (pixbufs->pixbuf_close)
1148 g_object_unref (pixbufs->pixbuf_close);
1149 g_slice_free (ThreePixbufs, pixbufs);
1153 icon_cell_data (GtkTreeViewColumn *column,
1154 GtkCellRenderer *renderer,
1155 GtkTreeModel *tree_model,
1159 GObject *rendobj = NULL, *instance = NULL;
1160 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1161 gboolean has_children;
1162 ThreePixbufs *pixbufs;
1163 ModestFolderView *folder_view = (ModestFolderView *) data;
1165 rendobj = (GObject *) renderer;
1167 gtk_tree_model_get (tree_model, iter,
1169 INSTANCE_COLUMN, &instance,
1175 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1176 pixbufs = get_folder_icons (folder_view, type, instance);
1177 g_object_unref (instance);
1180 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1183 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1184 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1187 free_pixbufs (pixbufs);
1191 add_columns (GtkWidget *treeview)
1193 GtkTreeViewColumn *column;
1194 GtkCellRenderer *renderer;
1195 GtkTreeSelection *sel;
1196 ModestFolderViewPrivate *priv;
1198 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1201 column = gtk_tree_view_column_new ();
1203 /* Set icon and text render function */
1204 renderer = gtk_cell_renderer_pixbuf_new();
1205 #ifdef MODEST_TOOLKIT_HILDON2
1206 g_object_set (renderer,
1207 "xpad", MODEST_MARGIN_DEFAULT,
1208 "ypad", MODEST_MARGIN_DEFAULT,
1211 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1212 gtk_tree_view_column_set_cell_data_func(column, renderer,
1213 icon_cell_data, treeview, NULL);
1215 renderer = gtk_cell_renderer_text_new();
1216 g_object_set (renderer,
1217 #ifdef MODEST_TOOLKIT_HILDON2
1218 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1219 "ypad", MODEST_MARGIN_DEFAULT,
1220 "xpad", MODEST_MARGIN_DEFAULT,
1222 "ellipsize", PANGO_ELLIPSIZE_END,
1224 "ellipsize-set", TRUE, NULL);
1225 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1226 gtk_tree_view_column_set_cell_data_func(column, renderer,
1227 text_cell_data, treeview, NULL);
1229 priv->messages_renderer = gtk_cell_renderer_text_new ();
1230 g_object_set (priv->messages_renderer,
1231 #ifdef MODEST_TOOLKIT_HILDON2
1233 "ypad", MODEST_MARGIN_DEFAULT,
1234 "xpad", MODEST_MARGIN_DOUBLE,
1236 "scale", PANGO_SCALE_X_SMALL,
1239 "alignment", PANGO_ALIGN_RIGHT,
1243 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1244 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1245 messages_cell_data, treeview, NULL);
1247 /* Set selection mode */
1248 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1249 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1251 /* Set treeview appearance */
1252 gtk_tree_view_column_set_spacing (column, 2);
1253 gtk_tree_view_column_set_resizable (column, TRUE);
1254 gtk_tree_view_column_set_fixed_width (column, TRUE);
1255 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1256 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1259 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1263 modest_folder_view_init (ModestFolderView *obj)
1265 ModestFolderViewPrivate *priv;
1268 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1270 priv->timer_expander = 0;
1271 priv->account_store = NULL;
1273 priv->do_refresh = TRUE;
1274 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1275 priv->cur_folder_store = NULL;
1276 priv->visible_account_id = NULL;
1277 priv->mailbox = NULL;
1278 priv->folder_to_select = NULL;
1279 priv->outbox_deleted_handler = 0;
1280 priv->reexpand = TRUE;
1281 priv->activity_changed_handler = 0;
1283 /* Initialize the local account name */
1284 conf = modest_runtime_get_conf();
1285 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1287 /* Init email clipboard */
1288 priv->clipboard = modest_runtime_get_email_clipboard ();
1289 priv->hidding_ids = NULL;
1290 priv->n_selected = 0;
1291 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1292 priv->reselect = FALSE;
1293 priv->show_non_move = TRUE;
1294 priv->list_to_move = NULL;
1295 priv->show_message_count = TRUE;
1297 /* Build treeview */
1298 add_columns (GTK_WIDGET (obj));
1300 /* Setup drag and drop */
1301 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1303 /* Connect signals */
1304 g_signal_connect (G_OBJECT (obj),
1306 G_CALLBACK (on_key_pressed), NULL);
1308 priv->display_name_changed_signal =
1309 g_signal_connect (modest_runtime_get_account_mgr (),
1310 "display_name_changed",
1311 G_CALLBACK (on_display_name_changed),
1315 * Track changes in the local account name (in the device it
1316 * will be the device name)
1318 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1320 G_CALLBACK(on_configuration_key_changed),
1323 priv->active_color = NULL;
1326 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1332 tny_account_store_view_init (gpointer g, gpointer iface_data)
1334 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1336 klass->set_account_store = modest_folder_view_set_account_store;
1340 modest_folder_view_finalize (GObject *obj)
1342 ModestFolderViewPrivate *priv;
1343 GtkTreeSelection *sel;
1344 TnyAccount *local_account;
1346 g_return_if_fail (obj);
1348 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1350 if (priv->active_color) {
1351 gdk_color_free (priv->active_color);
1352 priv->active_color = NULL;
1355 if (priv->timer_expander != 0) {
1356 g_source_remove (priv->timer_expander);
1357 priv->timer_expander = 0;
1360 local_account = (TnyAccount *)
1361 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1362 if (local_account) {
1363 if (g_signal_handler_is_connected (local_account,
1364 priv->outbox_deleted_handler))
1365 g_signal_handler_disconnect (local_account,
1366 priv->outbox_deleted_handler);
1367 g_object_unref (local_account);
1370 if (priv->account_store) {
1371 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1372 priv->account_inserted_signal);
1373 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1374 priv->account_removed_signal);
1375 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1376 priv->account_changed_signal);
1377 g_object_unref (G_OBJECT(priv->account_store));
1378 priv->account_store = NULL;
1381 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1382 priv->display_name_changed_signal)) {
1383 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1384 priv->display_name_changed_signal);
1385 priv->display_name_changed_signal = 0;
1389 g_object_unref (G_OBJECT (priv->query));
1393 if (priv->folder_to_select) {
1394 g_object_unref (G_OBJECT(priv->folder_to_select));
1395 priv->folder_to_select = NULL;
1398 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1400 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1402 g_free (priv->local_account_name);
1403 g_free (priv->visible_account_id);
1404 g_free (priv->mailbox);
1406 if (priv->conf_key_signal) {
1407 g_signal_handler_disconnect (modest_runtime_get_conf (),
1408 priv->conf_key_signal);
1409 priv->conf_key_signal = 0;
1412 if (priv->cur_folder_store) {
1413 g_object_unref (priv->cur_folder_store);
1414 priv->cur_folder_store = NULL;
1417 if (priv->list_to_move) {
1418 g_object_unref (priv->list_to_move);
1419 priv->list_to_move = NULL;
1422 /* Clear hidding array created by cut operation */
1423 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1425 G_OBJECT_CLASS(parent_class)->finalize (obj);
1430 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1432 ModestFolderViewPrivate *priv;
1435 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1436 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1438 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1439 device = tny_account_store_get_device (account_store);
1441 if (G_UNLIKELY (priv->account_store)) {
1443 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1444 priv->account_inserted_signal))
1445 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1446 priv->account_inserted_signal);
1447 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1448 priv->account_removed_signal))
1449 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1450 priv->account_removed_signal);
1451 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1452 priv->account_changed_signal))
1453 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1454 priv->account_changed_signal);
1455 g_object_unref (G_OBJECT (priv->account_store));
1458 priv->account_store = g_object_ref (G_OBJECT (account_store));
1460 priv->account_removed_signal =
1461 g_signal_connect (G_OBJECT(account_store), "account_removed",
1462 G_CALLBACK (on_account_removed), self);
1464 priv->account_inserted_signal =
1465 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1466 G_CALLBACK (on_account_inserted), self);
1468 priv->account_changed_signal =
1469 g_signal_connect (G_OBJECT(account_store), "account_changed",
1470 G_CALLBACK (on_account_changed), self);
1472 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1473 priv->reselect = FALSE;
1474 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1476 g_object_unref (G_OBJECT (device));
1480 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1483 ModestFolderView *self;
1484 GtkTreeModel *model, *filter_model;
1487 self = MODEST_FOLDER_VIEW (user_data);
1489 if (!get_inner_models (self, &filter_model, NULL, &model))
1492 /* Remove outbox from model */
1493 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1494 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1495 g_object_unref (outbox);
1498 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1502 on_account_inserted (TnyAccountStore *account_store,
1503 TnyAccount *account,
1506 ModestFolderViewPrivate *priv;
1507 GtkTreeModel *model, *filter_model;
1509 /* Ignore transport account insertions, we're not showing them
1510 in the folder view */
1511 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1514 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1517 /* If we're adding a new account, and there is no previous
1518 one, we need to select the visible server account */
1519 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1520 !priv->visible_account_id)
1521 modest_widget_memory_restore (modest_runtime_get_conf(),
1522 G_OBJECT (user_data),
1523 MODEST_CONF_FOLDER_VIEW_KEY);
1527 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1528 &filter_model, NULL, &model))
1531 /* Insert the account in the model */
1532 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1534 /* When the model is a list store (plain representation) the
1535 outbox is not a child of any account so we have to manually
1536 delete it because removing the local folders account won't
1537 delete it (because tny_folder_get_account() is not defined
1538 for a merge folder */
1539 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1540 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1542 priv->outbox_deleted_handler =
1543 g_signal_connect (account,
1545 G_CALLBACK (on_outbox_deleted_cb),
1549 /* Refilter the model */
1550 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1555 same_account_selected (ModestFolderView *self,
1556 TnyAccount *account)
1558 ModestFolderViewPrivate *priv;
1559 gboolean same_account = FALSE;
1561 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1563 if (priv->cur_folder_store) {
1564 TnyAccount *selected_folder_account = NULL;
1566 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1567 selected_folder_account =
1568 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1570 selected_folder_account =
1571 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1574 if (selected_folder_account == account)
1575 same_account = TRUE;
1577 g_object_unref (selected_folder_account);
1579 return same_account;
1584 * Selects the first inbox or the local account in an idle
1587 on_idle_select_first_inbox_or_local (gpointer user_data)
1589 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1591 gdk_threads_enter ();
1592 modest_folder_view_select_first_inbox_or_local (self);
1593 gdk_threads_leave ();
1599 on_account_changed (TnyAccountStore *account_store,
1600 TnyAccount *tny_account,
1603 ModestFolderView *self;
1604 ModestFolderViewPrivate *priv;
1605 GtkTreeModel *model, *filter_model;
1606 GtkTreeSelection *sel;
1607 gboolean same_account;
1609 /* Ignore transport account insertions, we're not showing them
1610 in the folder view */
1611 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1614 self = MODEST_FOLDER_VIEW (user_data);
1615 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1617 /* Get the inner model */
1618 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1619 &filter_model, NULL, &model))
1622 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1624 /* Invalidate the cur_folder_store only if the selected folder
1625 belongs to the account that is being removed */
1626 same_account = same_account_selected (self, tny_account);
1628 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1629 gtk_tree_selection_unselect_all (sel);
1632 /* Remove the account from the model */
1633 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1635 /* Insert the account in the model */
1636 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1638 /* Refilter the model */
1639 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1641 /* Select the first INBOX if the currently selected folder
1642 belongs to the account that is being deleted */
1643 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1644 g_idle_add (on_idle_select_first_inbox_or_local, self);
1648 on_account_removed (TnyAccountStore *account_store,
1649 TnyAccount *account,
1652 ModestFolderView *self = NULL;
1653 ModestFolderViewPrivate *priv;
1654 GtkTreeModel *model, *filter_model;
1655 GtkTreeSelection *sel = NULL;
1656 gboolean same_account = FALSE;
1658 /* Ignore transport account removals, we're not showing them
1659 in the folder view */
1660 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1663 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1664 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1668 self = MODEST_FOLDER_VIEW (user_data);
1669 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1671 /* Invalidate the cur_folder_store only if the selected folder
1672 belongs to the account that is being removed */
1673 same_account = same_account_selected (self, account);
1675 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1676 gtk_tree_selection_unselect_all (sel);
1679 /* Invalidate row to select only if the folder to select
1680 belongs to the account that is being removed*/
1681 if (priv->folder_to_select) {
1682 TnyAccount *folder_to_select_account = NULL;
1684 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1685 if (folder_to_select_account == account) {
1686 modest_folder_view_disable_next_folder_selection (self);
1687 g_object_unref (priv->folder_to_select);
1688 priv->folder_to_select = NULL;
1690 g_object_unref (folder_to_select_account);
1693 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1694 &filter_model, NULL, &model))
1697 /* Disconnect the signal handler */
1698 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1699 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1700 if (g_signal_handler_is_connected (account,
1701 priv->outbox_deleted_handler))
1702 g_signal_handler_disconnect (account,
1703 priv->outbox_deleted_handler);
1706 /* Remove the account from the model */
1707 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1709 /* If the removed account is the currently viewed one then
1710 clear the configuration value. The new visible account will be the default account */
1711 if (priv->visible_account_id &&
1712 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1714 /* Clear the current visible account_id */
1715 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1716 modest_folder_view_set_mailbox (self, NULL);
1718 /* Call the restore method, this will set the new visible account */
1719 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1720 MODEST_CONF_FOLDER_VIEW_KEY);
1723 /* Refilter the model */
1724 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1726 /* Select the first INBOX if the currently selected folder
1727 belongs to the account that is being deleted */
1729 g_idle_add (on_idle_select_first_inbox_or_local, self);
1733 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1735 GtkTreeViewColumn *col;
1737 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1739 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1741 g_printerr ("modest: failed get column for title\n");
1745 gtk_tree_view_column_set_title (col, title);
1746 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1751 modest_folder_view_on_map (ModestFolderView *self,
1752 GdkEventExpose *event,
1755 ModestFolderViewPrivate *priv;
1757 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1759 /* This won't happen often */
1760 if (G_UNLIKELY (priv->reselect)) {
1761 /* Select the first inbox or the local account if not found */
1763 /* TODO: this could cause a lock at startup, so we
1764 comment it for the moment. We know that this will
1765 be a bug, because the INBOX is not selected, but we
1766 need to rewrite some parts of Modest to avoid the
1767 deathlock situation */
1768 /* TODO: check if this is still the case */
1769 priv->reselect = FALSE;
1770 modest_folder_view_select_first_inbox_or_local (self);
1771 /* Notify the display name observers */
1772 g_signal_emit (G_OBJECT(self),
1773 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1777 if (priv->reexpand) {
1778 expand_root_items (self);
1779 priv->reexpand = FALSE;
1786 modest_folder_view_new (TnyFolderStoreQuery *query)
1788 return modest_folder_view_new_full (query, TRUE);
1792 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1795 ModestFolderViewPrivate *priv;
1796 GtkTreeSelection *sel;
1798 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1799 #ifdef MODEST_TOOLKIT_HILDON2
1800 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1803 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1806 priv->query = g_object_ref (query);
1808 priv->do_refresh = do_refresh;
1810 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1811 priv->changed_signal = g_signal_connect (sel, "changed",
1812 G_CALLBACK (on_selection_changed), self);
1814 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1816 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1818 return GTK_WIDGET(self);
1821 /* this feels dirty; any other way to expand all the root items? */
1823 expand_root_items (ModestFolderView *self)
1826 GtkTreeModel *model;
1829 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1830 path = gtk_tree_path_new_first ();
1832 /* all folders should have child items, so.. */
1834 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1835 gtk_tree_path_next (path);
1836 } while (gtk_tree_model_get_iter (model, &iter, path));
1838 gtk_tree_path_free (path);
1842 is_parent_of (TnyFolder *a, TnyFolder *b)
1845 gboolean retval = FALSE;
1847 a_id = tny_folder_get_id (a);
1849 gchar *string_to_match;
1852 string_to_match = g_strconcat (a_id, "/", NULL);
1853 b_id = tny_folder_get_id (b);
1854 retval = g_str_has_prefix (b_id, string_to_match);
1855 g_free (string_to_match);
1861 typedef struct _ForeachFolderInfo {
1864 } ForeachFolderInfo;
1867 foreach_folder_with_id (GtkTreeModel *model,
1872 ForeachFolderInfo *info;
1875 info = (ForeachFolderInfo *) data;
1876 gtk_tree_model_get (model, iter,
1877 INSTANCE_COLUMN, &instance,
1880 if (TNY_IS_FOLDER (instance)) {
1883 id = tny_folder_get_id (TNY_FOLDER (instance));
1885 collate = g_utf8_collate_key (id, -1);
1886 info->found = !strcmp (info->needle, collate);
1892 g_object_unref (instance);
1900 has_folder_with_id (ModestFolderView *self, const gchar *id)
1902 GtkTreeModel *model;
1903 ForeachFolderInfo info = {NULL, FALSE};
1905 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1906 info.needle = g_utf8_collate_key (id, -1);
1908 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1909 g_free (info.needle);
1915 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1918 gboolean retval = FALSE;
1920 a_id = tny_folder_get_id (a);
1923 b_id = tny_folder_get_id (b);
1926 const gchar *last_bar;
1927 gchar *string_to_match;
1928 last_bar = g_strrstr (b_id, "/");
1933 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1934 retval = has_folder_with_id (self, string_to_match);
1935 g_free (string_to_match);
1943 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1945 ModestFolderViewPrivate *priv;
1946 TnyIterator *iterator;
1947 gboolean retval = TRUE;
1949 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1950 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1952 for (iterator = tny_list_create_iterator (priv->list_to_move);
1953 retval && !tny_iterator_is_done (iterator);
1954 tny_iterator_next (iterator)) {
1956 instance = tny_iterator_get_current (iterator);
1957 if (instance == (GObject *) folder) {
1959 } else if (TNY_IS_FOLDER (instance)) {
1960 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1962 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1965 g_object_unref (instance);
1967 g_object_unref (iterator);
1974 * We use this function to implement the
1975 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1976 * account in this case, and the local folders.
1979 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1981 ModestFolderViewPrivate *priv;
1982 gboolean retval = TRUE;
1983 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1984 GObject *instance = NULL;
1985 const gchar *id = NULL;
1987 gboolean found = FALSE;
1988 gboolean cleared = FALSE;
1989 ModestTnyFolderRules rules = 0;
1992 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1993 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1995 gtk_tree_model_get (model, iter,
1996 NAME_COLUMN, &fname,
1998 INSTANCE_COLUMN, &instance,
2001 /* Do not show if there is no instance, this could indeed
2002 happen when the model is being modified while it's being
2003 drawn. This could occur for example when moving folders
2010 if (TNY_IS_ACCOUNT (instance)) {
2011 TnyAccount *acc = TNY_ACCOUNT (instance);
2012 const gchar *account_id = tny_account_get_id (acc);
2014 /* If it isn't a special folder,
2015 * don't show it unless it is the visible account: */
2016 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2017 !modest_tny_account_is_virtual_local_folders (acc) &&
2018 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2020 /* Show only the visible account id */
2021 if (priv->visible_account_id) {
2022 if (strcmp (account_id, priv->visible_account_id))
2029 /* Never show these to the user. They are merged into one folder
2030 * in the local-folders account instead: */
2031 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2034 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2035 /* Only show special folders for current account if needed */
2036 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2037 TnyAccount *account;
2039 account = tny_folder_get_account (TNY_FOLDER (instance));
2041 if (TNY_IS_ACCOUNT (account)) {
2042 const gchar *account_id = tny_account_get_id (account);
2044 if (!modest_tny_account_is_virtual_local_folders (account) &&
2045 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2046 /* Show only the visible account id */
2047 if (priv->visible_account_id) {
2048 if (strcmp (account_id, priv->visible_account_id)) {
2050 } else if (priv->mailbox) {
2051 /* Filter mailboxes */
2052 if (!g_str_has_prefix (fname, priv->mailbox)) {
2054 } else if (!strcmp (fname, priv->mailbox)) {
2055 /* Hide mailbox parent */
2061 g_object_unref (account);
2068 /* Check hiding (if necessary) */
2069 cleared = modest_email_clipboard_cleared (priv->clipboard);
2070 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2071 id = tny_folder_get_id (TNY_FOLDER(instance));
2072 if (priv->hidding_ids != NULL)
2073 for (i=0; i < priv->n_selected && !found; i++)
2074 if (priv->hidding_ids[i] != NULL && id != NULL)
2075 found = (!strcmp (priv->hidding_ids[i], id));
2080 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2081 folder as no message can be move there according to UI specs */
2082 if (retval && !priv->show_non_move) {
2083 if (priv->list_to_move &&
2084 tny_list_get_length (priv->list_to_move) > 0 &&
2085 TNY_IS_FOLDER (instance)) {
2086 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2088 if (retval && TNY_IS_FOLDER (instance) &&
2089 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2091 case TNY_FOLDER_TYPE_OUTBOX:
2092 case TNY_FOLDER_TYPE_SENT:
2093 case TNY_FOLDER_TYPE_DRAFTS:
2096 case TNY_FOLDER_TYPE_UNKNOWN:
2097 case TNY_FOLDER_TYPE_NORMAL:
2098 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2099 if (type == TNY_FOLDER_TYPE_INVALID)
2100 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2102 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2103 type == TNY_FOLDER_TYPE_SENT
2104 || type == TNY_FOLDER_TYPE_DRAFTS)
2111 if (retval && TNY_IS_ACCOUNT (instance) &&
2112 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2113 ModestProtocolType protocol_type;
2115 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2116 retval = !modest_protocol_registry_protocol_type_has_tag
2117 (modest_runtime_get_protocol_registry (),
2119 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2123 /* apply special filters */
2124 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2125 if (TNY_IS_ACCOUNT (instance))
2129 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2130 if (TNY_IS_FOLDER (instance))
2134 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2135 if (TNY_IS_ACCOUNT (instance)) {
2136 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2138 } else if (TNY_IS_FOLDER (instance)) {
2139 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2144 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2145 if (TNY_IS_ACCOUNT (instance)) {
2146 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2148 } else if (TNY_IS_FOLDER (instance)) {
2149 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2154 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2155 /* A mailbox is a fake folder with an @ in the middle of the name */
2156 if (!TNY_IS_FOLDER (instance) ||
2157 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2160 const gchar *folder_name;
2161 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2162 if (!folder_name || strchr (folder_name, '@') == NULL)
2168 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2169 if (TNY_IS_FOLDER (instance)) {
2170 /* Check folder rules */
2171 ModestTnyFolderRules rules;
2173 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2174 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2175 } else if (TNY_IS_ACCOUNT (instance)) {
2176 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2184 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2185 if (TNY_IS_FOLDER (instance)) {
2186 TnyFolderType guess_type;
2188 if (TNY_FOLDER_TYPE_NORMAL) {
2189 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2195 case TNY_FOLDER_TYPE_OUTBOX:
2196 case TNY_FOLDER_TYPE_SENT:
2197 case TNY_FOLDER_TYPE_DRAFTS:
2198 case TNY_FOLDER_TYPE_ARCHIVE:
2199 case TNY_FOLDER_TYPE_INBOX:
2202 case TNY_FOLDER_TYPE_UNKNOWN:
2203 case TNY_FOLDER_TYPE_NORMAL:
2209 } else if (TNY_IS_ACCOUNT (instance)) {
2214 if (retval && TNY_IS_FOLDER (instance)) {
2215 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2218 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2219 if (TNY_IS_FOLDER (instance)) {
2220 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2221 } else if (TNY_IS_ACCOUNT (instance)) {
2226 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2227 if (TNY_IS_FOLDER (instance)) {
2228 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2229 } else if (TNY_IS_ACCOUNT (instance)) {
2234 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2235 if (TNY_IS_FOLDER (instance)) {
2236 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2237 } else if (TNY_IS_ACCOUNT (instance)) {
2243 g_object_unref (instance);
2251 modest_folder_view_update_model (ModestFolderView *self,
2252 TnyAccountStore *account_store)
2254 ModestFolderViewPrivate *priv;
2255 GtkTreeModel *model;
2256 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2258 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2259 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2262 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2264 /* Notify that there is no folder selected */
2265 g_signal_emit (G_OBJECT(self),
2266 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2268 if (priv->cur_folder_store) {
2269 g_object_unref (priv->cur_folder_store);
2270 priv->cur_folder_store = NULL;
2273 /* FIXME: the local accounts are not shown when the query
2274 selects only the subscribed folders */
2275 #ifdef MODEST_TOOLKIT_HILDON2
2276 TnyGtkFolderListStoreFlags flags;
2277 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2278 if (!priv->do_refresh)
2279 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2280 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2282 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2283 MODEST_FOLDER_PATH_SEPARATOR);
2285 model = tny_gtk_folder_store_tree_model_new (NULL);
2288 /* When the model is a list store (plain representation) the
2289 outbox is not a child of any account so we have to manually
2290 delete it because removing the local folders account won't
2291 delete it (because tny_folder_get_account() is not defined
2292 for a merge folder */
2293 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2294 TnyAccount *account;
2295 ModestTnyAccountStore *acc_store;
2297 acc_store = modest_runtime_get_account_store ();
2298 account = modest_tny_account_store_get_local_folders_account (acc_store);
2300 if (g_signal_handler_is_connected (account,
2301 priv->outbox_deleted_handler))
2302 g_signal_handler_disconnect (account,
2303 priv->outbox_deleted_handler);
2305 priv->outbox_deleted_handler =
2306 g_signal_connect (account,
2308 G_CALLBACK (on_outbox_deleted_cb),
2310 g_object_unref (account);
2313 /* Get the accounts: */
2314 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2316 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2318 sortable = gtk_tree_model_sort_new_with_model (model);
2319 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2321 GTK_SORT_ASCENDING);
2322 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2324 cmp_rows, NULL, NULL);
2326 /* Create filter model */
2327 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2328 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2333 if (priv->activity_changed_handler > 0) {
2334 GtkTreeModel *old_tny_model;
2336 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2337 g_signal_handler_disconnect (G_OBJECT (old_tny_model), priv->activity_changed_handler);
2339 priv->activity_changed_handler = 0;
2343 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2344 #ifndef MODEST_TOOLKIT_HILDON2
2345 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2346 (GCallback) on_row_inserted_maybe_select_folder, self);
2349 #ifdef MODEST_TOOLKIT_HILDON2
2350 priv->activity_changed_handler =
2351 g_signal_connect (G_OBJECT (model), "activity-changed", G_CALLBACK (on_activity_changed), self);
2354 g_object_unref (model);
2355 g_object_unref (filter_model);
2356 g_object_unref (sortable);
2358 /* Force a reselection of the INBOX next time the widget is shown */
2359 priv->reselect = TRUE;
2366 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2368 GtkTreeModel *model = NULL;
2369 TnyFolderStore *folder = NULL;
2371 ModestFolderView *tree_view = NULL;
2372 ModestFolderViewPrivate *priv = NULL;
2373 gboolean selected = FALSE;
2375 g_return_if_fail (sel);
2376 g_return_if_fail (user_data);
2378 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2380 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2382 tree_view = MODEST_FOLDER_VIEW (user_data);
2385 gtk_tree_model_get (model, &iter,
2386 INSTANCE_COLUMN, &folder,
2389 /* If the folder is the same do not notify */
2390 if (folder && priv->cur_folder_store == folder) {
2391 g_object_unref (folder);
2396 /* Current folder was unselected */
2397 if (priv->cur_folder_store) {
2398 /* We must do this firstly because a libtinymail-camel
2399 implementation detail. If we issue the signal
2400 before doing the sync_async, then that signal could
2401 cause (and it actually does it) a free of the
2402 summary of the folder (because the main window will
2403 clear the headers view */
2404 if (TNY_IS_FOLDER(priv->cur_folder_store))
2405 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2406 FALSE, NULL, NULL, NULL);
2408 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2409 priv->cur_folder_store, FALSE);
2411 g_object_unref (priv->cur_folder_store);
2412 priv->cur_folder_store = NULL;
2415 /* New current references */
2416 priv->cur_folder_store = folder;
2418 /* New folder has been selected. Do not notify if there is
2419 nothing new selected */
2421 g_signal_emit (G_OBJECT(tree_view),
2422 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2423 0, priv->cur_folder_store, TRUE);
2428 on_row_activated (GtkTreeView *treeview,
2429 GtkTreePath *treepath,
2430 GtkTreeViewColumn *column,
2433 GtkTreeModel *model = NULL;
2434 TnyFolderStore *folder = NULL;
2436 ModestFolderView *self = NULL;
2437 ModestFolderViewPrivate *priv = NULL;
2439 g_return_if_fail (treeview);
2440 g_return_if_fail (user_data);
2442 self = MODEST_FOLDER_VIEW (user_data);
2443 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2445 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2447 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2450 gtk_tree_model_get (model, &iter,
2451 INSTANCE_COLUMN, &folder,
2454 g_signal_emit (G_OBJECT(self),
2455 signals[FOLDER_ACTIVATED_SIGNAL],
2458 #ifdef MODEST_TOOLKIT_HILDON2
2459 HildonUIMode ui_mode;
2460 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2461 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2462 if (priv->cur_folder_store)
2463 g_object_unref (priv->cur_folder_store);
2464 priv->cur_folder_store = g_object_ref (folder);
2468 g_object_unref (folder);
2472 modest_folder_view_get_selected (ModestFolderView *self)
2474 ModestFolderViewPrivate *priv;
2476 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2478 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2479 if (priv->cur_folder_store)
2480 g_object_ref (priv->cur_folder_store);
2482 return priv->cur_folder_store;
2486 get_cmp_rows_type_pos (GObject *folder)
2488 /* Remote accounts -> Local account -> MMC account .*/
2491 if (TNY_IS_ACCOUNT (folder) &&
2492 modest_tny_account_is_virtual_local_folders (
2493 TNY_ACCOUNT (folder))) {
2495 } else if (TNY_IS_ACCOUNT (folder)) {
2496 TnyAccount *account = TNY_ACCOUNT (folder);
2497 const gchar *account_id = tny_account_get_id (account);
2498 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2504 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2505 return -1; /* Should never happen */
2510 inbox_is_special (TnyFolderStore *folder_store)
2512 gboolean is_special = TRUE;
2514 if (TNY_IS_FOLDER (folder_store)) {
2518 gchar *last_inbox_bar;
2520 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2521 downcase = g_utf8_strdown (id, -1);
2522 last_bar = g_strrstr (downcase, "/");
2524 last_inbox_bar = g_strrstr (downcase, "inbox/");
2525 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2536 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2538 TnyAccount *account;
2539 gboolean is_special;
2540 /* Inbox, Outbox, Drafts, Sent, User */
2543 if (!TNY_IS_FOLDER (folder_store))
2546 case TNY_FOLDER_TYPE_INBOX:
2548 account = tny_folder_get_account (folder_store);
2549 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2551 /* In inbox case we need to know if the inbox is really the top
2552 * inbox of the account, or if it's a submailbox inbox. To do
2553 * this we'll apply an heuristic rule: Find last "/" and check
2554 * if it's preceeded by another Inbox */
2555 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2556 g_object_unref (account);
2557 return is_special?0:4;
2560 case TNY_FOLDER_TYPE_OUTBOX:
2561 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2563 case TNY_FOLDER_TYPE_DRAFTS:
2565 account = tny_folder_get_account (folder_store);
2566 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2567 g_object_unref (account);
2568 return is_special?1:4;
2571 case TNY_FOLDER_TYPE_SENT:
2573 account = tny_folder_get_account (folder_store);
2574 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2575 g_object_unref (account);
2576 return is_special?3:4;
2585 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2587 const gchar *a1_name, *a2_name;
2589 a1_name = tny_account_get_name (a1);
2590 a2_name = tny_account_get_name (a2);
2592 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2596 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2598 TnyAccount *a1 = NULL, *a2 = NULL;
2601 if (TNY_IS_ACCOUNT (s1)) {
2602 a1 = TNY_ACCOUNT (g_object_ref (s1));
2603 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2604 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2607 if (TNY_IS_ACCOUNT (s2)) {
2608 a2 = TNY_ACCOUNT (g_object_ref (s2));
2609 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2610 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2627 /* First we sort with the type of account */
2628 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2632 cmp = compare_account_names (a1, a2);
2636 g_object_unref (a1);
2638 g_object_unref (a2);
2644 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2646 gint is_account1, is_account2;
2648 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2649 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2651 return is_account2 - is_account1;
2655 * This function orders the mail accounts according to these rules:
2656 * 1st - remote accounts
2657 * 2nd - local account
2661 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2665 gchar *name1 = NULL;
2666 gchar *name2 = NULL;
2667 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2668 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2669 GObject *folder1 = NULL;
2670 GObject *folder2 = NULL;
2672 gtk_tree_model_get (tree_model, iter1,
2673 NAME_COLUMN, &name1,
2675 INSTANCE_COLUMN, &folder1,
2677 gtk_tree_model_get (tree_model, iter2,
2678 NAME_COLUMN, &name2,
2679 TYPE_COLUMN, &type2,
2680 INSTANCE_COLUMN, &folder2,
2683 /* Return if we get no folder. This could happen when folder
2684 operations are happening. The model is updated after the
2685 folder copy/move actually occurs, so there could be
2686 situations where the model to be drawn is not correct */
2687 if (!folder1 || !folder2)
2690 /* Sort by type. First the special folders, then the archives */
2691 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2695 /* Now we sort using the account of each folder */
2696 if (TNY_IS_FOLDER_STORE (folder1) &&
2697 TNY_IS_FOLDER_STORE (folder2)) {
2698 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2702 /* Each group is preceeded by its account */
2703 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2708 /* Pure sort by name */
2709 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2712 g_object_unref(G_OBJECT(folder1));
2714 g_object_unref(G_OBJECT(folder2));
2722 /*****************************************************************************/
2723 /* DRAG and DROP stuff */
2724 /*****************************************************************************/
2726 * This function fills the #GtkSelectionData with the row and the
2727 * model that has been dragged. It's called when this widget is a
2728 * source for dnd after the event drop happened
2731 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2732 guint info, guint time, gpointer data)
2734 GtkTreeSelection *selection;
2735 GtkTreeModel *model;
2737 GtkTreePath *source_row;
2739 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2740 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2742 source_row = gtk_tree_model_get_path (model, &iter);
2743 gtk_tree_set_row_drag_data (selection_data,
2747 gtk_tree_path_free (source_row);
2751 typedef struct _DndHelper {
2752 ModestFolderView *folder_view;
2753 gboolean delete_source;
2754 GtkTreePath *source_row;
2758 dnd_helper_destroyer (DndHelper *helper)
2760 /* Free the helper */
2761 gtk_tree_path_free (helper->source_row);
2762 g_slice_free (DndHelper, helper);
2766 xfer_folder_cb (ModestMailOperation *mail_op,
2767 TnyFolder *new_folder,
2771 /* Select the folder */
2772 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2778 /* get the folder for the row the treepath refers to. */
2779 /* folder must be unref'd */
2780 static TnyFolderStore *
2781 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2784 TnyFolderStore *folder = NULL;
2786 if (gtk_tree_model_get_iter (model,&iter, path))
2787 gtk_tree_model_get (model, &iter,
2788 INSTANCE_COLUMN, &folder,
2795 * This function is used by drag_data_received_cb to manage drag and
2796 * drop of a header, i.e, and drag from the header view to the folder
2800 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2801 GtkTreeModel *dest_model,
2802 GtkTreePath *dest_row,
2803 GtkSelectionData *selection_data)
2805 TnyList *headers = NULL;
2806 TnyFolder *folder = NULL, *src_folder = NULL;
2807 TnyFolderType folder_type;
2808 GtkTreeIter source_iter, dest_iter;
2809 ModestWindowMgr *mgr = NULL;
2810 ModestWindow *main_win = NULL;
2811 gchar **uris, **tmp;
2813 /* Build the list of headers */
2814 mgr = modest_runtime_get_window_mgr ();
2815 headers = tny_simple_list_new ();
2816 uris = modest_dnd_selection_data_get_paths (selection_data);
2819 while (*tmp != NULL) {
2822 gboolean first = TRUE;
2825 path = gtk_tree_path_new_from_string (*tmp);
2826 gtk_tree_model_get_iter (source_model, &source_iter, path);
2827 gtk_tree_model_get (source_model, &source_iter,
2828 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2831 /* Do not enable d&d of headers already opened */
2832 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2833 tny_list_append (headers, G_OBJECT (header));
2835 if (G_UNLIKELY (first)) {
2836 src_folder = tny_header_get_folder (header);
2840 /* Free and go on */
2841 gtk_tree_path_free (path);
2842 g_object_unref (header);
2847 /* This could happen ig we perform a d&d very quickly over the
2848 same row that row could dissapear because message is
2850 if (!TNY_IS_FOLDER (src_folder))
2853 /* Get the target folder */
2854 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2855 gtk_tree_model_get (dest_model, &dest_iter,
2859 if (!folder || !TNY_IS_FOLDER(folder)) {
2860 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2864 folder_type = modest_tny_folder_guess_folder_type (folder);
2865 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2866 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2867 goto cleanup; /* cannot move messages there */
2870 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2871 /* g_warning ("folder not writable"); */
2872 goto cleanup; /* verboten! */
2875 /* Ask for confirmation to move */
2876 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2878 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2882 /* Transfer messages */
2883 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2888 if (G_IS_OBJECT (src_folder))
2889 g_object_unref (src_folder);
2890 if (G_IS_OBJECT(folder))
2891 g_object_unref (G_OBJECT (folder));
2892 if (G_IS_OBJECT(headers))
2893 g_object_unref (headers);
2897 TnyFolderStore *src_folder;
2898 TnyFolderStore *dst_folder;
2899 ModestFolderView *folder_view;
2904 dnd_folder_info_destroyer (DndFolderInfo *info)
2906 if (info->src_folder)
2907 g_object_unref (info->src_folder);
2908 if (info->dst_folder)
2909 g_object_unref (info->dst_folder);
2910 g_slice_free (DndFolderInfo, info);
2914 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2915 GtkWindow *parent_window,
2916 TnyAccount *account)
2919 modest_ui_actions_on_account_connection_error (parent_window, account);
2921 /* Free the helper & info */
2922 dnd_helper_destroyer (info->helper);
2923 dnd_folder_info_destroyer (info);
2927 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2929 GtkWindow *parent_window,
2930 TnyAccount *account,
2933 DndFolderInfo *info = NULL;
2934 ModestMailOperation *mail_op;
2936 info = (DndFolderInfo *) user_data;
2938 if (err || canceled) {
2939 dnd_on_connection_failed_destroyer (info, parent_window, account);
2943 /* Do the mail operation */
2944 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2945 modest_ui_actions_move_folder_error_handler,
2946 info->src_folder, NULL);
2948 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2951 /* Transfer the folder */
2952 modest_mail_operation_xfer_folder (mail_op,
2953 TNY_FOLDER (info->src_folder),
2955 info->helper->delete_source,
2957 info->helper->folder_view);
2960 g_object_unref (G_OBJECT (mail_op));
2961 dnd_helper_destroyer (info->helper);
2962 dnd_folder_info_destroyer (info);
2967 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2969 GtkWindow *parent_window,
2970 TnyAccount *account,
2973 DndFolderInfo *info = NULL;
2975 info = (DndFolderInfo *) user_data;
2977 if (err || canceled) {
2978 dnd_on_connection_failed_destroyer (info, parent_window, account);
2982 /* Connect to source folder and perform the copy/move */
2983 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2985 drag_and_drop_from_folder_view_src_folder_performer,
2990 * This function is used by drag_data_received_cb to manage drag and
2991 * drop of a folder, i.e, and drag from the folder view to the same
2995 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2996 GtkTreeModel *dest_model,
2997 GtkTreePath *dest_row,
2998 GtkSelectionData *selection_data,
3001 GtkTreeIter dest_iter, iter;
3002 TnyFolderStore *dest_folder = NULL;
3003 TnyFolderStore *folder = NULL;
3004 gboolean forbidden = FALSE;
3006 DndFolderInfo *info = NULL;
3008 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3010 g_warning ("%s: BUG: no main window", __FUNCTION__);
3011 dnd_helper_destroyer (helper);
3016 /* check the folder rules for the destination */
3017 folder = tree_path_to_folder (dest_model, dest_row);
3018 if (TNY_IS_FOLDER(folder)) {
3019 ModestTnyFolderRules rules =
3020 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3021 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3022 } else if (TNY_IS_FOLDER_STORE(folder)) {
3023 /* enable local root as destination for folders */
3024 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3025 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3028 g_object_unref (folder);
3031 /* check the folder rules for the source */
3032 folder = tree_path_to_folder (source_model, helper->source_row);
3033 if (TNY_IS_FOLDER(folder)) {
3034 ModestTnyFolderRules rules =
3035 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3036 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3039 g_object_unref (folder);
3043 /* Check if the drag is possible */
3044 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3046 modest_platform_run_information_dialog ((GtkWindow *) win,
3047 _("mail_in_ui_folder_move_target_error"),
3049 /* Restore the previous selection */
3050 folder = tree_path_to_folder (source_model, helper->source_row);
3052 if (TNY_IS_FOLDER (folder))
3053 modest_folder_view_select_folder (helper->folder_view,
3054 TNY_FOLDER (folder), FALSE);
3055 g_object_unref (folder);
3057 dnd_helper_destroyer (helper);
3062 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3063 gtk_tree_model_get (dest_model, &dest_iter,
3066 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3067 gtk_tree_model_get (source_model, &iter,
3071 /* Create the info for the performer */
3072 info = g_slice_new0 (DndFolderInfo);
3073 info->src_folder = g_object_ref (folder);
3074 info->dst_folder = g_object_ref (dest_folder);
3075 info->helper = helper;
3077 /* Connect to the destination folder and perform the copy/move */
3078 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3080 drag_and_drop_from_folder_view_dst_folder_performer,
3084 g_object_unref (dest_folder);
3085 g_object_unref (folder);
3089 * This function receives the data set by the "drag-data-get" signal
3090 * handler. This information comes within the #GtkSelectionData. This
3091 * function will manage both the drags of folders of the treeview and
3092 * drags of headers of the header view widget.
3095 on_drag_data_received (GtkWidget *widget,
3096 GdkDragContext *context,
3099 GtkSelectionData *selection_data,
3104 GtkWidget *source_widget;
3105 GtkTreeModel *dest_model, *source_model;
3106 GtkTreePath *source_row, *dest_row;
3107 GtkTreeViewDropPosition pos;
3108 gboolean delete_source = FALSE;
3109 gboolean success = FALSE;
3111 /* Do not allow further process */
3112 g_signal_stop_emission_by_name (widget, "drag-data-received");
3113 source_widget = gtk_drag_get_source_widget (context);
3115 /* Get the action */
3116 if (context->action == GDK_ACTION_MOVE) {
3117 delete_source = TRUE;
3119 /* Notify that there is no folder selected. We need to
3120 do this in order to update the headers view (and
3121 its monitors, because when moving, the old folder
3122 won't longer exist. We can not wait for the end of
3123 the operation, because the operation won't start if
3124 the folder is in use */
3125 if (source_widget == widget) {
3126 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3127 gtk_tree_selection_unselect_all (sel);
3131 /* Check if the get_data failed */
3132 if (selection_data == NULL || selection_data->length < 0)
3135 /* Select the destination model */
3136 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3138 /* Get the path to the destination row. Can not call
3139 gtk_tree_view_get_drag_dest_row() because the source row
3140 is not selected anymore */
3141 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3144 /* Only allow drops IN other rows */
3146 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3147 pos == GTK_TREE_VIEW_DROP_AFTER)
3151 /* Drags from the header view */
3152 if (source_widget != widget) {
3153 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3155 drag_and_drop_from_header_view (source_model,
3160 DndHelper *helper = NULL;
3162 /* Get the source model and row */
3163 gtk_tree_get_row_drag_data (selection_data,
3167 /* Create the helper */
3168 helper = g_slice_new0 (DndHelper);
3169 helper->delete_source = delete_source;
3170 helper->source_row = gtk_tree_path_copy (source_row);
3171 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3173 drag_and_drop_from_folder_view (source_model,
3179 gtk_tree_path_free (source_row);
3183 gtk_tree_path_free (dest_row);
3186 /* Finish the drag and drop */
3187 gtk_drag_finish (context, success, FALSE, time);
3191 * We define a "drag-drop" signal handler because we do not want to
3192 * use the default one, because the default one always calls
3193 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3194 * signal handler, because there we have all the information available
3195 * to know if the dnd was a success or not.
3198 drag_drop_cb (GtkWidget *widget,
3199 GdkDragContext *context,
3207 if (!context->targets)
3210 /* Check if we're dragging a folder row */
3211 target = gtk_drag_dest_find_target (widget, context, NULL);
3213 /* Request the data from the source. */
3214 gtk_drag_get_data(widget, context, target, time);
3220 * This function expands a node of a tree view if it's not expanded
3221 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3222 * does that, so that's why they're here.
3225 expand_row_timeout (gpointer data)
3227 GtkTreeView *tree_view = data;
3228 GtkTreePath *dest_path = NULL;
3229 GtkTreeViewDropPosition pos;
3230 gboolean result = FALSE;
3232 gdk_threads_enter ();
3234 gtk_tree_view_get_drag_dest_row (tree_view,
3239 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3240 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3241 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3242 gtk_tree_path_free (dest_path);
3246 gtk_tree_path_free (dest_path);
3251 gdk_threads_leave ();
3257 * This function is called whenever the pointer is moved over a widget
3258 * while dragging some data. It installs a timeout that will expand a
3259 * node of the treeview if not expanded yet. This function also calls
3260 * gdk_drag_status in order to set the suggested action that will be
3261 * used by the "drag-data-received" signal handler to know if we
3262 * should do a move or just a copy of the data.
3265 on_drag_motion (GtkWidget *widget,
3266 GdkDragContext *context,
3272 GtkTreeViewDropPosition pos;
3273 GtkTreePath *dest_row;
3274 GtkTreeModel *dest_model;
3275 ModestFolderViewPrivate *priv;
3276 GdkDragAction suggested_action;
3277 gboolean valid_location = FALSE;
3278 TnyFolderStore *folder = NULL;
3280 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3282 if (priv->timer_expander != 0) {
3283 g_source_remove (priv->timer_expander);
3284 priv->timer_expander = 0;
3287 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3292 /* Do not allow drops between folders */
3294 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3295 pos == GTK_TREE_VIEW_DROP_AFTER) {
3296 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3297 gdk_drag_status(context, 0, time);
3298 valid_location = FALSE;
3301 valid_location = TRUE;
3304 /* Check that the destination folder is writable */
3305 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3306 folder = tree_path_to_folder (dest_model, dest_row);
3307 if (folder && TNY_IS_FOLDER (folder)) {
3308 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3310 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3311 valid_location = FALSE;
3316 /* Expand the selected row after 1/2 second */
3317 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3318 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3320 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3322 /* Select the desired action. By default we pick MOVE */
3323 suggested_action = GDK_ACTION_MOVE;
3325 if (context->actions == GDK_ACTION_COPY)
3326 gdk_drag_status(context, GDK_ACTION_COPY, time);
3327 else if (context->actions == GDK_ACTION_MOVE)
3328 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3329 else if (context->actions & suggested_action)
3330 gdk_drag_status(context, suggested_action, time);
3332 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3336 g_object_unref (folder);
3338 gtk_tree_path_free (dest_row);
3340 g_signal_stop_emission_by_name (widget, "drag-motion");
3342 return valid_location;
3346 * This function sets the treeview as a source and a target for dnd
3347 * events. It also connects all the requirede signals.
3350 setup_drag_and_drop (GtkTreeView *self)
3352 /* Set up the folder view as a dnd destination. Set only the
3353 highlight flag, otherwise gtk will have a different
3355 #ifdef MODEST_TOOLKIT_HILDON2
3358 gtk_drag_dest_set (GTK_WIDGET (self),
3359 GTK_DEST_DEFAULT_HIGHLIGHT,
3360 folder_view_drag_types,
3361 G_N_ELEMENTS (folder_view_drag_types),
3362 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3364 g_signal_connect (G_OBJECT (self),
3365 "drag_data_received",
3366 G_CALLBACK (on_drag_data_received),
3370 /* Set up the treeview as a dnd source */
3371 gtk_drag_source_set (GTK_WIDGET (self),
3373 folder_view_drag_types,
3374 G_N_ELEMENTS (folder_view_drag_types),
3375 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3377 g_signal_connect (G_OBJECT (self),
3379 G_CALLBACK (on_drag_motion),
3382 g_signal_connect (G_OBJECT (self),
3384 G_CALLBACK (on_drag_data_get),
3387 g_signal_connect (G_OBJECT (self),
3389 G_CALLBACK (drag_drop_cb),
3394 * This function manages the navigation through the folders using the
3395 * keyboard or the hardware keys in the device
3398 on_key_pressed (GtkWidget *self,
3402 GtkTreeSelection *selection;
3404 GtkTreeModel *model;
3405 gboolean retval = FALSE;
3407 /* Up and Down are automatically managed by the treeview */
3408 if (event->keyval == GDK_Return) {
3409 /* Expand/Collapse the selected row */
3410 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3411 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3414 path = gtk_tree_model_get_path (model, &iter);
3416 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3417 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3419 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3420 gtk_tree_path_free (path);
3422 /* No further processing */
3430 * We listen to the changes in the local folder account name key,
3431 * because we want to show the right name in the view. The local
3432 * folder account name corresponds to the device name in the Maemo
3433 * version. We do this because we do not want to query gconf on each
3434 * tree view refresh. It's better to cache it and change whenever
3438 on_configuration_key_changed (ModestConf* conf,
3440 ModestConfEvent event,
3441 ModestConfNotificationId id,
3442 ModestFolderView *self)
3444 ModestFolderViewPrivate *priv;
3447 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3448 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3450 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3451 g_free (priv->local_account_name);
3453 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3454 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3456 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3457 MODEST_CONF_DEVICE_NAME, NULL);
3459 /* Force a redraw */
3460 #if GTK_CHECK_VERSION(2, 8, 0)
3461 GtkTreeViewColumn * tree_column;
3463 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3465 gtk_tree_view_column_queue_resize (tree_column);
3467 gtk_widget_queue_draw (GTK_WIDGET (self));
3473 modest_folder_view_set_style (ModestFolderView *self,
3474 ModestFolderViewStyle style)
3476 ModestFolderViewPrivate *priv;
3478 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3479 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3480 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3485 priv->style = style;
3489 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3490 const gchar *account_id)
3492 ModestFolderViewPrivate *priv;
3493 GtkTreeModel *model;
3495 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3497 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3499 /* This will be used by the filter_row callback,
3500 * to decided which rows to show: */
3501 if (priv->visible_account_id) {
3502 g_free (priv->visible_account_id);
3503 priv->visible_account_id = NULL;
3506 priv->visible_account_id = g_strdup (account_id);
3509 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3510 if (GTK_IS_TREE_MODEL_FILTER (model))
3511 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3513 /* Save settings to gconf */
3514 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3515 MODEST_CONF_FOLDER_VIEW_KEY);
3517 /* Notify observers */
3518 g_signal_emit (G_OBJECT(self),
3519 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3524 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3526 ModestFolderViewPrivate *priv;
3528 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3530 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3532 return (const gchar *) priv->visible_account_id;
3536 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3540 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3542 gtk_tree_model_get (model, iter,
3546 gboolean result = FALSE;
3547 if (type == TNY_FOLDER_TYPE_INBOX) {
3551 *inbox_iter = *iter;
3555 if (gtk_tree_model_iter_children (model, &child, iter)) {
3556 if (find_inbox_iter (model, &child, inbox_iter))
3560 } while (gtk_tree_model_iter_next (model, iter));
3569 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3571 #ifndef MODEST_TOOLKIT_HILDON2
3572 GtkTreeModel *model;
3573 GtkTreeIter iter, inbox_iter;
3574 GtkTreeSelection *sel;
3575 GtkTreePath *path = NULL;
3577 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3579 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3583 expand_root_items (self);
3584 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3586 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3587 g_warning ("%s: model is empty", __FUNCTION__);
3591 if (find_inbox_iter (model, &iter, &inbox_iter))
3592 path = gtk_tree_model_get_path (model, &inbox_iter);
3594 path = gtk_tree_path_new_first ();
3596 /* Select the row and free */
3597 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3598 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3599 gtk_tree_path_free (path);
3602 gtk_widget_grab_focus (GTK_WIDGET(self));
3609 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3614 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3615 TnyFolder* a_folder;
3618 gtk_tree_model_get (model, iter,
3619 INSTANCE_COLUMN, &a_folder,
3625 if (folder == a_folder) {
3626 g_object_unref (a_folder);
3627 *folder_iter = *iter;
3630 g_object_unref (a_folder);
3632 if (gtk_tree_model_iter_children (model, &child, iter)) {
3633 if (find_folder_iter (model, &child, folder_iter, folder))
3637 } while (gtk_tree_model_iter_next (model, iter));
3642 #ifndef MODEST_TOOLKIT_HILDON2
3644 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3647 ModestFolderView *self)
3649 ModestFolderViewPrivate *priv = NULL;
3650 GtkTreeSelection *sel;
3651 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3652 GObject *instance = NULL;
3654 if (!MODEST_IS_FOLDER_VIEW(self))
3657 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3659 priv->reexpand = TRUE;
3661 gtk_tree_model_get (tree_model, iter,
3663 INSTANCE_COLUMN, &instance,
3669 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3670 priv->folder_to_select = g_object_ref (instance);
3672 g_object_unref (instance);
3674 if (priv->folder_to_select) {
3676 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3679 path = gtk_tree_model_get_path (tree_model, iter);
3680 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3682 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3684 gtk_tree_selection_select_iter (sel, iter);
3685 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3687 gtk_tree_path_free (path);
3691 modest_folder_view_disable_next_folder_selection (self);
3693 /* Refilter the model */
3694 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3700 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3702 ModestFolderViewPrivate *priv;
3704 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3706 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3708 if (priv->folder_to_select)
3709 g_object_unref(priv->folder_to_select);
3711 priv->folder_to_select = NULL;
3715 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3716 gboolean after_change)
3718 GtkTreeModel *model;
3719 GtkTreeIter iter, folder_iter;
3720 GtkTreeSelection *sel;
3721 ModestFolderViewPrivate *priv = NULL;
3723 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3724 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3726 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3729 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3730 gtk_tree_selection_unselect_all (sel);
3732 if (priv->folder_to_select)
3733 g_object_unref(priv->folder_to_select);
3734 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3738 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3743 /* Refilter the model, before selecting the folder */
3744 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3746 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3747 g_warning ("%s: model is empty", __FUNCTION__);
3751 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3754 path = gtk_tree_model_get_path (model, &folder_iter);
3755 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3757 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3758 gtk_tree_selection_select_iter (sel, &folder_iter);
3759 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3761 gtk_tree_path_free (path);
3769 modest_folder_view_copy_selection (ModestFolderView *self)
3771 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3773 /* Copy selection */
3774 _clipboard_set_selected_data (self, FALSE);
3778 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3780 ModestFolderViewPrivate *priv = NULL;
3781 GtkTreeModel *model = NULL;
3782 const gchar **hidding = NULL;
3783 guint i, n_selected;
3785 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3786 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3788 /* Copy selection */
3789 if (!_clipboard_set_selected_data (folder_view, TRUE))
3792 /* Get hidding ids */
3793 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3795 /* Clear hidding array created by previous cut operation */
3796 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3798 /* Copy hidding array */
3799 priv->n_selected = n_selected;
3800 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3801 for (i=0; i < n_selected; i++)
3802 priv->hidding_ids[i] = g_strdup(hidding[i]);
3804 /* Hide cut folders */
3805 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3806 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3810 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3811 ModestFolderView *folder_view_dst)
3813 GtkTreeModel *filter_model = NULL;
3814 GtkTreeModel *model = NULL;
3815 GtkTreeModel *new_filter_model = NULL;
3816 GtkTreeModel *old_tny_model = NULL;
3817 GtkTreeModel *new_tny_model = NULL;
3818 ModestFolderViewPrivate *dst_priv;
3820 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3821 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3823 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3824 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3825 new_tny_model = NULL;
3828 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3829 g_signal_handler_disconnect (G_OBJECT (old_tny_model), dst_priv->activity_changed_handler);
3830 dst_priv->activity_changed_handler = 0;
3832 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3833 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3835 /* Build new filter model */
3836 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3837 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3844 /* Set copied model */
3845 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3846 #ifndef MODEST_TOOLKIT_HILDON2
3847 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3848 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3850 #ifdef MODEST_TOOLKIT_HILDON2
3852 dst_priv->activity_changed_handler = g_signal_connect (G_OBJECT (new_tny_model), "activity-changed",
3853 G_CALLBACK (on_activity_changed), folder_view_dst);
3857 g_object_unref (new_filter_model);
3861 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3864 GtkTreeModel *model = NULL;
3865 ModestFolderViewPrivate* priv;
3867 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3869 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3870 priv->show_non_move = show;
3871 /* modest_folder_view_update_model(folder_view, */
3872 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3874 /* Hide special folders */
3875 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3876 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3877 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3882 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3885 ModestFolderViewPrivate* priv;
3887 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3889 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3890 priv->show_message_count = show;
3892 g_object_set (G_OBJECT (priv->messages_renderer),
3893 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3897 /* Returns FALSE if it did not selected anything */
3899 _clipboard_set_selected_data (ModestFolderView *folder_view,
3902 ModestFolderViewPrivate *priv = NULL;
3903 TnyFolderStore *folder = NULL;
3904 gboolean retval = FALSE;
3906 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3907 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3909 /* Set selected data on clipboard */
3910 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3911 folder = modest_folder_view_get_selected (folder_view);
3913 /* Do not allow to select an account */
3914 if (TNY_IS_FOLDER (folder)) {
3915 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3920 g_object_unref (folder);
3926 _clear_hidding_filter (ModestFolderView *folder_view)
3928 ModestFolderViewPrivate *priv;
3931 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3932 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3934 if (priv->hidding_ids != NULL) {
3935 for (i=0; i < priv->n_selected; i++)
3936 g_free (priv->hidding_ids[i]);
3937 g_free(priv->hidding_ids);
3943 on_display_name_changed (ModestAccountMgr *mgr,
3944 const gchar *account,
3947 ModestFolderView *self;
3949 self = MODEST_FOLDER_VIEW (user_data);
3951 /* Force a redraw */
3952 #if GTK_CHECK_VERSION(2, 8, 0)
3953 GtkTreeViewColumn * tree_column;
3955 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3957 gtk_tree_view_column_queue_resize (tree_column);
3959 gtk_widget_queue_draw (GTK_WIDGET (self));
3964 modest_folder_view_set_cell_style (ModestFolderView *self,
3965 ModestFolderViewCellStyle cell_style)
3967 ModestFolderViewPrivate *priv = NULL;
3969 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3970 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3972 priv->cell_style = cell_style;
3974 g_object_set (G_OBJECT (priv->messages_renderer),
3975 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3978 gtk_widget_queue_draw (GTK_WIDGET (self));
3982 update_style (ModestFolderView *self)
3984 ModestFolderViewPrivate *priv;
3985 GdkColor style_color, style_active_color;
3986 PangoAttrList *attr_list;
3988 PangoAttribute *attr;
3990 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3991 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3995 attr_list = pango_attr_list_new ();
3996 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3997 gdk_color_parse ("grey", &style_color);
3999 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4000 pango_attr_list_insert (attr_list, attr);
4003 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4005 "SmallSystemFont", NULL,
4008 attr = pango_attr_font_desc_new (pango_font_description_copy
4009 (style->font_desc));
4010 pango_attr_list_insert (attr_list, attr);
4012 g_object_set (G_OBJECT (priv->messages_renderer),
4013 "foreground-gdk", &style_color,
4014 "foreground-set", TRUE,
4015 "attributes", attr_list,
4017 pango_attr_list_unref (attr_list);
4019 if (priv->active_color)
4020 gdk_color_free (priv->active_color);
4022 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4023 priv->active_color = gdk_color_copy (&style_active_color);
4028 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4030 if (strcmp ("style", spec->name) == 0) {
4031 update_style (MODEST_FOLDER_VIEW (obj));
4032 gtk_widget_queue_draw (GTK_WIDGET (obj));
4037 modest_folder_view_set_filter (ModestFolderView *self,
4038 ModestFolderViewFilter filter)
4040 ModestFolderViewPrivate *priv;
4041 GtkTreeModel *filter_model;
4043 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4044 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4046 priv->filter |= filter;
4048 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4049 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4050 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4055 modest_folder_view_unset_filter (ModestFolderView *self,
4056 ModestFolderViewFilter filter)
4058 ModestFolderViewPrivate *priv;
4059 GtkTreeModel *filter_model;
4061 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4062 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4064 priv->filter &= ~filter;
4066 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4067 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4068 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4073 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4074 ModestTnyFolderRules rules)
4076 GtkTreeModel *filter_model;
4078 gboolean fulfil = FALSE;
4080 if (!get_inner_models (self, &filter_model, NULL, NULL))
4083 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4087 TnyFolderStore *folder;
4089 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4091 if (TNY_IS_FOLDER (folder)) {
4092 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4093 /* Folder rules are negative: non_writable, non_deletable... */
4094 if (!(folder_rules & rules))
4097 g_object_unref (folder);
4100 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4106 modest_folder_view_set_list_to_move (ModestFolderView *self,
4109 ModestFolderViewPrivate *priv;
4111 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4112 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4114 if (priv->list_to_move)
4115 g_object_unref (priv->list_to_move);
4118 g_object_ref (list);
4120 priv->list_to_move = list;
4124 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4126 ModestFolderViewPrivate *priv;
4128 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4129 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4132 g_free (priv->mailbox);
4134 priv->mailbox = g_strdup (mailbox);
4136 /* Notify observers */
4137 g_signal_emit (G_OBJECT(self),
4138 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4139 priv->visible_account_id);
4143 modest_folder_view_get_mailbox (ModestFolderView *self)
4145 ModestFolderViewPrivate *priv;
4147 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4148 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4150 return (const gchar *) priv->mailbox;
4154 modest_folder_view_get_activity (ModestFolderView *self)
4156 ModestFolderViewPrivate *priv;
4157 GtkTreeModel *inner_model;
4159 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4160 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4161 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4163 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4164 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4170 #ifdef MODEST_TOOLKIT_HILDON2
4172 on_activity_changed (TnyGtkFolderListStore *store,
4174 ModestFolderView *folder_view)
4176 ModestFolderViewPrivate *priv;
4178 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4179 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4180 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4182 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,