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_va_new_messages"),
855 } else if (number == 1) {
856 item_name = g_strdup_printf (drafts?_("mcen_ti_message"):_("mcen_va_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;
1388 #ifdef MODEST_TOOLKIT_HILDON2
1389 GtkTreeModel *tny_model;
1391 if (get_inner_models (MODEST_FOLDER_VIEW (obj), NULL, NULL, &tny_model)) {
1392 if (g_signal_handler_is_connected (tny_model,
1393 priv->activity_changed_handler)) {
1394 g_signal_handler_disconnect (tny_model,
1395 priv->activity_changed_handler);
1396 priv->activity_changed_handler = 0;
1402 g_object_unref (G_OBJECT (priv->query));
1406 if (priv->folder_to_select) {
1407 g_object_unref (G_OBJECT(priv->folder_to_select));
1408 priv->folder_to_select = NULL;
1411 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1413 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1415 g_free (priv->local_account_name);
1416 g_free (priv->visible_account_id);
1417 g_free (priv->mailbox);
1419 if (priv->conf_key_signal) {
1420 g_signal_handler_disconnect (modest_runtime_get_conf (),
1421 priv->conf_key_signal);
1422 priv->conf_key_signal = 0;
1425 if (priv->cur_folder_store) {
1426 g_object_unref (priv->cur_folder_store);
1427 priv->cur_folder_store = NULL;
1430 if (priv->list_to_move) {
1431 g_object_unref (priv->list_to_move);
1432 priv->list_to_move = NULL;
1435 /* Clear hidding array created by cut operation */
1436 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1438 G_OBJECT_CLASS(parent_class)->finalize (obj);
1443 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1445 ModestFolderViewPrivate *priv;
1448 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1449 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1451 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1452 device = tny_account_store_get_device (account_store);
1454 if (G_UNLIKELY (priv->account_store)) {
1456 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1457 priv->account_inserted_signal))
1458 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1459 priv->account_inserted_signal);
1460 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1461 priv->account_removed_signal))
1462 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1463 priv->account_removed_signal);
1464 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1465 priv->account_changed_signal))
1466 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1467 priv->account_changed_signal);
1468 g_object_unref (G_OBJECT (priv->account_store));
1471 priv->account_store = g_object_ref (G_OBJECT (account_store));
1473 priv->account_removed_signal =
1474 g_signal_connect (G_OBJECT(account_store), "account_removed",
1475 G_CALLBACK (on_account_removed), self);
1477 priv->account_inserted_signal =
1478 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1479 G_CALLBACK (on_account_inserted), self);
1481 priv->account_changed_signal =
1482 g_signal_connect (G_OBJECT(account_store), "account_changed",
1483 G_CALLBACK (on_account_changed), self);
1485 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1486 priv->reselect = FALSE;
1487 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1489 g_object_unref (G_OBJECT (device));
1493 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1496 ModestFolderView *self;
1497 GtkTreeModel *model, *filter_model;
1500 self = MODEST_FOLDER_VIEW (user_data);
1502 if (!get_inner_models (self, &filter_model, NULL, &model))
1505 /* Remove outbox from model */
1506 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1507 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1508 g_object_unref (outbox);
1511 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1515 on_account_inserted (TnyAccountStore *account_store,
1516 TnyAccount *account,
1519 ModestFolderViewPrivate *priv;
1520 GtkTreeModel *model, *filter_model;
1522 /* Ignore transport account insertions, we're not showing them
1523 in the folder view */
1524 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1527 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1530 /* If we're adding a new account, and there is no previous
1531 one, we need to select the visible server account */
1532 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1533 !priv->visible_account_id)
1534 modest_widget_memory_restore (modest_runtime_get_conf(),
1535 G_OBJECT (user_data),
1536 MODEST_CONF_FOLDER_VIEW_KEY);
1540 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1541 &filter_model, NULL, &model))
1544 /* Insert the account in the model */
1545 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1547 /* When the model is a list store (plain representation) the
1548 outbox is not a child of any account so we have to manually
1549 delete it because removing the local folders account won't
1550 delete it (because tny_folder_get_account() is not defined
1551 for a merge folder */
1552 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1553 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1555 priv->outbox_deleted_handler =
1556 g_signal_connect (account,
1558 G_CALLBACK (on_outbox_deleted_cb),
1562 /* Refilter the model */
1563 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1568 same_account_selected (ModestFolderView *self,
1569 TnyAccount *account)
1571 ModestFolderViewPrivate *priv;
1572 gboolean same_account = FALSE;
1574 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1576 if (priv->cur_folder_store) {
1577 TnyAccount *selected_folder_account = NULL;
1579 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1580 selected_folder_account =
1581 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1583 selected_folder_account =
1584 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1587 if (selected_folder_account == account)
1588 same_account = TRUE;
1590 g_object_unref (selected_folder_account);
1592 return same_account;
1597 * Selects the first inbox or the local account in an idle
1600 on_idle_select_first_inbox_or_local (gpointer user_data)
1602 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1604 gdk_threads_enter ();
1605 modest_folder_view_select_first_inbox_or_local (self);
1606 gdk_threads_leave ();
1612 on_account_changed (TnyAccountStore *account_store,
1613 TnyAccount *tny_account,
1616 ModestFolderView *self;
1617 ModestFolderViewPrivate *priv;
1618 GtkTreeModel *model, *filter_model;
1619 GtkTreeSelection *sel;
1620 gboolean same_account;
1622 /* Ignore transport account insertions, we're not showing them
1623 in the folder view */
1624 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1627 self = MODEST_FOLDER_VIEW (user_data);
1628 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1630 /* Get the inner model */
1631 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1632 &filter_model, NULL, &model))
1635 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1637 /* Invalidate the cur_folder_store only if the selected folder
1638 belongs to the account that is being removed */
1639 same_account = same_account_selected (self, tny_account);
1641 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1642 gtk_tree_selection_unselect_all (sel);
1645 /* Remove the account from the model */
1646 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1648 /* Insert the account in the model */
1649 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1651 /* Refilter the model */
1652 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1654 /* Select the first INBOX if the currently selected folder
1655 belongs to the account that is being deleted */
1656 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1657 g_idle_add (on_idle_select_first_inbox_or_local, self);
1661 on_account_removed (TnyAccountStore *account_store,
1662 TnyAccount *account,
1665 ModestFolderView *self = NULL;
1666 ModestFolderViewPrivate *priv;
1667 GtkTreeModel *model, *filter_model;
1668 GtkTreeSelection *sel = NULL;
1669 gboolean same_account = FALSE;
1671 /* Ignore transport account removals, we're not showing them
1672 in the folder view */
1673 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1676 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1677 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1681 self = MODEST_FOLDER_VIEW (user_data);
1682 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1684 /* Invalidate the cur_folder_store only if the selected folder
1685 belongs to the account that is being removed */
1686 same_account = same_account_selected (self, account);
1688 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1689 gtk_tree_selection_unselect_all (sel);
1692 /* Invalidate row to select only if the folder to select
1693 belongs to the account that is being removed*/
1694 if (priv->folder_to_select) {
1695 TnyAccount *folder_to_select_account = NULL;
1697 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1698 if (folder_to_select_account == account) {
1699 modest_folder_view_disable_next_folder_selection (self);
1700 g_object_unref (priv->folder_to_select);
1701 priv->folder_to_select = NULL;
1703 g_object_unref (folder_to_select_account);
1706 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1707 &filter_model, NULL, &model))
1710 /* Disconnect the signal handler */
1711 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1712 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1713 if (g_signal_handler_is_connected (account,
1714 priv->outbox_deleted_handler))
1715 g_signal_handler_disconnect (account,
1716 priv->outbox_deleted_handler);
1719 /* Remove the account from the model */
1720 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1722 /* If the removed account is the currently viewed one then
1723 clear the configuration value. The new visible account will be the default account */
1724 if (priv->visible_account_id &&
1725 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1727 /* Clear the current visible account_id */
1728 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1729 modest_folder_view_set_mailbox (self, NULL);
1731 /* Call the restore method, this will set the new visible account */
1732 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1733 MODEST_CONF_FOLDER_VIEW_KEY);
1736 /* Refilter the model */
1737 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1739 /* Select the first INBOX if the currently selected folder
1740 belongs to the account that is being deleted */
1742 g_idle_add (on_idle_select_first_inbox_or_local, self);
1746 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1748 GtkTreeViewColumn *col;
1750 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1752 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1754 g_printerr ("modest: failed get column for title\n");
1758 gtk_tree_view_column_set_title (col, title);
1759 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1764 modest_folder_view_on_map (ModestFolderView *self,
1765 GdkEventExpose *event,
1768 ModestFolderViewPrivate *priv;
1770 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1772 /* This won't happen often */
1773 if (G_UNLIKELY (priv->reselect)) {
1774 /* Select the first inbox or the local account if not found */
1776 /* TODO: this could cause a lock at startup, so we
1777 comment it for the moment. We know that this will
1778 be a bug, because the INBOX is not selected, but we
1779 need to rewrite some parts of Modest to avoid the
1780 deathlock situation */
1781 /* TODO: check if this is still the case */
1782 priv->reselect = FALSE;
1783 modest_folder_view_select_first_inbox_or_local (self);
1784 /* Notify the display name observers */
1785 g_signal_emit (G_OBJECT(self),
1786 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1790 if (priv->reexpand) {
1791 expand_root_items (self);
1792 priv->reexpand = FALSE;
1799 modest_folder_view_new (TnyFolderStoreQuery *query)
1801 return modest_folder_view_new_full (query, TRUE);
1805 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1808 ModestFolderViewPrivate *priv;
1809 GtkTreeSelection *sel;
1811 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1812 #ifdef MODEST_TOOLKIT_HILDON2
1813 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1816 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1819 priv->query = g_object_ref (query);
1821 priv->do_refresh = do_refresh;
1823 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1824 priv->changed_signal = g_signal_connect (sel, "changed",
1825 G_CALLBACK (on_selection_changed), self);
1827 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1829 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1831 return GTK_WIDGET(self);
1834 /* this feels dirty; any other way to expand all the root items? */
1836 expand_root_items (ModestFolderView *self)
1839 GtkTreeModel *model;
1842 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1843 path = gtk_tree_path_new_first ();
1845 /* all folders should have child items, so.. */
1847 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1848 gtk_tree_path_next (path);
1849 } while (gtk_tree_model_get_iter (model, &iter, path));
1851 gtk_tree_path_free (path);
1855 is_parent_of (TnyFolder *a, TnyFolder *b)
1858 gboolean retval = FALSE;
1860 a_id = tny_folder_get_id (a);
1862 gchar *string_to_match;
1865 string_to_match = g_strconcat (a_id, "/", NULL);
1866 b_id = tny_folder_get_id (b);
1867 retval = g_str_has_prefix (b_id, string_to_match);
1868 g_free (string_to_match);
1874 typedef struct _ForeachFolderInfo {
1877 } ForeachFolderInfo;
1880 foreach_folder_with_id (GtkTreeModel *model,
1885 ForeachFolderInfo *info;
1888 info = (ForeachFolderInfo *) data;
1889 gtk_tree_model_get (model, iter,
1890 INSTANCE_COLUMN, &instance,
1893 if (TNY_IS_FOLDER (instance)) {
1896 id = tny_folder_get_id (TNY_FOLDER (instance));
1898 collate = g_utf8_collate_key (id, -1);
1899 info->found = !strcmp (info->needle, collate);
1905 g_object_unref (instance);
1913 has_folder_with_id (ModestFolderView *self, const gchar *id)
1915 GtkTreeModel *model;
1916 ForeachFolderInfo info = {NULL, FALSE};
1918 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1919 info.needle = g_utf8_collate_key (id, -1);
1921 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1922 g_free (info.needle);
1928 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1931 gboolean retval = FALSE;
1933 a_id = tny_folder_get_id (a);
1936 b_id = tny_folder_get_id (b);
1939 const gchar *last_bar;
1940 gchar *string_to_match;
1941 last_bar = g_strrstr (b_id, "/");
1946 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1947 retval = has_folder_with_id (self, string_to_match);
1948 g_free (string_to_match);
1956 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1958 ModestFolderViewPrivate *priv;
1959 TnyIterator *iterator;
1960 gboolean retval = TRUE;
1962 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1963 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1965 for (iterator = tny_list_create_iterator (priv->list_to_move);
1966 retval && !tny_iterator_is_done (iterator);
1967 tny_iterator_next (iterator)) {
1969 instance = tny_iterator_get_current (iterator);
1970 if (instance == (GObject *) folder) {
1972 } else if (TNY_IS_FOLDER (instance)) {
1973 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1975 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1978 g_object_unref (instance);
1980 g_object_unref (iterator);
1987 * We use this function to implement the
1988 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1989 * account in this case, and the local folders.
1992 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1994 ModestFolderViewPrivate *priv;
1995 gboolean retval = TRUE;
1996 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1997 GObject *instance = NULL;
1998 const gchar *id = NULL;
2000 gboolean found = FALSE;
2001 gboolean cleared = FALSE;
2002 ModestTnyFolderRules rules = 0;
2005 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2006 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2008 gtk_tree_model_get (model, iter,
2009 NAME_COLUMN, &fname,
2011 INSTANCE_COLUMN, &instance,
2014 /* Do not show if there is no instance, this could indeed
2015 happen when the model is being modified while it's being
2016 drawn. This could occur for example when moving folders
2023 if (TNY_IS_ACCOUNT (instance)) {
2024 TnyAccount *acc = TNY_ACCOUNT (instance);
2025 const gchar *account_id = tny_account_get_id (acc);
2027 /* If it isn't a special folder,
2028 * don't show it unless it is the visible account: */
2029 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2030 !modest_tny_account_is_virtual_local_folders (acc) &&
2031 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2033 /* Show only the visible account id */
2034 if (priv->visible_account_id) {
2035 if (strcmp (account_id, priv->visible_account_id))
2042 /* Never show these to the user. They are merged into one folder
2043 * in the local-folders account instead: */
2044 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2047 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2048 /* Only show special folders for current account if needed */
2049 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2050 TnyAccount *account;
2052 account = tny_folder_get_account (TNY_FOLDER (instance));
2054 if (TNY_IS_ACCOUNT (account)) {
2055 const gchar *account_id = tny_account_get_id (account);
2057 if (!modest_tny_account_is_virtual_local_folders (account) &&
2058 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2059 /* Show only the visible account id */
2060 if (priv->visible_account_id) {
2061 if (strcmp (account_id, priv->visible_account_id)) {
2063 } else if (priv->mailbox) {
2064 /* Filter mailboxes */
2065 if (!g_str_has_prefix (fname, priv->mailbox)) {
2067 } else if (!strcmp (fname, priv->mailbox)) {
2068 /* Hide mailbox parent */
2074 g_object_unref (account);
2081 /* Check hiding (if necessary) */
2082 cleared = modest_email_clipboard_cleared (priv->clipboard);
2083 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2084 id = tny_folder_get_id (TNY_FOLDER(instance));
2085 if (priv->hidding_ids != NULL)
2086 for (i=0; i < priv->n_selected && !found; i++)
2087 if (priv->hidding_ids[i] != NULL && id != NULL)
2088 found = (!strcmp (priv->hidding_ids[i], id));
2093 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2094 folder as no message can be move there according to UI specs */
2095 if (retval && !priv->show_non_move) {
2096 if (priv->list_to_move &&
2097 tny_list_get_length (priv->list_to_move) > 0 &&
2098 TNY_IS_FOLDER (instance)) {
2099 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2101 if (retval && TNY_IS_FOLDER (instance) &&
2102 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2104 case TNY_FOLDER_TYPE_OUTBOX:
2105 case TNY_FOLDER_TYPE_SENT:
2106 case TNY_FOLDER_TYPE_DRAFTS:
2109 case TNY_FOLDER_TYPE_UNKNOWN:
2110 case TNY_FOLDER_TYPE_NORMAL:
2111 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2112 if (type == TNY_FOLDER_TYPE_INVALID)
2113 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2115 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2116 type == TNY_FOLDER_TYPE_SENT
2117 || type == TNY_FOLDER_TYPE_DRAFTS)
2124 if (retval && TNY_IS_ACCOUNT (instance) &&
2125 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2126 ModestProtocolType protocol_type;
2128 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2129 retval = !modest_protocol_registry_protocol_type_has_tag
2130 (modest_runtime_get_protocol_registry (),
2132 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2136 /* apply special filters */
2137 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2138 if (TNY_IS_ACCOUNT (instance))
2142 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2143 if (TNY_IS_FOLDER (instance))
2147 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2148 if (TNY_IS_ACCOUNT (instance)) {
2149 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2151 } else if (TNY_IS_FOLDER (instance)) {
2152 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2157 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2158 if (TNY_IS_ACCOUNT (instance)) {
2159 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2161 } else if (TNY_IS_FOLDER (instance)) {
2162 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2167 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2168 /* A mailbox is a fake folder with an @ in the middle of the name */
2169 if (!TNY_IS_FOLDER (instance) ||
2170 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2173 const gchar *folder_name;
2174 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2175 if (!folder_name || strchr (folder_name, '@') == NULL)
2181 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2182 if (TNY_IS_FOLDER (instance)) {
2183 /* Check folder rules */
2184 ModestTnyFolderRules rules;
2186 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2187 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2188 } else if (TNY_IS_ACCOUNT (instance)) {
2189 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2197 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2198 if (TNY_IS_FOLDER (instance)) {
2199 TnyFolderType guess_type;
2201 if (TNY_FOLDER_TYPE_NORMAL) {
2202 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2208 case TNY_FOLDER_TYPE_OUTBOX:
2209 case TNY_FOLDER_TYPE_SENT:
2210 case TNY_FOLDER_TYPE_DRAFTS:
2211 case TNY_FOLDER_TYPE_ARCHIVE:
2212 case TNY_FOLDER_TYPE_INBOX:
2215 case TNY_FOLDER_TYPE_UNKNOWN:
2216 case TNY_FOLDER_TYPE_NORMAL:
2222 } else if (TNY_IS_ACCOUNT (instance)) {
2227 if (retval && TNY_IS_FOLDER (instance)) {
2228 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2231 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2232 if (TNY_IS_FOLDER (instance)) {
2233 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2234 } else if (TNY_IS_ACCOUNT (instance)) {
2239 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2240 if (TNY_IS_FOLDER (instance)) {
2241 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2242 } else if (TNY_IS_ACCOUNT (instance)) {
2247 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2248 if (TNY_IS_FOLDER (instance)) {
2249 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2250 } else if (TNY_IS_ACCOUNT (instance)) {
2256 g_object_unref (instance);
2264 modest_folder_view_update_model (ModestFolderView *self,
2265 TnyAccountStore *account_store)
2267 ModestFolderViewPrivate *priv;
2268 GtkTreeModel *model;
2269 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2271 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2272 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2275 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2277 /* Notify that there is no folder selected */
2278 g_signal_emit (G_OBJECT(self),
2279 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2281 if (priv->cur_folder_store) {
2282 g_object_unref (priv->cur_folder_store);
2283 priv->cur_folder_store = NULL;
2286 /* FIXME: the local accounts are not shown when the query
2287 selects only the subscribed folders */
2288 #ifdef MODEST_TOOLKIT_HILDON2
2289 TnyGtkFolderListStoreFlags flags;
2290 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2291 if (!priv->do_refresh)
2292 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2293 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2295 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2296 MODEST_FOLDER_PATH_SEPARATOR);
2298 model = tny_gtk_folder_store_tree_model_new (NULL);
2301 /* When the model is a list store (plain representation) the
2302 outbox is not a child of any account so we have to manually
2303 delete it because removing the local folders account won't
2304 delete it (because tny_folder_get_account() is not defined
2305 for a merge folder */
2306 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2307 TnyAccount *account;
2308 ModestTnyAccountStore *acc_store;
2310 acc_store = modest_runtime_get_account_store ();
2311 account = modest_tny_account_store_get_local_folders_account (acc_store);
2313 if (g_signal_handler_is_connected (account,
2314 priv->outbox_deleted_handler))
2315 g_signal_handler_disconnect (account,
2316 priv->outbox_deleted_handler);
2318 priv->outbox_deleted_handler =
2319 g_signal_connect (account,
2321 G_CALLBACK (on_outbox_deleted_cb),
2323 g_object_unref (account);
2326 /* Get the accounts: */
2327 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2329 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2331 sortable = gtk_tree_model_sort_new_with_model (model);
2332 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2334 GTK_SORT_ASCENDING);
2335 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2337 cmp_rows, NULL, NULL);
2339 /* Create filter model */
2340 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2341 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2346 if (priv->activity_changed_handler > 0) {
2347 GtkTreeModel *old_tny_model;
2349 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2350 g_signal_handler_disconnect (G_OBJECT (old_tny_model), priv->activity_changed_handler);
2352 priv->activity_changed_handler = 0;
2356 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2357 #ifndef MODEST_TOOLKIT_HILDON2
2358 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2359 (GCallback) on_row_inserted_maybe_select_folder, self);
2362 #ifdef MODEST_TOOLKIT_HILDON2
2363 priv->activity_changed_handler =
2364 g_signal_connect (G_OBJECT (model), "activity-changed", G_CALLBACK (on_activity_changed), self);
2367 g_object_unref (model);
2368 g_object_unref (filter_model);
2369 g_object_unref (sortable);
2371 /* Force a reselection of the INBOX next time the widget is shown */
2372 priv->reselect = TRUE;
2379 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2381 GtkTreeModel *model = NULL;
2382 TnyFolderStore *folder = NULL;
2384 ModestFolderView *tree_view = NULL;
2385 ModestFolderViewPrivate *priv = NULL;
2386 gboolean selected = FALSE;
2388 g_return_if_fail (sel);
2389 g_return_if_fail (user_data);
2391 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2393 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2395 tree_view = MODEST_FOLDER_VIEW (user_data);
2398 gtk_tree_model_get (model, &iter,
2399 INSTANCE_COLUMN, &folder,
2402 /* If the folder is the same do not notify */
2403 if (folder && priv->cur_folder_store == folder) {
2404 g_object_unref (folder);
2409 /* Current folder was unselected */
2410 if (priv->cur_folder_store) {
2411 /* We must do this firstly because a libtinymail-camel
2412 implementation detail. If we issue the signal
2413 before doing the sync_async, then that signal could
2414 cause (and it actually does it) a free of the
2415 summary of the folder (because the main window will
2416 clear the headers view */
2417 #ifndef MODEST_TOOLKIT_HILDON2
2418 if (TNY_IS_FOLDER(priv->cur_folder_store))
2419 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2420 FALSE, NULL, NULL, NULL);
2423 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2424 priv->cur_folder_store, FALSE);
2426 g_object_unref (priv->cur_folder_store);
2427 priv->cur_folder_store = NULL;
2430 /* New current references */
2431 priv->cur_folder_store = folder;
2433 /* New folder has been selected. Do not notify if there is
2434 nothing new selected */
2436 g_signal_emit (G_OBJECT(tree_view),
2437 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2438 0, priv->cur_folder_store, TRUE);
2443 on_row_activated (GtkTreeView *treeview,
2444 GtkTreePath *treepath,
2445 GtkTreeViewColumn *column,
2448 GtkTreeModel *model = NULL;
2449 TnyFolderStore *folder = NULL;
2451 ModestFolderView *self = NULL;
2452 ModestFolderViewPrivate *priv = NULL;
2454 g_return_if_fail (treeview);
2455 g_return_if_fail (user_data);
2457 self = MODEST_FOLDER_VIEW (user_data);
2458 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2460 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2462 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2465 gtk_tree_model_get (model, &iter,
2466 INSTANCE_COLUMN, &folder,
2469 g_signal_emit (G_OBJECT(self),
2470 signals[FOLDER_ACTIVATED_SIGNAL],
2473 #ifdef MODEST_TOOLKIT_HILDON2
2474 HildonUIMode ui_mode;
2475 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2476 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2477 if (priv->cur_folder_store)
2478 g_object_unref (priv->cur_folder_store);
2479 priv->cur_folder_store = g_object_ref (folder);
2483 g_object_unref (folder);
2487 modest_folder_view_get_selected (ModestFolderView *self)
2489 ModestFolderViewPrivate *priv;
2491 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2493 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2494 if (priv->cur_folder_store)
2495 g_object_ref (priv->cur_folder_store);
2497 return priv->cur_folder_store;
2501 get_cmp_rows_type_pos (GObject *folder)
2503 /* Remote accounts -> Local account -> MMC account .*/
2506 if (TNY_IS_ACCOUNT (folder) &&
2507 modest_tny_account_is_virtual_local_folders (
2508 TNY_ACCOUNT (folder))) {
2510 } else if (TNY_IS_ACCOUNT (folder)) {
2511 TnyAccount *account = TNY_ACCOUNT (folder);
2512 const gchar *account_id = tny_account_get_id (account);
2513 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2519 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2520 return -1; /* Should never happen */
2525 inbox_is_special (TnyFolderStore *folder_store)
2527 gboolean is_special = TRUE;
2529 if (TNY_IS_FOLDER (folder_store)) {
2533 gchar *last_inbox_bar;
2535 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2536 downcase = g_utf8_strdown (id, -1);
2537 last_bar = g_strrstr (downcase, "/");
2539 last_inbox_bar = g_strrstr (downcase, "inbox/");
2540 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2551 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2553 TnyAccount *account;
2554 gboolean is_special;
2555 /* Inbox, Outbox, Drafts, Sent, User */
2558 if (!TNY_IS_FOLDER (folder_store))
2561 case TNY_FOLDER_TYPE_INBOX:
2563 account = tny_folder_get_account (folder_store);
2564 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2566 /* In inbox case we need to know if the inbox is really the top
2567 * inbox of the account, or if it's a submailbox inbox. To do
2568 * this we'll apply an heuristic rule: Find last "/" and check
2569 * if it's preceeded by another Inbox */
2570 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2571 g_object_unref (account);
2572 return is_special?0:4;
2575 case TNY_FOLDER_TYPE_OUTBOX:
2576 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2578 case TNY_FOLDER_TYPE_DRAFTS:
2580 account = tny_folder_get_account (folder_store);
2581 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2582 g_object_unref (account);
2583 return is_special?1:4;
2586 case TNY_FOLDER_TYPE_SENT:
2588 account = tny_folder_get_account (folder_store);
2589 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2590 g_object_unref (account);
2591 return is_special?3:4;
2600 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2602 const gchar *a1_name, *a2_name;
2604 a1_name = tny_account_get_name (a1);
2605 a2_name = tny_account_get_name (a2);
2607 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2611 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2613 TnyAccount *a1 = NULL, *a2 = NULL;
2616 if (TNY_IS_ACCOUNT (s1)) {
2617 a1 = TNY_ACCOUNT (g_object_ref (s1));
2618 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2619 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2622 if (TNY_IS_ACCOUNT (s2)) {
2623 a2 = TNY_ACCOUNT (g_object_ref (s2));
2624 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2625 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2642 /* First we sort with the type of account */
2643 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2647 cmp = compare_account_names (a1, a2);
2651 g_object_unref (a1);
2653 g_object_unref (a2);
2659 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2661 gint is_account1, is_account2;
2663 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2664 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2666 return is_account2 - is_account1;
2670 * This function orders the mail accounts according to these rules:
2671 * 1st - remote accounts
2672 * 2nd - local account
2676 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2680 gchar *name1 = NULL;
2681 gchar *name2 = NULL;
2682 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2683 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2684 GObject *folder1 = NULL;
2685 GObject *folder2 = NULL;
2687 gtk_tree_model_get (tree_model, iter1,
2688 NAME_COLUMN, &name1,
2690 INSTANCE_COLUMN, &folder1,
2692 gtk_tree_model_get (tree_model, iter2,
2693 NAME_COLUMN, &name2,
2694 TYPE_COLUMN, &type2,
2695 INSTANCE_COLUMN, &folder2,
2698 /* Return if we get no folder. This could happen when folder
2699 operations are happening. The model is updated after the
2700 folder copy/move actually occurs, so there could be
2701 situations where the model to be drawn is not correct */
2702 if (!folder1 || !folder2)
2705 /* Sort by type. First the special folders, then the archives */
2706 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2710 /* Now we sort using the account of each folder */
2711 if (TNY_IS_FOLDER_STORE (folder1) &&
2712 TNY_IS_FOLDER_STORE (folder2)) {
2713 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2717 /* Each group is preceeded by its account */
2718 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2723 /* Pure sort by name */
2724 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2727 g_object_unref(G_OBJECT(folder1));
2729 g_object_unref(G_OBJECT(folder2));
2737 /*****************************************************************************/
2738 /* DRAG and DROP stuff */
2739 /*****************************************************************************/
2741 * This function fills the #GtkSelectionData with the row and the
2742 * model that has been dragged. It's called when this widget is a
2743 * source for dnd after the event drop happened
2746 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2747 guint info, guint time, gpointer data)
2749 GtkTreeSelection *selection;
2750 GtkTreeModel *model;
2752 GtkTreePath *source_row;
2754 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2755 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2757 source_row = gtk_tree_model_get_path (model, &iter);
2758 gtk_tree_set_row_drag_data (selection_data,
2762 gtk_tree_path_free (source_row);
2766 typedef struct _DndHelper {
2767 ModestFolderView *folder_view;
2768 gboolean delete_source;
2769 GtkTreePath *source_row;
2773 dnd_helper_destroyer (DndHelper *helper)
2775 /* Free the helper */
2776 gtk_tree_path_free (helper->source_row);
2777 g_slice_free (DndHelper, helper);
2781 xfer_folder_cb (ModestMailOperation *mail_op,
2782 TnyFolder *new_folder,
2786 /* Select the folder */
2787 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2793 /* get the folder for the row the treepath refers to. */
2794 /* folder must be unref'd */
2795 static TnyFolderStore *
2796 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2799 TnyFolderStore *folder = NULL;
2801 if (gtk_tree_model_get_iter (model,&iter, path))
2802 gtk_tree_model_get (model, &iter,
2803 INSTANCE_COLUMN, &folder,
2810 * This function is used by drag_data_received_cb to manage drag and
2811 * drop of a header, i.e, and drag from the header view to the folder
2815 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2816 GtkTreeModel *dest_model,
2817 GtkTreePath *dest_row,
2818 GtkSelectionData *selection_data)
2820 TnyList *headers = NULL;
2821 TnyFolder *folder = NULL, *src_folder = NULL;
2822 TnyFolderType folder_type;
2823 GtkTreeIter source_iter, dest_iter;
2824 ModestWindowMgr *mgr = NULL;
2825 ModestWindow *main_win = NULL;
2826 gchar **uris, **tmp;
2828 /* Build the list of headers */
2829 mgr = modest_runtime_get_window_mgr ();
2830 headers = tny_simple_list_new ();
2831 uris = modest_dnd_selection_data_get_paths (selection_data);
2834 while (*tmp != NULL) {
2837 gboolean first = TRUE;
2840 path = gtk_tree_path_new_from_string (*tmp);
2841 gtk_tree_model_get_iter (source_model, &source_iter, path);
2842 gtk_tree_model_get (source_model, &source_iter,
2843 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2846 /* Do not enable d&d of headers already opened */
2847 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2848 tny_list_append (headers, G_OBJECT (header));
2850 if (G_UNLIKELY (first)) {
2851 src_folder = tny_header_get_folder (header);
2855 /* Free and go on */
2856 gtk_tree_path_free (path);
2857 g_object_unref (header);
2862 /* This could happen ig we perform a d&d very quickly over the
2863 same row that row could dissapear because message is
2865 if (!TNY_IS_FOLDER (src_folder))
2868 /* Get the target folder */
2869 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2870 gtk_tree_model_get (dest_model, &dest_iter,
2874 if (!folder || !TNY_IS_FOLDER(folder)) {
2875 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2879 folder_type = modest_tny_folder_guess_folder_type (folder);
2880 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2881 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2882 goto cleanup; /* cannot move messages there */
2885 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2886 /* g_warning ("folder not writable"); */
2887 goto cleanup; /* verboten! */
2890 /* Ask for confirmation to move */
2891 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2893 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2897 /* Transfer messages */
2898 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2903 if (G_IS_OBJECT (src_folder))
2904 g_object_unref (src_folder);
2905 if (G_IS_OBJECT(folder))
2906 g_object_unref (G_OBJECT (folder));
2907 if (G_IS_OBJECT(headers))
2908 g_object_unref (headers);
2912 TnyFolderStore *src_folder;
2913 TnyFolderStore *dst_folder;
2914 ModestFolderView *folder_view;
2919 dnd_folder_info_destroyer (DndFolderInfo *info)
2921 if (info->src_folder)
2922 g_object_unref (info->src_folder);
2923 if (info->dst_folder)
2924 g_object_unref (info->dst_folder);
2925 g_slice_free (DndFolderInfo, info);
2929 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2930 GtkWindow *parent_window,
2931 TnyAccount *account)
2934 modest_ui_actions_on_account_connection_error (parent_window, account);
2936 /* Free the helper & info */
2937 dnd_helper_destroyer (info->helper);
2938 dnd_folder_info_destroyer (info);
2942 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2944 GtkWindow *parent_window,
2945 TnyAccount *account,
2948 DndFolderInfo *info = NULL;
2949 ModestMailOperation *mail_op;
2951 info = (DndFolderInfo *) user_data;
2953 if (err || canceled) {
2954 dnd_on_connection_failed_destroyer (info, parent_window, account);
2958 /* Do the mail operation */
2959 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2960 modest_ui_actions_move_folder_error_handler,
2961 info->src_folder, NULL);
2963 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2966 /* Transfer the folder */
2967 modest_mail_operation_xfer_folder (mail_op,
2968 TNY_FOLDER (info->src_folder),
2970 info->helper->delete_source,
2972 info->helper->folder_view);
2975 g_object_unref (G_OBJECT (mail_op));
2976 dnd_helper_destroyer (info->helper);
2977 dnd_folder_info_destroyer (info);
2982 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2984 GtkWindow *parent_window,
2985 TnyAccount *account,
2988 DndFolderInfo *info = NULL;
2990 info = (DndFolderInfo *) user_data;
2992 if (err || canceled) {
2993 dnd_on_connection_failed_destroyer (info, parent_window, account);
2997 /* Connect to source folder and perform the copy/move */
2998 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3000 drag_and_drop_from_folder_view_src_folder_performer,
3005 * This function is used by drag_data_received_cb to manage drag and
3006 * drop of a folder, i.e, and drag from the folder view to the same
3010 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3011 GtkTreeModel *dest_model,
3012 GtkTreePath *dest_row,
3013 GtkSelectionData *selection_data,
3016 GtkTreeIter dest_iter, iter;
3017 TnyFolderStore *dest_folder = NULL;
3018 TnyFolderStore *folder = NULL;
3019 gboolean forbidden = FALSE;
3021 DndFolderInfo *info = NULL;
3023 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3025 g_warning ("%s: BUG: no main window", __FUNCTION__);
3026 dnd_helper_destroyer (helper);
3031 /* check the folder rules for the destination */
3032 folder = tree_path_to_folder (dest_model, dest_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_WRITEABLE;
3037 } else if (TNY_IS_FOLDER_STORE(folder)) {
3038 /* enable local root as destination for folders */
3039 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3040 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3043 g_object_unref (folder);
3046 /* check the folder rules for the source */
3047 folder = tree_path_to_folder (source_model, helper->source_row);
3048 if (TNY_IS_FOLDER(folder)) {
3049 ModestTnyFolderRules rules =
3050 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3051 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3054 g_object_unref (folder);
3058 /* Check if the drag is possible */
3059 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3061 modest_platform_run_information_dialog ((GtkWindow *) win,
3062 _("mail_in_ui_folder_move_target_error"),
3064 /* Restore the previous selection */
3065 folder = tree_path_to_folder (source_model, helper->source_row);
3067 if (TNY_IS_FOLDER (folder))
3068 modest_folder_view_select_folder (helper->folder_view,
3069 TNY_FOLDER (folder), FALSE);
3070 g_object_unref (folder);
3072 dnd_helper_destroyer (helper);
3077 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3078 gtk_tree_model_get (dest_model, &dest_iter,
3081 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3082 gtk_tree_model_get (source_model, &iter,
3086 /* Create the info for the performer */
3087 info = g_slice_new0 (DndFolderInfo);
3088 info->src_folder = g_object_ref (folder);
3089 info->dst_folder = g_object_ref (dest_folder);
3090 info->helper = helper;
3092 /* Connect to the destination folder and perform the copy/move */
3093 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3095 drag_and_drop_from_folder_view_dst_folder_performer,
3099 g_object_unref (dest_folder);
3100 g_object_unref (folder);
3104 * This function receives the data set by the "drag-data-get" signal
3105 * handler. This information comes within the #GtkSelectionData. This
3106 * function will manage both the drags of folders of the treeview and
3107 * drags of headers of the header view widget.
3110 on_drag_data_received (GtkWidget *widget,
3111 GdkDragContext *context,
3114 GtkSelectionData *selection_data,
3119 GtkWidget *source_widget;
3120 GtkTreeModel *dest_model, *source_model;
3121 GtkTreePath *source_row, *dest_row;
3122 GtkTreeViewDropPosition pos;
3123 gboolean delete_source = FALSE;
3124 gboolean success = FALSE;
3126 /* Do not allow further process */
3127 g_signal_stop_emission_by_name (widget, "drag-data-received");
3128 source_widget = gtk_drag_get_source_widget (context);
3130 /* Get the action */
3131 if (context->action == GDK_ACTION_MOVE) {
3132 delete_source = TRUE;
3134 /* Notify that there is no folder selected. We need to
3135 do this in order to update the headers view (and
3136 its monitors, because when moving, the old folder
3137 won't longer exist. We can not wait for the end of
3138 the operation, because the operation won't start if
3139 the folder is in use */
3140 if (source_widget == widget) {
3141 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3142 gtk_tree_selection_unselect_all (sel);
3146 /* Check if the get_data failed */
3147 if (selection_data == NULL || selection_data->length < 0)
3150 /* Select the destination model */
3151 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3153 /* Get the path to the destination row. Can not call
3154 gtk_tree_view_get_drag_dest_row() because the source row
3155 is not selected anymore */
3156 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3159 /* Only allow drops IN other rows */
3161 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3162 pos == GTK_TREE_VIEW_DROP_AFTER)
3166 /* Drags from the header view */
3167 if (source_widget != widget) {
3168 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3170 drag_and_drop_from_header_view (source_model,
3175 DndHelper *helper = NULL;
3177 /* Get the source model and row */
3178 gtk_tree_get_row_drag_data (selection_data,
3182 /* Create the helper */
3183 helper = g_slice_new0 (DndHelper);
3184 helper->delete_source = delete_source;
3185 helper->source_row = gtk_tree_path_copy (source_row);
3186 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3188 drag_and_drop_from_folder_view (source_model,
3194 gtk_tree_path_free (source_row);
3198 gtk_tree_path_free (dest_row);
3201 /* Finish the drag and drop */
3202 gtk_drag_finish (context, success, FALSE, time);
3206 * We define a "drag-drop" signal handler because we do not want to
3207 * use the default one, because the default one always calls
3208 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3209 * signal handler, because there we have all the information available
3210 * to know if the dnd was a success or not.
3213 drag_drop_cb (GtkWidget *widget,
3214 GdkDragContext *context,
3222 if (!context->targets)
3225 /* Check if we're dragging a folder row */
3226 target = gtk_drag_dest_find_target (widget, context, NULL);
3228 /* Request the data from the source. */
3229 gtk_drag_get_data(widget, context, target, time);
3235 * This function expands a node of a tree view if it's not expanded
3236 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3237 * does that, so that's why they're here.
3240 expand_row_timeout (gpointer data)
3242 GtkTreeView *tree_view = data;
3243 GtkTreePath *dest_path = NULL;
3244 GtkTreeViewDropPosition pos;
3245 gboolean result = FALSE;
3247 gdk_threads_enter ();
3249 gtk_tree_view_get_drag_dest_row (tree_view,
3254 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3255 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3256 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3257 gtk_tree_path_free (dest_path);
3261 gtk_tree_path_free (dest_path);
3266 gdk_threads_leave ();
3272 * This function is called whenever the pointer is moved over a widget
3273 * while dragging some data. It installs a timeout that will expand a
3274 * node of the treeview if not expanded yet. This function also calls
3275 * gdk_drag_status in order to set the suggested action that will be
3276 * used by the "drag-data-received" signal handler to know if we
3277 * should do a move or just a copy of the data.
3280 on_drag_motion (GtkWidget *widget,
3281 GdkDragContext *context,
3287 GtkTreeViewDropPosition pos;
3288 GtkTreePath *dest_row;
3289 GtkTreeModel *dest_model;
3290 ModestFolderViewPrivate *priv;
3291 GdkDragAction suggested_action;
3292 gboolean valid_location = FALSE;
3293 TnyFolderStore *folder = NULL;
3295 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3297 if (priv->timer_expander != 0) {
3298 g_source_remove (priv->timer_expander);
3299 priv->timer_expander = 0;
3302 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3307 /* Do not allow drops between folders */
3309 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3310 pos == GTK_TREE_VIEW_DROP_AFTER) {
3311 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3312 gdk_drag_status(context, 0, time);
3313 valid_location = FALSE;
3316 valid_location = TRUE;
3319 /* Check that the destination folder is writable */
3320 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3321 folder = tree_path_to_folder (dest_model, dest_row);
3322 if (folder && TNY_IS_FOLDER (folder)) {
3323 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3325 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3326 valid_location = FALSE;
3331 /* Expand the selected row after 1/2 second */
3332 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3333 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3335 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3337 /* Select the desired action. By default we pick MOVE */
3338 suggested_action = GDK_ACTION_MOVE;
3340 if (context->actions == GDK_ACTION_COPY)
3341 gdk_drag_status(context, GDK_ACTION_COPY, time);
3342 else if (context->actions == GDK_ACTION_MOVE)
3343 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3344 else if (context->actions & suggested_action)
3345 gdk_drag_status(context, suggested_action, time);
3347 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3351 g_object_unref (folder);
3353 gtk_tree_path_free (dest_row);
3355 g_signal_stop_emission_by_name (widget, "drag-motion");
3357 return valid_location;
3361 * This function sets the treeview as a source and a target for dnd
3362 * events. It also connects all the requirede signals.
3365 setup_drag_and_drop (GtkTreeView *self)
3367 /* Set up the folder view as a dnd destination. Set only the
3368 highlight flag, otherwise gtk will have a different
3370 #ifdef MODEST_TOOLKIT_HILDON2
3373 gtk_drag_dest_set (GTK_WIDGET (self),
3374 GTK_DEST_DEFAULT_HIGHLIGHT,
3375 folder_view_drag_types,
3376 G_N_ELEMENTS (folder_view_drag_types),
3377 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3379 g_signal_connect (G_OBJECT (self),
3380 "drag_data_received",
3381 G_CALLBACK (on_drag_data_received),
3385 /* Set up the treeview as a dnd source */
3386 gtk_drag_source_set (GTK_WIDGET (self),
3388 folder_view_drag_types,
3389 G_N_ELEMENTS (folder_view_drag_types),
3390 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3392 g_signal_connect (G_OBJECT (self),
3394 G_CALLBACK (on_drag_motion),
3397 g_signal_connect (G_OBJECT (self),
3399 G_CALLBACK (on_drag_data_get),
3402 g_signal_connect (G_OBJECT (self),
3404 G_CALLBACK (drag_drop_cb),
3409 * This function manages the navigation through the folders using the
3410 * keyboard or the hardware keys in the device
3413 on_key_pressed (GtkWidget *self,
3417 GtkTreeSelection *selection;
3419 GtkTreeModel *model;
3420 gboolean retval = FALSE;
3422 /* Up and Down are automatically managed by the treeview */
3423 if (event->keyval == GDK_Return) {
3424 /* Expand/Collapse the selected row */
3425 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3426 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3429 path = gtk_tree_model_get_path (model, &iter);
3431 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3432 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3434 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3435 gtk_tree_path_free (path);
3437 /* No further processing */
3445 * We listen to the changes in the local folder account name key,
3446 * because we want to show the right name in the view. The local
3447 * folder account name corresponds to the device name in the Maemo
3448 * version. We do this because we do not want to query gconf on each
3449 * tree view refresh. It's better to cache it and change whenever
3453 on_configuration_key_changed (ModestConf* conf,
3455 ModestConfEvent event,
3456 ModestConfNotificationId id,
3457 ModestFolderView *self)
3459 ModestFolderViewPrivate *priv;
3462 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3463 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3465 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3466 g_free (priv->local_account_name);
3468 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3469 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3471 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3472 MODEST_CONF_DEVICE_NAME, NULL);
3474 /* Force a redraw */
3475 #if GTK_CHECK_VERSION(2, 8, 0)
3476 GtkTreeViewColumn * tree_column;
3478 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3480 gtk_tree_view_column_queue_resize (tree_column);
3482 gtk_widget_queue_draw (GTK_WIDGET (self));
3488 modest_folder_view_set_style (ModestFolderView *self,
3489 ModestFolderViewStyle style)
3491 ModestFolderViewPrivate *priv;
3493 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3494 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3495 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3497 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3500 priv->style = style;
3504 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3505 const gchar *account_id)
3507 ModestFolderViewPrivate *priv;
3508 GtkTreeModel *model;
3510 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3512 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3514 /* This will be used by the filter_row callback,
3515 * to decided which rows to show: */
3516 if (priv->visible_account_id) {
3517 g_free (priv->visible_account_id);
3518 priv->visible_account_id = NULL;
3521 priv->visible_account_id = g_strdup (account_id);
3524 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3525 if (GTK_IS_TREE_MODEL_FILTER (model))
3526 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3528 /* Save settings to gconf */
3529 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3530 MODEST_CONF_FOLDER_VIEW_KEY);
3532 /* Notify observers */
3533 g_signal_emit (G_OBJECT(self),
3534 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3539 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3541 ModestFolderViewPrivate *priv;
3543 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3545 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3547 return (const gchar *) priv->visible_account_id;
3551 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3555 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3557 gtk_tree_model_get (model, iter,
3561 gboolean result = FALSE;
3562 if (type == TNY_FOLDER_TYPE_INBOX) {
3566 *inbox_iter = *iter;
3570 if (gtk_tree_model_iter_children (model, &child, iter)) {
3571 if (find_inbox_iter (model, &child, inbox_iter))
3575 } while (gtk_tree_model_iter_next (model, iter));
3584 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3586 #ifndef MODEST_TOOLKIT_HILDON2
3587 GtkTreeModel *model;
3588 GtkTreeIter iter, inbox_iter;
3589 GtkTreeSelection *sel;
3590 GtkTreePath *path = NULL;
3592 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3594 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3598 expand_root_items (self);
3599 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3601 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3602 g_warning ("%s: model is empty", __FUNCTION__);
3606 if (find_inbox_iter (model, &iter, &inbox_iter))
3607 path = gtk_tree_model_get_path (model, &inbox_iter);
3609 path = gtk_tree_path_new_first ();
3611 /* Select the row and free */
3612 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3613 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3614 gtk_tree_path_free (path);
3617 gtk_widget_grab_focus (GTK_WIDGET(self));
3624 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3629 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3630 TnyFolder* a_folder;
3633 gtk_tree_model_get (model, iter,
3634 INSTANCE_COLUMN, &a_folder,
3640 if (folder == a_folder) {
3641 g_object_unref (a_folder);
3642 *folder_iter = *iter;
3645 g_object_unref (a_folder);
3647 if (gtk_tree_model_iter_children (model, &child, iter)) {
3648 if (find_folder_iter (model, &child, folder_iter, folder))
3652 } while (gtk_tree_model_iter_next (model, iter));
3657 #ifndef MODEST_TOOLKIT_HILDON2
3659 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3662 ModestFolderView *self)
3664 ModestFolderViewPrivate *priv = NULL;
3665 GtkTreeSelection *sel;
3666 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3667 GObject *instance = NULL;
3669 if (!MODEST_IS_FOLDER_VIEW(self))
3672 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3674 priv->reexpand = TRUE;
3676 gtk_tree_model_get (tree_model, iter,
3678 INSTANCE_COLUMN, &instance,
3684 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3685 priv->folder_to_select = g_object_ref (instance);
3687 g_object_unref (instance);
3689 if (priv->folder_to_select) {
3691 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3694 path = gtk_tree_model_get_path (tree_model, iter);
3695 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3697 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3699 gtk_tree_selection_select_iter (sel, iter);
3700 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3702 gtk_tree_path_free (path);
3706 modest_folder_view_disable_next_folder_selection (self);
3708 /* Refilter the model */
3709 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3715 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3717 ModestFolderViewPrivate *priv;
3719 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3721 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3723 if (priv->folder_to_select)
3724 g_object_unref(priv->folder_to_select);
3726 priv->folder_to_select = NULL;
3730 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3731 gboolean after_change)
3733 GtkTreeModel *model;
3734 GtkTreeIter iter, folder_iter;
3735 GtkTreeSelection *sel;
3736 ModestFolderViewPrivate *priv = NULL;
3738 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3739 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3741 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3744 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3745 gtk_tree_selection_unselect_all (sel);
3747 if (priv->folder_to_select)
3748 g_object_unref(priv->folder_to_select);
3749 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3753 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3758 /* Refilter the model, before selecting the folder */
3759 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3761 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3762 g_warning ("%s: model is empty", __FUNCTION__);
3766 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3769 path = gtk_tree_model_get_path (model, &folder_iter);
3770 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3772 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3773 gtk_tree_selection_select_iter (sel, &folder_iter);
3774 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3776 gtk_tree_path_free (path);
3784 modest_folder_view_copy_selection (ModestFolderView *self)
3786 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3788 /* Copy selection */
3789 _clipboard_set_selected_data (self, FALSE);
3793 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3795 ModestFolderViewPrivate *priv = NULL;
3796 GtkTreeModel *model = NULL;
3797 const gchar **hidding = NULL;
3798 guint i, n_selected;
3800 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3801 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3803 /* Copy selection */
3804 if (!_clipboard_set_selected_data (folder_view, TRUE))
3807 /* Get hidding ids */
3808 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3810 /* Clear hidding array created by previous cut operation */
3811 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3813 /* Copy hidding array */
3814 priv->n_selected = n_selected;
3815 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3816 for (i=0; i < n_selected; i++)
3817 priv->hidding_ids[i] = g_strdup(hidding[i]);
3819 /* Hide cut folders */
3820 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3821 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3825 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3826 ModestFolderView *folder_view_dst)
3828 GtkTreeModel *filter_model = NULL;
3829 GtkTreeModel *model = NULL;
3830 GtkTreeModel *new_filter_model = NULL;
3831 GtkTreeModel *old_tny_model = NULL;
3832 GtkTreeModel *new_tny_model = NULL;
3833 ModestFolderViewPrivate *dst_priv;
3835 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3836 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3838 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3839 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3840 new_tny_model = NULL;
3843 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3844 g_signal_handler_disconnect (G_OBJECT (old_tny_model), dst_priv->activity_changed_handler);
3845 dst_priv->activity_changed_handler = 0;
3847 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3848 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3850 /* Build new filter model */
3851 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3852 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3859 /* Set copied model */
3860 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3861 #ifndef MODEST_TOOLKIT_HILDON2
3862 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3863 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3865 #ifdef MODEST_TOOLKIT_HILDON2
3867 dst_priv->activity_changed_handler = g_signal_connect (G_OBJECT (new_tny_model), "activity-changed",
3868 G_CALLBACK (on_activity_changed), folder_view_dst);
3872 g_object_unref (new_filter_model);
3876 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3879 GtkTreeModel *model = NULL;
3880 ModestFolderViewPrivate* priv;
3882 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3884 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3885 priv->show_non_move = show;
3886 /* modest_folder_view_update_model(folder_view, */
3887 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3889 /* Hide special folders */
3890 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3891 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3892 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3897 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3900 ModestFolderViewPrivate* priv;
3902 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3904 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3905 priv->show_message_count = show;
3907 g_object_set (G_OBJECT (priv->messages_renderer),
3908 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3912 /* Returns FALSE if it did not selected anything */
3914 _clipboard_set_selected_data (ModestFolderView *folder_view,
3917 ModestFolderViewPrivate *priv = NULL;
3918 TnyFolderStore *folder = NULL;
3919 gboolean retval = FALSE;
3921 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3922 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3924 /* Set selected data on clipboard */
3925 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3926 folder = modest_folder_view_get_selected (folder_view);
3928 /* Do not allow to select an account */
3929 if (TNY_IS_FOLDER (folder)) {
3930 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3935 g_object_unref (folder);
3941 _clear_hidding_filter (ModestFolderView *folder_view)
3943 ModestFolderViewPrivate *priv;
3946 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3947 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3949 if (priv->hidding_ids != NULL) {
3950 for (i=0; i < priv->n_selected; i++)
3951 g_free (priv->hidding_ids[i]);
3952 g_free(priv->hidding_ids);
3958 on_display_name_changed (ModestAccountMgr *mgr,
3959 const gchar *account,
3962 ModestFolderView *self;
3964 self = MODEST_FOLDER_VIEW (user_data);
3966 /* Force a redraw */
3967 #if GTK_CHECK_VERSION(2, 8, 0)
3968 GtkTreeViewColumn * tree_column;
3970 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3972 gtk_tree_view_column_queue_resize (tree_column);
3974 gtk_widget_queue_draw (GTK_WIDGET (self));
3979 modest_folder_view_set_cell_style (ModestFolderView *self,
3980 ModestFolderViewCellStyle cell_style)
3982 ModestFolderViewPrivate *priv = NULL;
3984 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3985 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3987 priv->cell_style = cell_style;
3989 g_object_set (G_OBJECT (priv->messages_renderer),
3990 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3993 gtk_widget_queue_draw (GTK_WIDGET (self));
3997 update_style (ModestFolderView *self)
3999 ModestFolderViewPrivate *priv;
4000 GdkColor style_color, style_active_color;
4001 PangoAttrList *attr_list;
4003 PangoAttribute *attr;
4005 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4006 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4010 attr_list = pango_attr_list_new ();
4011 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4012 gdk_color_parse ("grey", &style_color);
4014 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4015 pango_attr_list_insert (attr_list, attr);
4018 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4020 "SmallSystemFont", NULL,
4023 attr = pango_attr_font_desc_new (pango_font_description_copy
4024 (style->font_desc));
4025 pango_attr_list_insert (attr_list, attr);
4027 g_object_set (G_OBJECT (priv->messages_renderer),
4028 "foreground-gdk", &style_color,
4029 "foreground-set", TRUE,
4030 "attributes", attr_list,
4032 pango_attr_list_unref (attr_list);
4034 if (priv->active_color)
4035 gdk_color_free (priv->active_color);
4037 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4038 priv->active_color = gdk_color_copy (&style_active_color);
4043 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4045 if (strcmp ("style", spec->name) == 0) {
4046 update_style (MODEST_FOLDER_VIEW (obj));
4047 gtk_widget_queue_draw (GTK_WIDGET (obj));
4052 modest_folder_view_set_filter (ModestFolderView *self,
4053 ModestFolderViewFilter filter)
4055 ModestFolderViewPrivate *priv;
4056 GtkTreeModel *filter_model;
4058 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4059 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4061 priv->filter |= filter;
4063 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4064 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4065 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4070 modest_folder_view_unset_filter (ModestFolderView *self,
4071 ModestFolderViewFilter filter)
4073 ModestFolderViewPrivate *priv;
4074 GtkTreeModel *filter_model;
4076 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4077 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4079 priv->filter &= ~filter;
4081 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4082 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4083 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4088 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4089 ModestTnyFolderRules rules)
4091 GtkTreeModel *filter_model;
4093 gboolean fulfil = FALSE;
4095 if (!get_inner_models (self, &filter_model, NULL, NULL))
4098 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4102 TnyFolderStore *folder;
4104 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4106 if (TNY_IS_FOLDER (folder)) {
4107 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4108 /* Folder rules are negative: non_writable, non_deletable... */
4109 if (!(folder_rules & rules))
4112 g_object_unref (folder);
4115 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4121 modest_folder_view_set_list_to_move (ModestFolderView *self,
4124 ModestFolderViewPrivate *priv;
4126 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4127 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4129 if (priv->list_to_move)
4130 g_object_unref (priv->list_to_move);
4133 g_object_ref (list);
4135 priv->list_to_move = list;
4139 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4141 ModestFolderViewPrivate *priv;
4143 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4144 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4147 g_free (priv->mailbox);
4149 priv->mailbox = g_strdup (mailbox);
4151 /* Notify observers */
4152 g_signal_emit (G_OBJECT(self),
4153 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4154 priv->visible_account_id);
4158 modest_folder_view_get_mailbox (ModestFolderView *self)
4160 ModestFolderViewPrivate *priv;
4162 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4163 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4165 return (const gchar *) priv->mailbox;
4169 modest_folder_view_get_activity (ModestFolderView *self)
4171 ModestFolderViewPrivate *priv;
4172 GtkTreeModel *inner_model;
4174 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4175 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4176 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4178 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4179 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4185 #ifdef MODEST_TOOLKIT_HILDON2
4187 on_activity_changed (TnyGtkFolderListStore *store,
4189 ModestFolderView *folder_view)
4191 ModestFolderViewPrivate *priv;
4193 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4194 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4195 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4197 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,