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);
147 #ifndef MODEST_TOOLKIT_HILDON2
149 static void on_drag_data_get (GtkWidget *widget,
150 GdkDragContext *context,
151 GtkSelectionData *selection_data,
156 static void on_drag_data_received (GtkWidget *widget,
157 GdkDragContext *context,
160 GtkSelectionData *selection_data,
165 static gboolean on_drag_motion (GtkWidget *widget,
166 GdkDragContext *context,
172 static void setup_drag_and_drop (GtkTreeView *self);
174 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
177 ModestFolderView *self);
179 static gint expand_row_timeout (gpointer data);
182 static void expand_root_items (ModestFolderView *self);
184 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
187 static void _clear_hidding_filter (ModestFolderView *folder_view);
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 #ifndef MODEST_TOOLKIT_HILDON2
1329 /* Setup drag and drop */
1330 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1333 /* Connect signals */
1334 g_signal_connect (G_OBJECT (obj),
1336 G_CALLBACK (on_key_pressed), NULL);
1338 priv->display_name_changed_signal =
1339 g_signal_connect (modest_runtime_get_account_mgr (),
1340 "display_name_changed",
1341 G_CALLBACK (on_display_name_changed),
1345 * Track changes in the local account name (in the device it
1346 * will be the device name)
1348 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1350 G_CALLBACK(on_configuration_key_changed),
1353 gdk_color_parse ("000", &priv->active_color);
1356 g_signal_connect (G_OBJECT (obj), "notify::style",
1357 G_CALLBACK (on_notify_style), (gpointer) obj);
1361 tny_account_store_view_init (gpointer g, gpointer iface_data)
1363 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1365 klass->set_account_store = modest_folder_view_set_account_store;
1369 modest_folder_view_dispose (GObject *obj)
1371 ModestFolderViewPrivate *priv;
1373 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1375 #ifdef MODEST_TOOLKIT_HILDON2
1376 if (priv->signal_handlers) {
1377 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1378 priv->signal_handlers = NULL;
1382 /* Free external references */
1383 if (priv->account_store) {
1384 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1385 priv->account_inserted_signal);
1386 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1387 priv->account_removed_signal);
1388 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1389 priv->account_changed_signal);
1390 g_object_unref (G_OBJECT(priv->account_store));
1391 priv->account_store = NULL;
1395 g_object_unref (G_OBJECT (priv->query));
1399 if (priv->folder_to_select) {
1400 g_object_unref (G_OBJECT(priv->folder_to_select));
1401 priv->folder_to_select = NULL;
1404 if (priv->cur_folder_store) {
1405 g_object_unref (priv->cur_folder_store);
1406 priv->cur_folder_store = NULL;
1409 if (priv->list_to_move) {
1410 g_object_unref (priv->list_to_move);
1411 priv->list_to_move = NULL;
1414 G_OBJECT_CLASS(parent_class)->dispose (obj);
1418 modest_folder_view_finalize (GObject *obj)
1420 ModestFolderViewPrivate *priv;
1421 GtkTreeSelection *sel;
1422 TnyAccount *local_account;
1424 g_return_if_fail (obj);
1426 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1428 if (priv->timer_expander != 0) {
1429 g_source_remove (priv->timer_expander);
1430 priv->timer_expander = 0;
1433 local_account = (TnyAccount *)
1434 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1435 if (local_account) {
1436 if (g_signal_handler_is_connected (local_account,
1437 priv->outbox_deleted_handler))
1438 g_signal_handler_disconnect (local_account,
1439 priv->outbox_deleted_handler);
1440 g_object_unref (local_account);
1443 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1444 priv->display_name_changed_signal)) {
1445 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1446 priv->display_name_changed_signal);
1447 priv->display_name_changed_signal = 0;
1450 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1452 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1454 g_free (priv->local_account_name);
1455 g_free (priv->visible_account_id);
1456 g_free (priv->mailbox);
1458 if (priv->conf_key_signal) {
1459 g_signal_handler_disconnect (modest_runtime_get_conf (),
1460 priv->conf_key_signal);
1461 priv->conf_key_signal = 0;
1464 /* Clear hidding array created by cut operation */
1465 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1467 gdk_color_parse ("000", &priv->active_color);
1469 G_OBJECT_CLASS(parent_class)->finalize (obj);
1474 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1476 ModestFolderViewPrivate *priv;
1479 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1480 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1482 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1483 device = tny_account_store_get_device (account_store);
1485 if (G_UNLIKELY (priv->account_store)) {
1487 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1488 priv->account_inserted_signal))
1489 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1490 priv->account_inserted_signal);
1491 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1492 priv->account_removed_signal))
1493 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1494 priv->account_removed_signal);
1495 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1496 priv->account_changed_signal))
1497 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1498 priv->account_changed_signal);
1499 g_object_unref (G_OBJECT (priv->account_store));
1502 priv->account_store = g_object_ref (G_OBJECT (account_store));
1504 priv->account_removed_signal =
1505 g_signal_connect (G_OBJECT(account_store), "account_removed",
1506 G_CALLBACK (on_account_removed), self);
1508 priv->account_inserted_signal =
1509 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1510 G_CALLBACK (on_account_inserted), self);
1512 priv->account_changed_signal =
1513 g_signal_connect (G_OBJECT(account_store), "account_changed",
1514 G_CALLBACK (on_account_changed), self);
1516 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1517 priv->reselect = FALSE;
1518 #ifndef MODEST_TOOLKIT_HILDON2
1519 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1522 g_object_unref (G_OBJECT (device));
1526 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1529 ModestFolderView *self;
1530 GtkTreeModel *model, *filter_model;
1533 self = MODEST_FOLDER_VIEW (user_data);
1535 if (!get_inner_models (self, &filter_model, NULL, &model))
1538 /* Remove outbox from model */
1539 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1540 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1541 g_object_unref (outbox);
1544 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1548 on_account_inserted (TnyAccountStore *account_store,
1549 TnyAccount *account,
1552 ModestFolderViewPrivate *priv;
1553 GtkTreeModel *model, *filter_model;
1555 /* Ignore transport account insertions, we're not showing them
1556 in the folder view */
1557 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1560 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1563 /* If we're adding a new account, and there is no previous
1564 one, we need to select the visible server account */
1565 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1566 !priv->visible_account_id)
1567 modest_widget_memory_restore (modest_runtime_get_conf(),
1568 G_OBJECT (user_data),
1569 MODEST_CONF_FOLDER_VIEW_KEY);
1573 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1574 &filter_model, NULL, &model))
1577 /* Insert the account in the model */
1578 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1580 /* When the model is a list store (plain representation) the
1581 outbox is not a child of any account so we have to manually
1582 delete it because removing the local folders account won't
1583 delete it (because tny_folder_get_account() is not defined
1584 for a merge folder */
1585 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1586 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1588 priv->outbox_deleted_handler =
1589 g_signal_connect (account,
1591 G_CALLBACK (on_outbox_deleted_cb),
1595 /* Refilter the model */
1596 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1601 same_account_selected (ModestFolderView *self,
1602 TnyAccount *account)
1604 ModestFolderViewPrivate *priv;
1605 gboolean same_account = FALSE;
1607 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1609 if (priv->cur_folder_store) {
1610 TnyAccount *selected_folder_account = NULL;
1612 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1613 selected_folder_account =
1614 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1616 selected_folder_account =
1617 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1620 if (selected_folder_account == account)
1621 same_account = TRUE;
1623 g_object_unref (selected_folder_account);
1625 return same_account;
1628 #ifndef MODEST_TOOLKIT_HILDON2
1631 * Selects the first inbox or the local account in an idle
1634 on_idle_select_first_inbox_or_local (gpointer user_data)
1636 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1638 gdk_threads_enter ();
1639 modest_folder_view_select_first_inbox_or_local (self);
1640 gdk_threads_leave ();
1647 on_account_changed (TnyAccountStore *account_store,
1648 TnyAccount *tny_account,
1651 ModestFolderView *self;
1652 ModestFolderViewPrivate *priv;
1653 GtkTreeModel *model, *filter_model;
1654 GtkTreeSelection *sel;
1655 gboolean same_account;
1657 /* Ignore transport account insertions, we're not showing them
1658 in the folder view */
1659 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1662 self = MODEST_FOLDER_VIEW (user_data);
1663 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1665 /* Get the inner model */
1666 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1667 &filter_model, NULL, &model))
1670 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1672 /* Invalidate the cur_folder_store only if the selected folder
1673 belongs to the account that is being removed */
1674 same_account = same_account_selected (self, tny_account);
1676 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1677 gtk_tree_selection_unselect_all (sel);
1680 /* Remove the account from the model */
1681 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1683 /* Insert the account in the model */
1684 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1686 /* Refilter the model */
1687 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1689 #ifndef MODEST_TOOLKIT_HILDON2
1690 /* Select the first INBOX if the currently selected folder
1691 belongs to the account that is being deleted */
1692 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1693 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 #ifndef MODEST_TOOLKIT_HILDON2
1777 /* Select the first INBOX if the currently selected folder
1778 belongs to the account that is being deleted */
1780 g_idle_add (on_idle_select_first_inbox_or_local, self);
1785 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1787 GtkTreeViewColumn *col;
1789 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1791 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1793 g_printerr ("modest: failed get column for title\n");
1797 gtk_tree_view_column_set_title (col, title);
1798 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1803 modest_folder_view_on_map (ModestFolderView *self,
1804 GdkEventExpose *event,
1807 ModestFolderViewPrivate *priv;
1809 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1811 /* This won't happen often */
1812 if (G_UNLIKELY (priv->reselect)) {
1813 /* Select the first inbox or the local account if not found */
1815 /* TODO: this could cause a lock at startup, so we
1816 comment it for the moment. We know that this will
1817 be a bug, because the INBOX is not selected, but we
1818 need to rewrite some parts of Modest to avoid the
1819 deathlock situation */
1820 /* TODO: check if this is still the case */
1821 priv->reselect = FALSE;
1822 #ifndef MODEST_TOOLKIT_HILDON2
1823 modest_folder_view_select_first_inbox_or_local (self);
1825 /* Notify the display name observers */
1826 g_signal_emit (G_OBJECT(self),
1827 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1831 if (priv->reexpand) {
1832 expand_root_items (self);
1833 priv->reexpand = FALSE;
1840 modest_folder_view_new (TnyFolderStoreQuery *query)
1842 return modest_folder_view_new_full (query, TRUE);
1846 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1849 ModestFolderViewPrivate *priv;
1850 GtkTreeSelection *sel;
1852 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1853 #ifdef MODEST_TOOLKIT_HILDON2
1854 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1857 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1860 priv->query = g_object_ref (query);
1862 priv->do_refresh = do_refresh;
1864 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1865 priv->changed_signal = g_signal_connect (sel, "changed",
1866 G_CALLBACK (on_selection_changed), self);
1868 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1870 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1872 return GTK_WIDGET(self);
1875 /* this feels dirty; any other way to expand all the root items? */
1877 expand_root_items (ModestFolderView *self)
1880 GtkTreeModel *model;
1883 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1884 path = gtk_tree_path_new_first ();
1886 /* all folders should have child items, so.. */
1888 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1889 gtk_tree_path_next (path);
1890 } while (gtk_tree_model_get_iter (model, &iter, path));
1892 gtk_tree_path_free (path);
1896 is_parent_of (TnyFolder *a, TnyFolder *b)
1899 gboolean retval = FALSE;
1901 a_id = tny_folder_get_id (a);
1903 gchar *string_to_match;
1906 string_to_match = g_strconcat (a_id, "/", NULL);
1907 b_id = tny_folder_get_id (b);
1908 retval = g_str_has_prefix (b_id, string_to_match);
1909 g_free (string_to_match);
1915 typedef struct _ForeachFolderInfo {
1918 } ForeachFolderInfo;
1921 foreach_folder_with_id (GtkTreeModel *model,
1926 ForeachFolderInfo *info;
1929 info = (ForeachFolderInfo *) data;
1930 gtk_tree_model_get (model, iter,
1931 INSTANCE_COLUMN, &instance,
1934 if (TNY_IS_FOLDER (instance)) {
1937 id = tny_folder_get_id (TNY_FOLDER (instance));
1939 collate = g_utf8_collate_key (id, -1);
1940 info->found = !strcmp (info->needle, collate);
1946 g_object_unref (instance);
1954 has_folder_with_id (ModestFolderView *self, const gchar *id)
1956 GtkTreeModel *model;
1957 ForeachFolderInfo info = {NULL, FALSE};
1959 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1960 info.needle = g_utf8_collate_key (id, -1);
1962 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1963 g_free (info.needle);
1969 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1972 gboolean retval = FALSE;
1974 a_id = tny_folder_get_id (a);
1977 b_id = tny_folder_get_id (b);
1980 const gchar *last_bar;
1981 gchar *string_to_match;
1982 last_bar = g_strrstr (b_id, "/");
1987 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1988 retval = has_folder_with_id (self, string_to_match);
1989 g_free (string_to_match);
1997 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1999 ModestFolderViewPrivate *priv;
2000 TnyIterator *iterator;
2001 gboolean retval = TRUE;
2003 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2004 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2006 for (iterator = tny_list_create_iterator (priv->list_to_move);
2007 retval && !tny_iterator_is_done (iterator);
2008 tny_iterator_next (iterator)) {
2010 instance = tny_iterator_get_current (iterator);
2011 if (instance == (GObject *) folder) {
2013 } else if (TNY_IS_FOLDER (instance)) {
2014 retval = !is_parent_of (TNY_FOLDER (instance), folder);
2016 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
2019 g_object_unref (instance);
2021 g_object_unref (iterator);
2028 * We use this function to implement the
2029 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
2030 * account in this case, and the local folders.
2033 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2035 ModestFolderViewPrivate *priv;
2036 gboolean retval = TRUE;
2037 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2038 GObject *instance = NULL;
2039 const gchar *id = NULL;
2041 gboolean found = FALSE;
2042 gboolean cleared = FALSE;
2043 ModestTnyFolderRules rules = 0;
2046 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2047 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2049 gtk_tree_model_get (model, iter,
2050 NAME_COLUMN, &fname,
2052 INSTANCE_COLUMN, &instance,
2055 /* Do not show if there is no instance, this could indeed
2056 happen when the model is being modified while it's being
2057 drawn. This could occur for example when moving folders
2064 if (TNY_IS_ACCOUNT (instance)) {
2065 TnyAccount *acc = TNY_ACCOUNT (instance);
2066 const gchar *account_id = tny_account_get_id (acc);
2068 /* If it isn't a special folder,
2069 * don't show it unless it is the visible account: */
2070 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2071 !modest_tny_account_is_virtual_local_folders (acc) &&
2072 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2074 /* Show only the visible account id */
2075 if (priv->visible_account_id) {
2076 if (strcmp (account_id, priv->visible_account_id))
2083 /* Never show these to the user. They are merged into one folder
2084 * in the local-folders account instead: */
2085 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2088 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2089 /* Only show special folders for current account if needed */
2090 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2091 TnyAccount *account;
2093 account = tny_folder_get_account (TNY_FOLDER (instance));
2095 if (TNY_IS_ACCOUNT (account)) {
2096 const gchar *account_id = tny_account_get_id (account);
2098 if (!modest_tny_account_is_virtual_local_folders (account) &&
2099 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2100 /* Show only the visible account id */
2101 if (priv->visible_account_id) {
2102 if (strcmp (account_id, priv->visible_account_id)) {
2104 } else if (priv->mailbox) {
2105 /* Filter mailboxes */
2106 if (!g_str_has_prefix (fname, priv->mailbox)) {
2108 } else if (!strcmp (fname, priv->mailbox)) {
2109 /* Hide mailbox parent */
2115 g_object_unref (account);
2122 /* Check hiding (if necessary) */
2123 cleared = modest_email_clipboard_cleared (priv->clipboard);
2124 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2125 id = tny_folder_get_id (TNY_FOLDER(instance));
2126 if (priv->hidding_ids != NULL)
2127 for (i=0; i < priv->n_selected && !found; i++)
2128 if (priv->hidding_ids[i] != NULL && id != NULL)
2129 found = (!strcmp (priv->hidding_ids[i], id));
2134 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2135 folder as no message can be move there according to UI specs */
2136 if (retval && !priv->show_non_move) {
2137 if (priv->list_to_move &&
2138 tny_list_get_length (priv->list_to_move) > 0 &&
2139 TNY_IS_FOLDER (instance)) {
2140 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2142 if (retval && TNY_IS_FOLDER (instance) &&
2143 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2145 case TNY_FOLDER_TYPE_OUTBOX:
2146 case TNY_FOLDER_TYPE_SENT:
2147 case TNY_FOLDER_TYPE_DRAFTS:
2150 case TNY_FOLDER_TYPE_UNKNOWN:
2151 case TNY_FOLDER_TYPE_NORMAL:
2152 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2153 if (type == TNY_FOLDER_TYPE_INVALID)
2154 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2156 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2157 type == TNY_FOLDER_TYPE_SENT
2158 || type == TNY_FOLDER_TYPE_DRAFTS)
2165 if (retval && TNY_IS_ACCOUNT (instance) &&
2166 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2167 ModestProtocolType protocol_type;
2169 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2170 retval = !modest_protocol_registry_protocol_type_has_tag
2171 (modest_runtime_get_protocol_registry (),
2173 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2177 /* apply special filters */
2178 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2179 if (TNY_IS_ACCOUNT (instance))
2183 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2184 if (TNY_IS_FOLDER (instance))
2188 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2189 if (TNY_IS_ACCOUNT (instance)) {
2190 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2192 } else if (TNY_IS_FOLDER (instance)) {
2193 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2198 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2199 if (TNY_IS_ACCOUNT (instance)) {
2200 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2202 } else if (TNY_IS_FOLDER (instance)) {
2203 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2208 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2209 /* A mailbox is a fake folder with an @ in the middle of the name */
2210 if (!TNY_IS_FOLDER (instance) ||
2211 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2214 const gchar *folder_name;
2215 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2216 if (!folder_name || strchr (folder_name, '@') == NULL)
2222 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2223 if (TNY_IS_FOLDER (instance)) {
2224 /* Check folder rules */
2225 ModestTnyFolderRules rules;
2227 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2228 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2229 } else if (TNY_IS_ACCOUNT (instance)) {
2230 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2238 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2239 if (TNY_IS_FOLDER (instance)) {
2240 TnyFolderType guess_type;
2242 if (TNY_FOLDER_TYPE_NORMAL) {
2243 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2249 case TNY_FOLDER_TYPE_OUTBOX:
2250 case TNY_FOLDER_TYPE_SENT:
2251 case TNY_FOLDER_TYPE_DRAFTS:
2252 case TNY_FOLDER_TYPE_ARCHIVE:
2253 case TNY_FOLDER_TYPE_INBOX:
2256 case TNY_FOLDER_TYPE_UNKNOWN:
2257 case TNY_FOLDER_TYPE_NORMAL:
2263 } else if (TNY_IS_ACCOUNT (instance)) {
2268 if (retval && TNY_IS_FOLDER (instance)) {
2269 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2272 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2273 if (TNY_IS_FOLDER (instance)) {
2274 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2275 } else if (TNY_IS_ACCOUNT (instance)) {
2280 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2281 if (TNY_IS_FOLDER (instance)) {
2282 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2283 } else if (TNY_IS_ACCOUNT (instance)) {
2288 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2289 if (TNY_IS_FOLDER (instance)) {
2290 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2291 } else if (TNY_IS_ACCOUNT (instance)) {
2297 g_object_unref (instance);
2305 modest_folder_view_update_model (ModestFolderView *self,
2306 TnyAccountStore *account_store)
2308 ModestFolderViewPrivate *priv;
2309 GtkTreeModel *model;
2310 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2312 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2313 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2316 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2318 /* Notify that there is no folder selected */
2319 g_signal_emit (G_OBJECT(self),
2320 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2322 if (priv->cur_folder_store) {
2323 g_object_unref (priv->cur_folder_store);
2324 priv->cur_folder_store = NULL;
2327 /* FIXME: the local accounts are not shown when the query
2328 selects only the subscribed folders */
2329 #ifdef MODEST_TOOLKIT_HILDON2
2330 TnyGtkFolderListStoreFlags flags;
2331 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2332 if (priv->do_refresh)
2333 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2335 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2336 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2338 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2339 MODEST_FOLDER_PATH_SEPARATOR);
2341 model = tny_gtk_folder_store_tree_model_new (NULL);
2344 /* When the model is a list store (plain representation) the
2345 outbox is not a child of any account so we have to manually
2346 delete it because removing the local folders account won't
2347 delete it (because tny_folder_get_account() is not defined
2348 for a merge folder */
2349 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2350 TnyAccount *account;
2351 ModestTnyAccountStore *acc_store;
2353 acc_store = modest_runtime_get_account_store ();
2354 account = modest_tny_account_store_get_local_folders_account (acc_store);
2356 if (g_signal_handler_is_connected (account,
2357 priv->outbox_deleted_handler))
2358 g_signal_handler_disconnect (account,
2359 priv->outbox_deleted_handler);
2361 priv->outbox_deleted_handler =
2362 g_signal_connect (account,
2364 G_CALLBACK (on_outbox_deleted_cb),
2366 g_object_unref (account);
2369 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2370 /* Get the accounts */
2371 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2373 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2375 if (priv->visible_account_id) {
2376 TnyAccount *account;
2378 /* Add local folders account */
2379 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2382 tny_list_append (TNY_LIST (model), (GObject *) account);
2383 g_object_unref (account);
2386 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2389 tny_list_append (TNY_LIST (model), (GObject *) account);
2390 g_object_unref (account);
2393 /* Add visible account */
2394 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2395 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2396 priv->visible_account_id);
2398 tny_list_append (TNY_LIST (model), (GObject *) account);
2399 g_object_unref (account);
2401 g_warning ("You need to set an account first");
2402 g_object_unref (model);
2406 g_warning ("You need to set an account first");
2407 g_object_unref (model);
2412 sortable = gtk_tree_model_sort_new_with_model (model);
2413 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2415 GTK_SORT_ASCENDING);
2416 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2418 cmp_rows, NULL, NULL);
2420 /* Create filter model */
2421 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2422 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2427 GtkTreeModel *old_tny_model = NULL;
2428 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2429 if (priv->signal_handlers > 0) {
2430 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2431 G_OBJECT (old_tny_model),
2432 "activity-changed");
2437 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2438 #ifndef MODEST_TOOLKIT_HILDON2
2439 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2440 (GCallback) on_row_inserted_maybe_select_folder, self);
2443 #ifdef MODEST_TOOLKIT_HILDON2
2444 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2447 G_CALLBACK (on_activity_changed),
2451 g_object_unref (model);
2452 g_object_unref (filter_model);
2453 g_object_unref (sortable);
2455 /* Force a reselection of the INBOX next time the widget is shown */
2456 priv->reselect = TRUE;
2463 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2465 GtkTreeModel *model = NULL;
2466 TnyFolderStore *folder = NULL;
2468 ModestFolderView *tree_view = NULL;
2469 ModestFolderViewPrivate *priv = NULL;
2470 gboolean selected = FALSE;
2472 g_return_if_fail (sel);
2473 g_return_if_fail (user_data);
2475 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2477 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2479 tree_view = MODEST_FOLDER_VIEW (user_data);
2482 gtk_tree_model_get (model, &iter,
2483 INSTANCE_COLUMN, &folder,
2486 /* If the folder is the same do not notify */
2487 if (folder && priv->cur_folder_store == folder) {
2488 g_object_unref (folder);
2493 /* Current folder was unselected */
2494 if (priv->cur_folder_store) {
2495 /* We must do this firstly because a libtinymail-camel
2496 implementation detail. If we issue the signal
2497 before doing the sync_async, then that signal could
2498 cause (and it actually does it) a free of the
2499 summary of the folder (because the main window will
2500 clear the headers view */
2501 #ifndef MODEST_TOOLKIT_HILDON2
2502 if (TNY_IS_FOLDER(priv->cur_folder_store))
2503 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2504 FALSE, NULL, NULL, NULL);
2507 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2508 priv->cur_folder_store, FALSE);
2510 g_object_unref (priv->cur_folder_store);
2511 priv->cur_folder_store = NULL;
2514 /* New current references */
2515 priv->cur_folder_store = folder;
2517 /* New folder has been selected. Do not notify if there is
2518 nothing new selected */
2520 g_signal_emit (G_OBJECT(tree_view),
2521 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2522 0, priv->cur_folder_store, TRUE);
2527 on_row_activated (GtkTreeView *treeview,
2528 GtkTreePath *treepath,
2529 GtkTreeViewColumn *column,
2532 GtkTreeModel *model = NULL;
2533 TnyFolderStore *folder = NULL;
2535 ModestFolderView *self = NULL;
2536 ModestFolderViewPrivate *priv = NULL;
2538 g_return_if_fail (treeview);
2539 g_return_if_fail (user_data);
2541 self = MODEST_FOLDER_VIEW (user_data);
2542 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2544 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2546 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2549 gtk_tree_model_get (model, &iter,
2550 INSTANCE_COLUMN, &folder,
2553 g_signal_emit (G_OBJECT(self),
2554 signals[FOLDER_ACTIVATED_SIGNAL],
2557 #ifdef MODEST_TOOLKIT_HILDON2
2558 HildonUIMode ui_mode;
2559 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2560 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2561 if (priv->cur_folder_store)
2562 g_object_unref (priv->cur_folder_store);
2563 priv->cur_folder_store = g_object_ref (folder);
2567 g_object_unref (folder);
2571 modest_folder_view_get_selected (ModestFolderView *self)
2573 ModestFolderViewPrivate *priv;
2575 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2577 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2578 if (priv->cur_folder_store)
2579 g_object_ref (priv->cur_folder_store);
2581 return priv->cur_folder_store;
2585 get_cmp_rows_type_pos (GObject *folder)
2587 /* Remote accounts -> Local account -> MMC account .*/
2590 if (TNY_IS_ACCOUNT (folder) &&
2591 modest_tny_account_is_virtual_local_folders (
2592 TNY_ACCOUNT (folder))) {
2594 } else if (TNY_IS_ACCOUNT (folder)) {
2595 TnyAccount *account = TNY_ACCOUNT (folder);
2596 const gchar *account_id = tny_account_get_id (account);
2597 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2603 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2604 return -1; /* Should never happen */
2609 inbox_is_special (TnyFolderStore *folder_store)
2611 gboolean is_special = TRUE;
2613 if (TNY_IS_FOLDER (folder_store)) {
2617 gchar *last_inbox_bar;
2619 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2620 downcase = g_utf8_strdown (id, -1);
2621 last_bar = g_strrstr (downcase, "/");
2623 last_inbox_bar = g_strrstr (downcase, "inbox/");
2624 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2635 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2637 TnyAccount *account;
2638 gboolean is_special;
2639 /* Inbox, Outbox, Drafts, Sent, User */
2642 if (!TNY_IS_FOLDER (folder_store))
2645 case TNY_FOLDER_TYPE_INBOX:
2647 account = tny_folder_get_account (folder_store);
2648 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2650 /* In inbox case we need to know if the inbox is really the top
2651 * inbox of the account, or if it's a submailbox inbox. To do
2652 * this we'll apply an heuristic rule: Find last "/" and check
2653 * if it's preceeded by another Inbox */
2654 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2655 g_object_unref (account);
2656 return is_special?0:4;
2659 case TNY_FOLDER_TYPE_OUTBOX:
2660 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2662 case TNY_FOLDER_TYPE_DRAFTS:
2664 account = tny_folder_get_account (folder_store);
2665 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2666 g_object_unref (account);
2667 return is_special?1:4;
2670 case TNY_FOLDER_TYPE_SENT:
2672 account = tny_folder_get_account (folder_store);
2673 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2674 g_object_unref (account);
2675 return is_special?3:4;
2684 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2686 const gchar *a1_name, *a2_name;
2688 a1_name = tny_account_get_name (a1);
2689 a2_name = tny_account_get_name (a2);
2691 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2695 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2697 TnyAccount *a1 = NULL, *a2 = NULL;
2700 if (TNY_IS_ACCOUNT (s1)) {
2701 a1 = TNY_ACCOUNT (g_object_ref (s1));
2702 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2703 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2706 if (TNY_IS_ACCOUNT (s2)) {
2707 a2 = TNY_ACCOUNT (g_object_ref (s2));
2708 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2709 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2726 /* First we sort with the type of account */
2727 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2731 cmp = compare_account_names (a1, a2);
2735 g_object_unref (a1);
2737 g_object_unref (a2);
2743 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2745 gint is_account1, is_account2;
2747 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2748 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2750 return is_account2 - is_account1;
2754 compare_folders (const gchar *name1, const gchar *name2)
2756 const gchar *separator1, *separator2;
2757 const gchar *next1, *next2;
2761 if (name1 == NULL || name1[0] == '\0')
2763 if (name2 == NULL || name2[0] == '\0')
2766 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2768 top1 = g_strndup (name1, separator1 - name1);
2770 top1 = g_strdup (name1);
2773 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2775 top2 = g_strndup (name2, separator2 - name2);
2777 top2 = g_strdup (name2);
2781 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2788 if (separator1 == NULL && separator2 == NULL)
2791 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2792 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2794 return compare_folders (next1, next2);
2799 * This function orders the mail accounts according to these rules:
2800 * 1st - remote accounts
2801 * 2nd - local account
2805 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2809 gchar *name1 = NULL;
2810 gchar *name2 = NULL;
2811 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2812 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2813 GObject *folder1 = NULL;
2814 GObject *folder2 = NULL;
2816 gtk_tree_model_get (tree_model, iter1,
2817 NAME_COLUMN, &name1,
2819 INSTANCE_COLUMN, &folder1,
2821 gtk_tree_model_get (tree_model, iter2,
2822 NAME_COLUMN, &name2,
2823 TYPE_COLUMN, &type2,
2824 INSTANCE_COLUMN, &folder2,
2827 /* Return if we get no folder. This could happen when folder
2828 operations are happening. The model is updated after the
2829 folder copy/move actually occurs, so there could be
2830 situations where the model to be drawn is not correct */
2831 if (!folder1 || !folder2)
2834 /* Sort by type. First the special folders, then the archives */
2835 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2839 /* Now we sort using the account of each folder */
2840 if (TNY_IS_FOLDER_STORE (folder1) &&
2841 TNY_IS_FOLDER_STORE (folder2)) {
2842 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2846 /* Each group is preceeded by its account */
2847 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2852 /* Pure sort by name */
2853 cmp = compare_folders (name1, name2);
2856 g_object_unref(G_OBJECT(folder1));
2858 g_object_unref(G_OBJECT(folder2));
2866 #ifndef MODEST_TOOLKIT_HILDON2
2867 /*****************************************************************************/
2868 /* DRAG and DROP stuff */
2869 /*****************************************************************************/
2871 * This function fills the #GtkSelectionData with the row and the
2872 * model that has been dragged. It's called when this widget is a
2873 * source for dnd after the event drop happened
2876 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2877 guint info, guint time, gpointer data)
2879 GtkTreeSelection *selection;
2880 GtkTreeModel *model;
2882 GtkTreePath *source_row;
2884 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2885 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2887 source_row = gtk_tree_model_get_path (model, &iter);
2888 gtk_tree_set_row_drag_data (selection_data,
2892 gtk_tree_path_free (source_row);
2896 typedef struct _DndHelper {
2897 ModestFolderView *folder_view;
2898 gboolean delete_source;
2899 GtkTreePath *source_row;
2903 dnd_helper_destroyer (DndHelper *helper)
2905 /* Free the helper */
2906 gtk_tree_path_free (helper->source_row);
2907 g_slice_free (DndHelper, helper);
2911 xfer_folder_cb (ModestMailOperation *mail_op,
2912 TnyFolder *new_folder,
2916 /* Select the folder */
2917 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2923 /* get the folder for the row the treepath refers to. */
2924 /* folder must be unref'd */
2925 static TnyFolderStore *
2926 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2929 TnyFolderStore *folder = NULL;
2931 if (gtk_tree_model_get_iter (model,&iter, path))
2932 gtk_tree_model_get (model, &iter,
2933 INSTANCE_COLUMN, &folder,
2939 * This function is used by drag_data_received_cb to manage drag and
2940 * drop of a header, i.e, and drag from the header view to the folder
2944 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2945 GtkTreeModel *dest_model,
2946 GtkTreePath *dest_row,
2947 GtkSelectionData *selection_data)
2949 TnyList *headers = NULL;
2950 TnyFolder *folder = NULL, *src_folder = NULL;
2951 TnyFolderType folder_type;
2952 GtkTreeIter source_iter, dest_iter;
2953 ModestWindowMgr *mgr = NULL;
2954 ModestWindow *main_win = NULL;
2955 gchar **uris, **tmp;
2957 /* Build the list of headers */
2958 mgr = modest_runtime_get_window_mgr ();
2959 headers = tny_simple_list_new ();
2960 uris = modest_dnd_selection_data_get_paths (selection_data);
2963 while (*tmp != NULL) {
2966 gboolean first = TRUE;
2969 path = gtk_tree_path_new_from_string (*tmp);
2970 gtk_tree_model_get_iter (source_model, &source_iter, path);
2971 gtk_tree_model_get (source_model, &source_iter,
2972 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2975 /* Do not enable d&d of headers already opened */
2976 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2977 tny_list_append (headers, G_OBJECT (header));
2979 if (G_UNLIKELY (first)) {
2980 src_folder = tny_header_get_folder (header);
2984 /* Free and go on */
2985 gtk_tree_path_free (path);
2986 g_object_unref (header);
2991 /* This could happen ig we perform a d&d very quickly over the
2992 same row that row could dissapear because message is
2994 if (!TNY_IS_FOLDER (src_folder))
2997 /* Get the target folder */
2998 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2999 gtk_tree_model_get (dest_model, &dest_iter,
3003 if (!folder || !TNY_IS_FOLDER(folder)) {
3004 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
3008 folder_type = modest_tny_folder_guess_folder_type (folder);
3009 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
3010 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
3011 goto cleanup; /* cannot move messages there */
3014 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3015 /* g_warning ("folder not writable"); */
3016 goto cleanup; /* verboten! */
3019 /* Ask for confirmation to move */
3020 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
3022 g_warning ("%s: BUG: no main window found", __FUNCTION__);
3026 /* Transfer messages */
3027 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
3032 if (G_IS_OBJECT (src_folder))
3033 g_object_unref (src_folder);
3034 if (G_IS_OBJECT(folder))
3035 g_object_unref (G_OBJECT (folder));
3036 if (G_IS_OBJECT(headers))
3037 g_object_unref (headers);
3041 TnyFolderStore *src_folder;
3042 TnyFolderStore *dst_folder;
3043 ModestFolderView *folder_view;
3048 dnd_folder_info_destroyer (DndFolderInfo *info)
3050 if (info->src_folder)
3051 g_object_unref (info->src_folder);
3052 if (info->dst_folder)
3053 g_object_unref (info->dst_folder);
3054 g_slice_free (DndFolderInfo, info);
3058 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
3059 GtkWindow *parent_window,
3060 TnyAccount *account)
3063 modest_ui_actions_on_account_connection_error (parent_window, account);
3065 /* Free the helper & info */
3066 dnd_helper_destroyer (info->helper);
3067 dnd_folder_info_destroyer (info);
3071 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
3073 GtkWindow *parent_window,
3074 TnyAccount *account,
3077 DndFolderInfo *info = NULL;
3078 ModestMailOperation *mail_op;
3080 info = (DndFolderInfo *) user_data;
3082 if (err || canceled) {
3083 dnd_on_connection_failed_destroyer (info, parent_window, account);
3087 /* Do the mail operation */
3088 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
3089 modest_ui_actions_move_folder_error_handler,
3090 info->src_folder, NULL);
3092 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3095 /* Transfer the folder */
3096 modest_mail_operation_xfer_folder (mail_op,
3097 TNY_FOLDER (info->src_folder),
3099 info->helper->delete_source,
3101 info->helper->folder_view);
3104 g_object_unref (G_OBJECT (mail_op));
3105 dnd_helper_destroyer (info->helper);
3106 dnd_folder_info_destroyer (info);
3111 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3113 GtkWindow *parent_window,
3114 TnyAccount *account,
3117 DndFolderInfo *info = NULL;
3119 info = (DndFolderInfo *) user_data;
3121 if (err || canceled) {
3122 dnd_on_connection_failed_destroyer (info, parent_window, account);
3126 /* Connect to source folder and perform the copy/move */
3127 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3129 drag_and_drop_from_folder_view_src_folder_performer,
3134 * This function is used by drag_data_received_cb to manage drag and
3135 * drop of a folder, i.e, and drag from the folder view to the same
3139 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3140 GtkTreeModel *dest_model,
3141 GtkTreePath *dest_row,
3142 GtkSelectionData *selection_data,
3145 GtkTreeIter dest_iter, iter;
3146 TnyFolderStore *dest_folder = NULL;
3147 TnyFolderStore *folder = NULL;
3148 gboolean forbidden = FALSE;
3150 DndFolderInfo *info = NULL;
3152 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3154 g_warning ("%s: BUG: no main window", __FUNCTION__);
3155 dnd_helper_destroyer (helper);
3160 /* check the folder rules for the destination */
3161 folder = tree_path_to_folder (dest_model, dest_row);
3162 if (TNY_IS_FOLDER(folder)) {
3163 ModestTnyFolderRules rules =
3164 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3165 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3166 } else if (TNY_IS_FOLDER_STORE(folder)) {
3167 /* enable local root as destination for folders */
3168 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3169 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3172 g_object_unref (folder);
3175 /* check the folder rules for the source */
3176 folder = tree_path_to_folder (source_model, helper->source_row);
3177 if (TNY_IS_FOLDER(folder)) {
3178 ModestTnyFolderRules rules =
3179 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3180 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3183 g_object_unref (folder);
3187 /* Check if the drag is possible */
3188 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3190 modest_platform_run_information_dialog ((GtkWindow *) win,
3191 _("mail_in_ui_folder_move_target_error"),
3193 /* Restore the previous selection */
3194 folder = tree_path_to_folder (source_model, helper->source_row);
3196 if (TNY_IS_FOLDER (folder))
3197 modest_folder_view_select_folder (helper->folder_view,
3198 TNY_FOLDER (folder), FALSE);
3199 g_object_unref (folder);
3201 dnd_helper_destroyer (helper);
3206 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3207 gtk_tree_model_get (dest_model, &dest_iter,
3210 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3211 gtk_tree_model_get (source_model, &iter,
3215 /* Create the info for the performer */
3216 info = g_slice_new0 (DndFolderInfo);
3217 info->src_folder = g_object_ref (folder);
3218 info->dst_folder = g_object_ref (dest_folder);
3219 info->helper = helper;
3221 /* Connect to the destination folder and perform the copy/move */
3222 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3224 drag_and_drop_from_folder_view_dst_folder_performer,
3228 g_object_unref (dest_folder);
3229 g_object_unref (folder);
3233 * This function receives the data set by the "drag-data-get" signal
3234 * handler. This information comes within the #GtkSelectionData. This
3235 * function will manage both the drags of folders of the treeview and
3236 * drags of headers of the header view widget.
3239 on_drag_data_received (GtkWidget *widget,
3240 GdkDragContext *context,
3243 GtkSelectionData *selection_data,
3248 GtkWidget *source_widget;
3249 GtkTreeModel *dest_model, *source_model;
3250 GtkTreePath *source_row, *dest_row;
3251 GtkTreeViewDropPosition pos;
3252 gboolean delete_source = FALSE;
3253 gboolean success = FALSE;
3255 /* Do not allow further process */
3256 g_signal_stop_emission_by_name (widget, "drag-data-received");
3257 source_widget = gtk_drag_get_source_widget (context);
3259 /* Get the action */
3260 if (context->action == GDK_ACTION_MOVE) {
3261 delete_source = TRUE;
3263 /* Notify that there is no folder selected. We need to
3264 do this in order to update the headers view (and
3265 its monitors, because when moving, the old folder
3266 won't longer exist. We can not wait for the end of
3267 the operation, because the operation won't start if
3268 the folder is in use */
3269 if (source_widget == widget) {
3270 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3271 gtk_tree_selection_unselect_all (sel);
3275 /* Check if the get_data failed */
3276 #if GTK_CHECK_VERSION (2,14,0)
3277 if ((selection_data == NULL) || (gtk_selection_data_get_length (selection_data) < 0))
3279 if (selection_data == NULL || selection_data->length < 0)
3283 /* Select the destination model */
3284 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3286 /* Get the path to the destination row. Can not call
3287 gtk_tree_view_get_drag_dest_row() because the source row
3288 is not selected anymore */
3289 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3292 /* Only allow drops IN other rows */
3294 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3295 pos == GTK_TREE_VIEW_DROP_AFTER)
3299 /* Drags from the header view */
3300 if (source_widget != widget) {
3301 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3303 drag_and_drop_from_header_view (source_model,
3308 DndHelper *helper = NULL;
3310 /* Get the source model and row */
3311 gtk_tree_get_row_drag_data (selection_data,
3315 /* Create the helper */
3316 helper = g_slice_new0 (DndHelper);
3317 helper->delete_source = delete_source;
3318 helper->source_row = gtk_tree_path_copy (source_row);
3319 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3321 drag_and_drop_from_folder_view (source_model,
3327 gtk_tree_path_free (source_row);
3331 gtk_tree_path_free (dest_row);
3334 /* Finish the drag and drop */
3335 gtk_drag_finish (context, success, FALSE, time);
3339 * We define a "drag-drop" signal handler because we do not want to
3340 * use the default one, because the default one always calls
3341 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3342 * signal handler, because there we have all the information available
3343 * to know if the dnd was a success or not.
3346 drag_drop_cb (GtkWidget *widget,
3347 GdkDragContext *context,
3355 if (!context->targets)
3358 /* Check if we're dragging a folder row */
3359 target = gtk_drag_dest_find_target (widget, context, NULL);
3361 /* Request the data from the source. */
3362 gtk_drag_get_data(widget, context, target, time);
3368 * This function expands a node of a tree view if it's not expanded
3369 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3370 * does that, so that's why they're here.
3373 expand_row_timeout (gpointer data)
3375 GtkTreeView *tree_view = data;
3376 GtkTreePath *dest_path = NULL;
3377 GtkTreeViewDropPosition pos;
3378 gboolean result = FALSE;
3380 gdk_threads_enter ();
3382 gtk_tree_view_get_drag_dest_row (tree_view,
3387 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3388 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3389 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3390 gtk_tree_path_free (dest_path);
3394 gtk_tree_path_free (dest_path);
3399 gdk_threads_leave ();
3405 * This function is called whenever the pointer is moved over a widget
3406 * while dragging some data. It installs a timeout that will expand a
3407 * node of the treeview if not expanded yet. This function also calls
3408 * gdk_drag_status in order to set the suggested action that will be
3409 * used by the "drag-data-received" signal handler to know if we
3410 * should do a move or just a copy of the data.
3413 on_drag_motion (GtkWidget *widget,
3414 GdkDragContext *context,
3420 GtkTreeViewDropPosition pos;
3421 GtkTreePath *dest_row;
3422 GtkTreeModel *dest_model;
3423 ModestFolderViewPrivate *priv;
3424 GdkDragAction suggested_action;
3425 gboolean valid_location = FALSE;
3426 TnyFolderStore *folder = NULL;
3428 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3430 if (priv->timer_expander != 0) {
3431 g_source_remove (priv->timer_expander);
3432 priv->timer_expander = 0;
3435 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3440 /* Do not allow drops between folders */
3442 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3443 pos == GTK_TREE_VIEW_DROP_AFTER) {
3444 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3445 gdk_drag_status(context, 0, time);
3446 valid_location = FALSE;
3449 valid_location = TRUE;
3452 /* Check that the destination folder is writable */
3453 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3454 folder = tree_path_to_folder (dest_model, dest_row);
3455 if (folder && TNY_IS_FOLDER (folder)) {
3456 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3458 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3459 valid_location = FALSE;
3464 /* Expand the selected row after 1/2 second */
3465 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3466 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3468 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3470 /* Select the desired action. By default we pick MOVE */
3471 suggested_action = GDK_ACTION_MOVE;
3473 if (context->actions == GDK_ACTION_COPY)
3474 gdk_drag_status(context, GDK_ACTION_COPY, time);
3475 else if (context->actions == GDK_ACTION_MOVE)
3476 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3477 else if (context->actions & suggested_action)
3478 gdk_drag_status(context, suggested_action, time);
3480 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3484 g_object_unref (folder);
3486 gtk_tree_path_free (dest_row);
3488 g_signal_stop_emission_by_name (widget, "drag-motion");
3490 return valid_location;
3494 * This function sets the treeview as a source and a target for dnd
3495 * events. It also connects all the requirede signals.
3498 setup_drag_and_drop (GtkTreeView *self)
3500 /* Set up the folder view as a dnd destination. Set only the
3501 highlight flag, otherwise gtk will have a different
3503 gtk_drag_dest_set (GTK_WIDGET (self),
3504 GTK_DEST_DEFAULT_HIGHLIGHT,
3505 folder_view_drag_types,
3506 G_N_ELEMENTS (folder_view_drag_types),
3507 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3509 g_signal_connect (G_OBJECT (self),
3510 "drag_data_received",
3511 G_CALLBACK (on_drag_data_received),
3515 /* Set up the treeview as a dnd source */
3516 gtk_drag_source_set (GTK_WIDGET (self),
3518 folder_view_drag_types,
3519 G_N_ELEMENTS (folder_view_drag_types),
3520 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3522 g_signal_connect (G_OBJECT (self),
3524 G_CALLBACK (on_drag_motion),
3527 g_signal_connect (G_OBJECT (self),
3529 G_CALLBACK (on_drag_data_get),
3532 g_signal_connect (G_OBJECT (self),
3534 G_CALLBACK (drag_drop_cb),
3540 * This function manages the navigation through the folders using the
3541 * keyboard or the hardware keys in the device
3544 on_key_pressed (GtkWidget *self,
3548 GtkTreeSelection *selection;
3550 GtkTreeModel *model;
3551 gboolean retval = FALSE;
3553 /* Up and Down are automatically managed by the treeview */
3554 if (event->keyval == GDK_Return) {
3555 /* Expand/Collapse the selected row */
3556 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3557 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3560 path = gtk_tree_model_get_path (model, &iter);
3562 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3563 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3565 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3566 gtk_tree_path_free (path);
3568 /* No further processing */
3576 * We listen to the changes in the local folder account name key,
3577 * because we want to show the right name in the view. The local
3578 * folder account name corresponds to the device name in the Maemo
3579 * version. We do this because we do not want to query gconf on each
3580 * tree view refresh. It's better to cache it and change whenever
3584 on_configuration_key_changed (ModestConf* conf,
3586 ModestConfEvent event,
3587 ModestConfNotificationId id,
3588 ModestFolderView *self)
3590 ModestFolderViewPrivate *priv;
3593 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3594 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3596 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3597 g_free (priv->local_account_name);
3599 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3600 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3602 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3603 MODEST_CONF_DEVICE_NAME, NULL);
3605 /* Force a redraw */
3606 #if GTK_CHECK_VERSION(2, 8, 0)
3607 GtkTreeViewColumn * tree_column;
3609 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3611 gtk_tree_view_column_queue_resize (tree_column);
3613 gtk_widget_queue_draw (GTK_WIDGET (self));
3619 modest_folder_view_set_style (ModestFolderView *self,
3620 ModestFolderViewStyle style)
3622 ModestFolderViewPrivate *priv;
3624 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3625 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3626 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3628 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3631 priv->style = style;
3635 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3636 const gchar *account_id)
3638 ModestFolderViewPrivate *priv;
3639 GtkTreeModel *model;
3641 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3643 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3645 /* This will be used by the filter_row callback,
3646 * to decided which rows to show: */
3647 if (priv->visible_account_id) {
3648 g_free (priv->visible_account_id);
3649 priv->visible_account_id = NULL;
3652 priv->visible_account_id = g_strdup (account_id);
3655 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3656 if (GTK_IS_TREE_MODEL_FILTER (model))
3657 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3659 modest_folder_view_update_model(self,
3660 (TnyAccountStore *) modest_runtime_get_account_store());
3662 /* Save settings to gconf */
3663 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3664 MODEST_CONF_FOLDER_VIEW_KEY);
3666 /* Notify observers */
3667 g_signal_emit (G_OBJECT(self),
3668 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3673 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3675 ModestFolderViewPrivate *priv;
3677 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3679 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3681 return (const gchar *) priv->visible_account_id;
3685 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3689 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3691 gtk_tree_model_get (model, iter,
3695 gboolean result = FALSE;
3696 if (type == TNY_FOLDER_TYPE_INBOX) {
3700 *inbox_iter = *iter;
3704 if (gtk_tree_model_iter_children (model, &child, iter)) {
3705 if (find_inbox_iter (model, &child, inbox_iter))
3709 } while (gtk_tree_model_iter_next (model, iter));
3714 #ifndef MODEST_TOOLKIT_HILDON2
3716 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3718 GtkTreeModel *model;
3719 GtkTreeIter iter, inbox_iter;
3720 GtkTreeSelection *sel;
3721 GtkTreePath *path = NULL;
3723 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3725 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3729 expand_root_items (self);
3730 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3732 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3733 g_warning ("%s: model is empty", __FUNCTION__);
3737 if (find_inbox_iter (model, &iter, &inbox_iter))
3738 path = gtk_tree_model_get_path (model, &inbox_iter);
3740 path = gtk_tree_path_new_first ();
3742 /* Select the row and free */
3743 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3744 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3745 gtk_tree_path_free (path);
3748 gtk_widget_grab_focus (GTK_WIDGET(self));
3754 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3759 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3760 TnyFolder* a_folder;
3763 gtk_tree_model_get (model, iter,
3764 INSTANCE_COLUMN, &a_folder,
3770 if (folder == a_folder) {
3771 g_object_unref (a_folder);
3772 *folder_iter = *iter;
3775 g_object_unref (a_folder);
3777 if (gtk_tree_model_iter_children (model, &child, iter)) {
3778 if (find_folder_iter (model, &child, folder_iter, folder))
3782 } while (gtk_tree_model_iter_next (model, iter));
3787 #ifndef MODEST_TOOLKIT_HILDON2
3789 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3792 ModestFolderView *self)
3794 ModestFolderViewPrivate *priv = NULL;
3795 GtkTreeSelection *sel;
3796 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3797 GObject *instance = NULL;
3799 if (!MODEST_IS_FOLDER_VIEW(self))
3802 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3804 priv->reexpand = TRUE;
3806 gtk_tree_model_get (tree_model, iter,
3808 INSTANCE_COLUMN, &instance,
3814 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3815 priv->folder_to_select = g_object_ref (instance);
3817 g_object_unref (instance);
3819 if (priv->folder_to_select) {
3821 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3824 path = gtk_tree_model_get_path (tree_model, iter);
3825 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3827 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3829 gtk_tree_selection_select_iter (sel, iter);
3830 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3832 gtk_tree_path_free (path);
3836 modest_folder_view_disable_next_folder_selection (self);
3838 /* Refilter the model */
3839 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3845 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3847 ModestFolderViewPrivate *priv;
3849 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3851 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3853 if (priv->folder_to_select)
3854 g_object_unref(priv->folder_to_select);
3856 priv->folder_to_select = NULL;
3860 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3861 gboolean after_change)
3863 GtkTreeModel *model;
3864 GtkTreeIter iter, folder_iter;
3865 GtkTreeSelection *sel;
3866 ModestFolderViewPrivate *priv = NULL;
3868 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3869 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3871 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3874 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3875 gtk_tree_selection_unselect_all (sel);
3877 if (priv->folder_to_select)
3878 g_object_unref(priv->folder_to_select);
3879 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3883 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3888 /* Refilter the model, before selecting the folder */
3889 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3891 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3892 g_warning ("%s: model is empty", __FUNCTION__);
3896 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3899 path = gtk_tree_model_get_path (model, &folder_iter);
3900 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3902 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3903 gtk_tree_selection_select_iter (sel, &folder_iter);
3904 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3906 gtk_tree_path_free (path);
3914 modest_folder_view_copy_selection (ModestFolderView *self)
3916 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3918 /* Copy selection */
3919 _clipboard_set_selected_data (self, FALSE);
3923 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3925 ModestFolderViewPrivate *priv = NULL;
3926 GtkTreeModel *model = NULL;
3927 const gchar **hidding = NULL;
3928 guint i, n_selected;
3930 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3931 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3933 /* Copy selection */
3934 if (!_clipboard_set_selected_data (folder_view, TRUE))
3937 /* Get hidding ids */
3938 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3940 /* Clear hidding array created by previous cut operation */
3941 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3943 /* Copy hidding array */
3944 priv->n_selected = n_selected;
3945 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3946 for (i=0; i < n_selected; i++)
3947 priv->hidding_ids[i] = g_strdup(hidding[i]);
3949 /* Hide cut folders */
3950 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3951 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3955 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3956 ModestFolderView *folder_view_dst)
3958 GtkTreeModel *filter_model = NULL;
3959 GtkTreeModel *model = NULL;
3960 GtkTreeModel *new_filter_model = NULL;
3961 GtkTreeModel *old_tny_model = NULL;
3962 GtkTreeModel *new_tny_model = NULL;
3963 ModestFolderViewPrivate *dst_priv;
3965 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3966 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3968 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3969 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3970 new_tny_model = NULL;
3973 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3974 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3975 G_OBJECT (old_tny_model),
3976 "activity-changed");
3978 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3979 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3981 /* Build new filter model */
3982 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3983 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3990 /* Set copied model */
3991 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3992 #ifndef MODEST_TOOLKIT_HILDON2
3993 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3994 G_OBJECT(new_filter_model), "row-inserted",
3995 (GCallback) on_row_inserted_maybe_select_folder,
3998 #ifdef MODEST_TOOLKIT_HILDON2
3999 if (new_tny_model) {
4000 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
4001 G_OBJECT (new_tny_model),
4003 G_CALLBACK (on_activity_changed),
4009 g_object_unref (new_filter_model);
4013 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
4016 GtkTreeModel *model = NULL;
4017 ModestFolderViewPrivate* priv;
4019 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4021 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4022 priv->show_non_move = show;
4023 /* modest_folder_view_update_model(folder_view, */
4024 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
4026 /* Hide special folders */
4027 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
4028 if (GTK_IS_TREE_MODEL_FILTER (model)) {
4029 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
4034 modest_folder_view_show_message_count (ModestFolderView *folder_view,
4037 ModestFolderViewPrivate* priv;
4039 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4041 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4042 priv->show_message_count = show;
4044 g_object_set (G_OBJECT (priv->messages_renderer),
4045 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4049 /* Returns FALSE if it did not selected anything */
4051 _clipboard_set_selected_data (ModestFolderView *folder_view,
4054 ModestFolderViewPrivate *priv = NULL;
4055 TnyFolderStore *folder = NULL;
4056 gboolean retval = FALSE;
4058 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
4059 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4061 /* Set selected data on clipboard */
4062 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
4063 folder = modest_folder_view_get_selected (folder_view);
4065 /* Do not allow to select an account */
4066 if (TNY_IS_FOLDER (folder)) {
4067 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
4072 g_object_unref (folder);
4078 _clear_hidding_filter (ModestFolderView *folder_view)
4080 ModestFolderViewPrivate *priv;
4083 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4084 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4086 if (priv->hidding_ids != NULL) {
4087 for (i=0; i < priv->n_selected; i++)
4088 g_free (priv->hidding_ids[i]);
4089 g_free(priv->hidding_ids);
4095 on_display_name_changed (ModestAccountMgr *mgr,
4096 const gchar *account,
4099 ModestFolderView *self;
4101 self = MODEST_FOLDER_VIEW (user_data);
4103 /* Force a redraw */
4104 #if GTK_CHECK_VERSION(2, 8, 0)
4105 GtkTreeViewColumn * tree_column;
4107 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4109 gtk_tree_view_column_queue_resize (tree_column);
4111 gtk_widget_queue_draw (GTK_WIDGET (self));
4116 modest_folder_view_set_cell_style (ModestFolderView *self,
4117 ModestFolderViewCellStyle cell_style)
4119 ModestFolderViewPrivate *priv = NULL;
4121 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4122 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4124 priv->cell_style = cell_style;
4126 g_object_set (G_OBJECT (priv->messages_renderer),
4127 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4130 gtk_widget_queue_draw (GTK_WIDGET (self));
4134 update_style (ModestFolderView *self)
4136 ModestFolderViewPrivate *priv;
4137 GdkColor style_color, style_active_color;
4138 PangoAttrList *attr_list;
4140 PangoAttribute *attr;
4142 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4143 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4147 attr_list = pango_attr_list_new ();
4149 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
4150 gdk_color_parse ("grey", &style_color);
4152 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4153 pango_attr_list_insert (attr_list, attr);
4156 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4158 "SmallSystemFont", NULL,
4161 attr = pango_attr_font_desc_new (pango_font_description_copy
4162 (style->font_desc));
4163 pango_attr_list_insert (attr_list, attr);
4165 g_object_set (G_OBJECT (priv->messages_renderer),
4166 "foreground-gdk", &style_color,
4167 "foreground-set", TRUE,
4168 "attributes", attr_list,
4170 pango_attr_list_unref (attr_list);
4173 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
4174 priv->active_color = style_active_color;
4176 gdk_color_parse ("000", &(priv->active_color));
4181 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4183 if (strcmp ("style", spec->name) == 0) {
4184 update_style (MODEST_FOLDER_VIEW (obj));
4185 gtk_widget_queue_draw (GTK_WIDGET (obj));
4190 modest_folder_view_set_filter (ModestFolderView *self,
4191 ModestFolderViewFilter filter)
4193 ModestFolderViewPrivate *priv;
4194 GtkTreeModel *filter_model;
4196 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4197 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4199 priv->filter |= filter;
4201 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4202 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4203 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4208 modest_folder_view_unset_filter (ModestFolderView *self,
4209 ModestFolderViewFilter filter)
4211 ModestFolderViewPrivate *priv;
4212 GtkTreeModel *filter_model;
4214 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4215 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4217 priv->filter &= ~filter;
4219 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4220 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4221 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4226 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4227 ModestTnyFolderRules rules)
4229 GtkTreeModel *filter_model;
4231 gboolean fulfil = FALSE;
4233 if (!get_inner_models (self, &filter_model, NULL, NULL))
4236 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4240 TnyFolderStore *folder;
4242 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4244 if (TNY_IS_FOLDER (folder)) {
4245 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4246 /* Folder rules are negative: non_writable, non_deletable... */
4247 if (!(folder_rules & rules))
4250 g_object_unref (folder);
4253 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4259 modest_folder_view_set_list_to_move (ModestFolderView *self,
4262 ModestFolderViewPrivate *priv;
4264 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4265 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4267 if (priv->list_to_move)
4268 g_object_unref (priv->list_to_move);
4271 g_object_ref (list);
4273 priv->list_to_move = list;
4277 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4279 ModestFolderViewPrivate *priv;
4281 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4282 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4285 g_free (priv->mailbox);
4287 priv->mailbox = g_strdup (mailbox);
4289 /* Notify observers */
4290 g_signal_emit (G_OBJECT(self),
4291 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4292 priv->visible_account_id);
4296 modest_folder_view_get_mailbox (ModestFolderView *self)
4298 ModestFolderViewPrivate *priv;
4300 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4301 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4303 return (const gchar *) priv->mailbox;
4307 modest_folder_view_get_activity (ModestFolderView *self)
4309 ModestFolderViewPrivate *priv;
4310 GtkTreeModel *inner_model;
4312 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4313 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4314 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4316 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4317 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4323 #ifdef MODEST_TOOLKIT_HILDON2
4325 on_activity_changed (TnyGtkFolderListStore *store,
4327 ModestFolderView *folder_view)
4329 ModestFolderViewPrivate *priv;
4331 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4332 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4333 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4335 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
4341 modest_folder_view_get_model_tny_list (ModestFolderView *self)
4343 GtkTreeModel *model;
4349 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
4350 ret_value = TNY_LIST (model);
4351 g_object_ref (ret_value);