1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
65 #include <modest-account-protocol.h>
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
98 static void modest_folder_view_init (ModestFolderView *obj);
99 static void modest_folder_view_finalize (GObject *obj);
100 static void modest_folder_view_dispose (GObject *obj);
102 static void tny_account_store_view_init (gpointer g,
103 gpointer iface_data);
105 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
106 TnyAccountStore *account_store);
108 static void on_selection_changed (GtkTreeSelection *sel,
111 static void on_row_activated (GtkTreeView *treeview,
113 GtkTreeViewColumn *column,
116 static void on_account_removed (TnyAccountStore *self,
120 static void on_account_inserted (TnyAccountStore *self,
124 static void on_account_changed (TnyAccountStore *self,
128 static gint cmp_rows (GtkTreeModel *tree_model,
133 static gboolean filter_row (GtkTreeModel *model,
137 static gboolean on_key_pressed (GtkWidget *self,
141 static void on_configuration_key_changed (ModestConf* conf,
143 ModestConfEvent event,
144 ModestConfNotificationId notification_id,
145 ModestFolderView *self);
148 static void on_drag_data_get (GtkWidget *widget,
149 GdkDragContext *context,
150 GtkSelectionData *selection_data,
155 static void on_drag_data_received (GtkWidget *widget,
156 GdkDragContext *context,
159 GtkSelectionData *selection_data,
164 static gboolean on_drag_motion (GtkWidget *widget,
165 GdkDragContext *context,
171 static void expand_root_items (ModestFolderView *self);
173 static gint expand_row_timeout (gpointer data);
175 static void setup_drag_and_drop (GtkTreeView *self);
177 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
180 static void _clear_hidding_filter (ModestFolderView *folder_view);
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
186 ModestFolderView *self);
189 static void on_display_name_changed (ModestAccountMgr *self,
190 const gchar *account,
192 static void update_style (ModestFolderView *self);
193 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
195 static gboolean inbox_is_special (TnyFolderStore *folder_store);
197 static gboolean get_inner_models (ModestFolderView *self,
198 GtkTreeModel **filter_model,
199 GtkTreeModel **sort_model,
200 GtkTreeModel **tny_model);
201 #ifdef MODEST_TOOLKIT_HILDON2
202 static void on_activity_changed (TnyGtkFolderListStore *store,
204 ModestFolderView *folder_view);
208 FOLDER_SELECTION_CHANGED_SIGNAL,
209 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
210 FOLDER_ACTIVATED_SIGNAL,
211 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
212 ACTIVITY_CHANGED_SIGNAL,
216 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
217 struct _ModestFolderViewPrivate {
218 TnyAccountStore *account_store;
219 TnyFolderStore *cur_folder_store;
221 TnyFolder *folder_to_select; /* folder to select after the next update */
223 gulong changed_signal;
224 gulong account_inserted_signal;
225 gulong account_removed_signal;
226 gulong account_changed_signal;
227 gulong conf_key_signal;
228 gulong display_name_changed_signal;
230 /* not unref this object, its a singlenton */
231 ModestEmailClipboard *clipboard;
233 /* Filter tree model */
236 ModestFolderViewFilter filter;
238 TnyFolderStoreQuery *query;
240 guint timer_expander;
242 gchar *local_account_name;
243 gchar *visible_account_id;
245 ModestFolderViewStyle style;
246 ModestFolderViewCellStyle cell_style;
247 gboolean show_message_count;
249 gboolean reselect; /* we use this to force a reselection of the INBOX */
250 gboolean show_non_move;
251 TnyList *list_to_move;
252 gboolean reexpand; /* next time we expose, we'll expand all root folders */
254 GtkCellRenderer *messages_renderer;
256 gulong outbox_deleted_handler;
258 GSList *signal_handlers;
259 GdkColor active_color;
261 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
262 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
263 MODEST_TYPE_FOLDER_VIEW, \
264 ModestFolderViewPrivate))
266 static GObjectClass *parent_class = NULL;
268 static guint signals[LAST_SIGNAL] = {0};
271 modest_folder_view_get_type (void)
273 static GType my_type = 0;
275 static const GTypeInfo my_info = {
276 sizeof(ModestFolderViewClass),
277 NULL, /* base init */
278 NULL, /* base finalize */
279 (GClassInitFunc) modest_folder_view_class_init,
280 NULL, /* class finalize */
281 NULL, /* class data */
282 sizeof(ModestFolderView),
284 (GInstanceInitFunc) modest_folder_view_init,
288 static const GInterfaceInfo tny_account_store_view_info = {
289 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
290 NULL, /* interface_finalize */
291 NULL /* interface_data */
295 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
299 g_type_add_interface_static (my_type,
300 TNY_TYPE_ACCOUNT_STORE_VIEW,
301 &tny_account_store_view_info);
307 modest_folder_view_class_init (ModestFolderViewClass *klass)
309 GObjectClass *gobject_class;
310 GtkTreeViewClass *treeview_class;
311 gobject_class = (GObjectClass*) klass;
312 treeview_class = (GtkTreeViewClass*) klass;
314 parent_class = g_type_class_peek_parent (klass);
315 gobject_class->finalize = modest_folder_view_finalize;
316 gobject_class->dispose = modest_folder_view_dispose;
318 g_type_class_add_private (gobject_class,
319 sizeof(ModestFolderViewPrivate));
321 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
322 g_signal_new ("folder_selection_changed",
323 G_TYPE_FROM_CLASS (gobject_class),
325 G_STRUCT_OFFSET (ModestFolderViewClass,
326 folder_selection_changed),
328 modest_marshal_VOID__POINTER_BOOLEAN,
329 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
332 * This signal is emitted whenever the currently selected
333 * folder display name is computed. Note that the name could
334 * be different to the folder name, because we could append
335 * the unread messages count to the folder name to build the
336 * folder display name
338 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
339 g_signal_new ("folder-display-name-changed",
340 G_TYPE_FROM_CLASS (gobject_class),
342 G_STRUCT_OFFSET (ModestFolderViewClass,
343 folder_display_name_changed),
345 g_cclosure_marshal_VOID__STRING,
346 G_TYPE_NONE, 1, G_TYPE_STRING);
348 signals[FOLDER_ACTIVATED_SIGNAL] =
349 g_signal_new ("folder_activated",
350 G_TYPE_FROM_CLASS (gobject_class),
352 G_STRUCT_OFFSET (ModestFolderViewClass,
355 g_cclosure_marshal_VOID__POINTER,
356 G_TYPE_NONE, 1, G_TYPE_POINTER);
359 * Emitted whenever the visible account changes
361 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
362 g_signal_new ("visible-account-changed",
363 G_TYPE_FROM_CLASS (gobject_class),
365 G_STRUCT_OFFSET (ModestFolderViewClass,
366 visible_account_changed),
368 g_cclosure_marshal_VOID__STRING,
369 G_TYPE_NONE, 1, G_TYPE_STRING);
372 * Emitted when the underlying GtkListStore is updating data
374 signals[ACTIVITY_CHANGED_SIGNAL] =
375 g_signal_new ("activity-changed",
376 G_TYPE_FROM_CLASS (gobject_class),
378 G_STRUCT_OFFSET (ModestFolderViewClass,
381 g_cclosure_marshal_VOID__BOOLEAN,
382 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
384 treeview_class->select_cursor_parent = NULL;
386 #ifdef MODEST_TOOLKIT_HILDON2
387 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
393 /* Retrieves the filter, sort and tny models of the folder view. If
394 any of these does not exist then it returns FALSE */
396 get_inner_models (ModestFolderView *self,
397 GtkTreeModel **filter_model,
398 GtkTreeModel **sort_model,
399 GtkTreeModel **tny_model)
401 GtkTreeModel *s_model, *f_model, *t_model;
403 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
404 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
405 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
409 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
410 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
411 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
415 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
419 *filter_model = f_model;
421 *sort_model = s_model;
423 *tny_model = t_model;
428 /* Simplify checks for NULLs: */
430 strings_are_equal (const gchar *a, const gchar *b)
436 return (strcmp (a, b) == 0);
443 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
445 GObject *instance = NULL;
447 gtk_tree_model_get (model, iter,
448 INSTANCE_COLUMN, &instance,
452 return FALSE; /* keep walking */
454 if (!TNY_IS_ACCOUNT (instance)) {
455 g_object_unref (instance);
456 return FALSE; /* keep walking */
459 /* Check if this is the looked-for account: */
460 TnyAccount *this_account = TNY_ACCOUNT (instance);
461 TnyAccount *account = TNY_ACCOUNT (data);
463 const gchar *this_account_id = tny_account_get_id(this_account);
464 const gchar *account_id = tny_account_get_id(account);
465 g_object_unref (instance);
468 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
469 if (strings_are_equal(this_account_id, account_id)) {
470 /* Tell the model that the data has changed, so that
471 * it calls the cell_data_func callbacks again: */
472 /* TODO: This does not seem to actually cause the new string to be shown: */
473 gtk_tree_model_row_changed (model, path, iter);
475 return TRUE; /* stop walking */
478 return FALSE; /* keep walking */
483 ModestFolderView *self;
484 gchar *previous_name;
485 } GetMmcAccountNameData;
488 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
490 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
492 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
494 if (!strings_are_equal (
495 tny_account_get_name(TNY_ACCOUNT(account)),
496 data->previous_name)) {
498 /* Tell the model that the data has changed, so that
499 * it calls the cell_data_func callbacks again: */
500 ModestFolderView *self = data->self;
501 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
503 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
506 g_free (data->previous_name);
507 g_slice_free (GetMmcAccountNameData, data);
511 convert_parent_folders_to_dots (gchar **item_name)
514 gint n_inbox_parents = 0;
517 gchar *last_separator;
519 if (item_name == NULL)
522 path_start = *item_name;
523 for (c = *item_name; *c != '\0'; c++) {
524 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
526 if (c != path_start) {
527 compare = g_strndup (path_start, c - path_start);
528 compare = g_strstrip (compare);
529 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
539 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
540 if (last_separator != NULL) {
541 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
548 buffer = g_string_new ("");
549 for (i = 0; i < n_parents - n_inbox_parents; i++) {
550 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
552 buffer = g_string_append (buffer, last_separator);
554 *item_name = g_string_free (buffer, FALSE);
560 format_compact_style (gchar **item_name,
562 const gchar *mailbox,
564 gboolean multiaccount,
565 gboolean *use_markup)
569 TnyFolderType folder_type;
571 if (!TNY_IS_FOLDER (instance))
574 folder = (TnyFolder *) instance;
576 folder_type = tny_folder_get_folder_type (folder);
577 is_special = (get_cmp_pos (folder_type, folder)!= 4);
580 /* Remove mailbox prefix if any */
581 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
582 if (g_str_has_prefix (*item_name, prefix)) {
583 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
585 *item_name = new_item_name;
589 if (!is_special || multiaccount) {
590 TnyAccount *account = tny_folder_get_account (folder);
591 const gchar *folder_name;
592 gboolean concat_folder_name = FALSE;
595 /* Should not happen */
599 /* convert parent folders to dots */
600 convert_parent_folders_to_dots (item_name);
602 folder_name = tny_folder_get_name (folder);
603 if (g_str_has_suffix (*item_name, folder_name)) {
604 gchar *offset = g_strrstr (*item_name, folder_name);
606 concat_folder_name = TRUE;
609 buffer = g_string_new ("");
611 buffer = g_string_append (buffer, *item_name);
612 if (concat_folder_name) {
613 if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
614 buffer = g_string_append (buffer, folder_name);
615 /* TODO: append a sensitive string to the remote drafts to
616 * be able to know it's the remote one */
617 /* buffer = g_string_append (buffer, " (TODO:remote)"); */
619 buffer = g_string_append (buffer, folder_name);
623 g_object_unref (account);
625 *item_name = g_string_free (buffer, FALSE);
633 replace_special_folder_prefix (gchar **item_name)
635 const gchar *separator;
638 if (item_name == NULL || *item_name == NULL || **item_name == '\0')
640 separator = g_strstr_len (*item_name, -1, MODEST_FOLDER_PATH_SEPARATOR);
641 if (separator == NULL)
644 prefix = g_strndup (*item_name, separator - *item_name);
647 if (prefix && *prefix != '\0') {
648 TnyFolderType folder_type;
652 downcase = g_utf8_strdown (prefix, -1);
656 if (strcmp (downcase, "inbox") == 0) {
657 folder_type = TNY_FOLDER_TYPE_INBOX;
658 new_name = g_strconcat (_("mcen_me_folder_inbox"), separator, NULL);
660 *item_name = new_name;
662 folder_type = modest_local_folder_info_get_type (prefix);
663 switch (folder_type) {
664 case TNY_FOLDER_TYPE_INBOX:
665 case TNY_FOLDER_TYPE_ARCHIVE:
666 new_name = g_strconcat (modest_local_folder_info_get_type_display_name (folder_type), separator, NULL);
668 *item_name = new_name;
679 text_cell_data (GtkTreeViewColumn *column,
680 GtkCellRenderer *renderer,
681 GtkTreeModel *tree_model,
685 ModestFolderViewPrivate *priv;
686 GObject *rendobj = (GObject *) renderer;
688 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
689 GObject *instance = NULL;
690 gboolean use_markup = FALSE;
692 gtk_tree_model_get (tree_model, iter,
695 INSTANCE_COLUMN, &instance,
697 if (!fname || !instance)
700 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
701 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
703 gchar *item_name = NULL;
704 gint item_weight = 400;
706 if (type != TNY_FOLDER_TYPE_ROOT) {
711 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
712 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
715 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
716 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
718 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
721 /* Sometimes an special folder is reported by the server as
722 NORMAL, like some versions of Dovecot */
723 if (type == TNY_FOLDER_TYPE_NORMAL ||
724 type == TNY_FOLDER_TYPE_UNKNOWN) {
725 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
729 /* note: we cannot reliably get the counts from the
730 * tree model, we need to use explicit calls on
731 * tny_folder for some reason. Select the number to
732 * show: the unread or unsent messages. in case of
733 * outbox/drafts, show all */
734 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
735 (type == TNY_FOLDER_TYPE_OUTBOX) ||
736 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
737 number = tny_folder_get_all_count (TNY_FOLDER(instance));
740 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
744 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
745 item_name = g_strdup (fname);
752 /* Use bold font style if there are unread or unset messages */
754 if (priv->show_message_count) {
755 item_name = g_strdup_printf ("%s (%d)", fname, number);
757 item_name = g_strdup (fname);
761 item_name = g_strdup (fname);
766 } else if (TNY_IS_ACCOUNT (instance)) {
767 /* If it's a server account */
768 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
769 item_name = g_strdup (priv->local_account_name);
771 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
772 /* fname is only correct when the items are first
773 * added to the model, not when the account is
774 * changed later, so get the name from the account
776 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
779 item_name = g_strdup (fname);
785 if (type == TNY_FOLDER_TYPE_INBOX &&
786 g_str_has_suffix (fname, "Inbox")) {
788 item_name = g_strdup (_("mcen_me_folder_inbox"));
792 item_name = g_strdup ("unknown");
794 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
795 gboolean multiaccount;
797 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
798 /* Convert item_name to markup */
799 format_compact_style (&item_name, instance, priv->mailbox,
801 multiaccount, &use_markup);
803 replace_special_folder_prefix (&item_name);
806 if (item_name && item_weight) {
807 /* Set the name in the treeview cell: */
808 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
809 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
810 g_object_set (rendobj,
813 "foreground-set", TRUE,
814 "foreground-gdk", &(priv->active_color),
817 g_object_set (rendobj,
819 "foreground-set", FALSE,
821 "weight", item_weight,
825 /* Notify display name observers */
826 /* TODO: What listens for this signal, and how can it use only the new name? */
827 if (((GObject *) priv->cur_folder_store) == instance) {
828 g_signal_emit (G_OBJECT(self),
829 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
836 /* If it is a Memory card account, make sure that we have the correct name.
837 * This function will be trigerred again when the name has been retrieved: */
838 if (TNY_IS_STORE_ACCOUNT (instance) &&
839 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
841 /* Get the account name asynchronously: */
842 GetMmcAccountNameData *callback_data =
843 g_slice_new0(GetMmcAccountNameData);
844 callback_data->self = self;
846 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
848 callback_data->previous_name = g_strdup (name);
850 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
851 on_get_mmc_account_name, callback_data);
855 g_object_unref (G_OBJECT (instance));
861 messages_cell_data (GtkTreeViewColumn *column,
862 GtkCellRenderer *renderer,
863 GtkTreeModel *tree_model,
867 ModestFolderView *self;
868 ModestFolderViewPrivate *priv;
869 GObject *rendobj = (GObject *) renderer;
870 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
871 GObject *instance = NULL;
872 gchar *item_name = NULL;
874 gtk_tree_model_get (tree_model, iter,
876 INSTANCE_COLUMN, &instance,
881 self = MODEST_FOLDER_VIEW (data);
882 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
885 if (type != TNY_FOLDER_TYPE_ROOT) {
890 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
891 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
894 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
896 /* Sometimes an special folder is reported by the server as
897 NORMAL, like some versions of Dovecot */
898 if (type == TNY_FOLDER_TYPE_NORMAL ||
899 type == TNY_FOLDER_TYPE_UNKNOWN) {
900 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
904 /* note: we cannot reliably get the counts from the tree model, we need
905 * to use explicit calls on tny_folder for some reason.
907 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
908 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
909 (type == TNY_FOLDER_TYPE_OUTBOX) ||
910 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
911 number = tny_folder_get_all_count (TNY_FOLDER(instance));
914 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
918 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
920 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
921 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
927 item_name = g_strdup ("");
930 /* Set the name in the treeview cell: */
931 g_object_set (rendobj,"text", item_name, NULL);
939 g_object_unref (G_OBJECT (instance));
945 GdkPixbuf *pixbuf_open;
946 GdkPixbuf *pixbuf_close;
950 static inline GdkPixbuf *
951 get_composite_pixbuf (const gchar *icon_name,
953 GdkPixbuf *base_pixbuf)
955 GdkPixbuf *emblem, *retval = NULL;
957 emblem = modest_platform_get_icon (icon_name, size);
959 retval = gdk_pixbuf_copy (base_pixbuf);
960 gdk_pixbuf_composite (emblem, retval, 0, 0,
961 MIN (gdk_pixbuf_get_width (emblem),
962 gdk_pixbuf_get_width (retval)),
963 MIN (gdk_pixbuf_get_height (emblem),
964 gdk_pixbuf_get_height (retval)),
965 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
966 g_object_unref (emblem);
971 static inline ThreePixbufs *
972 get_composite_icons (const gchar *icon_code,
974 GdkPixbuf **pixbuf_open,
975 GdkPixbuf **pixbuf_close)
977 ThreePixbufs *retval;
979 if (pixbuf && !*pixbuf) {
981 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
983 *pixbuf = gdk_pixbuf_copy (icon);
989 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
990 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
994 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
995 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
999 retval = g_slice_new0 (ThreePixbufs);
1000 if (pixbuf && *pixbuf)
1001 retval->pixbuf = g_object_ref (*pixbuf);
1003 retval->pixbuf = NULL;
1004 if (pixbuf_open && *pixbuf_open)
1005 retval->pixbuf_open = g_object_ref (*pixbuf_open);
1007 retval->pixbuf_open = NULL;
1008 if (pixbuf_close && *pixbuf_close)
1009 retval->pixbuf_close = g_object_ref (*pixbuf_close);
1011 retval->pixbuf_close = NULL;
1016 static inline ThreePixbufs *
1017 get_account_protocol_pixbufs (ModestFolderView *folder_view,
1018 ModestProtocolType protocol_type,
1021 ModestProtocol *protocol;
1022 const GdkPixbuf *pixbuf = NULL;
1023 ModestFolderViewPrivate *priv;
1025 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1027 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1030 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
1031 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
1032 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
1033 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
1034 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
1035 object, FOLDER_ICON_SIZE);
1039 ThreePixbufs *retval;
1040 retval = g_slice_new0 (ThreePixbufs);
1041 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
1042 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
1043 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
1050 static inline ThreePixbufs*
1051 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
1053 TnyAccount *account = NULL;
1054 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
1055 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
1056 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
1057 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
1058 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
1060 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1061 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1062 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1063 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1064 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1066 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1067 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1068 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1069 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1070 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1072 ThreePixbufs *retval = NULL;
1074 if (TNY_IS_ACCOUNT (instance)) {
1075 account = g_object_ref (instance);
1076 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1077 account = tny_folder_get_account (TNY_FOLDER (instance));
1081 ModestProtocolType account_store_protocol;
1083 account_store_protocol = modest_tny_account_get_protocol_type (account);
1084 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1085 g_object_unref (account);
1091 /* Sometimes an special folder is reported by the server as
1092 NORMAL, like some versions of Dovecot */
1093 if (type == TNY_FOLDER_TYPE_NORMAL ||
1094 type == TNY_FOLDER_TYPE_UNKNOWN) {
1095 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1098 /* It's not enough with check the folder type. We need to
1099 ensure that we're not giving a special folder icon to a
1100 normal folder with the same name than a special folder */
1101 if (TNY_IS_FOLDER (instance) &&
1102 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1103 type = TNY_FOLDER_TYPE_NORMAL;
1105 /* Remote folders should not be treated as special folders */
1106 if (TNY_IS_FOLDER_STORE (instance) &&
1107 !TNY_IS_ACCOUNT (instance) &&
1108 type != TNY_FOLDER_TYPE_INBOX &&
1109 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1110 #ifdef MODEST_TOOLKIT_HILDON2
1111 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1114 &anorm_pixbuf_close);
1116 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1118 &normal_pixbuf_open,
1119 &normal_pixbuf_close);
1125 case TNY_FOLDER_TYPE_INVALID:
1126 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1129 case TNY_FOLDER_TYPE_ROOT:
1130 if (TNY_IS_ACCOUNT (instance)) {
1132 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1133 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1136 &avirt_pixbuf_close);
1138 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1140 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1141 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1144 &ammc_pixbuf_close);
1146 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1149 &anorm_pixbuf_close);
1154 case TNY_FOLDER_TYPE_INBOX:
1155 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1158 &inbox_pixbuf_close);
1160 case TNY_FOLDER_TYPE_OUTBOX:
1161 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1163 &outbox_pixbuf_open,
1164 &outbox_pixbuf_close);
1166 case TNY_FOLDER_TYPE_JUNK:
1167 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1170 &junk_pixbuf_close);
1172 case TNY_FOLDER_TYPE_SENT:
1173 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1176 &sent_pixbuf_close);
1178 case TNY_FOLDER_TYPE_TRASH:
1179 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1182 &trash_pixbuf_close);
1184 case TNY_FOLDER_TYPE_DRAFTS:
1185 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1188 &draft_pixbuf_close);
1190 case TNY_FOLDER_TYPE_ARCHIVE:
1191 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1196 case TNY_FOLDER_TYPE_NORMAL:
1198 /* Memory card folders could have an special icon */
1199 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1200 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1205 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1207 &normal_pixbuf_open,
1208 &normal_pixbuf_close);
1217 free_pixbufs (ThreePixbufs *pixbufs)
1219 if (pixbufs->pixbuf)
1220 g_object_unref (pixbufs->pixbuf);
1221 if (pixbufs->pixbuf_open)
1222 g_object_unref (pixbufs->pixbuf_open);
1223 if (pixbufs->pixbuf_close)
1224 g_object_unref (pixbufs->pixbuf_close);
1225 g_slice_free (ThreePixbufs, pixbufs);
1229 icon_cell_data (GtkTreeViewColumn *column,
1230 GtkCellRenderer *renderer,
1231 GtkTreeModel *tree_model,
1235 GObject *rendobj = NULL, *instance = NULL;
1236 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1237 gboolean has_children;
1238 ThreePixbufs *pixbufs;
1239 ModestFolderView *folder_view = (ModestFolderView *) data;
1241 rendobj = (GObject *) renderer;
1243 gtk_tree_model_get (tree_model, iter,
1245 INSTANCE_COLUMN, &instance,
1251 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1252 pixbufs = get_folder_icons (folder_view, type, instance);
1253 g_object_unref (instance);
1256 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1259 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1260 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1263 free_pixbufs (pixbufs);
1267 add_columns (GtkWidget *treeview)
1269 GtkTreeViewColumn *column;
1270 GtkCellRenderer *renderer;
1271 GtkTreeSelection *sel;
1272 ModestFolderViewPrivate *priv;
1274 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1277 column = gtk_tree_view_column_new ();
1279 /* Set icon and text render function */
1280 renderer = gtk_cell_renderer_pixbuf_new();
1281 #ifdef MODEST_TOOLKIT_HILDON2
1282 g_object_set (renderer,
1283 "xpad", MODEST_MARGIN_DEFAULT,
1284 "ypad", MODEST_MARGIN_DEFAULT,
1287 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1288 gtk_tree_view_column_set_cell_data_func(column, renderer,
1289 icon_cell_data, treeview, NULL);
1291 renderer = gtk_cell_renderer_text_new();
1292 g_object_set (renderer,
1293 #ifdef MODEST_TOOLKIT_HILDON2
1294 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1295 "ypad", MODEST_MARGIN_DEFAULT,
1296 "xpad", MODEST_MARGIN_DEFAULT,
1298 "ellipsize", PANGO_ELLIPSIZE_END,
1300 "ellipsize-set", TRUE, NULL);
1301 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1302 gtk_tree_view_column_set_cell_data_func(column, renderer,
1303 text_cell_data, treeview, NULL);
1305 priv->messages_renderer = gtk_cell_renderer_text_new ();
1306 g_object_set (priv->messages_renderer,
1307 #ifdef MODEST_TOOLKIT_HILDON2
1309 "ypad", MODEST_MARGIN_DEFAULT,
1310 "xpad", MODEST_MARGIN_DOUBLE,
1312 "scale", PANGO_SCALE_X_SMALL,
1315 "alignment", PANGO_ALIGN_RIGHT,
1319 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1320 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1321 messages_cell_data, treeview, NULL);
1323 /* Set selection mode */
1324 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1325 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1327 /* Set treeview appearance */
1328 gtk_tree_view_column_set_spacing (column, 2);
1329 gtk_tree_view_column_set_resizable (column, TRUE);
1330 gtk_tree_view_column_set_fixed_width (column, TRUE);
1331 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1332 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1335 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1339 modest_folder_view_init (ModestFolderView *obj)
1341 ModestFolderViewPrivate *priv;
1344 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1346 priv->timer_expander = 0;
1347 priv->account_store = NULL;
1349 priv->do_refresh = TRUE;
1350 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1351 priv->cur_folder_store = NULL;
1352 priv->visible_account_id = NULL;
1353 priv->mailbox = NULL;
1354 priv->folder_to_select = NULL;
1355 priv->outbox_deleted_handler = 0;
1356 priv->reexpand = TRUE;
1357 priv->signal_handlers = 0;
1359 /* Initialize the local account name */
1360 conf = modest_runtime_get_conf();
1361 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1363 /* Init email clipboard */
1364 priv->clipboard = modest_runtime_get_email_clipboard ();
1365 priv->hidding_ids = NULL;
1366 priv->n_selected = 0;
1367 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1368 priv->reselect = FALSE;
1369 priv->show_non_move = TRUE;
1370 priv->list_to_move = NULL;
1371 priv->show_message_count = TRUE;
1373 /* Build treeview */
1374 add_columns (GTK_WIDGET (obj));
1376 /* Setup drag and drop */
1377 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1379 /* Connect signals */
1380 g_signal_connect (G_OBJECT (obj),
1382 G_CALLBACK (on_key_pressed), NULL);
1384 priv->display_name_changed_signal =
1385 g_signal_connect (modest_runtime_get_account_mgr (),
1386 "display_name_changed",
1387 G_CALLBACK (on_display_name_changed),
1391 * Track changes in the local account name (in the device it
1392 * will be the device name)
1394 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1396 G_CALLBACK(on_configuration_key_changed),
1399 gdk_color_parse ("000", &priv->active_color);
1402 g_signal_connect (G_OBJECT (obj), "notify::style",
1403 G_CALLBACK (on_notify_style), (gpointer) obj);
1407 tny_account_store_view_init (gpointer g, gpointer iface_data)
1409 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1411 klass->set_account_store = modest_folder_view_set_account_store;
1415 modest_folder_view_dispose (GObject *obj)
1417 ModestFolderViewPrivate *priv;
1418 GtkTreeModel *model = NULL;
1420 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1422 get_inner_models (MODEST_FOLDER_VIEW (obj),
1423 NULL, NULL, &model);
1425 #ifdef MODEST_TOOLKIT_HILDON2
1426 if (priv->signal_handlers) {
1427 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1428 priv->signal_handlers = NULL;
1432 /* Free external references */
1433 if (priv->account_store) {
1434 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1435 priv->account_inserted_signal);
1436 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1437 priv->account_removed_signal);
1438 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1439 priv->account_changed_signal);
1440 g_object_unref (G_OBJECT(priv->account_store));
1441 priv->account_store = NULL;
1445 g_object_unref (G_OBJECT (priv->query));
1449 if (priv->folder_to_select) {
1450 g_object_unref (G_OBJECT(priv->folder_to_select));
1451 priv->folder_to_select = NULL;
1454 if (priv->cur_folder_store) {
1455 g_object_unref (priv->cur_folder_store);
1456 priv->cur_folder_store = NULL;
1459 if (priv->list_to_move) {
1460 g_object_unref (priv->list_to_move);
1461 priv->list_to_move = NULL;
1464 G_OBJECT_CLASS(parent_class)->dispose (obj);
1468 modest_folder_view_finalize (GObject *obj)
1470 ModestFolderViewPrivate *priv;
1471 GtkTreeSelection *sel;
1472 TnyAccount *local_account;
1474 g_return_if_fail (obj);
1476 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1478 if (priv->timer_expander != 0) {
1479 g_source_remove (priv->timer_expander);
1480 priv->timer_expander = 0;
1483 local_account = (TnyAccount *)
1484 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1485 if (local_account) {
1486 if (g_signal_handler_is_connected (local_account,
1487 priv->outbox_deleted_handler))
1488 g_signal_handler_disconnect (local_account,
1489 priv->outbox_deleted_handler);
1490 g_object_unref (local_account);
1493 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1494 priv->display_name_changed_signal)) {
1495 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1496 priv->display_name_changed_signal);
1497 priv->display_name_changed_signal = 0;
1500 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1502 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1504 g_free (priv->local_account_name);
1505 g_free (priv->visible_account_id);
1506 g_free (priv->mailbox);
1508 if (priv->conf_key_signal) {
1509 g_signal_handler_disconnect (modest_runtime_get_conf (),
1510 priv->conf_key_signal);
1511 priv->conf_key_signal = 0;
1514 /* Clear hidding array created by cut operation */
1515 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1517 gdk_color_parse ("000", &priv->active_color);
1519 G_OBJECT_CLASS(parent_class)->finalize (obj);
1524 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1526 ModestFolderViewPrivate *priv;
1529 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1530 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1532 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1533 device = tny_account_store_get_device (account_store);
1535 if (G_UNLIKELY (priv->account_store)) {
1537 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1538 priv->account_inserted_signal))
1539 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1540 priv->account_inserted_signal);
1541 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1542 priv->account_removed_signal))
1543 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1544 priv->account_removed_signal);
1545 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1546 priv->account_changed_signal))
1547 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1548 priv->account_changed_signal);
1549 g_object_unref (G_OBJECT (priv->account_store));
1552 priv->account_store = g_object_ref (G_OBJECT (account_store));
1554 priv->account_removed_signal =
1555 g_signal_connect (G_OBJECT(account_store), "account_removed",
1556 G_CALLBACK (on_account_removed), self);
1558 priv->account_inserted_signal =
1559 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1560 G_CALLBACK (on_account_inserted), self);
1562 priv->account_changed_signal =
1563 g_signal_connect (G_OBJECT(account_store), "account_changed",
1564 G_CALLBACK (on_account_changed), self);
1566 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1567 priv->reselect = FALSE;
1568 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1570 g_object_unref (G_OBJECT (device));
1574 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1577 ModestFolderView *self;
1578 GtkTreeModel *model, *filter_model;
1581 self = MODEST_FOLDER_VIEW (user_data);
1583 if (!get_inner_models (self, &filter_model, NULL, &model))
1586 /* Remove outbox from model */
1587 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1588 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1589 g_object_unref (outbox);
1592 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1596 on_account_inserted (TnyAccountStore *account_store,
1597 TnyAccount *account,
1600 ModestFolderViewPrivate *priv;
1601 GtkTreeModel *model, *filter_model;
1603 /* Ignore transport account insertions, we're not showing them
1604 in the folder view */
1605 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1608 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1611 /* If we're adding a new account, and there is no previous
1612 one, we need to select the visible server account */
1613 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1614 !priv->visible_account_id)
1615 modest_widget_memory_restore (modest_runtime_get_conf(),
1616 G_OBJECT (user_data),
1617 MODEST_CONF_FOLDER_VIEW_KEY);
1621 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1622 &filter_model, NULL, &model))
1625 /* Insert the account in the model */
1626 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1628 /* When the model is a list store (plain representation) the
1629 outbox is not a child of any account so we have to manually
1630 delete it because removing the local folders account won't
1631 delete it (because tny_folder_get_account() is not defined
1632 for a merge folder */
1633 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1634 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1636 priv->outbox_deleted_handler =
1637 g_signal_connect (account,
1639 G_CALLBACK (on_outbox_deleted_cb),
1643 /* Refilter the model */
1644 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1649 same_account_selected (ModestFolderView *self,
1650 TnyAccount *account)
1652 ModestFolderViewPrivate *priv;
1653 gboolean same_account = FALSE;
1655 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1657 if (priv->cur_folder_store) {
1658 TnyAccount *selected_folder_account = NULL;
1660 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1661 selected_folder_account =
1662 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1664 selected_folder_account =
1665 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1668 if (selected_folder_account == account)
1669 same_account = TRUE;
1671 g_object_unref (selected_folder_account);
1673 return same_account;
1678 * Selects the first inbox or the local account in an idle
1681 on_idle_select_first_inbox_or_local (gpointer user_data)
1683 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1685 gdk_threads_enter ();
1686 modest_folder_view_select_first_inbox_or_local (self);
1687 gdk_threads_leave ();
1693 on_account_changed (TnyAccountStore *account_store,
1694 TnyAccount *tny_account,
1697 ModestFolderView *self;
1698 ModestFolderViewPrivate *priv;
1699 GtkTreeModel *model, *filter_model;
1700 GtkTreeSelection *sel;
1701 gboolean same_account;
1703 /* Ignore transport account insertions, we're not showing them
1704 in the folder view */
1705 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1708 self = MODEST_FOLDER_VIEW (user_data);
1709 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1711 /* Get the inner model */
1712 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1713 &filter_model, NULL, &model))
1716 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1718 /* Invalidate the cur_folder_store only if the selected folder
1719 belongs to the account that is being removed */
1720 same_account = same_account_selected (self, tny_account);
1722 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1723 gtk_tree_selection_unselect_all (sel);
1726 /* Remove the account from the model */
1727 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1729 /* Insert the account in the model */
1730 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1732 /* Refilter the model */
1733 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1735 /* Select the first INBOX if the currently selected folder
1736 belongs to the account that is being deleted */
1737 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1738 g_idle_add (on_idle_select_first_inbox_or_local, self);
1742 on_account_removed (TnyAccountStore *account_store,
1743 TnyAccount *account,
1746 ModestFolderView *self = NULL;
1747 ModestFolderViewPrivate *priv;
1748 GtkTreeModel *model, *filter_model;
1749 GtkTreeSelection *sel = NULL;
1750 gboolean same_account = FALSE;
1752 /* Ignore transport account removals, we're not showing them
1753 in the folder view */
1754 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1757 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1758 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1762 self = MODEST_FOLDER_VIEW (user_data);
1763 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1765 /* Invalidate the cur_folder_store only if the selected folder
1766 belongs to the account that is being removed */
1767 same_account = same_account_selected (self, account);
1769 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1770 gtk_tree_selection_unselect_all (sel);
1773 /* Invalidate row to select only if the folder to select
1774 belongs to the account that is being removed*/
1775 if (priv->folder_to_select) {
1776 TnyAccount *folder_to_select_account = NULL;
1778 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1779 if (folder_to_select_account == account) {
1780 modest_folder_view_disable_next_folder_selection (self);
1781 g_object_unref (priv->folder_to_select);
1782 priv->folder_to_select = NULL;
1784 g_object_unref (folder_to_select_account);
1787 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1788 &filter_model, NULL, &model))
1791 /* Disconnect the signal handler */
1792 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1793 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1794 if (g_signal_handler_is_connected (account,
1795 priv->outbox_deleted_handler))
1796 g_signal_handler_disconnect (account,
1797 priv->outbox_deleted_handler);
1800 /* Remove the account from the model */
1801 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1803 /* If the removed account is the currently viewed one then
1804 clear the configuration value. The new visible account will be the default account */
1805 if (priv->visible_account_id &&
1806 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1808 /* Clear the current visible account_id */
1809 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1810 modest_folder_view_set_mailbox (self, NULL);
1812 /* Call the restore method, this will set the new visible account */
1813 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1814 MODEST_CONF_FOLDER_VIEW_KEY);
1817 /* Refilter the model */
1818 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1820 /* Select the first INBOX if the currently selected folder
1821 belongs to the account that is being deleted */
1823 g_idle_add (on_idle_select_first_inbox_or_local, self);
1827 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1829 GtkTreeViewColumn *col;
1831 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1833 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1835 g_printerr ("modest: failed get column for title\n");
1839 gtk_tree_view_column_set_title (col, title);
1840 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1845 modest_folder_view_on_map (ModestFolderView *self,
1846 GdkEventExpose *event,
1849 ModestFolderViewPrivate *priv;
1851 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1853 /* This won't happen often */
1854 if (G_UNLIKELY (priv->reselect)) {
1855 /* Select the first inbox or the local account if not found */
1857 /* TODO: this could cause a lock at startup, so we
1858 comment it for the moment. We know that this will
1859 be a bug, because the INBOX is not selected, but we
1860 need to rewrite some parts of Modest to avoid the
1861 deathlock situation */
1862 /* TODO: check if this is still the case */
1863 priv->reselect = FALSE;
1864 modest_folder_view_select_first_inbox_or_local (self);
1865 /* Notify the display name observers */
1866 g_signal_emit (G_OBJECT(self),
1867 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1871 if (priv->reexpand) {
1872 expand_root_items (self);
1873 priv->reexpand = FALSE;
1880 modest_folder_view_new (TnyFolderStoreQuery *query)
1882 return modest_folder_view_new_full (query, TRUE);
1886 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1889 ModestFolderViewPrivate *priv;
1890 GtkTreeSelection *sel;
1892 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1893 #ifdef MODEST_TOOLKIT_HILDON2
1894 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1897 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1900 priv->query = g_object_ref (query);
1902 priv->do_refresh = do_refresh;
1904 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1905 priv->changed_signal = g_signal_connect (sel, "changed",
1906 G_CALLBACK (on_selection_changed), self);
1908 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1910 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1912 return GTK_WIDGET(self);
1915 /* this feels dirty; any other way to expand all the root items? */
1917 expand_root_items (ModestFolderView *self)
1920 GtkTreeModel *model;
1923 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1924 path = gtk_tree_path_new_first ();
1926 /* all folders should have child items, so.. */
1928 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1929 gtk_tree_path_next (path);
1930 } while (gtk_tree_model_get_iter (model, &iter, path));
1932 gtk_tree_path_free (path);
1936 is_parent_of (TnyFolder *a, TnyFolder *b)
1939 gboolean retval = FALSE;
1941 a_id = tny_folder_get_id (a);
1943 gchar *string_to_match;
1946 string_to_match = g_strconcat (a_id, "/", NULL);
1947 b_id = tny_folder_get_id (b);
1948 retval = g_str_has_prefix (b_id, string_to_match);
1949 g_free (string_to_match);
1955 typedef struct _ForeachFolderInfo {
1958 } ForeachFolderInfo;
1961 foreach_folder_with_id (GtkTreeModel *model,
1966 ForeachFolderInfo *info;
1969 info = (ForeachFolderInfo *) data;
1970 gtk_tree_model_get (model, iter,
1971 INSTANCE_COLUMN, &instance,
1974 if (TNY_IS_FOLDER (instance)) {
1977 id = tny_folder_get_id (TNY_FOLDER (instance));
1979 collate = g_utf8_collate_key (id, -1);
1980 info->found = !strcmp (info->needle, collate);
1986 g_object_unref (instance);
1994 has_folder_with_id (ModestFolderView *self, const gchar *id)
1996 GtkTreeModel *model;
1997 ForeachFolderInfo info = {NULL, FALSE};
1999 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2000 info.needle = g_utf8_collate_key (id, -1);
2002 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
2003 g_free (info.needle);
2009 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
2012 gboolean retval = FALSE;
2014 a_id = tny_folder_get_id (a);
2017 b_id = tny_folder_get_id (b);
2020 const gchar *last_bar;
2021 gchar *string_to_match;
2022 last_bar = g_strrstr (b_id, "/");
2027 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
2028 retval = has_folder_with_id (self, string_to_match);
2029 g_free (string_to_match);
2037 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
2039 ModestFolderViewPrivate *priv;
2040 TnyIterator *iterator;
2041 gboolean retval = TRUE;
2043 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2044 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2046 for (iterator = tny_list_create_iterator (priv->list_to_move);
2047 retval && !tny_iterator_is_done (iterator);
2048 tny_iterator_next (iterator)) {
2050 instance = tny_iterator_get_current (iterator);
2051 if (instance == (GObject *) folder) {
2053 } else if (TNY_IS_FOLDER (instance)) {
2054 retval = !is_parent_of (TNY_FOLDER (instance), folder);
2056 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
2059 g_object_unref (instance);
2061 g_object_unref (iterator);
2068 * We use this function to implement the
2069 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
2070 * account in this case, and the local folders.
2073 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2075 ModestFolderViewPrivate *priv;
2076 gboolean retval = TRUE;
2077 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2078 GObject *instance = NULL;
2079 const gchar *id = NULL;
2081 gboolean found = FALSE;
2082 gboolean cleared = FALSE;
2083 ModestTnyFolderRules rules = 0;
2086 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2087 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2089 gtk_tree_model_get (model, iter,
2090 NAME_COLUMN, &fname,
2092 INSTANCE_COLUMN, &instance,
2095 /* Do not show if there is no instance, this could indeed
2096 happen when the model is being modified while it's being
2097 drawn. This could occur for example when moving folders
2104 if (TNY_IS_ACCOUNT (instance)) {
2105 TnyAccount *acc = TNY_ACCOUNT (instance);
2106 const gchar *account_id = tny_account_get_id (acc);
2108 /* If it isn't a special folder,
2109 * don't show it unless it is the visible account: */
2110 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2111 !modest_tny_account_is_virtual_local_folders (acc) &&
2112 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2114 /* Show only the visible account id */
2115 if (priv->visible_account_id) {
2116 if (strcmp (account_id, priv->visible_account_id))
2123 /* Never show these to the user. They are merged into one folder
2124 * in the local-folders account instead: */
2125 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2128 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2129 /* Only show special folders for current account if needed */
2130 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2131 TnyAccount *account;
2133 account = tny_folder_get_account (TNY_FOLDER (instance));
2135 if (TNY_IS_ACCOUNT (account)) {
2136 const gchar *account_id = tny_account_get_id (account);
2138 if (!modest_tny_account_is_virtual_local_folders (account) &&
2139 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2140 /* Show only the visible account id */
2141 if (priv->visible_account_id) {
2142 if (strcmp (account_id, priv->visible_account_id)) {
2144 } else if (priv->mailbox) {
2145 /* Filter mailboxes */
2146 if (!g_str_has_prefix (fname, priv->mailbox)) {
2148 } else if (!strcmp (fname, priv->mailbox)) {
2149 /* Hide mailbox parent */
2155 g_object_unref (account);
2162 /* Check hiding (if necessary) */
2163 cleared = modest_email_clipboard_cleared (priv->clipboard);
2164 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2165 id = tny_folder_get_id (TNY_FOLDER(instance));
2166 if (priv->hidding_ids != NULL)
2167 for (i=0; i < priv->n_selected && !found; i++)
2168 if (priv->hidding_ids[i] != NULL && id != NULL)
2169 found = (!strcmp (priv->hidding_ids[i], id));
2174 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2175 folder as no message can be move there according to UI specs */
2176 if (retval && !priv->show_non_move) {
2177 if (priv->list_to_move &&
2178 tny_list_get_length (priv->list_to_move) > 0 &&
2179 TNY_IS_FOLDER (instance)) {
2180 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2182 if (retval && TNY_IS_FOLDER (instance) &&
2183 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2185 case TNY_FOLDER_TYPE_OUTBOX:
2186 case TNY_FOLDER_TYPE_SENT:
2187 case TNY_FOLDER_TYPE_DRAFTS:
2190 case TNY_FOLDER_TYPE_UNKNOWN:
2191 case TNY_FOLDER_TYPE_NORMAL:
2192 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2193 if (type == TNY_FOLDER_TYPE_INVALID)
2194 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2196 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2197 type == TNY_FOLDER_TYPE_SENT
2198 || type == TNY_FOLDER_TYPE_DRAFTS)
2205 if (retval && TNY_IS_ACCOUNT (instance) &&
2206 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2207 ModestProtocolType protocol_type;
2209 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2210 retval = !modest_protocol_registry_protocol_type_has_tag
2211 (modest_runtime_get_protocol_registry (),
2213 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2217 /* apply special filters */
2218 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2219 if (TNY_IS_ACCOUNT (instance))
2223 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2224 if (TNY_IS_FOLDER (instance))
2228 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2229 if (TNY_IS_ACCOUNT (instance)) {
2230 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2232 } else if (TNY_IS_FOLDER (instance)) {
2233 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2238 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2239 if (TNY_IS_ACCOUNT (instance)) {
2240 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2242 } else if (TNY_IS_FOLDER (instance)) {
2243 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2248 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2249 /* A mailbox is a fake folder with an @ in the middle of the name */
2250 if (!TNY_IS_FOLDER (instance) ||
2251 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2254 const gchar *folder_name;
2255 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2256 if (!folder_name || strchr (folder_name, '@') == NULL)
2262 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2263 if (TNY_IS_FOLDER (instance)) {
2264 /* Check folder rules */
2265 ModestTnyFolderRules rules;
2267 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2268 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2269 } else if (TNY_IS_ACCOUNT (instance)) {
2270 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2278 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2279 if (TNY_IS_FOLDER (instance)) {
2280 TnyFolderType guess_type;
2282 if (TNY_FOLDER_TYPE_NORMAL) {
2283 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2289 case TNY_FOLDER_TYPE_OUTBOX:
2290 case TNY_FOLDER_TYPE_SENT:
2291 case TNY_FOLDER_TYPE_DRAFTS:
2292 case TNY_FOLDER_TYPE_ARCHIVE:
2293 case TNY_FOLDER_TYPE_INBOX:
2296 case TNY_FOLDER_TYPE_UNKNOWN:
2297 case TNY_FOLDER_TYPE_NORMAL:
2303 } else if (TNY_IS_ACCOUNT (instance)) {
2308 if (retval && TNY_IS_FOLDER (instance)) {
2309 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2312 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2313 if (TNY_IS_FOLDER (instance)) {
2314 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2315 } else if (TNY_IS_ACCOUNT (instance)) {
2320 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2321 if (TNY_IS_FOLDER (instance)) {
2322 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2323 } else if (TNY_IS_ACCOUNT (instance)) {
2328 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2329 if (TNY_IS_FOLDER (instance)) {
2330 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2331 } else if (TNY_IS_ACCOUNT (instance)) {
2337 g_object_unref (instance);
2345 modest_folder_view_update_model (ModestFolderView *self,
2346 TnyAccountStore *account_store)
2348 ModestFolderViewPrivate *priv;
2349 GtkTreeModel *model;
2350 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2352 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2353 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2356 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2358 /* Notify that there is no folder selected */
2359 g_signal_emit (G_OBJECT(self),
2360 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2362 if (priv->cur_folder_store) {
2363 g_object_unref (priv->cur_folder_store);
2364 priv->cur_folder_store = NULL;
2367 /* FIXME: the local accounts are not shown when the query
2368 selects only the subscribed folders */
2369 #ifdef MODEST_TOOLKIT_HILDON2
2370 TnyGtkFolderListStoreFlags flags;
2371 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2372 if (priv->do_refresh)
2373 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2375 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2376 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2378 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2379 MODEST_FOLDER_PATH_SEPARATOR);
2381 model = tny_gtk_folder_store_tree_model_new (NULL);
2384 /* When the model is a list store (plain representation) the
2385 outbox is not a child of any account so we have to manually
2386 delete it because removing the local folders account won't
2387 delete it (because tny_folder_get_account() is not defined
2388 for a merge folder */
2389 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2390 TnyAccount *account;
2391 ModestTnyAccountStore *acc_store;
2393 acc_store = modest_runtime_get_account_store ();
2394 account = modest_tny_account_store_get_local_folders_account (acc_store);
2396 if (g_signal_handler_is_connected (account,
2397 priv->outbox_deleted_handler))
2398 g_signal_handler_disconnect (account,
2399 priv->outbox_deleted_handler);
2401 priv->outbox_deleted_handler =
2402 g_signal_connect (account,
2404 G_CALLBACK (on_outbox_deleted_cb),
2406 g_object_unref (account);
2409 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2410 /* Get the accounts */
2411 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2413 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2415 if (priv->visible_account_id) {
2416 TnyAccount *account;
2418 /* Add local folders account */
2419 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2422 tny_list_append (TNY_LIST (model), (GObject *) account);
2423 g_object_unref (account);
2426 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2429 tny_list_append (TNY_LIST (model), (GObject *) account);
2430 g_object_unref (account);
2433 /* Add visible account */
2434 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2435 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2436 priv->visible_account_id);
2438 tny_list_append (TNY_LIST (model), (GObject *) account);
2439 g_object_unref (account);
2441 g_warning ("You need to set an account first");
2442 g_object_unref (model);
2446 g_warning ("You need to set an account first");
2447 g_object_unref (model);
2452 sortable = gtk_tree_model_sort_new_with_model (model);
2453 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2455 GTK_SORT_ASCENDING);
2456 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2458 cmp_rows, NULL, NULL);
2460 /* Create filter model */
2461 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2462 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2467 GtkTreeModel *old_tny_model = NULL;
2468 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2469 if (priv->signal_handlers > 0) {
2470 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2471 G_OBJECT (old_tny_model),
2472 "activity-changed");
2477 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2478 #ifndef MODEST_TOOLKIT_HILDON2
2479 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2480 (GCallback) on_row_inserted_maybe_select_folder, self);
2483 #ifdef MODEST_TOOLKIT_HILDON2
2484 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2487 G_CALLBACK (on_activity_changed),
2491 g_object_unref (model);
2492 g_object_unref (filter_model);
2493 g_object_unref (sortable);
2495 /* Force a reselection of the INBOX next time the widget is shown */
2496 priv->reselect = TRUE;
2503 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2505 GtkTreeModel *model = NULL;
2506 TnyFolderStore *folder = NULL;
2508 ModestFolderView *tree_view = NULL;
2509 ModestFolderViewPrivate *priv = NULL;
2510 gboolean selected = FALSE;
2512 g_return_if_fail (sel);
2513 g_return_if_fail (user_data);
2515 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2517 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2519 tree_view = MODEST_FOLDER_VIEW (user_data);
2522 gtk_tree_model_get (model, &iter,
2523 INSTANCE_COLUMN, &folder,
2526 /* If the folder is the same do not notify */
2527 if (folder && priv->cur_folder_store == folder) {
2528 g_object_unref (folder);
2533 /* Current folder was unselected */
2534 if (priv->cur_folder_store) {
2535 /* We must do this firstly because a libtinymail-camel
2536 implementation detail. If we issue the signal
2537 before doing the sync_async, then that signal could
2538 cause (and it actually does it) a free of the
2539 summary of the folder (because the main window will
2540 clear the headers view */
2541 #ifndef MODEST_TOOLKIT_HILDON2
2542 if (TNY_IS_FOLDER(priv->cur_folder_store))
2543 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2544 FALSE, NULL, NULL, NULL);
2547 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2548 priv->cur_folder_store, FALSE);
2550 g_object_unref (priv->cur_folder_store);
2551 priv->cur_folder_store = NULL;
2554 /* New current references */
2555 priv->cur_folder_store = folder;
2557 /* New folder has been selected. Do not notify if there is
2558 nothing new selected */
2560 g_signal_emit (G_OBJECT(tree_view),
2561 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2562 0, priv->cur_folder_store, TRUE);
2567 on_row_activated (GtkTreeView *treeview,
2568 GtkTreePath *treepath,
2569 GtkTreeViewColumn *column,
2572 GtkTreeModel *model = NULL;
2573 TnyFolderStore *folder = NULL;
2575 ModestFolderView *self = NULL;
2576 ModestFolderViewPrivate *priv = NULL;
2578 g_return_if_fail (treeview);
2579 g_return_if_fail (user_data);
2581 self = MODEST_FOLDER_VIEW (user_data);
2582 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2584 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2586 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2589 gtk_tree_model_get (model, &iter,
2590 INSTANCE_COLUMN, &folder,
2593 g_signal_emit (G_OBJECT(self),
2594 signals[FOLDER_ACTIVATED_SIGNAL],
2597 #ifdef MODEST_TOOLKIT_HILDON2
2598 HildonUIMode ui_mode;
2599 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2600 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2601 if (priv->cur_folder_store)
2602 g_object_unref (priv->cur_folder_store);
2603 priv->cur_folder_store = g_object_ref (folder);
2607 g_object_unref (folder);
2611 modest_folder_view_get_selected (ModestFolderView *self)
2613 ModestFolderViewPrivate *priv;
2615 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2617 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2618 if (priv->cur_folder_store)
2619 g_object_ref (priv->cur_folder_store);
2621 return priv->cur_folder_store;
2625 get_cmp_rows_type_pos (GObject *folder)
2627 /* Remote accounts -> Local account -> MMC account .*/
2630 if (TNY_IS_ACCOUNT (folder) &&
2631 modest_tny_account_is_virtual_local_folders (
2632 TNY_ACCOUNT (folder))) {
2634 } else if (TNY_IS_ACCOUNT (folder)) {
2635 TnyAccount *account = TNY_ACCOUNT (folder);
2636 const gchar *account_id = tny_account_get_id (account);
2637 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2643 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2644 return -1; /* Should never happen */
2649 inbox_is_special (TnyFolderStore *folder_store)
2651 gboolean is_special = TRUE;
2653 if (TNY_IS_FOLDER (folder_store)) {
2657 gchar *last_inbox_bar;
2659 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2660 downcase = g_utf8_strdown (id, -1);
2661 last_bar = g_strrstr (downcase, "/");
2663 last_inbox_bar = g_strrstr (downcase, "inbox/");
2664 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2675 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2677 TnyAccount *account;
2678 gboolean is_special;
2679 /* Inbox, Outbox, Drafts, Sent, User */
2682 if (!TNY_IS_FOLDER (folder_store))
2685 case TNY_FOLDER_TYPE_INBOX:
2687 account = tny_folder_get_account (folder_store);
2688 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2690 /* In inbox case we need to know if the inbox is really the top
2691 * inbox of the account, or if it's a submailbox inbox. To do
2692 * this we'll apply an heuristic rule: Find last "/" and check
2693 * if it's preceeded by another Inbox */
2694 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2695 g_object_unref (account);
2696 return is_special?0:4;
2699 case TNY_FOLDER_TYPE_OUTBOX:
2700 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2702 case TNY_FOLDER_TYPE_DRAFTS:
2704 account = tny_folder_get_account (folder_store);
2705 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2706 g_object_unref (account);
2707 return is_special?1:4;
2710 case TNY_FOLDER_TYPE_SENT:
2712 account = tny_folder_get_account (folder_store);
2713 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2714 g_object_unref (account);
2715 return is_special?3:4;
2724 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2726 const gchar *a1_name, *a2_name;
2728 a1_name = tny_account_get_name (a1);
2729 a2_name = tny_account_get_name (a2);
2731 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2735 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2737 TnyAccount *a1 = NULL, *a2 = NULL;
2740 if (TNY_IS_ACCOUNT (s1)) {
2741 a1 = TNY_ACCOUNT (g_object_ref (s1));
2742 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2743 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2746 if (TNY_IS_ACCOUNT (s2)) {
2747 a2 = TNY_ACCOUNT (g_object_ref (s2));
2748 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2749 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2766 /* First we sort with the type of account */
2767 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2771 cmp = compare_account_names (a1, a2);
2775 g_object_unref (a1);
2777 g_object_unref (a2);
2783 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2785 gint is_account1, is_account2;
2787 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2788 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2790 return is_account2 - is_account1;
2794 compare_folders (const gchar *name1, const gchar *name2)
2796 const gchar *separator1, *separator2;
2797 const gchar *next1, *next2;
2801 if (name1 == NULL || name1[0] == '\0')
2803 if (name2 == NULL || name2[0] == '\0')
2806 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2808 top1 = g_strndup (name1, separator1 - name1);
2810 top1 = g_strdup (name1);
2813 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2815 top2 = g_strndup (name2, separator2 - name2);
2817 top2 = g_strdup (name2);
2821 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2828 if (separator1 == NULL && separator2 == NULL)
2831 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2832 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2834 return compare_folders (next1, next2);
2839 * This function orders the mail accounts according to these rules:
2840 * 1st - remote accounts
2841 * 2nd - local account
2845 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2849 gchar *name1 = NULL;
2850 gchar *name2 = NULL;
2851 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2852 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2853 GObject *folder1 = NULL;
2854 GObject *folder2 = NULL;
2856 gtk_tree_model_get (tree_model, iter1,
2857 NAME_COLUMN, &name1,
2859 INSTANCE_COLUMN, &folder1,
2861 gtk_tree_model_get (tree_model, iter2,
2862 NAME_COLUMN, &name2,
2863 TYPE_COLUMN, &type2,
2864 INSTANCE_COLUMN, &folder2,
2867 /* Return if we get no folder. This could happen when folder
2868 operations are happening. The model is updated after the
2869 folder copy/move actually occurs, so there could be
2870 situations where the model to be drawn is not correct */
2871 if (!folder1 || !folder2)
2874 /* Sort by type. First the special folders, then the archives */
2875 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2879 /* Now we sort using the account of each folder */
2880 if (TNY_IS_FOLDER_STORE (folder1) &&
2881 TNY_IS_FOLDER_STORE (folder2)) {
2882 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2886 /* Each group is preceeded by its account */
2887 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2892 /* Pure sort by name */
2893 cmp = compare_folders (name1, name2);
2896 g_object_unref(G_OBJECT(folder1));
2898 g_object_unref(G_OBJECT(folder2));
2906 /*****************************************************************************/
2907 /* DRAG and DROP stuff */
2908 /*****************************************************************************/
2910 * This function fills the #GtkSelectionData with the row and the
2911 * model that has been dragged. It's called when this widget is a
2912 * source for dnd after the event drop happened
2915 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2916 guint info, guint time, gpointer data)
2918 GtkTreeSelection *selection;
2919 GtkTreeModel *model;
2921 GtkTreePath *source_row;
2923 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2924 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2926 source_row = gtk_tree_model_get_path (model, &iter);
2927 gtk_tree_set_row_drag_data (selection_data,
2931 gtk_tree_path_free (source_row);
2935 typedef struct _DndHelper {
2936 ModestFolderView *folder_view;
2937 gboolean delete_source;
2938 GtkTreePath *source_row;
2942 dnd_helper_destroyer (DndHelper *helper)
2944 /* Free the helper */
2945 gtk_tree_path_free (helper->source_row);
2946 g_slice_free (DndHelper, helper);
2950 xfer_folder_cb (ModestMailOperation *mail_op,
2951 TnyFolder *new_folder,
2955 /* Select the folder */
2956 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2962 /* get the folder for the row the treepath refers to. */
2963 /* folder must be unref'd */
2964 static TnyFolderStore *
2965 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2968 TnyFolderStore *folder = NULL;
2970 if (gtk_tree_model_get_iter (model,&iter, path))
2971 gtk_tree_model_get (model, &iter,
2972 INSTANCE_COLUMN, &folder,
2979 * This function is used by drag_data_received_cb to manage drag and
2980 * drop of a header, i.e, and drag from the header view to the folder
2984 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2985 GtkTreeModel *dest_model,
2986 GtkTreePath *dest_row,
2987 GtkSelectionData *selection_data)
2989 TnyList *headers = NULL;
2990 TnyFolder *folder = NULL, *src_folder = NULL;
2991 TnyFolderType folder_type;
2992 GtkTreeIter source_iter, dest_iter;
2993 ModestWindowMgr *mgr = NULL;
2994 ModestWindow *main_win = NULL;
2995 gchar **uris, **tmp;
2997 /* Build the list of headers */
2998 mgr = modest_runtime_get_window_mgr ();
2999 headers = tny_simple_list_new ();
3000 uris = modest_dnd_selection_data_get_paths (selection_data);
3003 while (*tmp != NULL) {
3006 gboolean first = TRUE;
3009 path = gtk_tree_path_new_from_string (*tmp);
3010 gtk_tree_model_get_iter (source_model, &source_iter, path);
3011 gtk_tree_model_get (source_model, &source_iter,
3012 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
3015 /* Do not enable d&d of headers already opened */
3016 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
3017 tny_list_append (headers, G_OBJECT (header));
3019 if (G_UNLIKELY (first)) {
3020 src_folder = tny_header_get_folder (header);
3024 /* Free and go on */
3025 gtk_tree_path_free (path);
3026 g_object_unref (header);
3031 /* This could happen ig we perform a d&d very quickly over the
3032 same row that row could dissapear because message is
3034 if (!TNY_IS_FOLDER (src_folder))
3037 /* Get the target folder */
3038 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3039 gtk_tree_model_get (dest_model, &dest_iter,
3043 if (!folder || !TNY_IS_FOLDER(folder)) {
3044 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
3048 folder_type = modest_tny_folder_guess_folder_type (folder);
3049 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
3050 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
3051 goto cleanup; /* cannot move messages there */
3054 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3055 /* g_warning ("folder not writable"); */
3056 goto cleanup; /* verboten! */
3059 /* Ask for confirmation to move */
3060 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
3062 g_warning ("%s: BUG: no main window found", __FUNCTION__);
3066 /* Transfer messages */
3067 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
3072 if (G_IS_OBJECT (src_folder))
3073 g_object_unref (src_folder);
3074 if (G_IS_OBJECT(folder))
3075 g_object_unref (G_OBJECT (folder));
3076 if (G_IS_OBJECT(headers))
3077 g_object_unref (headers);
3081 TnyFolderStore *src_folder;
3082 TnyFolderStore *dst_folder;
3083 ModestFolderView *folder_view;
3088 dnd_folder_info_destroyer (DndFolderInfo *info)
3090 if (info->src_folder)
3091 g_object_unref (info->src_folder);
3092 if (info->dst_folder)
3093 g_object_unref (info->dst_folder);
3094 g_slice_free (DndFolderInfo, info);
3098 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
3099 GtkWindow *parent_window,
3100 TnyAccount *account)
3103 modest_ui_actions_on_account_connection_error (parent_window, account);
3105 /* Free the helper & info */
3106 dnd_helper_destroyer (info->helper);
3107 dnd_folder_info_destroyer (info);
3111 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
3113 GtkWindow *parent_window,
3114 TnyAccount *account,
3117 DndFolderInfo *info = NULL;
3118 ModestMailOperation *mail_op;
3120 info = (DndFolderInfo *) user_data;
3122 if (err || canceled) {
3123 dnd_on_connection_failed_destroyer (info, parent_window, account);
3127 /* Do the mail operation */
3128 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
3129 modest_ui_actions_move_folder_error_handler,
3130 info->src_folder, NULL);
3132 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3135 /* Transfer the folder */
3136 modest_mail_operation_xfer_folder (mail_op,
3137 TNY_FOLDER (info->src_folder),
3139 info->helper->delete_source,
3141 info->helper->folder_view);
3144 g_object_unref (G_OBJECT (mail_op));
3145 dnd_helper_destroyer (info->helper);
3146 dnd_folder_info_destroyer (info);
3151 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3153 GtkWindow *parent_window,
3154 TnyAccount *account,
3157 DndFolderInfo *info = NULL;
3159 info = (DndFolderInfo *) user_data;
3161 if (err || canceled) {
3162 dnd_on_connection_failed_destroyer (info, parent_window, account);
3166 /* Connect to source folder and perform the copy/move */
3167 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3169 drag_and_drop_from_folder_view_src_folder_performer,
3174 * This function is used by drag_data_received_cb to manage drag and
3175 * drop of a folder, i.e, and drag from the folder view to the same
3179 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3180 GtkTreeModel *dest_model,
3181 GtkTreePath *dest_row,
3182 GtkSelectionData *selection_data,
3185 GtkTreeIter dest_iter, iter;
3186 TnyFolderStore *dest_folder = NULL;
3187 TnyFolderStore *folder = NULL;
3188 gboolean forbidden = FALSE;
3190 DndFolderInfo *info = NULL;
3192 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3194 g_warning ("%s: BUG: no main window", __FUNCTION__);
3195 dnd_helper_destroyer (helper);
3200 /* check the folder rules for the destination */
3201 folder = tree_path_to_folder (dest_model, dest_row);
3202 if (TNY_IS_FOLDER(folder)) {
3203 ModestTnyFolderRules rules =
3204 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3205 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3206 } else if (TNY_IS_FOLDER_STORE(folder)) {
3207 /* enable local root as destination for folders */
3208 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3209 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3212 g_object_unref (folder);
3215 /* check the folder rules for the source */
3216 folder = tree_path_to_folder (source_model, helper->source_row);
3217 if (TNY_IS_FOLDER(folder)) {
3218 ModestTnyFolderRules rules =
3219 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3220 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3223 g_object_unref (folder);
3227 /* Check if the drag is possible */
3228 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3230 modest_platform_run_information_dialog ((GtkWindow *) win,
3231 _("mail_in_ui_folder_move_target_error"),
3233 /* Restore the previous selection */
3234 folder = tree_path_to_folder (source_model, helper->source_row);
3236 if (TNY_IS_FOLDER (folder))
3237 modest_folder_view_select_folder (helper->folder_view,
3238 TNY_FOLDER (folder), FALSE);
3239 g_object_unref (folder);
3241 dnd_helper_destroyer (helper);
3246 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3247 gtk_tree_model_get (dest_model, &dest_iter,
3250 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3251 gtk_tree_model_get (source_model, &iter,
3255 /* Create the info for the performer */
3256 info = g_slice_new0 (DndFolderInfo);
3257 info->src_folder = g_object_ref (folder);
3258 info->dst_folder = g_object_ref (dest_folder);
3259 info->helper = helper;
3261 /* Connect to the destination folder and perform the copy/move */
3262 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3264 drag_and_drop_from_folder_view_dst_folder_performer,
3268 g_object_unref (dest_folder);
3269 g_object_unref (folder);
3273 * This function receives the data set by the "drag-data-get" signal
3274 * handler. This information comes within the #GtkSelectionData. This
3275 * function will manage both the drags of folders of the treeview and
3276 * drags of headers of the header view widget.
3279 on_drag_data_received (GtkWidget *widget,
3280 GdkDragContext *context,
3283 GtkSelectionData *selection_data,
3288 GtkWidget *source_widget;
3289 GtkTreeModel *dest_model, *source_model;
3290 GtkTreePath *source_row, *dest_row;
3291 GtkTreeViewDropPosition pos;
3292 gboolean delete_source = FALSE;
3293 gboolean success = FALSE;
3295 /* Do not allow further process */
3296 g_signal_stop_emission_by_name (widget, "drag-data-received");
3297 source_widget = gtk_drag_get_source_widget (context);
3299 /* Get the action */
3300 if (context->action == GDK_ACTION_MOVE) {
3301 delete_source = TRUE;
3303 /* Notify that there is no folder selected. We need to
3304 do this in order to update the headers view (and
3305 its monitors, because when moving, the old folder
3306 won't longer exist. We can not wait for the end of
3307 the operation, because the operation won't start if
3308 the folder is in use */
3309 if (source_widget == widget) {
3310 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3311 gtk_tree_selection_unselect_all (sel);
3315 /* Check if the get_data failed */
3316 if (selection_data == NULL || selection_data->length < 0)
3319 /* Select the destination model */
3320 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3322 /* Get the path to the destination row. Can not call
3323 gtk_tree_view_get_drag_dest_row() because the source row
3324 is not selected anymore */
3325 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3328 /* Only allow drops IN other rows */
3330 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3331 pos == GTK_TREE_VIEW_DROP_AFTER)
3335 /* Drags from the header view */
3336 if (source_widget != widget) {
3337 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3339 drag_and_drop_from_header_view (source_model,
3344 DndHelper *helper = NULL;
3346 /* Get the source model and row */
3347 gtk_tree_get_row_drag_data (selection_data,
3351 /* Create the helper */
3352 helper = g_slice_new0 (DndHelper);
3353 helper->delete_source = delete_source;
3354 helper->source_row = gtk_tree_path_copy (source_row);
3355 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3357 drag_and_drop_from_folder_view (source_model,
3363 gtk_tree_path_free (source_row);
3367 gtk_tree_path_free (dest_row);
3370 /* Finish the drag and drop */
3371 gtk_drag_finish (context, success, FALSE, time);
3375 * We define a "drag-drop" signal handler because we do not want to
3376 * use the default one, because the default one always calls
3377 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3378 * signal handler, because there we have all the information available
3379 * to know if the dnd was a success or not.
3382 drag_drop_cb (GtkWidget *widget,
3383 GdkDragContext *context,
3391 if (!context->targets)
3394 /* Check if we're dragging a folder row */
3395 target = gtk_drag_dest_find_target (widget, context, NULL);
3397 /* Request the data from the source. */
3398 gtk_drag_get_data(widget, context, target, time);
3404 * This function expands a node of a tree view if it's not expanded
3405 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3406 * does that, so that's why they're here.
3409 expand_row_timeout (gpointer data)
3411 GtkTreeView *tree_view = data;
3412 GtkTreePath *dest_path = NULL;
3413 GtkTreeViewDropPosition pos;
3414 gboolean result = FALSE;
3416 gdk_threads_enter ();
3418 gtk_tree_view_get_drag_dest_row (tree_view,
3423 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3424 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3425 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3426 gtk_tree_path_free (dest_path);
3430 gtk_tree_path_free (dest_path);
3435 gdk_threads_leave ();
3441 * This function is called whenever the pointer is moved over a widget
3442 * while dragging some data. It installs a timeout that will expand a
3443 * node of the treeview if not expanded yet. This function also calls
3444 * gdk_drag_status in order to set the suggested action that will be
3445 * used by the "drag-data-received" signal handler to know if we
3446 * should do a move or just a copy of the data.
3449 on_drag_motion (GtkWidget *widget,
3450 GdkDragContext *context,
3456 GtkTreeViewDropPosition pos;
3457 GtkTreePath *dest_row;
3458 GtkTreeModel *dest_model;
3459 ModestFolderViewPrivate *priv;
3460 GdkDragAction suggested_action;
3461 gboolean valid_location = FALSE;
3462 TnyFolderStore *folder = NULL;
3464 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3466 if (priv->timer_expander != 0) {
3467 g_source_remove (priv->timer_expander);
3468 priv->timer_expander = 0;
3471 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3476 /* Do not allow drops between folders */
3478 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3479 pos == GTK_TREE_VIEW_DROP_AFTER) {
3480 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3481 gdk_drag_status(context, 0, time);
3482 valid_location = FALSE;
3485 valid_location = TRUE;
3488 /* Check that the destination folder is writable */
3489 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3490 folder = tree_path_to_folder (dest_model, dest_row);
3491 if (folder && TNY_IS_FOLDER (folder)) {
3492 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3494 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3495 valid_location = FALSE;
3500 /* Expand the selected row after 1/2 second */
3501 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3502 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3504 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3506 /* Select the desired action. By default we pick MOVE */
3507 suggested_action = GDK_ACTION_MOVE;
3509 if (context->actions == GDK_ACTION_COPY)
3510 gdk_drag_status(context, GDK_ACTION_COPY, time);
3511 else if (context->actions == GDK_ACTION_MOVE)
3512 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3513 else if (context->actions & suggested_action)
3514 gdk_drag_status(context, suggested_action, time);
3516 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3520 g_object_unref (folder);
3522 gtk_tree_path_free (dest_row);
3524 g_signal_stop_emission_by_name (widget, "drag-motion");
3526 return valid_location;
3530 * This function sets the treeview as a source and a target for dnd
3531 * events. It also connects all the requirede signals.
3534 setup_drag_and_drop (GtkTreeView *self)
3536 /* Set up the folder view as a dnd destination. Set only the
3537 highlight flag, otherwise gtk will have a different
3539 #ifdef MODEST_TOOLKIT_HILDON2
3542 gtk_drag_dest_set (GTK_WIDGET (self),
3543 GTK_DEST_DEFAULT_HIGHLIGHT,
3544 folder_view_drag_types,
3545 G_N_ELEMENTS (folder_view_drag_types),
3546 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3548 g_signal_connect (G_OBJECT (self),
3549 "drag_data_received",
3550 G_CALLBACK (on_drag_data_received),
3554 /* Set up the treeview as a dnd source */
3555 gtk_drag_source_set (GTK_WIDGET (self),
3557 folder_view_drag_types,
3558 G_N_ELEMENTS (folder_view_drag_types),
3559 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3561 g_signal_connect (G_OBJECT (self),
3563 G_CALLBACK (on_drag_motion),
3566 g_signal_connect (G_OBJECT (self),
3568 G_CALLBACK (on_drag_data_get),
3571 g_signal_connect (G_OBJECT (self),
3573 G_CALLBACK (drag_drop_cb),
3578 * This function manages the navigation through the folders using the
3579 * keyboard or the hardware keys in the device
3582 on_key_pressed (GtkWidget *self,
3586 GtkTreeSelection *selection;
3588 GtkTreeModel *model;
3589 gboolean retval = FALSE;
3591 /* Up and Down are automatically managed by the treeview */
3592 if (event->keyval == GDK_Return) {
3593 /* Expand/Collapse the selected row */
3594 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3595 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3598 path = gtk_tree_model_get_path (model, &iter);
3600 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3601 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3603 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3604 gtk_tree_path_free (path);
3606 /* No further processing */
3614 * We listen to the changes in the local folder account name key,
3615 * because we want to show the right name in the view. The local
3616 * folder account name corresponds to the device name in the Maemo
3617 * version. We do this because we do not want to query gconf on each
3618 * tree view refresh. It's better to cache it and change whenever
3622 on_configuration_key_changed (ModestConf* conf,
3624 ModestConfEvent event,
3625 ModestConfNotificationId id,
3626 ModestFolderView *self)
3628 ModestFolderViewPrivate *priv;
3631 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3632 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3634 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3635 g_free (priv->local_account_name);
3637 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3638 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3640 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3641 MODEST_CONF_DEVICE_NAME, NULL);
3643 /* Force a redraw */
3644 #if GTK_CHECK_VERSION(2, 8, 0)
3645 GtkTreeViewColumn * tree_column;
3647 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3649 gtk_tree_view_column_queue_resize (tree_column);
3651 gtk_widget_queue_draw (GTK_WIDGET (self));
3657 modest_folder_view_set_style (ModestFolderView *self,
3658 ModestFolderViewStyle style)
3660 ModestFolderViewPrivate *priv;
3662 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3663 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3664 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3666 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3669 priv->style = style;
3673 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3674 const gchar *account_id)
3676 ModestFolderViewPrivate *priv;
3677 GtkTreeModel *model;
3679 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3681 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3683 /* This will be used by the filter_row callback,
3684 * to decided which rows to show: */
3685 if (priv->visible_account_id) {
3686 g_free (priv->visible_account_id);
3687 priv->visible_account_id = NULL;
3690 priv->visible_account_id = g_strdup (account_id);
3693 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3694 if (GTK_IS_TREE_MODEL_FILTER (model))
3695 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3697 modest_folder_view_update_model(self,
3698 (TnyAccountStore *) modest_runtime_get_account_store());
3700 /* Save settings to gconf */
3701 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3702 MODEST_CONF_FOLDER_VIEW_KEY);
3704 /* Notify observers */
3705 g_signal_emit (G_OBJECT(self),
3706 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3711 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3713 ModestFolderViewPrivate *priv;
3715 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3717 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3719 return (const gchar *) priv->visible_account_id;
3723 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3727 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3729 gtk_tree_model_get (model, iter,
3733 gboolean result = FALSE;
3734 if (type == TNY_FOLDER_TYPE_INBOX) {
3738 *inbox_iter = *iter;
3742 if (gtk_tree_model_iter_children (model, &child, iter)) {
3743 if (find_inbox_iter (model, &child, inbox_iter))
3747 } while (gtk_tree_model_iter_next (model, iter));
3756 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3758 #ifndef MODEST_TOOLKIT_HILDON2
3759 GtkTreeModel *model;
3760 GtkTreeIter iter, inbox_iter;
3761 GtkTreeSelection *sel;
3762 GtkTreePath *path = NULL;
3764 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3766 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3770 expand_root_items (self);
3771 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3773 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3774 g_warning ("%s: model is empty", __FUNCTION__);
3778 if (find_inbox_iter (model, &iter, &inbox_iter))
3779 path = gtk_tree_model_get_path (model, &inbox_iter);
3781 path = gtk_tree_path_new_first ();
3783 /* Select the row and free */
3784 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3785 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3786 gtk_tree_path_free (path);
3789 gtk_widget_grab_focus (GTK_WIDGET(self));
3796 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3801 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3802 TnyFolder* a_folder;
3805 gtk_tree_model_get (model, iter,
3806 INSTANCE_COLUMN, &a_folder,
3812 if (folder == a_folder) {
3813 g_object_unref (a_folder);
3814 *folder_iter = *iter;
3817 g_object_unref (a_folder);
3819 if (gtk_tree_model_iter_children (model, &child, iter)) {
3820 if (find_folder_iter (model, &child, folder_iter, folder))
3824 } while (gtk_tree_model_iter_next (model, iter));
3829 #ifndef MODEST_TOOLKIT_HILDON2
3831 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3834 ModestFolderView *self)
3836 ModestFolderViewPrivate *priv = NULL;
3837 GtkTreeSelection *sel;
3838 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3839 GObject *instance = NULL;
3841 if (!MODEST_IS_FOLDER_VIEW(self))
3844 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3846 priv->reexpand = TRUE;
3848 gtk_tree_model_get (tree_model, iter,
3850 INSTANCE_COLUMN, &instance,
3856 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3857 priv->folder_to_select = g_object_ref (instance);
3859 g_object_unref (instance);
3861 if (priv->folder_to_select) {
3863 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3866 path = gtk_tree_model_get_path (tree_model, iter);
3867 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3869 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3871 gtk_tree_selection_select_iter (sel, iter);
3872 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3874 gtk_tree_path_free (path);
3878 modest_folder_view_disable_next_folder_selection (self);
3880 /* Refilter the model */
3881 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3887 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3889 ModestFolderViewPrivate *priv;
3891 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3893 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3895 if (priv->folder_to_select)
3896 g_object_unref(priv->folder_to_select);
3898 priv->folder_to_select = NULL;
3902 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3903 gboolean after_change)
3905 GtkTreeModel *model;
3906 GtkTreeIter iter, folder_iter;
3907 GtkTreeSelection *sel;
3908 ModestFolderViewPrivate *priv = NULL;
3910 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3911 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3913 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3916 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3917 gtk_tree_selection_unselect_all (sel);
3919 if (priv->folder_to_select)
3920 g_object_unref(priv->folder_to_select);
3921 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3925 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3930 /* Refilter the model, before selecting the folder */
3931 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3933 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3934 g_warning ("%s: model is empty", __FUNCTION__);
3938 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3941 path = gtk_tree_model_get_path (model, &folder_iter);
3942 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3944 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3945 gtk_tree_selection_select_iter (sel, &folder_iter);
3946 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3948 gtk_tree_path_free (path);
3956 modest_folder_view_copy_selection (ModestFolderView *self)
3958 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3960 /* Copy selection */
3961 _clipboard_set_selected_data (self, FALSE);
3965 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3967 ModestFolderViewPrivate *priv = NULL;
3968 GtkTreeModel *model = NULL;
3969 const gchar **hidding = NULL;
3970 guint i, n_selected;
3972 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3973 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3975 /* Copy selection */
3976 if (!_clipboard_set_selected_data (folder_view, TRUE))
3979 /* Get hidding ids */
3980 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3982 /* Clear hidding array created by previous cut operation */
3983 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3985 /* Copy hidding array */
3986 priv->n_selected = n_selected;
3987 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3988 for (i=0; i < n_selected; i++)
3989 priv->hidding_ids[i] = g_strdup(hidding[i]);
3991 /* Hide cut folders */
3992 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3993 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3997 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3998 ModestFolderView *folder_view_dst)
4000 GtkTreeModel *filter_model = NULL;
4001 GtkTreeModel *model = NULL;
4002 GtkTreeModel *new_filter_model = NULL;
4003 GtkTreeModel *old_tny_model = NULL;
4004 GtkTreeModel *new_tny_model = NULL;
4005 ModestFolderViewPrivate *dst_priv;
4007 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
4008 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
4010 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
4011 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
4012 new_tny_model = NULL;
4015 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
4016 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
4017 G_OBJECT (old_tny_model),
4018 "activity-changed");
4020 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
4021 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
4023 /* Build new filter model */
4024 new_filter_model = gtk_tree_model_filter_new (model, NULL);
4025 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
4032 /* Set copied model */
4033 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
4034 #ifndef MODEST_TOOLKIT_HILDON2
4035 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
4036 G_OBJECT(new_filter_model), "row-inserted",
4037 (GCallback) on_row_inserted_maybe_select_folder,
4040 #ifdef MODEST_TOOLKIT_HILDON2
4041 if (new_tny_model) {
4042 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
4043 G_OBJECT (new_tny_model),
4045 G_CALLBACK (on_activity_changed),
4051 g_object_unref (new_filter_model);
4055 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
4058 GtkTreeModel *model = NULL;
4059 ModestFolderViewPrivate* priv;
4061 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4063 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4064 priv->show_non_move = show;
4065 /* modest_folder_view_update_model(folder_view, */
4066 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
4068 /* Hide special folders */
4069 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
4070 if (GTK_IS_TREE_MODEL_FILTER (model)) {
4071 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
4076 modest_folder_view_show_message_count (ModestFolderView *folder_view,
4079 ModestFolderViewPrivate* priv;
4081 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4083 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4084 priv->show_message_count = show;
4086 g_object_set (G_OBJECT (priv->messages_renderer),
4087 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4091 /* Returns FALSE if it did not selected anything */
4093 _clipboard_set_selected_data (ModestFolderView *folder_view,
4096 ModestFolderViewPrivate *priv = NULL;
4097 TnyFolderStore *folder = NULL;
4098 gboolean retval = FALSE;
4100 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
4101 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4103 /* Set selected data on clipboard */
4104 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
4105 folder = modest_folder_view_get_selected (folder_view);
4107 /* Do not allow to select an account */
4108 if (TNY_IS_FOLDER (folder)) {
4109 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
4114 g_object_unref (folder);
4120 _clear_hidding_filter (ModestFolderView *folder_view)
4122 ModestFolderViewPrivate *priv;
4125 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4126 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4128 if (priv->hidding_ids != NULL) {
4129 for (i=0; i < priv->n_selected; i++)
4130 g_free (priv->hidding_ids[i]);
4131 g_free(priv->hidding_ids);
4137 on_display_name_changed (ModestAccountMgr *mgr,
4138 const gchar *account,
4141 ModestFolderView *self;
4143 self = MODEST_FOLDER_VIEW (user_data);
4145 /* Force a redraw */
4146 #if GTK_CHECK_VERSION(2, 8, 0)
4147 GtkTreeViewColumn * tree_column;
4149 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4151 gtk_tree_view_column_queue_resize (tree_column);
4153 gtk_widget_queue_draw (GTK_WIDGET (self));
4158 modest_folder_view_set_cell_style (ModestFolderView *self,
4159 ModestFolderViewCellStyle cell_style)
4161 ModestFolderViewPrivate *priv = NULL;
4163 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4164 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4166 priv->cell_style = cell_style;
4168 g_object_set (G_OBJECT (priv->messages_renderer),
4169 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4172 gtk_widget_queue_draw (GTK_WIDGET (self));
4176 update_style (ModestFolderView *self)
4178 ModestFolderViewPrivate *priv;
4179 GdkColor style_color, style_active_color;
4180 PangoAttrList *attr_list;
4182 PangoAttribute *attr;
4184 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4185 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4189 attr_list = pango_attr_list_new ();
4190 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4191 gdk_color_parse ("grey", &style_color);
4193 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4194 pango_attr_list_insert (attr_list, attr);
4197 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4199 "SmallSystemFont", NULL,
4202 attr = pango_attr_font_desc_new (pango_font_description_copy
4203 (style->font_desc));
4204 pango_attr_list_insert (attr_list, attr);
4206 g_object_set (G_OBJECT (priv->messages_renderer),
4207 "foreground-gdk", &style_color,
4208 "foreground-set", TRUE,
4209 "attributes", attr_list,
4211 pango_attr_list_unref (attr_list);
4214 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4215 priv->active_color = style_active_color;
4217 gdk_color_parse ("000", &(priv->active_color));
4222 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4224 if (strcmp ("style", spec->name) == 0) {
4225 update_style (MODEST_FOLDER_VIEW (obj));
4226 gtk_widget_queue_draw (GTK_WIDGET (obj));
4231 modest_folder_view_set_filter (ModestFolderView *self,
4232 ModestFolderViewFilter filter)
4234 ModestFolderViewPrivate *priv;
4235 GtkTreeModel *filter_model;
4237 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4238 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4240 priv->filter |= filter;
4242 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4243 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4244 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4249 modest_folder_view_unset_filter (ModestFolderView *self,
4250 ModestFolderViewFilter filter)
4252 ModestFolderViewPrivate *priv;
4253 GtkTreeModel *filter_model;
4255 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4256 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4258 priv->filter &= ~filter;
4260 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4261 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4262 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4267 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4268 ModestTnyFolderRules rules)
4270 GtkTreeModel *filter_model;
4272 gboolean fulfil = FALSE;
4274 if (!get_inner_models (self, &filter_model, NULL, NULL))
4277 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4281 TnyFolderStore *folder;
4283 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4285 if (TNY_IS_FOLDER (folder)) {
4286 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4287 /* Folder rules are negative: non_writable, non_deletable... */
4288 if (!(folder_rules & rules))
4291 g_object_unref (folder);
4294 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4300 modest_folder_view_set_list_to_move (ModestFolderView *self,
4303 ModestFolderViewPrivate *priv;
4305 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4306 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4308 if (priv->list_to_move)
4309 g_object_unref (priv->list_to_move);
4312 g_object_ref (list);
4314 priv->list_to_move = list;
4318 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4320 ModestFolderViewPrivate *priv;
4322 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4323 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4326 g_free (priv->mailbox);
4328 priv->mailbox = g_strdup (mailbox);
4330 /* Notify observers */
4331 g_signal_emit (G_OBJECT(self),
4332 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4333 priv->visible_account_id);
4337 modest_folder_view_get_mailbox (ModestFolderView *self)
4339 ModestFolderViewPrivate *priv;
4341 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4342 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4344 return (const gchar *) priv->mailbox;
4348 modest_folder_view_get_activity (ModestFolderView *self)
4350 ModestFolderViewPrivate *priv;
4351 GtkTreeModel *inner_model;
4353 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4354 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4355 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4357 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4358 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4364 #ifdef MODEST_TOOLKIT_HILDON2
4366 on_activity_changed (TnyGtkFolderListStore *store,
4368 ModestFolderView *folder_view)
4370 ModestFolderViewPrivate *priv;
4372 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4373 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4374 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4376 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
4382 modest_folder_view_get_model_tny_list (ModestFolderView *self)
4384 GtkTreeModel *model;
4390 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
4391 ret_value = TNY_LIST (model);
4392 g_object_ref (ret_value);