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);
100 static void modest_folder_view_dispose (GObject *obj);
102 static void tny_account_store_view_init (gpointer g,
103 gpointer iface_data);
105 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
106 TnyAccountStore *account_store);
108 static void on_selection_changed (GtkTreeSelection *sel,
111 static void on_row_activated (GtkTreeView *treeview,
113 GtkTreeViewColumn *column,
116 static void on_account_removed (TnyAccountStore *self,
120 static void on_account_inserted (TnyAccountStore *self,
124 static void on_account_changed (TnyAccountStore *self,
128 static gint cmp_rows (GtkTreeModel *tree_model,
133 static gboolean filter_row (GtkTreeModel *model,
137 static gboolean on_key_pressed (GtkWidget *self,
141 static void on_configuration_key_changed (ModestConf* conf,
143 ModestConfEvent event,
144 ModestConfNotificationId notification_id,
145 ModestFolderView *self);
148 static void on_drag_data_get (GtkWidget *widget,
149 GdkDragContext *context,
150 GtkSelectionData *selection_data,
155 static void on_drag_data_received (GtkWidget *widget,
156 GdkDragContext *context,
159 GtkSelectionData *selection_data,
164 static gboolean on_drag_motion (GtkWidget *widget,
165 GdkDragContext *context,
171 static void expand_root_items (ModestFolderView *self);
173 static gint expand_row_timeout (gpointer data);
175 static void setup_drag_and_drop (GtkTreeView *self);
177 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
180 static void _clear_hidding_filter (ModestFolderView *folder_view);
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
186 ModestFolderView *self);
189 static void on_display_name_changed (ModestAccountMgr *self,
190 const gchar *account,
192 static void update_style (ModestFolderView *self);
193 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
195 static gboolean inbox_is_special (TnyFolderStore *folder_store);
197 static gboolean get_inner_models (ModestFolderView *self,
198 GtkTreeModel **filter_model,
199 GtkTreeModel **sort_model,
200 GtkTreeModel **tny_model);
201 #ifdef MODEST_TOOLKIT_HILDON2
202 static void 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 GSList *signal_handlers;
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;
316 gobject_class->dispose = modest_folder_view_dispose;
318 g_type_class_add_private (gobject_class,
319 sizeof(ModestFolderViewPrivate));
321 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
322 g_signal_new ("folder_selection_changed",
323 G_TYPE_FROM_CLASS (gobject_class),
325 G_STRUCT_OFFSET (ModestFolderViewClass,
326 folder_selection_changed),
328 modest_marshal_VOID__POINTER_BOOLEAN,
329 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
332 * This signal is emitted whenever the currently selected
333 * folder display name is computed. Note that the name could
334 * be different to the folder name, because we could append
335 * the unread messages count to the folder name to build the
336 * folder display name
338 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
339 g_signal_new ("folder-display-name-changed",
340 G_TYPE_FROM_CLASS (gobject_class),
342 G_STRUCT_OFFSET (ModestFolderViewClass,
343 folder_display_name_changed),
345 g_cclosure_marshal_VOID__STRING,
346 G_TYPE_NONE, 1, G_TYPE_STRING);
348 signals[FOLDER_ACTIVATED_SIGNAL] =
349 g_signal_new ("folder_activated",
350 G_TYPE_FROM_CLASS (gobject_class),
352 G_STRUCT_OFFSET (ModestFolderViewClass,
355 g_cclosure_marshal_VOID__POINTER,
356 G_TYPE_NONE, 1, G_TYPE_POINTER);
359 * Emitted whenever the visible account changes
361 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
362 g_signal_new ("visible-account-changed",
363 G_TYPE_FROM_CLASS (gobject_class),
365 G_STRUCT_OFFSET (ModestFolderViewClass,
366 visible_account_changed),
368 g_cclosure_marshal_VOID__STRING,
369 G_TYPE_NONE, 1, G_TYPE_STRING);
372 * Emitted when the underlying GtkListStore is updating data
374 signals[ACTIVITY_CHANGED_SIGNAL] =
375 g_signal_new ("activity-changed",
376 G_TYPE_FROM_CLASS (gobject_class),
378 G_STRUCT_OFFSET (ModestFolderViewClass,
381 g_cclosure_marshal_VOID__BOOLEAN,
382 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
384 treeview_class->select_cursor_parent = NULL;
386 #ifdef MODEST_TOOLKIT_HILDON2
387 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
393 /* Retrieves the filter, sort and tny models of the folder view. If
394 any of these does not exist then it returns FALSE */
396 get_inner_models (ModestFolderView *self,
397 GtkTreeModel **filter_model,
398 GtkTreeModel **sort_model,
399 GtkTreeModel **tny_model)
401 GtkTreeModel *s_model, *f_model, *t_model;
403 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
404 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
405 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
409 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
410 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
411 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
415 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
419 *filter_model = f_model;
421 *sort_model = s_model;
423 *tny_model = t_model;
428 /* Simplify checks for NULLs: */
430 strings_are_equal (const gchar *a, const gchar *b)
436 return (strcmp (a, b) == 0);
443 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
445 GObject *instance = NULL;
447 gtk_tree_model_get (model, iter,
448 INSTANCE_COLUMN, &instance,
452 return FALSE; /* keep walking */
454 if (!TNY_IS_ACCOUNT (instance)) {
455 g_object_unref (instance);
456 return FALSE; /* keep walking */
459 /* Check if this is the looked-for account: */
460 TnyAccount *this_account = TNY_ACCOUNT (instance);
461 TnyAccount *account = TNY_ACCOUNT (data);
463 const gchar *this_account_id = tny_account_get_id(this_account);
464 const gchar *account_id = tny_account_get_id(account);
465 g_object_unref (instance);
468 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
469 if (strings_are_equal(this_account_id, account_id)) {
470 /* Tell the model that the data has changed, so that
471 * it calls the cell_data_func callbacks again: */
472 /* TODO: This does not seem to actually cause the new string to be shown: */
473 gtk_tree_model_row_changed (model, path, iter);
475 return TRUE; /* stop walking */
478 return FALSE; /* keep walking */
483 ModestFolderView *self;
484 gchar *previous_name;
485 } GetMmcAccountNameData;
488 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
490 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
492 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
494 if (!strings_are_equal (
495 tny_account_get_name(TNY_ACCOUNT(account)),
496 data->previous_name)) {
498 /* Tell the model that the data has changed, so that
499 * it calls the cell_data_func callbacks again: */
500 ModestFolderView *self = data->self;
501 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
503 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
506 g_free (data->previous_name);
507 g_slice_free (GetMmcAccountNameData, data);
511 convert_parent_folders_to_dots (gchar **item_name)
514 gint n_inbox_parents = 0;
517 gchar *last_separator;
519 if (item_name == NULL)
522 path_start = *item_name;
523 for (c = *item_name; *c != '\0'; c++) {
524 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
526 if (c != path_start) {
527 compare = g_strndup (path_start, c - path_start);
528 compare = g_strstrip (compare);
529 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
539 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
540 if (last_separator != NULL) {
541 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
548 buffer = g_string_new ("");
549 for (i = 0; i < n_parents - n_inbox_parents; i++) {
550 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
552 buffer = g_string_append (buffer, last_separator);
554 *item_name = g_string_free (buffer, FALSE);
560 format_compact_style (gchar **item_name,
562 const gchar *mailbox,
564 gboolean multiaccount,
565 gboolean *use_markup)
569 TnyFolderType folder_type;
571 if (!TNY_IS_FOLDER (instance))
574 folder = (TnyFolder *) instance;
576 folder_type = tny_folder_get_folder_type (folder);
577 is_special = (get_cmp_pos (folder_type, folder)!= 4);
580 /* Remove mailbox prefix if any */
581 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
582 if (g_str_has_prefix (*item_name, prefix)) {
583 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
585 *item_name = new_item_name;
589 if (!is_special || multiaccount) {
590 TnyAccount *account = tny_folder_get_account (folder);
591 const gchar *folder_name;
592 gboolean concat_folder_name = FALSE;
595 /* Should not happen */
599 /* convert parent folders to dots */
600 convert_parent_folders_to_dots (item_name);
602 folder_name = tny_folder_get_name (folder);
603 if (g_str_has_suffix (*item_name, folder_name)) {
604 gchar *offset = g_strrstr (*item_name, folder_name);
606 concat_folder_name = TRUE;
609 buffer = g_string_new ("");
611 buffer = g_string_append (buffer, *item_name);
612 if (concat_folder_name) {
613 if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
614 buffer = g_string_append (buffer, folder_name);
615 /* TODO: append a sensitive string to the remote drafts to
616 * be able to know it's the remote one */
617 /* buffer = g_string_append (buffer, " (TODO:remote)"); */
619 buffer = g_string_append (buffer, folder_name);
623 g_object_unref (account);
625 *item_name = g_string_free (buffer, FALSE);
633 text_cell_data (GtkTreeViewColumn *column,
634 GtkCellRenderer *renderer,
635 GtkTreeModel *tree_model,
639 ModestFolderViewPrivate *priv;
640 GObject *rendobj = (GObject *) renderer;
642 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
643 GObject *instance = NULL;
644 gboolean use_markup = FALSE;
646 gtk_tree_model_get (tree_model, iter,
649 INSTANCE_COLUMN, &instance,
651 if (!fname || !instance)
654 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
655 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
657 gchar *item_name = NULL;
658 gint item_weight = 400;
660 if (type != TNY_FOLDER_TYPE_ROOT) {
665 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
666 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
669 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
670 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
672 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
675 /* Sometimes an special folder is reported by the server as
676 NORMAL, like some versions of Dovecot */
677 if (type == TNY_FOLDER_TYPE_NORMAL ||
678 type == TNY_FOLDER_TYPE_UNKNOWN) {
679 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
683 /* note: we cannot reliably get the counts from the
684 * tree model, we need to use explicit calls on
685 * tny_folder for some reason. Select the number to
686 * show: the unread or unsent messages. in case of
687 * outbox/drafts, show all */
688 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
689 (type == TNY_FOLDER_TYPE_OUTBOX) ||
690 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
691 number = tny_folder_get_all_count (TNY_FOLDER(instance));
694 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
698 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
699 item_name = g_strdup (fname);
706 /* Use bold font style if there are unread or unset messages */
708 if (priv->show_message_count) {
709 item_name = g_strdup_printf ("%s (%d)", fname, number);
711 item_name = g_strdup (fname);
715 item_name = g_strdup (fname);
720 } else if (TNY_IS_ACCOUNT (instance)) {
721 /* If it's a server account */
722 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
723 item_name = g_strdup (priv->local_account_name);
725 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
726 /* fname is only correct when the items are first
727 * added to the model, not when the account is
728 * changed later, so get the name from the account
730 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
733 item_name = g_strdup (fname);
739 if (type == TNY_FOLDER_TYPE_INBOX &&
740 g_str_has_suffix (fname, "Inbox")) {
742 item_name = g_strdup (_("mcen_me_folder_inbox"));
746 item_name = g_strdup ("unknown");
748 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
749 gboolean multiaccount;
751 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
752 /* Convert item_name to markup */
753 format_compact_style (&item_name, instance, priv->mailbox,
755 multiaccount, &use_markup);
758 if (item_name && item_weight) {
759 /* Set the name in the treeview cell: */
760 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
761 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
762 g_object_set (rendobj,
765 "foreground-set", TRUE,
766 "foreground-gdk", &(priv->active_color),
769 g_object_set (rendobj,
771 "foreground-set", FALSE,
773 "weight", item_weight,
777 /* Notify display name observers */
778 /* TODO: What listens for this signal, and how can it use only the new name? */
779 if (((GObject *) priv->cur_folder_store) == instance) {
780 g_signal_emit (G_OBJECT(self),
781 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
788 /* If it is a Memory card account, make sure that we have the correct name.
789 * This function will be trigerred again when the name has been retrieved: */
790 if (TNY_IS_STORE_ACCOUNT (instance) &&
791 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
793 /* Get the account name asynchronously: */
794 GetMmcAccountNameData *callback_data =
795 g_slice_new0(GetMmcAccountNameData);
796 callback_data->self = self;
798 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
800 callback_data->previous_name = g_strdup (name);
802 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
803 on_get_mmc_account_name, callback_data);
807 g_object_unref (G_OBJECT (instance));
813 messages_cell_data (GtkTreeViewColumn *column,
814 GtkCellRenderer *renderer,
815 GtkTreeModel *tree_model,
819 ModestFolderView *self;
820 ModestFolderViewPrivate *priv;
821 GObject *rendobj = (GObject *) renderer;
822 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
823 GObject *instance = NULL;
824 gchar *item_name = NULL;
826 gtk_tree_model_get (tree_model, iter,
828 INSTANCE_COLUMN, &instance,
833 self = MODEST_FOLDER_VIEW (data);
834 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
837 if (type != TNY_FOLDER_TYPE_ROOT) {
842 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
843 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
846 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
848 /* Sometimes an special folder is reported by the server as
849 NORMAL, like some versions of Dovecot */
850 if (type == TNY_FOLDER_TYPE_NORMAL ||
851 type == TNY_FOLDER_TYPE_UNKNOWN) {
852 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
856 /* note: we cannot reliably get the counts from the tree model, we need
857 * to use explicit calls on tny_folder for some reason.
859 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
860 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
861 (type == TNY_FOLDER_TYPE_OUTBOX) ||
862 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
863 number = tny_folder_get_all_count (TNY_FOLDER(instance));
866 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
870 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
872 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
873 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
879 item_name = g_strdup ("");
882 /* Set the name in the treeview cell: */
883 g_object_set (rendobj,"text", item_name, NULL);
891 g_object_unref (G_OBJECT (instance));
897 GdkPixbuf *pixbuf_open;
898 GdkPixbuf *pixbuf_close;
902 static inline GdkPixbuf *
903 get_composite_pixbuf (const gchar *icon_name,
905 GdkPixbuf *base_pixbuf)
907 GdkPixbuf *emblem, *retval = NULL;
909 emblem = modest_platform_get_icon (icon_name, size);
911 retval = gdk_pixbuf_copy (base_pixbuf);
912 gdk_pixbuf_composite (emblem, retval, 0, 0,
913 MIN (gdk_pixbuf_get_width (emblem),
914 gdk_pixbuf_get_width (retval)),
915 MIN (gdk_pixbuf_get_height (emblem),
916 gdk_pixbuf_get_height (retval)),
917 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
918 g_object_unref (emblem);
923 static inline ThreePixbufs *
924 get_composite_icons (const gchar *icon_code,
926 GdkPixbuf **pixbuf_open,
927 GdkPixbuf **pixbuf_close)
929 ThreePixbufs *retval;
931 if (pixbuf && !*pixbuf) {
933 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
935 *pixbuf = gdk_pixbuf_copy (icon);
941 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
942 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
946 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
947 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
951 retval = g_slice_new0 (ThreePixbufs);
952 if (pixbuf && *pixbuf)
953 retval->pixbuf = g_object_ref (*pixbuf);
955 retval->pixbuf = NULL;
956 if (pixbuf_open && *pixbuf_open)
957 retval->pixbuf_open = g_object_ref (*pixbuf_open);
959 retval->pixbuf_open = NULL;
960 if (pixbuf_close && *pixbuf_close)
961 retval->pixbuf_close = g_object_ref (*pixbuf_close);
963 retval->pixbuf_close = NULL;
968 static inline ThreePixbufs *
969 get_account_protocol_pixbufs (ModestFolderView *folder_view,
970 ModestProtocolType protocol_type,
973 ModestProtocol *protocol;
974 const GdkPixbuf *pixbuf = NULL;
975 ModestFolderViewPrivate *priv;
977 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
979 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
982 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
983 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
984 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
985 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
986 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
987 object, FOLDER_ICON_SIZE);
991 ThreePixbufs *retval;
992 retval = g_slice_new0 (ThreePixbufs);
993 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
994 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
995 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
1002 static inline ThreePixbufs*
1003 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
1005 TnyAccount *account = NULL;
1006 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
1007 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
1008 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
1009 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
1010 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
1012 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1013 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1014 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1015 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1016 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1018 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1019 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1020 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1021 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1022 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1024 ThreePixbufs *retval = NULL;
1026 if (TNY_IS_ACCOUNT (instance)) {
1027 account = g_object_ref (instance);
1028 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1029 account = tny_folder_get_account (TNY_FOLDER (instance));
1033 ModestProtocolType account_store_protocol;
1035 account_store_protocol = modest_tny_account_get_protocol_type (account);
1036 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1037 g_object_unref (account);
1043 /* Sometimes an special folder is reported by the server as
1044 NORMAL, like some versions of Dovecot */
1045 if (type == TNY_FOLDER_TYPE_NORMAL ||
1046 type == TNY_FOLDER_TYPE_UNKNOWN) {
1047 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1050 /* It's not enough with check the folder type. We need to
1051 ensure that we're not giving a special folder icon to a
1052 normal folder with the same name than a special folder */
1053 if (TNY_IS_FOLDER (instance) &&
1054 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1055 type = TNY_FOLDER_TYPE_NORMAL;
1057 /* Remote folders should not be treated as special folders */
1058 if (TNY_IS_FOLDER_STORE (instance) &&
1059 !TNY_IS_ACCOUNT (instance) &&
1060 type != TNY_FOLDER_TYPE_INBOX &&
1061 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1062 #ifdef MODEST_TOOLKIT_HILDON2
1063 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1066 &anorm_pixbuf_close);
1068 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1070 &normal_pixbuf_open,
1071 &normal_pixbuf_close);
1077 case TNY_FOLDER_TYPE_INVALID:
1078 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1081 case TNY_FOLDER_TYPE_ROOT:
1082 if (TNY_IS_ACCOUNT (instance)) {
1084 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1085 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1088 &avirt_pixbuf_close);
1090 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1092 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1093 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1096 &ammc_pixbuf_close);
1098 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1101 &anorm_pixbuf_close);
1106 case TNY_FOLDER_TYPE_INBOX:
1107 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1110 &inbox_pixbuf_close);
1112 case TNY_FOLDER_TYPE_OUTBOX:
1113 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1115 &outbox_pixbuf_open,
1116 &outbox_pixbuf_close);
1118 case TNY_FOLDER_TYPE_JUNK:
1119 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1122 &junk_pixbuf_close);
1124 case TNY_FOLDER_TYPE_SENT:
1125 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1128 &sent_pixbuf_close);
1130 case TNY_FOLDER_TYPE_TRASH:
1131 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1134 &trash_pixbuf_close);
1136 case TNY_FOLDER_TYPE_DRAFTS:
1137 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1140 &draft_pixbuf_close);
1142 case TNY_FOLDER_TYPE_ARCHIVE:
1143 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1148 case TNY_FOLDER_TYPE_NORMAL:
1150 /* Memory card folders could have an special icon */
1151 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1152 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1157 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1159 &normal_pixbuf_open,
1160 &normal_pixbuf_close);
1169 free_pixbufs (ThreePixbufs *pixbufs)
1171 if (pixbufs->pixbuf)
1172 g_object_unref (pixbufs->pixbuf);
1173 if (pixbufs->pixbuf_open)
1174 g_object_unref (pixbufs->pixbuf_open);
1175 if (pixbufs->pixbuf_close)
1176 g_object_unref (pixbufs->pixbuf_close);
1177 g_slice_free (ThreePixbufs, pixbufs);
1181 icon_cell_data (GtkTreeViewColumn *column,
1182 GtkCellRenderer *renderer,
1183 GtkTreeModel *tree_model,
1187 GObject *rendobj = NULL, *instance = NULL;
1188 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1189 gboolean has_children;
1190 ThreePixbufs *pixbufs;
1191 ModestFolderView *folder_view = (ModestFolderView *) data;
1193 rendobj = (GObject *) renderer;
1195 gtk_tree_model_get (tree_model, iter,
1197 INSTANCE_COLUMN, &instance,
1203 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1204 pixbufs = get_folder_icons (folder_view, type, instance);
1205 g_object_unref (instance);
1208 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1211 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1212 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1215 free_pixbufs (pixbufs);
1219 add_columns (GtkWidget *treeview)
1221 GtkTreeViewColumn *column;
1222 GtkCellRenderer *renderer;
1223 GtkTreeSelection *sel;
1224 ModestFolderViewPrivate *priv;
1226 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1229 column = gtk_tree_view_column_new ();
1231 /* Set icon and text render function */
1232 renderer = gtk_cell_renderer_pixbuf_new();
1233 #ifdef MODEST_TOOLKIT_HILDON2
1234 g_object_set (renderer,
1235 "xpad", MODEST_MARGIN_DEFAULT,
1236 "ypad", MODEST_MARGIN_DEFAULT,
1239 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1240 gtk_tree_view_column_set_cell_data_func(column, renderer,
1241 icon_cell_data, treeview, NULL);
1243 renderer = gtk_cell_renderer_text_new();
1244 g_object_set (renderer,
1245 #ifdef MODEST_TOOLKIT_HILDON2
1246 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1247 "ypad", MODEST_MARGIN_DEFAULT,
1248 "xpad", MODEST_MARGIN_DEFAULT,
1250 "ellipsize", PANGO_ELLIPSIZE_END,
1252 "ellipsize-set", TRUE, NULL);
1253 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1254 gtk_tree_view_column_set_cell_data_func(column, renderer,
1255 text_cell_data, treeview, NULL);
1257 priv->messages_renderer = gtk_cell_renderer_text_new ();
1258 g_object_set (priv->messages_renderer,
1259 #ifdef MODEST_TOOLKIT_HILDON2
1261 "ypad", MODEST_MARGIN_DEFAULT,
1262 "xpad", MODEST_MARGIN_DOUBLE,
1264 "scale", PANGO_SCALE_X_SMALL,
1267 "alignment", PANGO_ALIGN_RIGHT,
1271 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1272 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1273 messages_cell_data, treeview, NULL);
1275 /* Set selection mode */
1276 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1277 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1279 /* Set treeview appearance */
1280 gtk_tree_view_column_set_spacing (column, 2);
1281 gtk_tree_view_column_set_resizable (column, TRUE);
1282 gtk_tree_view_column_set_fixed_width (column, TRUE);
1283 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1284 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1287 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1291 modest_folder_view_init (ModestFolderView *obj)
1293 ModestFolderViewPrivate *priv;
1296 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1298 priv->timer_expander = 0;
1299 priv->account_store = NULL;
1301 priv->do_refresh = TRUE;
1302 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1303 priv->cur_folder_store = NULL;
1304 priv->visible_account_id = NULL;
1305 priv->mailbox = NULL;
1306 priv->folder_to_select = NULL;
1307 priv->outbox_deleted_handler = 0;
1308 priv->reexpand = TRUE;
1309 priv->signal_handlers = 0;
1311 /* Initialize the local account name */
1312 conf = modest_runtime_get_conf();
1313 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1315 /* Init email clipboard */
1316 priv->clipboard = modest_runtime_get_email_clipboard ();
1317 priv->hidding_ids = NULL;
1318 priv->n_selected = 0;
1319 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1320 priv->reselect = FALSE;
1321 priv->show_non_move = TRUE;
1322 priv->list_to_move = NULL;
1323 priv->show_message_count = TRUE;
1325 /* Build treeview */
1326 add_columns (GTK_WIDGET (obj));
1328 /* Setup drag and drop */
1329 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1331 /* Connect signals */
1332 g_signal_connect (G_OBJECT (obj),
1334 G_CALLBACK (on_key_pressed), NULL);
1336 priv->display_name_changed_signal =
1337 g_signal_connect (modest_runtime_get_account_mgr (),
1338 "display_name_changed",
1339 G_CALLBACK (on_display_name_changed),
1343 * Track changes in the local account name (in the device it
1344 * will be the device name)
1346 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1348 G_CALLBACK(on_configuration_key_changed),
1351 gdk_color_parse ("000", &priv->active_color);
1354 g_signal_connect (G_OBJECT (obj), "notify::style",
1355 G_CALLBACK (on_notify_style), (gpointer) obj);
1359 tny_account_store_view_init (gpointer g, gpointer iface_data)
1361 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1363 klass->set_account_store = modest_folder_view_set_account_store;
1367 modest_folder_view_dispose (GObject *obj)
1369 ModestFolderViewPrivate *priv;
1370 GtkTreeModel *model = NULL;
1372 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1374 get_inner_models (MODEST_FOLDER_VIEW (obj),
1375 NULL, NULL, &model);
1377 if (model && TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1378 g_object_run_dispose (G_OBJECT (model));
1381 #ifdef MODEST_TOOLKIT_HILDON2
1382 if (priv->signal_handlers) {
1383 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1384 priv->signal_handlers = NULL;
1388 /* Free external references */
1389 if (priv->account_store) {
1390 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1391 priv->account_inserted_signal);
1392 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1393 priv->account_removed_signal);
1394 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1395 priv->account_changed_signal);
1396 g_object_unref (G_OBJECT(priv->account_store));
1397 priv->account_store = NULL;
1401 g_object_unref (G_OBJECT (priv->query));
1405 if (priv->folder_to_select) {
1406 g_object_unref (G_OBJECT(priv->folder_to_select));
1407 priv->folder_to_select = NULL;
1410 if (priv->cur_folder_store) {
1411 g_object_unref (priv->cur_folder_store);
1412 priv->cur_folder_store = NULL;
1415 if (priv->list_to_move) {
1416 g_object_unref (priv->list_to_move);
1417 priv->list_to_move = NULL;
1420 G_OBJECT_CLASS(parent_class)->dispose (obj);
1424 modest_folder_view_finalize (GObject *obj)
1426 ModestFolderViewPrivate *priv;
1427 GtkTreeSelection *sel;
1428 TnyAccount *local_account;
1430 g_return_if_fail (obj);
1432 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1434 if (priv->timer_expander != 0) {
1435 g_source_remove (priv->timer_expander);
1436 priv->timer_expander = 0;
1439 local_account = (TnyAccount *)
1440 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1441 if (local_account) {
1442 if (g_signal_handler_is_connected (local_account,
1443 priv->outbox_deleted_handler))
1444 g_signal_handler_disconnect (local_account,
1445 priv->outbox_deleted_handler);
1446 g_object_unref (local_account);
1449 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1450 priv->display_name_changed_signal)) {
1451 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1452 priv->display_name_changed_signal);
1453 priv->display_name_changed_signal = 0;
1456 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1458 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1460 g_free (priv->local_account_name);
1461 g_free (priv->visible_account_id);
1462 g_free (priv->mailbox);
1464 if (priv->conf_key_signal) {
1465 g_signal_handler_disconnect (modest_runtime_get_conf (),
1466 priv->conf_key_signal);
1467 priv->conf_key_signal = 0;
1470 /* Clear hidding array created by cut operation */
1471 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1473 gdk_color_parse ("000", &priv->active_color);
1475 G_OBJECT_CLASS(parent_class)->finalize (obj);
1480 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1482 ModestFolderViewPrivate *priv;
1485 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1486 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1488 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1489 device = tny_account_store_get_device (account_store);
1491 if (G_UNLIKELY (priv->account_store)) {
1493 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1494 priv->account_inserted_signal))
1495 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1496 priv->account_inserted_signal);
1497 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1498 priv->account_removed_signal))
1499 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1500 priv->account_removed_signal);
1501 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1502 priv->account_changed_signal))
1503 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1504 priv->account_changed_signal);
1505 g_object_unref (G_OBJECT (priv->account_store));
1508 priv->account_store = g_object_ref (G_OBJECT (account_store));
1510 priv->account_removed_signal =
1511 g_signal_connect (G_OBJECT(account_store), "account_removed",
1512 G_CALLBACK (on_account_removed), self);
1514 priv->account_inserted_signal =
1515 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1516 G_CALLBACK (on_account_inserted), self);
1518 priv->account_changed_signal =
1519 g_signal_connect (G_OBJECT(account_store), "account_changed",
1520 G_CALLBACK (on_account_changed), self);
1522 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1523 priv->reselect = FALSE;
1524 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1526 g_object_unref (G_OBJECT (device));
1530 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1533 ModestFolderView *self;
1534 GtkTreeModel *model, *filter_model;
1537 self = MODEST_FOLDER_VIEW (user_data);
1539 if (!get_inner_models (self, &filter_model, NULL, &model))
1542 /* Remove outbox from model */
1543 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1544 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1545 g_object_unref (outbox);
1548 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1552 on_account_inserted (TnyAccountStore *account_store,
1553 TnyAccount *account,
1556 ModestFolderViewPrivate *priv;
1557 GtkTreeModel *model, *filter_model;
1559 /* Ignore transport account insertions, we're not showing them
1560 in the folder view */
1561 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1564 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1567 /* If we're adding a new account, and there is no previous
1568 one, we need to select the visible server account */
1569 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1570 !priv->visible_account_id)
1571 modest_widget_memory_restore (modest_runtime_get_conf(),
1572 G_OBJECT (user_data),
1573 MODEST_CONF_FOLDER_VIEW_KEY);
1577 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1578 &filter_model, NULL, &model))
1581 /* Insert the account in the model */
1582 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1584 /* When the model is a list store (plain representation) the
1585 outbox is not a child of any account so we have to manually
1586 delete it because removing the local folders account won't
1587 delete it (because tny_folder_get_account() is not defined
1588 for a merge folder */
1589 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1590 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1592 priv->outbox_deleted_handler =
1593 g_signal_connect (account,
1595 G_CALLBACK (on_outbox_deleted_cb),
1599 /* Refilter the model */
1600 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1605 same_account_selected (ModestFolderView *self,
1606 TnyAccount *account)
1608 ModestFolderViewPrivate *priv;
1609 gboolean same_account = FALSE;
1611 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1613 if (priv->cur_folder_store) {
1614 TnyAccount *selected_folder_account = NULL;
1616 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1617 selected_folder_account =
1618 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1620 selected_folder_account =
1621 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1624 if (selected_folder_account == account)
1625 same_account = TRUE;
1627 g_object_unref (selected_folder_account);
1629 return same_account;
1634 * Selects the first inbox or the local account in an idle
1637 on_idle_select_first_inbox_or_local (gpointer user_data)
1639 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1641 gdk_threads_enter ();
1642 modest_folder_view_select_first_inbox_or_local (self);
1643 gdk_threads_leave ();
1649 on_account_changed (TnyAccountStore *account_store,
1650 TnyAccount *tny_account,
1653 ModestFolderView *self;
1654 ModestFolderViewPrivate *priv;
1655 GtkTreeModel *model, *filter_model;
1656 GtkTreeSelection *sel;
1657 gboolean same_account;
1659 /* Ignore transport account insertions, we're not showing them
1660 in the folder view */
1661 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1664 self = MODEST_FOLDER_VIEW (user_data);
1665 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1667 /* Get the inner model */
1668 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1669 &filter_model, NULL, &model))
1672 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1674 /* Invalidate the cur_folder_store only if the selected folder
1675 belongs to the account that is being removed */
1676 same_account = same_account_selected (self, tny_account);
1678 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1679 gtk_tree_selection_unselect_all (sel);
1682 /* Remove the account from the model */
1683 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1685 /* Insert the account in the model */
1686 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1688 /* Refilter the model */
1689 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1691 /* Select the first INBOX if the currently selected folder
1692 belongs to the account that is being deleted */
1693 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1694 g_idle_add (on_idle_select_first_inbox_or_local, self);
1698 on_account_removed (TnyAccountStore *account_store,
1699 TnyAccount *account,
1702 ModestFolderView *self = NULL;
1703 ModestFolderViewPrivate *priv;
1704 GtkTreeModel *model, *filter_model;
1705 GtkTreeSelection *sel = NULL;
1706 gboolean same_account = FALSE;
1708 /* Ignore transport account removals, we're not showing them
1709 in the folder view */
1710 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1713 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1714 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1718 self = MODEST_FOLDER_VIEW (user_data);
1719 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1721 /* Invalidate the cur_folder_store only if the selected folder
1722 belongs to the account that is being removed */
1723 same_account = same_account_selected (self, account);
1725 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1726 gtk_tree_selection_unselect_all (sel);
1729 /* Invalidate row to select only if the folder to select
1730 belongs to the account that is being removed*/
1731 if (priv->folder_to_select) {
1732 TnyAccount *folder_to_select_account = NULL;
1734 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1735 if (folder_to_select_account == account) {
1736 modest_folder_view_disable_next_folder_selection (self);
1737 g_object_unref (priv->folder_to_select);
1738 priv->folder_to_select = NULL;
1740 g_object_unref (folder_to_select_account);
1743 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1744 &filter_model, NULL, &model))
1747 /* Disconnect the signal handler */
1748 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1749 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1750 if (g_signal_handler_is_connected (account,
1751 priv->outbox_deleted_handler))
1752 g_signal_handler_disconnect (account,
1753 priv->outbox_deleted_handler);
1756 /* Remove the account from the model */
1757 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1759 /* If the removed account is the currently viewed one then
1760 clear the configuration value. The new visible account will be the default account */
1761 if (priv->visible_account_id &&
1762 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1764 /* Clear the current visible account_id */
1765 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1766 modest_folder_view_set_mailbox (self, NULL);
1768 /* Call the restore method, this will set the new visible account */
1769 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1770 MODEST_CONF_FOLDER_VIEW_KEY);
1773 /* Refilter the model */
1774 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1776 /* Select the first INBOX if the currently selected folder
1777 belongs to the account that is being deleted */
1779 g_idle_add (on_idle_select_first_inbox_or_local, self);
1783 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1785 GtkTreeViewColumn *col;
1787 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1789 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1791 g_printerr ("modest: failed get column for title\n");
1795 gtk_tree_view_column_set_title (col, title);
1796 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1801 modest_folder_view_on_map (ModestFolderView *self,
1802 GdkEventExpose *event,
1805 ModestFolderViewPrivate *priv;
1807 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1809 /* This won't happen often */
1810 if (G_UNLIKELY (priv->reselect)) {
1811 /* Select the first inbox or the local account if not found */
1813 /* TODO: this could cause a lock at startup, so we
1814 comment it for the moment. We know that this will
1815 be a bug, because the INBOX is not selected, but we
1816 need to rewrite some parts of Modest to avoid the
1817 deathlock situation */
1818 /* TODO: check if this is still the case */
1819 priv->reselect = FALSE;
1820 modest_folder_view_select_first_inbox_or_local (self);
1821 /* Notify the display name observers */
1822 g_signal_emit (G_OBJECT(self),
1823 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1827 if (priv->reexpand) {
1828 expand_root_items (self);
1829 priv->reexpand = FALSE;
1836 modest_folder_view_new (TnyFolderStoreQuery *query)
1838 return modest_folder_view_new_full (query, TRUE);
1842 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1845 ModestFolderViewPrivate *priv;
1846 GtkTreeSelection *sel;
1848 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1849 #ifdef MODEST_TOOLKIT_HILDON2
1850 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1853 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1856 priv->query = g_object_ref (query);
1858 priv->do_refresh = do_refresh;
1860 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1861 priv->changed_signal = g_signal_connect (sel, "changed",
1862 G_CALLBACK (on_selection_changed), self);
1864 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1866 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1868 return GTK_WIDGET(self);
1871 /* this feels dirty; any other way to expand all the root items? */
1873 expand_root_items (ModestFolderView *self)
1876 GtkTreeModel *model;
1879 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1880 path = gtk_tree_path_new_first ();
1882 /* all folders should have child items, so.. */
1884 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1885 gtk_tree_path_next (path);
1886 } while (gtk_tree_model_get_iter (model, &iter, path));
1888 gtk_tree_path_free (path);
1892 is_parent_of (TnyFolder *a, TnyFolder *b)
1895 gboolean retval = FALSE;
1897 a_id = tny_folder_get_id (a);
1899 gchar *string_to_match;
1902 string_to_match = g_strconcat (a_id, "/", NULL);
1903 b_id = tny_folder_get_id (b);
1904 retval = g_str_has_prefix (b_id, string_to_match);
1905 g_free (string_to_match);
1911 typedef struct _ForeachFolderInfo {
1914 } ForeachFolderInfo;
1917 foreach_folder_with_id (GtkTreeModel *model,
1922 ForeachFolderInfo *info;
1925 info = (ForeachFolderInfo *) data;
1926 gtk_tree_model_get (model, iter,
1927 INSTANCE_COLUMN, &instance,
1930 if (TNY_IS_FOLDER (instance)) {
1933 id = tny_folder_get_id (TNY_FOLDER (instance));
1935 collate = g_utf8_collate_key (id, -1);
1936 info->found = !strcmp (info->needle, collate);
1942 g_object_unref (instance);
1950 has_folder_with_id (ModestFolderView *self, const gchar *id)
1952 GtkTreeModel *model;
1953 ForeachFolderInfo info = {NULL, FALSE};
1955 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1956 info.needle = g_utf8_collate_key (id, -1);
1958 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1959 g_free (info.needle);
1965 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1968 gboolean retval = FALSE;
1970 a_id = tny_folder_get_id (a);
1973 b_id = tny_folder_get_id (b);
1976 const gchar *last_bar;
1977 gchar *string_to_match;
1978 last_bar = g_strrstr (b_id, "/");
1983 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1984 retval = has_folder_with_id (self, string_to_match);
1985 g_free (string_to_match);
1993 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1995 ModestFolderViewPrivate *priv;
1996 TnyIterator *iterator;
1997 gboolean retval = TRUE;
1999 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2000 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2002 for (iterator = tny_list_create_iterator (priv->list_to_move);
2003 retval && !tny_iterator_is_done (iterator);
2004 tny_iterator_next (iterator)) {
2006 instance = tny_iterator_get_current (iterator);
2007 if (instance == (GObject *) folder) {
2009 } else if (TNY_IS_FOLDER (instance)) {
2010 retval = !is_parent_of (TNY_FOLDER (instance), folder);
2012 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
2015 g_object_unref (instance);
2017 g_object_unref (iterator);
2024 * We use this function to implement the
2025 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
2026 * account in this case, and the local folders.
2029 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2031 ModestFolderViewPrivate *priv;
2032 gboolean retval = TRUE;
2033 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2034 GObject *instance = NULL;
2035 const gchar *id = NULL;
2037 gboolean found = FALSE;
2038 gboolean cleared = FALSE;
2039 ModestTnyFolderRules rules = 0;
2042 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2043 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2045 gtk_tree_model_get (model, iter,
2046 NAME_COLUMN, &fname,
2048 INSTANCE_COLUMN, &instance,
2051 /* Do not show if there is no instance, this could indeed
2052 happen when the model is being modified while it's being
2053 drawn. This could occur for example when moving folders
2060 if (TNY_IS_ACCOUNT (instance)) {
2061 TnyAccount *acc = TNY_ACCOUNT (instance);
2062 const gchar *account_id = tny_account_get_id (acc);
2064 /* If it isn't a special folder,
2065 * don't show it unless it is the visible account: */
2066 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2067 !modest_tny_account_is_virtual_local_folders (acc) &&
2068 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2070 /* Show only the visible account id */
2071 if (priv->visible_account_id) {
2072 if (strcmp (account_id, priv->visible_account_id))
2079 /* Never show these to the user. They are merged into one folder
2080 * in the local-folders account instead: */
2081 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2084 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2085 /* Only show special folders for current account if needed */
2086 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2087 TnyAccount *account;
2089 account = tny_folder_get_account (TNY_FOLDER (instance));
2091 if (TNY_IS_ACCOUNT (account)) {
2092 const gchar *account_id = tny_account_get_id (account);
2094 if (!modest_tny_account_is_virtual_local_folders (account) &&
2095 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2096 /* Show only the visible account id */
2097 if (priv->visible_account_id) {
2098 if (strcmp (account_id, priv->visible_account_id)) {
2100 } else if (priv->mailbox) {
2101 /* Filter mailboxes */
2102 if (!g_str_has_prefix (fname, priv->mailbox)) {
2104 } else if (!strcmp (fname, priv->mailbox)) {
2105 /* Hide mailbox parent */
2111 g_object_unref (account);
2118 /* Check hiding (if necessary) */
2119 cleared = modest_email_clipboard_cleared (priv->clipboard);
2120 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2121 id = tny_folder_get_id (TNY_FOLDER(instance));
2122 if (priv->hidding_ids != NULL)
2123 for (i=0; i < priv->n_selected && !found; i++)
2124 if (priv->hidding_ids[i] != NULL && id != NULL)
2125 found = (!strcmp (priv->hidding_ids[i], id));
2130 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2131 folder as no message can be move there according to UI specs */
2132 if (retval && !priv->show_non_move) {
2133 if (priv->list_to_move &&
2134 tny_list_get_length (priv->list_to_move) > 0 &&
2135 TNY_IS_FOLDER (instance)) {
2136 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2138 if (retval && TNY_IS_FOLDER (instance) &&
2139 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2141 case TNY_FOLDER_TYPE_OUTBOX:
2142 case TNY_FOLDER_TYPE_SENT:
2143 case TNY_FOLDER_TYPE_DRAFTS:
2146 case TNY_FOLDER_TYPE_UNKNOWN:
2147 case TNY_FOLDER_TYPE_NORMAL:
2148 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2149 if (type == TNY_FOLDER_TYPE_INVALID)
2150 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2152 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2153 type == TNY_FOLDER_TYPE_SENT
2154 || type == TNY_FOLDER_TYPE_DRAFTS)
2161 if (retval && TNY_IS_ACCOUNT (instance) &&
2162 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2163 ModestProtocolType protocol_type;
2165 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2166 retval = !modest_protocol_registry_protocol_type_has_tag
2167 (modest_runtime_get_protocol_registry (),
2169 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2173 /* apply special filters */
2174 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2175 if (TNY_IS_ACCOUNT (instance))
2179 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2180 if (TNY_IS_FOLDER (instance))
2184 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2185 if (TNY_IS_ACCOUNT (instance)) {
2186 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2188 } else if (TNY_IS_FOLDER (instance)) {
2189 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2194 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2195 if (TNY_IS_ACCOUNT (instance)) {
2196 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2198 } else if (TNY_IS_FOLDER (instance)) {
2199 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2204 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2205 /* A mailbox is a fake folder with an @ in the middle of the name */
2206 if (!TNY_IS_FOLDER (instance) ||
2207 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2210 const gchar *folder_name;
2211 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2212 if (!folder_name || strchr (folder_name, '@') == NULL)
2218 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2219 if (TNY_IS_FOLDER (instance)) {
2220 /* Check folder rules */
2221 ModestTnyFolderRules rules;
2223 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2224 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2225 } else if (TNY_IS_ACCOUNT (instance)) {
2226 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2234 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2235 if (TNY_IS_FOLDER (instance)) {
2236 TnyFolderType guess_type;
2238 if (TNY_FOLDER_TYPE_NORMAL) {
2239 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2245 case TNY_FOLDER_TYPE_OUTBOX:
2246 case TNY_FOLDER_TYPE_SENT:
2247 case TNY_FOLDER_TYPE_DRAFTS:
2248 case TNY_FOLDER_TYPE_ARCHIVE:
2249 case TNY_FOLDER_TYPE_INBOX:
2252 case TNY_FOLDER_TYPE_UNKNOWN:
2253 case TNY_FOLDER_TYPE_NORMAL:
2259 } else if (TNY_IS_ACCOUNT (instance)) {
2264 if (retval && TNY_IS_FOLDER (instance)) {
2265 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2268 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2269 if (TNY_IS_FOLDER (instance)) {
2270 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2271 } else if (TNY_IS_ACCOUNT (instance)) {
2276 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2277 if (TNY_IS_FOLDER (instance)) {
2278 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2279 } else if (TNY_IS_ACCOUNT (instance)) {
2284 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2285 if (TNY_IS_FOLDER (instance)) {
2286 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2287 } else if (TNY_IS_ACCOUNT (instance)) {
2293 g_object_unref (instance);
2301 modest_folder_view_update_model (ModestFolderView *self,
2302 TnyAccountStore *account_store)
2304 ModestFolderViewPrivate *priv;
2305 GtkTreeModel *model;
2306 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2308 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2309 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2312 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2314 /* Notify that there is no folder selected */
2315 g_signal_emit (G_OBJECT(self),
2316 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2318 if (priv->cur_folder_store) {
2319 g_object_unref (priv->cur_folder_store);
2320 priv->cur_folder_store = NULL;
2323 /* FIXME: the local accounts are not shown when the query
2324 selects only the subscribed folders */
2325 #ifdef MODEST_TOOLKIT_HILDON2
2326 TnyGtkFolderListStoreFlags flags;
2327 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2328 if (priv->do_refresh)
2329 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2331 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2332 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2334 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2335 MODEST_FOLDER_PATH_SEPARATOR);
2337 model = tny_gtk_folder_store_tree_model_new (NULL);
2340 /* When the model is a list store (plain representation) the
2341 outbox is not a child of any account so we have to manually
2342 delete it because removing the local folders account won't
2343 delete it (because tny_folder_get_account() is not defined
2344 for a merge folder */
2345 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2346 TnyAccount *account;
2347 ModestTnyAccountStore *acc_store;
2349 acc_store = modest_runtime_get_account_store ();
2350 account = modest_tny_account_store_get_local_folders_account (acc_store);
2352 if (g_signal_handler_is_connected (account,
2353 priv->outbox_deleted_handler))
2354 g_signal_handler_disconnect (account,
2355 priv->outbox_deleted_handler);
2357 priv->outbox_deleted_handler =
2358 g_signal_connect (account,
2360 G_CALLBACK (on_outbox_deleted_cb),
2362 g_object_unref (account);
2365 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2366 /* Get the accounts */
2367 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2369 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2371 if (priv->visible_account_id) {
2372 TnyAccount *account;
2374 /* Add local folders account */
2375 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2378 tny_list_append (TNY_LIST (model), (GObject *) account);
2379 g_object_unref (account);
2382 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2385 tny_list_append (TNY_LIST (model), (GObject *) account);
2386 g_object_unref (account);
2389 /* Add visible account */
2390 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2391 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2392 priv->visible_account_id);
2394 tny_list_append (TNY_LIST (model), (GObject *) account);
2395 g_object_unref (account);
2397 g_warning ("You need to set an account first");
2398 g_object_unref (model);
2402 g_warning ("You need to set an account first");
2403 g_object_unref (model);
2408 sortable = gtk_tree_model_sort_new_with_model (model);
2409 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2411 GTK_SORT_ASCENDING);
2412 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2414 cmp_rows, NULL, NULL);
2416 /* Create filter model */
2417 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2418 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2423 GtkTreeModel *old_tny_model = NULL;
2424 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2425 if (priv->signal_handlers > 0) {
2426 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2427 G_OBJECT (old_tny_model),
2428 "activity-changed");
2433 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2434 #ifndef MODEST_TOOLKIT_HILDON2
2435 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2436 (GCallback) on_row_inserted_maybe_select_folder, self);
2439 #ifdef MODEST_TOOLKIT_HILDON2
2440 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2443 G_CALLBACK (on_activity_changed),
2447 g_object_unref (model);
2448 g_object_unref (filter_model);
2449 g_object_unref (sortable);
2451 /* Force a reselection of the INBOX next time the widget is shown */
2452 priv->reselect = TRUE;
2459 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2461 GtkTreeModel *model = NULL;
2462 TnyFolderStore *folder = NULL;
2464 ModestFolderView *tree_view = NULL;
2465 ModestFolderViewPrivate *priv = NULL;
2466 gboolean selected = FALSE;
2468 g_return_if_fail (sel);
2469 g_return_if_fail (user_data);
2471 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2473 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2475 tree_view = MODEST_FOLDER_VIEW (user_data);
2478 gtk_tree_model_get (model, &iter,
2479 INSTANCE_COLUMN, &folder,
2482 /* If the folder is the same do not notify */
2483 if (folder && priv->cur_folder_store == folder) {
2484 g_object_unref (folder);
2489 /* Current folder was unselected */
2490 if (priv->cur_folder_store) {
2491 /* We must do this firstly because a libtinymail-camel
2492 implementation detail. If we issue the signal
2493 before doing the sync_async, then that signal could
2494 cause (and it actually does it) a free of the
2495 summary of the folder (because the main window will
2496 clear the headers view */
2497 #ifndef MODEST_TOOLKIT_HILDON2
2498 if (TNY_IS_FOLDER(priv->cur_folder_store))
2499 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2500 FALSE, NULL, NULL, NULL);
2503 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2504 priv->cur_folder_store, FALSE);
2506 g_object_unref (priv->cur_folder_store);
2507 priv->cur_folder_store = NULL;
2510 /* New current references */
2511 priv->cur_folder_store = folder;
2513 /* New folder has been selected. Do not notify if there is
2514 nothing new selected */
2516 g_signal_emit (G_OBJECT(tree_view),
2517 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2518 0, priv->cur_folder_store, TRUE);
2523 on_row_activated (GtkTreeView *treeview,
2524 GtkTreePath *treepath,
2525 GtkTreeViewColumn *column,
2528 GtkTreeModel *model = NULL;
2529 TnyFolderStore *folder = NULL;
2531 ModestFolderView *self = NULL;
2532 ModestFolderViewPrivate *priv = NULL;
2534 g_return_if_fail (treeview);
2535 g_return_if_fail (user_data);
2537 self = MODEST_FOLDER_VIEW (user_data);
2538 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2540 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2542 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2545 gtk_tree_model_get (model, &iter,
2546 INSTANCE_COLUMN, &folder,
2549 g_signal_emit (G_OBJECT(self),
2550 signals[FOLDER_ACTIVATED_SIGNAL],
2553 #ifdef MODEST_TOOLKIT_HILDON2
2554 HildonUIMode ui_mode;
2555 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2556 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2557 if (priv->cur_folder_store)
2558 g_object_unref (priv->cur_folder_store);
2559 priv->cur_folder_store = g_object_ref (folder);
2563 g_object_unref (folder);
2567 modest_folder_view_get_selected (ModestFolderView *self)
2569 ModestFolderViewPrivate *priv;
2571 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2573 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2574 if (priv->cur_folder_store)
2575 g_object_ref (priv->cur_folder_store);
2577 return priv->cur_folder_store;
2581 get_cmp_rows_type_pos (GObject *folder)
2583 /* Remote accounts -> Local account -> MMC account .*/
2586 if (TNY_IS_ACCOUNT (folder) &&
2587 modest_tny_account_is_virtual_local_folders (
2588 TNY_ACCOUNT (folder))) {
2590 } else if (TNY_IS_ACCOUNT (folder)) {
2591 TnyAccount *account = TNY_ACCOUNT (folder);
2592 const gchar *account_id = tny_account_get_id (account);
2593 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2599 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2600 return -1; /* Should never happen */
2605 inbox_is_special (TnyFolderStore *folder_store)
2607 gboolean is_special = TRUE;
2609 if (TNY_IS_FOLDER (folder_store)) {
2613 gchar *last_inbox_bar;
2615 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2616 downcase = g_utf8_strdown (id, -1);
2617 last_bar = g_strrstr (downcase, "/");
2619 last_inbox_bar = g_strrstr (downcase, "inbox/");
2620 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2631 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2633 TnyAccount *account;
2634 gboolean is_special;
2635 /* Inbox, Outbox, Drafts, Sent, User */
2638 if (!TNY_IS_FOLDER (folder_store))
2641 case TNY_FOLDER_TYPE_INBOX:
2643 account = tny_folder_get_account (folder_store);
2644 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2646 /* In inbox case we need to know if the inbox is really the top
2647 * inbox of the account, or if it's a submailbox inbox. To do
2648 * this we'll apply an heuristic rule: Find last "/" and check
2649 * if it's preceeded by another Inbox */
2650 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2651 g_object_unref (account);
2652 return is_special?0:4;
2655 case TNY_FOLDER_TYPE_OUTBOX:
2656 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2658 case TNY_FOLDER_TYPE_DRAFTS:
2660 account = tny_folder_get_account (folder_store);
2661 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2662 g_object_unref (account);
2663 return is_special?1:4;
2666 case TNY_FOLDER_TYPE_SENT:
2668 account = tny_folder_get_account (folder_store);
2669 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2670 g_object_unref (account);
2671 return is_special?3:4;
2680 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2682 const gchar *a1_name, *a2_name;
2684 a1_name = tny_account_get_name (a1);
2685 a2_name = tny_account_get_name (a2);
2687 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2691 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2693 TnyAccount *a1 = NULL, *a2 = NULL;
2696 if (TNY_IS_ACCOUNT (s1)) {
2697 a1 = TNY_ACCOUNT (g_object_ref (s1));
2698 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2699 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2702 if (TNY_IS_ACCOUNT (s2)) {
2703 a2 = TNY_ACCOUNT (g_object_ref (s2));
2704 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2705 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2722 /* First we sort with the type of account */
2723 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2727 cmp = compare_account_names (a1, a2);
2731 g_object_unref (a1);
2733 g_object_unref (a2);
2739 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2741 gint is_account1, is_account2;
2743 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2744 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2746 return is_account2 - is_account1;
2750 compare_folders (const gchar *name1, const gchar *name2)
2752 const gchar *separator1, *separator2;
2753 const gchar *next1, *next2;
2757 if (name1 == NULL || name1[0] == '\0')
2759 if (name2 == NULL || name2[0] == '\0')
2762 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2764 top1 = g_strndup (name1, separator1 - name1);
2766 top1 = g_strdup (name1);
2769 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2771 top2 = g_strndup (name2, separator2 - name2);
2773 top2 = g_strdup (name2);
2777 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2784 if (separator1 == NULL && separator2 == NULL)
2787 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2788 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2790 return compare_folders (next1, next2);
2795 * This function orders the mail accounts according to these rules:
2796 * 1st - remote accounts
2797 * 2nd - local account
2801 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2805 gchar *name1 = NULL;
2806 gchar *name2 = NULL;
2807 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2808 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2809 GObject *folder1 = NULL;
2810 GObject *folder2 = NULL;
2812 gtk_tree_model_get (tree_model, iter1,
2813 NAME_COLUMN, &name1,
2815 INSTANCE_COLUMN, &folder1,
2817 gtk_tree_model_get (tree_model, iter2,
2818 NAME_COLUMN, &name2,
2819 TYPE_COLUMN, &type2,
2820 INSTANCE_COLUMN, &folder2,
2823 /* Return if we get no folder. This could happen when folder
2824 operations are happening. The model is updated after the
2825 folder copy/move actually occurs, so there could be
2826 situations where the model to be drawn is not correct */
2827 if (!folder1 || !folder2)
2830 /* Sort by type. First the special folders, then the archives */
2831 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2835 /* Now we sort using the account of each folder */
2836 if (TNY_IS_FOLDER_STORE (folder1) &&
2837 TNY_IS_FOLDER_STORE (folder2)) {
2838 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2842 /* Each group is preceeded by its account */
2843 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2848 /* Pure sort by name */
2849 cmp = compare_folders (name1, name2);
2852 g_object_unref(G_OBJECT(folder1));
2854 g_object_unref(G_OBJECT(folder2));
2862 /*****************************************************************************/
2863 /* DRAG and DROP stuff */
2864 /*****************************************************************************/
2866 * This function fills the #GtkSelectionData with the row and the
2867 * model that has been dragged. It's called when this widget is a
2868 * source for dnd after the event drop happened
2871 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2872 guint info, guint time, gpointer data)
2874 GtkTreeSelection *selection;
2875 GtkTreeModel *model;
2877 GtkTreePath *source_row;
2879 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2880 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2882 source_row = gtk_tree_model_get_path (model, &iter);
2883 gtk_tree_set_row_drag_data (selection_data,
2887 gtk_tree_path_free (source_row);
2891 typedef struct _DndHelper {
2892 ModestFolderView *folder_view;
2893 gboolean delete_source;
2894 GtkTreePath *source_row;
2898 dnd_helper_destroyer (DndHelper *helper)
2900 /* Free the helper */
2901 gtk_tree_path_free (helper->source_row);
2902 g_slice_free (DndHelper, helper);
2906 xfer_folder_cb (ModestMailOperation *mail_op,
2907 TnyFolder *new_folder,
2911 /* Select the folder */
2912 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2918 /* get the folder for the row the treepath refers to. */
2919 /* folder must be unref'd */
2920 static TnyFolderStore *
2921 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2924 TnyFolderStore *folder = NULL;
2926 if (gtk_tree_model_get_iter (model,&iter, path))
2927 gtk_tree_model_get (model, &iter,
2928 INSTANCE_COLUMN, &folder,
2935 * This function is used by drag_data_received_cb to manage drag and
2936 * drop of a header, i.e, and drag from the header view to the folder
2940 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2941 GtkTreeModel *dest_model,
2942 GtkTreePath *dest_row,
2943 GtkSelectionData *selection_data)
2945 TnyList *headers = NULL;
2946 TnyFolder *folder = NULL, *src_folder = NULL;
2947 TnyFolderType folder_type;
2948 GtkTreeIter source_iter, dest_iter;
2949 ModestWindowMgr *mgr = NULL;
2950 ModestWindow *main_win = NULL;
2951 gchar **uris, **tmp;
2953 /* Build the list of headers */
2954 mgr = modest_runtime_get_window_mgr ();
2955 headers = tny_simple_list_new ();
2956 uris = modest_dnd_selection_data_get_paths (selection_data);
2959 while (*tmp != NULL) {
2962 gboolean first = TRUE;
2965 path = gtk_tree_path_new_from_string (*tmp);
2966 gtk_tree_model_get_iter (source_model, &source_iter, path);
2967 gtk_tree_model_get (source_model, &source_iter,
2968 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2971 /* Do not enable d&d of headers already opened */
2972 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2973 tny_list_append (headers, G_OBJECT (header));
2975 if (G_UNLIKELY (first)) {
2976 src_folder = tny_header_get_folder (header);
2980 /* Free and go on */
2981 gtk_tree_path_free (path);
2982 g_object_unref (header);
2987 /* This could happen ig we perform a d&d very quickly over the
2988 same row that row could dissapear because message is
2990 if (!TNY_IS_FOLDER (src_folder))
2993 /* Get the target folder */
2994 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2995 gtk_tree_model_get (dest_model, &dest_iter,
2999 if (!folder || !TNY_IS_FOLDER(folder)) {
3000 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
3004 folder_type = modest_tny_folder_guess_folder_type (folder);
3005 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
3006 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
3007 goto cleanup; /* cannot move messages there */
3010 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3011 /* g_warning ("folder not writable"); */
3012 goto cleanup; /* verboten! */
3015 /* Ask for confirmation to move */
3016 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
3018 g_warning ("%s: BUG: no main window found", __FUNCTION__);
3022 /* Transfer messages */
3023 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
3028 if (G_IS_OBJECT (src_folder))
3029 g_object_unref (src_folder);
3030 if (G_IS_OBJECT(folder))
3031 g_object_unref (G_OBJECT (folder));
3032 if (G_IS_OBJECT(headers))
3033 g_object_unref (headers);
3037 TnyFolderStore *src_folder;
3038 TnyFolderStore *dst_folder;
3039 ModestFolderView *folder_view;
3044 dnd_folder_info_destroyer (DndFolderInfo *info)
3046 if (info->src_folder)
3047 g_object_unref (info->src_folder);
3048 if (info->dst_folder)
3049 g_object_unref (info->dst_folder);
3050 g_slice_free (DndFolderInfo, info);
3054 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
3055 GtkWindow *parent_window,
3056 TnyAccount *account)
3059 modest_ui_actions_on_account_connection_error (parent_window, account);
3061 /* Free the helper & info */
3062 dnd_helper_destroyer (info->helper);
3063 dnd_folder_info_destroyer (info);
3067 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
3069 GtkWindow *parent_window,
3070 TnyAccount *account,
3073 DndFolderInfo *info = NULL;
3074 ModestMailOperation *mail_op;
3076 info = (DndFolderInfo *) user_data;
3078 if (err || canceled) {
3079 dnd_on_connection_failed_destroyer (info, parent_window, account);
3083 /* Do the mail operation */
3084 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
3085 modest_ui_actions_move_folder_error_handler,
3086 info->src_folder, NULL);
3088 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3091 /* Transfer the folder */
3092 modest_mail_operation_xfer_folder (mail_op,
3093 TNY_FOLDER (info->src_folder),
3095 info->helper->delete_source,
3097 info->helper->folder_view);
3100 g_object_unref (G_OBJECT (mail_op));
3101 dnd_helper_destroyer (info->helper);
3102 dnd_folder_info_destroyer (info);
3107 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3109 GtkWindow *parent_window,
3110 TnyAccount *account,
3113 DndFolderInfo *info = NULL;
3115 info = (DndFolderInfo *) user_data;
3117 if (err || canceled) {
3118 dnd_on_connection_failed_destroyer (info, parent_window, account);
3122 /* Connect to source folder and perform the copy/move */
3123 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3125 drag_and_drop_from_folder_view_src_folder_performer,
3130 * This function is used by drag_data_received_cb to manage drag and
3131 * drop of a folder, i.e, and drag from the folder view to the same
3135 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3136 GtkTreeModel *dest_model,
3137 GtkTreePath *dest_row,
3138 GtkSelectionData *selection_data,
3141 GtkTreeIter dest_iter, iter;
3142 TnyFolderStore *dest_folder = NULL;
3143 TnyFolderStore *folder = NULL;
3144 gboolean forbidden = FALSE;
3146 DndFolderInfo *info = NULL;
3148 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3150 g_warning ("%s: BUG: no main window", __FUNCTION__);
3151 dnd_helper_destroyer (helper);
3156 /* check the folder rules for the destination */
3157 folder = tree_path_to_folder (dest_model, dest_row);
3158 if (TNY_IS_FOLDER(folder)) {
3159 ModestTnyFolderRules rules =
3160 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3161 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3162 } else if (TNY_IS_FOLDER_STORE(folder)) {
3163 /* enable local root as destination for folders */
3164 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3165 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3168 g_object_unref (folder);
3171 /* check the folder rules for the source */
3172 folder = tree_path_to_folder (source_model, helper->source_row);
3173 if (TNY_IS_FOLDER(folder)) {
3174 ModestTnyFolderRules rules =
3175 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3176 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3179 g_object_unref (folder);
3183 /* Check if the drag is possible */
3184 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3186 modest_platform_run_information_dialog ((GtkWindow *) win,
3187 _("mail_in_ui_folder_move_target_error"),
3189 /* Restore the previous selection */
3190 folder = tree_path_to_folder (source_model, helper->source_row);
3192 if (TNY_IS_FOLDER (folder))
3193 modest_folder_view_select_folder (helper->folder_view,
3194 TNY_FOLDER (folder), FALSE);
3195 g_object_unref (folder);
3197 dnd_helper_destroyer (helper);
3202 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3203 gtk_tree_model_get (dest_model, &dest_iter,
3206 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3207 gtk_tree_model_get (source_model, &iter,
3211 /* Create the info for the performer */
3212 info = g_slice_new0 (DndFolderInfo);
3213 info->src_folder = g_object_ref (folder);
3214 info->dst_folder = g_object_ref (dest_folder);
3215 info->helper = helper;
3217 /* Connect to the destination folder and perform the copy/move */
3218 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3220 drag_and_drop_from_folder_view_dst_folder_performer,
3224 g_object_unref (dest_folder);
3225 g_object_unref (folder);
3229 * This function receives the data set by the "drag-data-get" signal
3230 * handler. This information comes within the #GtkSelectionData. This
3231 * function will manage both the drags of folders of the treeview and
3232 * drags of headers of the header view widget.
3235 on_drag_data_received (GtkWidget *widget,
3236 GdkDragContext *context,
3239 GtkSelectionData *selection_data,
3244 GtkWidget *source_widget;
3245 GtkTreeModel *dest_model, *source_model;
3246 GtkTreePath *source_row, *dest_row;
3247 GtkTreeViewDropPosition pos;
3248 gboolean delete_source = FALSE;
3249 gboolean success = FALSE;
3251 /* Do not allow further process */
3252 g_signal_stop_emission_by_name (widget, "drag-data-received");
3253 source_widget = gtk_drag_get_source_widget (context);
3255 /* Get the action */
3256 if (context->action == GDK_ACTION_MOVE) {
3257 delete_source = TRUE;
3259 /* Notify that there is no folder selected. We need to
3260 do this in order to update the headers view (and
3261 its monitors, because when moving, the old folder
3262 won't longer exist. We can not wait for the end of
3263 the operation, because the operation won't start if
3264 the folder is in use */
3265 if (source_widget == widget) {
3266 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3267 gtk_tree_selection_unselect_all (sel);
3271 /* Check if the get_data failed */
3272 if (selection_data == NULL || selection_data->length < 0)
3275 /* Select the destination model */
3276 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3278 /* Get the path to the destination row. Can not call
3279 gtk_tree_view_get_drag_dest_row() because the source row
3280 is not selected anymore */
3281 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3284 /* Only allow drops IN other rows */
3286 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3287 pos == GTK_TREE_VIEW_DROP_AFTER)
3291 /* Drags from the header view */
3292 if (source_widget != widget) {
3293 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3295 drag_and_drop_from_header_view (source_model,
3300 DndHelper *helper = NULL;
3302 /* Get the source model and row */
3303 gtk_tree_get_row_drag_data (selection_data,
3307 /* Create the helper */
3308 helper = g_slice_new0 (DndHelper);
3309 helper->delete_source = delete_source;
3310 helper->source_row = gtk_tree_path_copy (source_row);
3311 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3313 drag_and_drop_from_folder_view (source_model,
3319 gtk_tree_path_free (source_row);
3323 gtk_tree_path_free (dest_row);
3326 /* Finish the drag and drop */
3327 gtk_drag_finish (context, success, FALSE, time);
3331 * We define a "drag-drop" signal handler because we do not want to
3332 * use the default one, because the default one always calls
3333 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3334 * signal handler, because there we have all the information available
3335 * to know if the dnd was a success or not.
3338 drag_drop_cb (GtkWidget *widget,
3339 GdkDragContext *context,
3347 if (!context->targets)
3350 /* Check if we're dragging a folder row */
3351 target = gtk_drag_dest_find_target (widget, context, NULL);
3353 /* Request the data from the source. */
3354 gtk_drag_get_data(widget, context, target, time);
3360 * This function expands a node of a tree view if it's not expanded
3361 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3362 * does that, so that's why they're here.
3365 expand_row_timeout (gpointer data)
3367 GtkTreeView *tree_view = data;
3368 GtkTreePath *dest_path = NULL;
3369 GtkTreeViewDropPosition pos;
3370 gboolean result = FALSE;
3372 gdk_threads_enter ();
3374 gtk_tree_view_get_drag_dest_row (tree_view,
3379 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3380 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3381 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3382 gtk_tree_path_free (dest_path);
3386 gtk_tree_path_free (dest_path);
3391 gdk_threads_leave ();
3397 * This function is called whenever the pointer is moved over a widget
3398 * while dragging some data. It installs a timeout that will expand a
3399 * node of the treeview if not expanded yet. This function also calls
3400 * gdk_drag_status in order to set the suggested action that will be
3401 * used by the "drag-data-received" signal handler to know if we
3402 * should do a move or just a copy of the data.
3405 on_drag_motion (GtkWidget *widget,
3406 GdkDragContext *context,
3412 GtkTreeViewDropPosition pos;
3413 GtkTreePath *dest_row;
3414 GtkTreeModel *dest_model;
3415 ModestFolderViewPrivate *priv;
3416 GdkDragAction suggested_action;
3417 gboolean valid_location = FALSE;
3418 TnyFolderStore *folder = NULL;
3420 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3422 if (priv->timer_expander != 0) {
3423 g_source_remove (priv->timer_expander);
3424 priv->timer_expander = 0;
3427 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3432 /* Do not allow drops between folders */
3434 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3435 pos == GTK_TREE_VIEW_DROP_AFTER) {
3436 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3437 gdk_drag_status(context, 0, time);
3438 valid_location = FALSE;
3441 valid_location = TRUE;
3444 /* Check that the destination folder is writable */
3445 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3446 folder = tree_path_to_folder (dest_model, dest_row);
3447 if (folder && TNY_IS_FOLDER (folder)) {
3448 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3450 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3451 valid_location = FALSE;
3456 /* Expand the selected row after 1/2 second */
3457 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3458 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3460 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3462 /* Select the desired action. By default we pick MOVE */
3463 suggested_action = GDK_ACTION_MOVE;
3465 if (context->actions == GDK_ACTION_COPY)
3466 gdk_drag_status(context, GDK_ACTION_COPY, time);
3467 else if (context->actions == GDK_ACTION_MOVE)
3468 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3469 else if (context->actions & suggested_action)
3470 gdk_drag_status(context, suggested_action, time);
3472 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3476 g_object_unref (folder);
3478 gtk_tree_path_free (dest_row);
3480 g_signal_stop_emission_by_name (widget, "drag-motion");
3482 return valid_location;
3486 * This function sets the treeview as a source and a target for dnd
3487 * events. It also connects all the requirede signals.
3490 setup_drag_and_drop (GtkTreeView *self)
3492 /* Set up the folder view as a dnd destination. Set only the
3493 highlight flag, otherwise gtk will have a different
3495 #ifdef MODEST_TOOLKIT_HILDON2
3498 gtk_drag_dest_set (GTK_WIDGET (self),
3499 GTK_DEST_DEFAULT_HIGHLIGHT,
3500 folder_view_drag_types,
3501 G_N_ELEMENTS (folder_view_drag_types),
3502 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3504 g_signal_connect (G_OBJECT (self),
3505 "drag_data_received",
3506 G_CALLBACK (on_drag_data_received),
3510 /* Set up the treeview as a dnd source */
3511 gtk_drag_source_set (GTK_WIDGET (self),
3513 folder_view_drag_types,
3514 G_N_ELEMENTS (folder_view_drag_types),
3515 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3517 g_signal_connect (G_OBJECT (self),
3519 G_CALLBACK (on_drag_motion),
3522 g_signal_connect (G_OBJECT (self),
3524 G_CALLBACK (on_drag_data_get),
3527 g_signal_connect (G_OBJECT (self),
3529 G_CALLBACK (drag_drop_cb),
3534 * This function manages the navigation through the folders using the
3535 * keyboard or the hardware keys in the device
3538 on_key_pressed (GtkWidget *self,
3542 GtkTreeSelection *selection;
3544 GtkTreeModel *model;
3545 gboolean retval = FALSE;
3547 /* Up and Down are automatically managed by the treeview */
3548 if (event->keyval == GDK_Return) {
3549 /* Expand/Collapse the selected row */
3550 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3551 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3554 path = gtk_tree_model_get_path (model, &iter);
3556 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3557 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3559 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3560 gtk_tree_path_free (path);
3562 /* No further processing */
3570 * We listen to the changes in the local folder account name key,
3571 * because we want to show the right name in the view. The local
3572 * folder account name corresponds to the device name in the Maemo
3573 * version. We do this because we do not want to query gconf on each
3574 * tree view refresh. It's better to cache it and change whenever
3578 on_configuration_key_changed (ModestConf* conf,
3580 ModestConfEvent event,
3581 ModestConfNotificationId id,
3582 ModestFolderView *self)
3584 ModestFolderViewPrivate *priv;
3587 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3588 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3590 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3591 g_free (priv->local_account_name);
3593 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3594 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3596 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3597 MODEST_CONF_DEVICE_NAME, NULL);
3599 /* Force a redraw */
3600 #if GTK_CHECK_VERSION(2, 8, 0)
3601 GtkTreeViewColumn * tree_column;
3603 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3605 gtk_tree_view_column_queue_resize (tree_column);
3607 gtk_widget_queue_draw (GTK_WIDGET (self));
3613 modest_folder_view_set_style (ModestFolderView *self,
3614 ModestFolderViewStyle style)
3616 ModestFolderViewPrivate *priv;
3618 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3619 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3620 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3622 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3625 priv->style = style;
3629 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3630 const gchar *account_id)
3632 ModestFolderViewPrivate *priv;
3633 GtkTreeModel *model;
3635 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3637 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3639 /* This will be used by the filter_row callback,
3640 * to decided which rows to show: */
3641 if (priv->visible_account_id) {
3642 g_free (priv->visible_account_id);
3643 priv->visible_account_id = NULL;
3646 priv->visible_account_id = g_strdup (account_id);
3649 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3650 if (GTK_IS_TREE_MODEL_FILTER (model))
3651 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3653 modest_folder_view_update_model(self,
3654 (TnyAccountStore *) modest_runtime_get_account_store());
3656 /* Save settings to gconf */
3657 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3658 MODEST_CONF_FOLDER_VIEW_KEY);
3660 /* Notify observers */
3661 g_signal_emit (G_OBJECT(self),
3662 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3667 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3669 ModestFolderViewPrivate *priv;
3671 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3673 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3675 return (const gchar *) priv->visible_account_id;
3679 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3683 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3685 gtk_tree_model_get (model, iter,
3689 gboolean result = FALSE;
3690 if (type == TNY_FOLDER_TYPE_INBOX) {
3694 *inbox_iter = *iter;
3698 if (gtk_tree_model_iter_children (model, &child, iter)) {
3699 if (find_inbox_iter (model, &child, inbox_iter))
3703 } while (gtk_tree_model_iter_next (model, iter));
3712 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3714 #ifndef MODEST_TOOLKIT_HILDON2
3715 GtkTreeModel *model;
3716 GtkTreeIter iter, inbox_iter;
3717 GtkTreeSelection *sel;
3718 GtkTreePath *path = NULL;
3720 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3722 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3726 expand_root_items (self);
3727 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3729 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3730 g_warning ("%s: model is empty", __FUNCTION__);
3734 if (find_inbox_iter (model, &iter, &inbox_iter))
3735 path = gtk_tree_model_get_path (model, &inbox_iter);
3737 path = gtk_tree_path_new_first ();
3739 /* Select the row and free */
3740 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3741 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3742 gtk_tree_path_free (path);
3745 gtk_widget_grab_focus (GTK_WIDGET(self));
3752 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3757 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3758 TnyFolder* a_folder;
3761 gtk_tree_model_get (model, iter,
3762 INSTANCE_COLUMN, &a_folder,
3768 if (folder == a_folder) {
3769 g_object_unref (a_folder);
3770 *folder_iter = *iter;
3773 g_object_unref (a_folder);
3775 if (gtk_tree_model_iter_children (model, &child, iter)) {
3776 if (find_folder_iter (model, &child, folder_iter, folder))
3780 } while (gtk_tree_model_iter_next (model, iter));
3785 #ifndef MODEST_TOOLKIT_HILDON2
3787 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3790 ModestFolderView *self)
3792 ModestFolderViewPrivate *priv = NULL;
3793 GtkTreeSelection *sel;
3794 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3795 GObject *instance = NULL;
3797 if (!MODEST_IS_FOLDER_VIEW(self))
3800 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3802 priv->reexpand = TRUE;
3804 gtk_tree_model_get (tree_model, iter,
3806 INSTANCE_COLUMN, &instance,
3812 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3813 priv->folder_to_select = g_object_ref (instance);
3815 g_object_unref (instance);
3817 if (priv->folder_to_select) {
3819 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3822 path = gtk_tree_model_get_path (tree_model, iter);
3823 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3825 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3827 gtk_tree_selection_select_iter (sel, iter);
3828 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3830 gtk_tree_path_free (path);
3834 modest_folder_view_disable_next_folder_selection (self);
3836 /* Refilter the model */
3837 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3843 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3845 ModestFolderViewPrivate *priv;
3847 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3849 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3851 if (priv->folder_to_select)
3852 g_object_unref(priv->folder_to_select);
3854 priv->folder_to_select = NULL;
3858 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3859 gboolean after_change)
3861 GtkTreeModel *model;
3862 GtkTreeIter iter, folder_iter;
3863 GtkTreeSelection *sel;
3864 ModestFolderViewPrivate *priv = NULL;
3866 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3867 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3869 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3872 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3873 gtk_tree_selection_unselect_all (sel);
3875 if (priv->folder_to_select)
3876 g_object_unref(priv->folder_to_select);
3877 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3881 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3886 /* Refilter the model, before selecting the folder */
3887 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3889 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3890 g_warning ("%s: model is empty", __FUNCTION__);
3894 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3897 path = gtk_tree_model_get_path (model, &folder_iter);
3898 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3900 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3901 gtk_tree_selection_select_iter (sel, &folder_iter);
3902 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3904 gtk_tree_path_free (path);
3912 modest_folder_view_copy_selection (ModestFolderView *self)
3914 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3916 /* Copy selection */
3917 _clipboard_set_selected_data (self, FALSE);
3921 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3923 ModestFolderViewPrivate *priv = NULL;
3924 GtkTreeModel *model = NULL;
3925 const gchar **hidding = NULL;
3926 guint i, n_selected;
3928 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3929 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3931 /* Copy selection */
3932 if (!_clipboard_set_selected_data (folder_view, TRUE))
3935 /* Get hidding ids */
3936 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3938 /* Clear hidding array created by previous cut operation */
3939 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3941 /* Copy hidding array */
3942 priv->n_selected = n_selected;
3943 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3944 for (i=0; i < n_selected; i++)
3945 priv->hidding_ids[i] = g_strdup(hidding[i]);
3947 /* Hide cut folders */
3948 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3949 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3953 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3954 ModestFolderView *folder_view_dst)
3956 GtkTreeModel *filter_model = NULL;
3957 GtkTreeModel *model = NULL;
3958 GtkTreeModel *new_filter_model = NULL;
3959 GtkTreeModel *old_tny_model = NULL;
3960 GtkTreeModel *new_tny_model = NULL;
3961 ModestFolderViewPrivate *dst_priv;
3963 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3964 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3966 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3967 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3968 new_tny_model = NULL;
3971 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3972 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3973 G_OBJECT (old_tny_model),
3974 "activity-changed");
3976 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3977 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3979 /* Build new filter model */
3980 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3981 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3988 /* Set copied model */
3989 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3990 #ifndef MODEST_TOOLKIT_HILDON2
3991 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3992 G_OBJECT(new_filter_model), "row-inserted",
3993 (GCallback) on_row_inserted_maybe_select_folder,
3996 #ifdef MODEST_TOOLKIT_HILDON2
3997 if (new_tny_model) {
3998 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3999 G_OBJECT (new_tny_model),
4001 G_CALLBACK (on_activity_changed),
4007 g_object_unref (new_filter_model);
4011 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
4014 GtkTreeModel *model = NULL;
4015 ModestFolderViewPrivate* priv;
4017 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4019 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4020 priv->show_non_move = show;
4021 /* modest_folder_view_update_model(folder_view, */
4022 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
4024 /* Hide special folders */
4025 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
4026 if (GTK_IS_TREE_MODEL_FILTER (model)) {
4027 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
4032 modest_folder_view_show_message_count (ModestFolderView *folder_view,
4035 ModestFolderViewPrivate* priv;
4037 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4039 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4040 priv->show_message_count = show;
4042 g_object_set (G_OBJECT (priv->messages_renderer),
4043 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4047 /* Returns FALSE if it did not selected anything */
4049 _clipboard_set_selected_data (ModestFolderView *folder_view,
4052 ModestFolderViewPrivate *priv = NULL;
4053 TnyFolderStore *folder = NULL;
4054 gboolean retval = FALSE;
4056 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
4057 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4059 /* Set selected data on clipboard */
4060 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
4061 folder = modest_folder_view_get_selected (folder_view);
4063 /* Do not allow to select an account */
4064 if (TNY_IS_FOLDER (folder)) {
4065 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
4070 g_object_unref (folder);
4076 _clear_hidding_filter (ModestFolderView *folder_view)
4078 ModestFolderViewPrivate *priv;
4081 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4082 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4084 if (priv->hidding_ids != NULL) {
4085 for (i=0; i < priv->n_selected; i++)
4086 g_free (priv->hidding_ids[i]);
4087 g_free(priv->hidding_ids);
4093 on_display_name_changed (ModestAccountMgr *mgr,
4094 const gchar *account,
4097 ModestFolderView *self;
4099 self = MODEST_FOLDER_VIEW (user_data);
4101 /* Force a redraw */
4102 #if GTK_CHECK_VERSION(2, 8, 0)
4103 GtkTreeViewColumn * tree_column;
4105 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4107 gtk_tree_view_column_queue_resize (tree_column);
4109 gtk_widget_queue_draw (GTK_WIDGET (self));
4114 modest_folder_view_set_cell_style (ModestFolderView *self,
4115 ModestFolderViewCellStyle cell_style)
4117 ModestFolderViewPrivate *priv = NULL;
4119 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4120 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4122 priv->cell_style = cell_style;
4124 g_object_set (G_OBJECT (priv->messages_renderer),
4125 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4128 gtk_widget_queue_draw (GTK_WIDGET (self));
4132 update_style (ModestFolderView *self)
4134 ModestFolderViewPrivate *priv;
4135 GdkColor style_color, style_active_color;
4136 PangoAttrList *attr_list;
4138 PangoAttribute *attr;
4140 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4141 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4145 attr_list = pango_attr_list_new ();
4146 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4147 gdk_color_parse ("grey", &style_color);
4149 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4150 pango_attr_list_insert (attr_list, attr);
4153 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4155 "SmallSystemFont", NULL,
4158 attr = pango_attr_font_desc_new (pango_font_description_copy
4159 (style->font_desc));
4160 pango_attr_list_insert (attr_list, attr);
4162 g_object_set (G_OBJECT (priv->messages_renderer),
4163 "foreground-gdk", &style_color,
4164 "foreground-set", TRUE,
4165 "attributes", attr_list,
4167 pango_attr_list_unref (attr_list);
4170 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4171 priv->active_color = style_active_color;
4173 gdk_color_parse ("000", &(priv->active_color));
4178 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4180 if (strcmp ("style", spec->name) == 0) {
4181 update_style (MODEST_FOLDER_VIEW (obj));
4182 gtk_widget_queue_draw (GTK_WIDGET (obj));
4187 modest_folder_view_set_filter (ModestFolderView *self,
4188 ModestFolderViewFilter filter)
4190 ModestFolderViewPrivate *priv;
4191 GtkTreeModel *filter_model;
4193 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4194 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4196 priv->filter |= filter;
4198 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4199 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4200 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4205 modest_folder_view_unset_filter (ModestFolderView *self,
4206 ModestFolderViewFilter filter)
4208 ModestFolderViewPrivate *priv;
4209 GtkTreeModel *filter_model;
4211 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4212 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4214 priv->filter &= ~filter;
4216 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4217 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4218 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4223 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4224 ModestTnyFolderRules rules)
4226 GtkTreeModel *filter_model;
4228 gboolean fulfil = FALSE;
4230 if (!get_inner_models (self, &filter_model, NULL, NULL))
4233 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4237 TnyFolderStore *folder;
4239 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4241 if (TNY_IS_FOLDER (folder)) {
4242 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4243 /* Folder rules are negative: non_writable, non_deletable... */
4244 if (!(folder_rules & rules))
4247 g_object_unref (folder);
4250 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4256 modest_folder_view_set_list_to_move (ModestFolderView *self,
4259 ModestFolderViewPrivate *priv;
4261 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4262 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4264 if (priv->list_to_move)
4265 g_object_unref (priv->list_to_move);
4268 g_object_ref (list);
4270 priv->list_to_move = list;
4274 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4276 ModestFolderViewPrivate *priv;
4278 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4279 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4282 g_free (priv->mailbox);
4284 priv->mailbox = g_strdup (mailbox);
4286 /* Notify observers */
4287 g_signal_emit (G_OBJECT(self),
4288 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4289 priv->visible_account_id);
4293 modest_folder_view_get_mailbox (ModestFolderView *self)
4295 ModestFolderViewPrivate *priv;
4297 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4298 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4300 return (const gchar *) priv->mailbox;
4304 modest_folder_view_get_activity (ModestFolderView *self)
4306 ModestFolderViewPrivate *priv;
4307 GtkTreeModel *inner_model;
4309 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4310 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4311 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4313 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4314 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4320 #ifdef MODEST_TOOLKIT_HILDON2
4322 on_activity_changed (TnyGtkFolderListStore *store,
4324 ModestFolderView *folder_view)
4326 ModestFolderViewPrivate *priv;
4328 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4329 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4330 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4332 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,