1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
65 #include <modest-account-protocol.h>
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
98 static void modest_folder_view_init (ModestFolderView *obj);
99 static void modest_folder_view_finalize (GObject *obj);
100 static void modest_folder_view_dispose (GObject *obj);
102 static void tny_account_store_view_init (gpointer g,
103 gpointer iface_data);
105 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
106 TnyAccountStore *account_store);
108 static void on_selection_changed (GtkTreeSelection *sel,
111 static void on_row_activated (GtkTreeView *treeview,
113 GtkTreeViewColumn *column,
116 static void on_account_removed (TnyAccountStore *self,
120 static void on_account_inserted (TnyAccountStore *self,
124 static void on_account_changed (TnyAccountStore *self,
128 static gint cmp_rows (GtkTreeModel *tree_model,
133 static gboolean filter_row (GtkTreeModel *model,
137 static gboolean on_key_pressed (GtkWidget *self,
141 static void on_configuration_key_changed (ModestConf* conf,
143 ModestConfEvent event,
144 ModestConfNotificationId notification_id,
145 ModestFolderView *self);
148 static void on_drag_data_get (GtkWidget *widget,
149 GdkDragContext *context,
150 GtkSelectionData *selection_data,
155 static void on_drag_data_received (GtkWidget *widget,
156 GdkDragContext *context,
159 GtkSelectionData *selection_data,
164 static gboolean on_drag_motion (GtkWidget *widget,
165 GdkDragContext *context,
171 static void expand_root_items (ModestFolderView *self);
173 static gint expand_row_timeout (gpointer data);
175 static void setup_drag_and_drop (GtkTreeView *self);
177 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
180 static void _clear_hidding_filter (ModestFolderView *folder_view);
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
186 ModestFolderView *self);
189 static void on_display_name_changed (ModestAccountMgr *self,
190 const gchar *account,
192 static void update_style (ModestFolderView *self);
193 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
195 static gboolean inbox_is_special (TnyFolderStore *folder_store);
197 static gboolean get_inner_models (ModestFolderView *self,
198 GtkTreeModel **filter_model,
199 GtkTreeModel **sort_model,
200 GtkTreeModel **tny_model);
201 #ifdef MODEST_TOOLKIT_HILDON2
202 static void on_activity_changed (TnyGtkFolderListStore *store,
204 ModestFolderView *folder_view);
208 FOLDER_SELECTION_CHANGED_SIGNAL,
209 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
210 FOLDER_ACTIVATED_SIGNAL,
211 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
212 ACTIVITY_CHANGED_SIGNAL,
216 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
217 struct _ModestFolderViewPrivate {
218 TnyAccountStore *account_store;
219 TnyFolderStore *cur_folder_store;
221 TnyFolder *folder_to_select; /* folder to select after the next update */
223 gulong changed_signal;
224 gulong account_inserted_signal;
225 gulong account_removed_signal;
226 gulong account_changed_signal;
227 gulong conf_key_signal;
228 gulong display_name_changed_signal;
230 /* not unref this object, its a singlenton */
231 ModestEmailClipboard *clipboard;
233 /* Filter tree model */
236 ModestFolderViewFilter filter;
238 TnyFolderStoreQuery *query;
240 guint timer_expander;
242 gchar *local_account_name;
243 gchar *visible_account_id;
245 ModestFolderViewStyle style;
246 ModestFolderViewCellStyle cell_style;
247 gboolean show_message_count;
249 gboolean reselect; /* we use this to force a reselection of the INBOX */
250 gboolean show_non_move;
251 TnyList *list_to_move;
252 gboolean reexpand; /* next time we expose, we'll expand all root folders */
254 GtkCellRenderer *messages_renderer;
256 gulong outbox_deleted_handler;
258 GSList *signal_handlers;
259 GdkColor active_color;
261 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
262 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
263 MODEST_TYPE_FOLDER_VIEW, \
264 ModestFolderViewPrivate))
266 static GObjectClass *parent_class = NULL;
268 static guint signals[LAST_SIGNAL] = {0};
271 modest_folder_view_get_type (void)
273 static GType my_type = 0;
275 static const GTypeInfo my_info = {
276 sizeof(ModestFolderViewClass),
277 NULL, /* base init */
278 NULL, /* base finalize */
279 (GClassInitFunc) modest_folder_view_class_init,
280 NULL, /* class finalize */
281 NULL, /* class data */
282 sizeof(ModestFolderView),
284 (GInstanceInitFunc) modest_folder_view_init,
288 static const GInterfaceInfo tny_account_store_view_info = {
289 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
290 NULL, /* interface_finalize */
291 NULL /* interface_data */
295 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
299 g_type_add_interface_static (my_type,
300 TNY_TYPE_ACCOUNT_STORE_VIEW,
301 &tny_account_store_view_info);
307 modest_folder_view_class_init (ModestFolderViewClass *klass)
309 GObjectClass *gobject_class;
310 GtkTreeViewClass *treeview_class;
311 gobject_class = (GObjectClass*) klass;
312 treeview_class = (GtkTreeViewClass*) klass;
314 parent_class = g_type_class_peek_parent (klass);
315 gobject_class->finalize = modest_folder_view_finalize;
316 gobject_class->dispose = modest_folder_view_dispose;
318 g_type_class_add_private (gobject_class,
319 sizeof(ModestFolderViewPrivate));
321 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
322 g_signal_new ("folder_selection_changed",
323 G_TYPE_FROM_CLASS (gobject_class),
325 G_STRUCT_OFFSET (ModestFolderViewClass,
326 folder_selection_changed),
328 modest_marshal_VOID__POINTER_BOOLEAN,
329 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
332 * This signal is emitted whenever the currently selected
333 * folder display name is computed. Note that the name could
334 * be different to the folder name, because we could append
335 * the unread messages count to the folder name to build the
336 * folder display name
338 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
339 g_signal_new ("folder-display-name-changed",
340 G_TYPE_FROM_CLASS (gobject_class),
342 G_STRUCT_OFFSET (ModestFolderViewClass,
343 folder_display_name_changed),
345 g_cclosure_marshal_VOID__STRING,
346 G_TYPE_NONE, 1, G_TYPE_STRING);
348 signals[FOLDER_ACTIVATED_SIGNAL] =
349 g_signal_new ("folder_activated",
350 G_TYPE_FROM_CLASS (gobject_class),
352 G_STRUCT_OFFSET (ModestFolderViewClass,
355 g_cclosure_marshal_VOID__POINTER,
356 G_TYPE_NONE, 1, G_TYPE_POINTER);
359 * Emitted whenever the visible account changes
361 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
362 g_signal_new ("visible-account-changed",
363 G_TYPE_FROM_CLASS (gobject_class),
365 G_STRUCT_OFFSET (ModestFolderViewClass,
366 visible_account_changed),
368 g_cclosure_marshal_VOID__STRING,
369 G_TYPE_NONE, 1, G_TYPE_STRING);
372 * Emitted when the underlying GtkListStore is updating data
374 signals[ACTIVITY_CHANGED_SIGNAL] =
375 g_signal_new ("activity-changed",
376 G_TYPE_FROM_CLASS (gobject_class),
378 G_STRUCT_OFFSET (ModestFolderViewClass,
381 g_cclosure_marshal_VOID__BOOLEAN,
382 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
384 treeview_class->select_cursor_parent = NULL;
386 #ifdef MODEST_TOOLKIT_HILDON2
387 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
393 /* Retrieves the filter, sort and tny models of the folder view. If
394 any of these does not exist then it returns FALSE */
396 get_inner_models (ModestFolderView *self,
397 GtkTreeModel **filter_model,
398 GtkTreeModel **sort_model,
399 GtkTreeModel **tny_model)
401 GtkTreeModel *s_model, *f_model, *t_model;
403 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
404 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
405 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
409 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
410 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
411 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
415 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
419 *filter_model = f_model;
421 *sort_model = s_model;
423 *tny_model = t_model;
428 /* Simplify checks for NULLs: */
430 strings_are_equal (const gchar *a, const gchar *b)
436 return (strcmp (a, b) == 0);
443 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
445 GObject *instance = NULL;
447 gtk_tree_model_get (model, iter,
448 INSTANCE_COLUMN, &instance,
452 return FALSE; /* keep walking */
454 if (!TNY_IS_ACCOUNT (instance)) {
455 g_object_unref (instance);
456 return FALSE; /* keep walking */
459 /* Check if this is the looked-for account: */
460 TnyAccount *this_account = TNY_ACCOUNT (instance);
461 TnyAccount *account = TNY_ACCOUNT (data);
463 const gchar *this_account_id = tny_account_get_id(this_account);
464 const gchar *account_id = tny_account_get_id(account);
465 g_object_unref (instance);
468 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
469 if (strings_are_equal(this_account_id, account_id)) {
470 /* Tell the model that the data has changed, so that
471 * it calls the cell_data_func callbacks again: */
472 /* TODO: This does not seem to actually cause the new string to be shown: */
473 gtk_tree_model_row_changed (model, path, iter);
475 return TRUE; /* stop walking */
478 return FALSE; /* keep walking */
483 ModestFolderView *self;
484 gchar *previous_name;
485 } GetMmcAccountNameData;
488 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
490 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
492 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
494 if (!strings_are_equal (
495 tny_account_get_name(TNY_ACCOUNT(account)),
496 data->previous_name)) {
498 /* Tell the model that the data has changed, so that
499 * it calls the cell_data_func callbacks again: */
500 ModestFolderView *self = data->self;
501 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
503 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
506 g_free (data->previous_name);
507 g_slice_free (GetMmcAccountNameData, data);
511 convert_parent_folders_to_dots (gchar **item_name)
514 gint n_inbox_parents = 0;
517 gchar *last_separator;
519 if (item_name == NULL)
522 path_start = *item_name;
523 for (c = *item_name; *c != '\0'; c++) {
524 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
526 if (c != path_start) {
527 compare = g_strndup (path_start, c - path_start);
528 compare = g_strstrip (compare);
529 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
539 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
540 if (last_separator != NULL) {
541 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
548 buffer = g_string_new ("");
549 for (i = 0; i < n_parents - n_inbox_parents; i++) {
550 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
552 buffer = g_string_append (buffer, last_separator);
554 *item_name = g_string_free (buffer, FALSE);
560 format_compact_style (gchar **item_name,
562 const gchar *mailbox,
564 gboolean multiaccount,
565 gboolean *use_markup)
569 TnyFolderType folder_type;
571 if (!TNY_IS_FOLDER (instance))
574 folder = (TnyFolder *) instance;
576 folder_type = tny_folder_get_folder_type (folder);
577 is_special = (get_cmp_pos (folder_type, folder)!= 4);
580 /* Remove mailbox prefix if any */
581 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
582 if (g_str_has_prefix (*item_name, prefix)) {
583 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
585 *item_name = new_item_name;
589 if (!is_special || multiaccount) {
590 TnyAccount *account = tny_folder_get_account (folder);
591 const gchar *folder_name;
592 gboolean concat_folder_name = FALSE;
595 /* Should not happen */
599 /* convert parent folders to dots */
600 convert_parent_folders_to_dots (item_name);
602 folder_name = tny_folder_get_name (folder);
603 if (g_str_has_suffix (*item_name, folder_name)) {
604 gchar *offset = g_strrstr (*item_name, folder_name);
606 concat_folder_name = TRUE;
609 buffer = g_string_new ("");
611 buffer = g_string_append (buffer, *item_name);
612 if (concat_folder_name) {
613 if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
614 buffer = g_string_append (buffer, folder_name);
615 /* TODO: append a sensitive string to the remote drafts to
616 * be able to know it's the remote one */
617 /* buffer = g_string_append (buffer, " (TODO:remote)"); */
619 buffer = g_string_append (buffer, folder_name);
623 g_object_unref (account);
625 *item_name = g_string_free (buffer, FALSE);
633 text_cell_data (GtkTreeViewColumn *column,
634 GtkCellRenderer *renderer,
635 GtkTreeModel *tree_model,
639 ModestFolderViewPrivate *priv;
640 GObject *rendobj = (GObject *) renderer;
642 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
643 GObject *instance = NULL;
644 gboolean use_markup = FALSE;
646 gtk_tree_model_get (tree_model, iter,
649 INSTANCE_COLUMN, &instance,
651 if (!fname || !instance)
654 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
655 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
657 gchar *item_name = NULL;
658 gint item_weight = 400;
660 if (type != TNY_FOLDER_TYPE_ROOT) {
665 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
666 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
669 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
670 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
672 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
675 /* Sometimes an special folder is reported by the server as
676 NORMAL, like some versions of Dovecot */
677 if (type == TNY_FOLDER_TYPE_NORMAL ||
678 type == TNY_FOLDER_TYPE_UNKNOWN) {
679 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
683 /* note: we cannot reliably get the counts from the
684 * tree model, we need to use explicit calls on
685 * tny_folder for some reason. Select the number to
686 * show: the unread or unsent messages. in case of
687 * outbox/drafts, show all */
688 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
689 (type == TNY_FOLDER_TYPE_OUTBOX) ||
690 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
691 number = tny_folder_get_all_count (TNY_FOLDER(instance));
694 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
698 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
699 item_name = g_strdup (fname);
706 /* Use bold font style if there are unread or unset messages */
708 if (priv->show_message_count) {
709 item_name = g_strdup_printf ("%s (%d)", fname, number);
711 item_name = g_strdup (fname);
715 item_name = g_strdup (fname);
720 } else if (TNY_IS_ACCOUNT (instance)) {
721 /* If it's a server account */
722 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
723 item_name = g_strdup (priv->local_account_name);
725 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
726 /* fname is only correct when the items are first
727 * added to the model, not when the account is
728 * changed later, so get the name from the account
730 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
733 item_name = g_strdup (fname);
739 if (type == TNY_FOLDER_TYPE_INBOX &&
740 g_str_has_suffix (fname, "Inbox")) {
742 item_name = g_strdup (_("mcen_me_folder_inbox"));
746 item_name = g_strdup ("unknown");
748 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
749 gboolean multiaccount;
751 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
752 /* Convert item_name to markup */
753 format_compact_style (&item_name, instance, priv->mailbox,
755 multiaccount, &use_markup);
758 if (item_name && item_weight) {
759 /* Set the name in the treeview cell: */
760 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
761 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
762 g_object_set (rendobj,
765 "foreground-set", TRUE,
766 "foreground-gdk", &(priv->active_color),
769 g_object_set (rendobj,
771 "foreground-set", FALSE,
773 "weight", item_weight,
777 /* Notify display name observers */
778 /* TODO: What listens for this signal, and how can it use only the new name? */
779 if (((GObject *) priv->cur_folder_store) == instance) {
780 g_signal_emit (G_OBJECT(self),
781 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
788 /* If it is a Memory card account, make sure that we have the correct name.
789 * This function will be trigerred again when the name has been retrieved: */
790 if (TNY_IS_STORE_ACCOUNT (instance) &&
791 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
793 /* Get the account name asynchronously: */
794 GetMmcAccountNameData *callback_data =
795 g_slice_new0(GetMmcAccountNameData);
796 callback_data->self = self;
798 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
800 callback_data->previous_name = g_strdup (name);
802 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
803 on_get_mmc_account_name, callback_data);
807 g_object_unref (G_OBJECT (instance));
813 messages_cell_data (GtkTreeViewColumn *column,
814 GtkCellRenderer *renderer,
815 GtkTreeModel *tree_model,
819 ModestFolderView *self;
820 ModestFolderViewPrivate *priv;
821 GObject *rendobj = (GObject *) renderer;
822 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
823 GObject *instance = NULL;
824 gchar *item_name = NULL;
826 gtk_tree_model_get (tree_model, iter,
828 INSTANCE_COLUMN, &instance,
833 self = MODEST_FOLDER_VIEW (data);
834 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
837 if (type != TNY_FOLDER_TYPE_ROOT) {
842 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
843 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
846 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
848 /* Sometimes an special folder is reported by the server as
849 NORMAL, like some versions of Dovecot */
850 if (type == TNY_FOLDER_TYPE_NORMAL ||
851 type == TNY_FOLDER_TYPE_UNKNOWN) {
852 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
856 /* note: we cannot reliably get the counts from the tree model, we need
857 * to use explicit calls on tny_folder for some reason.
859 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
860 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
861 (type == TNY_FOLDER_TYPE_OUTBOX) ||
862 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
863 number = tny_folder_get_all_count (TNY_FOLDER(instance));
866 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
870 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
872 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
873 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
879 item_name = g_strdup ("");
882 /* Set the name in the treeview cell: */
883 g_object_set (rendobj,"text", item_name, NULL);
891 g_object_unref (G_OBJECT (instance));
897 GdkPixbuf *pixbuf_open;
898 GdkPixbuf *pixbuf_close;
902 static inline GdkPixbuf *
903 get_composite_pixbuf (const gchar *icon_name,
905 GdkPixbuf *base_pixbuf)
907 GdkPixbuf *emblem, *retval = NULL;
909 emblem = modest_platform_get_icon (icon_name, size);
911 retval = gdk_pixbuf_copy (base_pixbuf);
912 gdk_pixbuf_composite (emblem, retval, 0, 0,
913 MIN (gdk_pixbuf_get_width (emblem),
914 gdk_pixbuf_get_width (retval)),
915 MIN (gdk_pixbuf_get_height (emblem),
916 gdk_pixbuf_get_height (retval)),
917 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
918 g_object_unref (emblem);
923 static inline ThreePixbufs *
924 get_composite_icons (const gchar *icon_code,
926 GdkPixbuf **pixbuf_open,
927 GdkPixbuf **pixbuf_close)
929 ThreePixbufs *retval;
931 if (pixbuf && !*pixbuf) {
933 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
935 *pixbuf = gdk_pixbuf_copy (icon);
941 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
942 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
946 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
947 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
951 retval = g_slice_new0 (ThreePixbufs);
952 if (pixbuf && *pixbuf)
953 retval->pixbuf = g_object_ref (*pixbuf);
955 retval->pixbuf = NULL;
956 if (pixbuf_open && *pixbuf_open)
957 retval->pixbuf_open = g_object_ref (*pixbuf_open);
959 retval->pixbuf_open = NULL;
960 if (pixbuf_close && *pixbuf_close)
961 retval->pixbuf_close = g_object_ref (*pixbuf_close);
963 retval->pixbuf_close = NULL;
968 static inline ThreePixbufs *
969 get_account_protocol_pixbufs (ModestFolderView *folder_view,
970 ModestProtocolType protocol_type,
973 ModestProtocol *protocol;
974 const GdkPixbuf *pixbuf = NULL;
975 ModestFolderViewPrivate *priv;
977 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
979 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
982 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
983 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
984 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
985 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
986 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
987 object, FOLDER_ICON_SIZE);
991 ThreePixbufs *retval;
992 retval = g_slice_new0 (ThreePixbufs);
993 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
994 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
995 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
1002 static inline ThreePixbufs*
1003 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
1005 TnyAccount *account = NULL;
1006 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
1007 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
1008 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
1009 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
1010 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
1012 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1013 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1014 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1015 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1016 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1018 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1019 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1020 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1021 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1022 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1024 ThreePixbufs *retval = NULL;
1026 if (TNY_IS_ACCOUNT (instance)) {
1027 account = g_object_ref (instance);
1028 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1029 account = tny_folder_get_account (TNY_FOLDER (instance));
1033 ModestProtocolType account_store_protocol;
1035 account_store_protocol = modest_tny_account_get_protocol_type (account);
1036 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1037 g_object_unref (account);
1043 /* Sometimes an special folder is reported by the server as
1044 NORMAL, like some versions of Dovecot */
1045 if (type == TNY_FOLDER_TYPE_NORMAL ||
1046 type == TNY_FOLDER_TYPE_UNKNOWN) {
1047 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1050 /* It's not enough with check the folder type. We need to
1051 ensure that we're not giving a special folder icon to a
1052 normal folder with the same name than a special folder */
1053 if (TNY_IS_FOLDER (instance) &&
1054 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1055 type = TNY_FOLDER_TYPE_NORMAL;
1057 /* Remote folders should not be treated as special folders */
1058 if (TNY_IS_FOLDER_STORE (instance) &&
1059 !TNY_IS_ACCOUNT (instance) &&
1060 type != TNY_FOLDER_TYPE_INBOX &&
1061 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1062 #ifdef MODEST_TOOLKIT_HILDON2
1063 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1066 &anorm_pixbuf_close);
1068 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1070 &normal_pixbuf_open,
1071 &normal_pixbuf_close);
1077 case TNY_FOLDER_TYPE_INVALID:
1078 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1081 case TNY_FOLDER_TYPE_ROOT:
1082 if (TNY_IS_ACCOUNT (instance)) {
1084 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1085 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1088 &avirt_pixbuf_close);
1090 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1092 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1093 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1096 &ammc_pixbuf_close);
1098 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1101 &anorm_pixbuf_close);
1106 case TNY_FOLDER_TYPE_INBOX:
1107 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1110 &inbox_pixbuf_close);
1112 case TNY_FOLDER_TYPE_OUTBOX:
1113 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1115 &outbox_pixbuf_open,
1116 &outbox_pixbuf_close);
1118 case TNY_FOLDER_TYPE_JUNK:
1119 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1122 &junk_pixbuf_close);
1124 case TNY_FOLDER_TYPE_SENT:
1125 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1128 &sent_pixbuf_close);
1130 case TNY_FOLDER_TYPE_TRASH:
1131 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1134 &trash_pixbuf_close);
1136 case TNY_FOLDER_TYPE_DRAFTS:
1137 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1140 &draft_pixbuf_close);
1142 case TNY_FOLDER_TYPE_ARCHIVE:
1143 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1148 case TNY_FOLDER_TYPE_NORMAL:
1150 /* Memory card folders could have an special icon */
1151 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1152 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1157 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1159 &normal_pixbuf_open,
1160 &normal_pixbuf_close);
1169 free_pixbufs (ThreePixbufs *pixbufs)
1171 if (pixbufs->pixbuf)
1172 g_object_unref (pixbufs->pixbuf);
1173 if (pixbufs->pixbuf_open)
1174 g_object_unref (pixbufs->pixbuf_open);
1175 if (pixbufs->pixbuf_close)
1176 g_object_unref (pixbufs->pixbuf_close);
1177 g_slice_free (ThreePixbufs, pixbufs);
1181 icon_cell_data (GtkTreeViewColumn *column,
1182 GtkCellRenderer *renderer,
1183 GtkTreeModel *tree_model,
1187 GObject *rendobj = NULL, *instance = NULL;
1188 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1189 gboolean has_children;
1190 ThreePixbufs *pixbufs;
1191 ModestFolderView *folder_view = (ModestFolderView *) data;
1193 rendobj = (GObject *) renderer;
1195 gtk_tree_model_get (tree_model, iter,
1197 INSTANCE_COLUMN, &instance,
1203 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1204 pixbufs = get_folder_icons (folder_view, type, instance);
1205 g_object_unref (instance);
1208 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1211 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1212 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1215 free_pixbufs (pixbufs);
1219 add_columns (GtkWidget *treeview)
1221 GtkTreeViewColumn *column;
1222 GtkCellRenderer *renderer;
1223 GtkTreeSelection *sel;
1224 ModestFolderViewPrivate *priv;
1226 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1229 column = gtk_tree_view_column_new ();
1231 /* Set icon and text render function */
1232 renderer = gtk_cell_renderer_pixbuf_new();
1233 #ifdef MODEST_TOOLKIT_HILDON2
1234 g_object_set (renderer,
1235 "xpad", MODEST_MARGIN_DEFAULT,
1236 "ypad", MODEST_MARGIN_DEFAULT,
1239 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1240 gtk_tree_view_column_set_cell_data_func(column, renderer,
1241 icon_cell_data, treeview, NULL);
1243 renderer = gtk_cell_renderer_text_new();
1244 g_object_set (renderer,
1245 #ifdef MODEST_TOOLKIT_HILDON2
1246 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1247 "ypad", MODEST_MARGIN_DEFAULT,
1248 "xpad", MODEST_MARGIN_DEFAULT,
1250 "ellipsize", PANGO_ELLIPSIZE_END,
1252 "ellipsize-set", TRUE, NULL);
1253 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1254 gtk_tree_view_column_set_cell_data_func(column, renderer,
1255 text_cell_data, treeview, NULL);
1257 priv->messages_renderer = gtk_cell_renderer_text_new ();
1258 g_object_set (priv->messages_renderer,
1259 #ifdef MODEST_TOOLKIT_HILDON2
1261 "ypad", MODEST_MARGIN_DEFAULT,
1262 "xpad", MODEST_MARGIN_DOUBLE,
1264 "scale", PANGO_SCALE_X_SMALL,
1267 "alignment", PANGO_ALIGN_RIGHT,
1271 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1272 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1273 messages_cell_data, treeview, NULL);
1275 /* Set selection mode */
1276 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1277 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1279 /* Set treeview appearance */
1280 gtk_tree_view_column_set_spacing (column, 2);
1281 gtk_tree_view_column_set_resizable (column, TRUE);
1282 gtk_tree_view_column_set_fixed_width (column, TRUE);
1283 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1284 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1287 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1291 modest_folder_view_init (ModestFolderView *obj)
1293 ModestFolderViewPrivate *priv;
1296 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1298 priv->timer_expander = 0;
1299 priv->account_store = NULL;
1301 priv->do_refresh = TRUE;
1302 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1303 priv->cur_folder_store = NULL;
1304 priv->visible_account_id = NULL;
1305 priv->mailbox = NULL;
1306 priv->folder_to_select = NULL;
1307 priv->outbox_deleted_handler = 0;
1308 priv->reexpand = TRUE;
1309 priv->signal_handlers = 0;
1311 /* Initialize the local account name */
1312 conf = modest_runtime_get_conf();
1313 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1315 /* Init email clipboard */
1316 priv->clipboard = modest_runtime_get_email_clipboard ();
1317 priv->hidding_ids = NULL;
1318 priv->n_selected = 0;
1319 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1320 priv->reselect = FALSE;
1321 priv->show_non_move = TRUE;
1322 priv->list_to_move = NULL;
1323 priv->show_message_count = TRUE;
1325 /* Build treeview */
1326 add_columns (GTK_WIDGET (obj));
1328 /* Setup drag and drop */
1329 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1331 /* Connect signals */
1332 g_signal_connect (G_OBJECT (obj),
1334 G_CALLBACK (on_key_pressed), NULL);
1336 priv->display_name_changed_signal =
1337 g_signal_connect (modest_runtime_get_account_mgr (),
1338 "display_name_changed",
1339 G_CALLBACK (on_display_name_changed),
1343 * Track changes in the local account name (in the device it
1344 * will be the device name)
1346 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1348 G_CALLBACK(on_configuration_key_changed),
1351 gdk_color_parse ("000", &priv->active_color);
1354 g_signal_connect (G_OBJECT (obj), "notify::style",
1355 G_CALLBACK (on_notify_style), (gpointer) obj);
1359 tny_account_store_view_init (gpointer g, gpointer iface_data)
1361 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1363 klass->set_account_store = modest_folder_view_set_account_store;
1367 modest_folder_view_dispose (GObject *obj)
1369 ModestFolderViewPrivate *priv;
1370 GtkTreeModel *model = NULL;
1372 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1374 get_inner_models (MODEST_FOLDER_VIEW (obj),
1375 NULL, NULL, &model);
1377 #ifdef MODEST_TOOLKIT_HILDON2
1378 if (priv->signal_handlers) {
1379 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1380 priv->signal_handlers = NULL;
1384 /* Free external references */
1385 if (priv->account_store) {
1386 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1387 priv->account_inserted_signal);
1388 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1389 priv->account_removed_signal);
1390 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1391 priv->account_changed_signal);
1392 g_object_unref (G_OBJECT(priv->account_store));
1393 priv->account_store = NULL;
1397 g_object_unref (G_OBJECT (priv->query));
1401 if (priv->folder_to_select) {
1402 g_object_unref (G_OBJECT(priv->folder_to_select));
1403 priv->folder_to_select = NULL;
1406 if (priv->cur_folder_store) {
1407 g_object_unref (priv->cur_folder_store);
1408 priv->cur_folder_store = NULL;
1411 if (priv->list_to_move) {
1412 g_object_unref (priv->list_to_move);
1413 priv->list_to_move = NULL;
1416 G_OBJECT_CLASS(parent_class)->dispose (obj);
1420 modest_folder_view_finalize (GObject *obj)
1422 ModestFolderViewPrivate *priv;
1423 GtkTreeSelection *sel;
1424 TnyAccount *local_account;
1426 g_return_if_fail (obj);
1428 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1430 if (priv->timer_expander != 0) {
1431 g_source_remove (priv->timer_expander);
1432 priv->timer_expander = 0;
1435 local_account = (TnyAccount *)
1436 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1437 if (local_account) {
1438 if (g_signal_handler_is_connected (local_account,
1439 priv->outbox_deleted_handler))
1440 g_signal_handler_disconnect (local_account,
1441 priv->outbox_deleted_handler);
1442 g_object_unref (local_account);
1445 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1446 priv->display_name_changed_signal)) {
1447 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1448 priv->display_name_changed_signal);
1449 priv->display_name_changed_signal = 0;
1452 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1454 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1456 g_free (priv->local_account_name);
1457 g_free (priv->visible_account_id);
1458 g_free (priv->mailbox);
1460 if (priv->conf_key_signal) {
1461 g_signal_handler_disconnect (modest_runtime_get_conf (),
1462 priv->conf_key_signal);
1463 priv->conf_key_signal = 0;
1466 /* Clear hidding array created by cut operation */
1467 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1469 gdk_color_parse ("000", &priv->active_color);
1471 G_OBJECT_CLASS(parent_class)->finalize (obj);
1476 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1478 ModestFolderViewPrivate *priv;
1481 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1482 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1484 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1485 device = tny_account_store_get_device (account_store);
1487 if (G_UNLIKELY (priv->account_store)) {
1489 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1490 priv->account_inserted_signal))
1491 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1492 priv->account_inserted_signal);
1493 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1494 priv->account_removed_signal))
1495 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1496 priv->account_removed_signal);
1497 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1498 priv->account_changed_signal))
1499 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1500 priv->account_changed_signal);
1501 g_object_unref (G_OBJECT (priv->account_store));
1504 priv->account_store = g_object_ref (G_OBJECT (account_store));
1506 priv->account_removed_signal =
1507 g_signal_connect (G_OBJECT(account_store), "account_removed",
1508 G_CALLBACK (on_account_removed), self);
1510 priv->account_inserted_signal =
1511 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1512 G_CALLBACK (on_account_inserted), self);
1514 priv->account_changed_signal =
1515 g_signal_connect (G_OBJECT(account_store), "account_changed",
1516 G_CALLBACK (on_account_changed), self);
1518 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1519 priv->reselect = FALSE;
1520 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1522 g_object_unref (G_OBJECT (device));
1526 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1529 ModestFolderView *self;
1530 GtkTreeModel *model, *filter_model;
1533 self = MODEST_FOLDER_VIEW (user_data);
1535 if (!get_inner_models (self, &filter_model, NULL, &model))
1538 /* Remove outbox from model */
1539 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1540 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1541 g_object_unref (outbox);
1544 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1548 on_account_inserted (TnyAccountStore *account_store,
1549 TnyAccount *account,
1552 ModestFolderViewPrivate *priv;
1553 GtkTreeModel *model, *filter_model;
1555 /* Ignore transport account insertions, we're not showing them
1556 in the folder view */
1557 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1560 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1563 /* If we're adding a new account, and there is no previous
1564 one, we need to select the visible server account */
1565 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1566 !priv->visible_account_id)
1567 modest_widget_memory_restore (modest_runtime_get_conf(),
1568 G_OBJECT (user_data),
1569 MODEST_CONF_FOLDER_VIEW_KEY);
1573 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1574 &filter_model, NULL, &model))
1577 /* Insert the account in the model */
1578 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1580 /* When the model is a list store (plain representation) the
1581 outbox is not a child of any account so we have to manually
1582 delete it because removing the local folders account won't
1583 delete it (because tny_folder_get_account() is not defined
1584 for a merge folder */
1585 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1586 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1588 priv->outbox_deleted_handler =
1589 g_signal_connect (account,
1591 G_CALLBACK (on_outbox_deleted_cb),
1595 /* Refilter the model */
1596 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1601 same_account_selected (ModestFolderView *self,
1602 TnyAccount *account)
1604 ModestFolderViewPrivate *priv;
1605 gboolean same_account = FALSE;
1607 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1609 if (priv->cur_folder_store) {
1610 TnyAccount *selected_folder_account = NULL;
1612 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1613 selected_folder_account =
1614 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1616 selected_folder_account =
1617 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1620 if (selected_folder_account == account)
1621 same_account = TRUE;
1623 g_object_unref (selected_folder_account);
1625 return same_account;
1630 * Selects the first inbox or the local account in an idle
1633 on_idle_select_first_inbox_or_local (gpointer user_data)
1635 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1637 gdk_threads_enter ();
1638 modest_folder_view_select_first_inbox_or_local (self);
1639 gdk_threads_leave ();
1645 on_account_changed (TnyAccountStore *account_store,
1646 TnyAccount *tny_account,
1649 ModestFolderView *self;
1650 ModestFolderViewPrivate *priv;
1651 GtkTreeModel *model, *filter_model;
1652 GtkTreeSelection *sel;
1653 gboolean same_account;
1655 /* Ignore transport account insertions, we're not showing them
1656 in the folder view */
1657 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1660 self = MODEST_FOLDER_VIEW (user_data);
1661 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1663 /* Get the inner model */
1664 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1665 &filter_model, NULL, &model))
1668 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1670 /* Invalidate the cur_folder_store only if the selected folder
1671 belongs to the account that is being removed */
1672 same_account = same_account_selected (self, tny_account);
1674 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1675 gtk_tree_selection_unselect_all (sel);
1678 /* Remove the account from the model */
1679 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1681 /* Insert the account in the model */
1682 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1684 /* Refilter the model */
1685 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1687 /* Select the first INBOX if the currently selected folder
1688 belongs to the account that is being deleted */
1689 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1690 g_idle_add (on_idle_select_first_inbox_or_local, self);
1694 on_account_removed (TnyAccountStore *account_store,
1695 TnyAccount *account,
1698 ModestFolderView *self = NULL;
1699 ModestFolderViewPrivate *priv;
1700 GtkTreeModel *model, *filter_model;
1701 GtkTreeSelection *sel = NULL;
1702 gboolean same_account = FALSE;
1704 /* Ignore transport account removals, we're not showing them
1705 in the folder view */
1706 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1709 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1710 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1714 self = MODEST_FOLDER_VIEW (user_data);
1715 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1717 /* Invalidate the cur_folder_store only if the selected folder
1718 belongs to the account that is being removed */
1719 same_account = same_account_selected (self, account);
1721 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1722 gtk_tree_selection_unselect_all (sel);
1725 /* Invalidate row to select only if the folder to select
1726 belongs to the account that is being removed*/
1727 if (priv->folder_to_select) {
1728 TnyAccount *folder_to_select_account = NULL;
1730 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1731 if (folder_to_select_account == account) {
1732 modest_folder_view_disable_next_folder_selection (self);
1733 g_object_unref (priv->folder_to_select);
1734 priv->folder_to_select = NULL;
1736 g_object_unref (folder_to_select_account);
1739 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1740 &filter_model, NULL, &model))
1743 /* Disconnect the signal handler */
1744 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1745 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1746 if (g_signal_handler_is_connected (account,
1747 priv->outbox_deleted_handler))
1748 g_signal_handler_disconnect (account,
1749 priv->outbox_deleted_handler);
1752 /* Remove the account from the model */
1753 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1755 /* If the removed account is the currently viewed one then
1756 clear the configuration value. The new visible account will be the default account */
1757 if (priv->visible_account_id &&
1758 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1760 /* Clear the current visible account_id */
1761 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1762 modest_folder_view_set_mailbox (self, NULL);
1764 /* Call the restore method, this will set the new visible account */
1765 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1766 MODEST_CONF_FOLDER_VIEW_KEY);
1769 /* Refilter the model */
1770 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1772 /* Select the first INBOX if the currently selected folder
1773 belongs to the account that is being deleted */
1775 g_idle_add (on_idle_select_first_inbox_or_local, self);
1779 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1781 GtkTreeViewColumn *col;
1783 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1785 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1787 g_printerr ("modest: failed get column for title\n");
1791 gtk_tree_view_column_set_title (col, title);
1792 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1797 modest_folder_view_on_map (ModestFolderView *self,
1798 GdkEventExpose *event,
1801 ModestFolderViewPrivate *priv;
1803 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1805 /* This won't happen often */
1806 if (G_UNLIKELY (priv->reselect)) {
1807 /* Select the first inbox or the local account if not found */
1809 /* TODO: this could cause a lock at startup, so we
1810 comment it for the moment. We know that this will
1811 be a bug, because the INBOX is not selected, but we
1812 need to rewrite some parts of Modest to avoid the
1813 deathlock situation */
1814 /* TODO: check if this is still the case */
1815 priv->reselect = FALSE;
1816 modest_folder_view_select_first_inbox_or_local (self);
1817 /* Notify the display name observers */
1818 g_signal_emit (G_OBJECT(self),
1819 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1823 if (priv->reexpand) {
1824 expand_root_items (self);
1825 priv->reexpand = FALSE;
1832 modest_folder_view_new (TnyFolderStoreQuery *query)
1834 return modest_folder_view_new_full (query, TRUE);
1838 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1841 ModestFolderViewPrivate *priv;
1842 GtkTreeSelection *sel;
1844 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1845 #ifdef MODEST_TOOLKIT_HILDON2
1846 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1849 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1852 priv->query = g_object_ref (query);
1854 priv->do_refresh = do_refresh;
1856 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1857 priv->changed_signal = g_signal_connect (sel, "changed",
1858 G_CALLBACK (on_selection_changed), self);
1860 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1862 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1864 return GTK_WIDGET(self);
1867 /* this feels dirty; any other way to expand all the root items? */
1869 expand_root_items (ModestFolderView *self)
1872 GtkTreeModel *model;
1875 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1876 path = gtk_tree_path_new_first ();
1878 /* all folders should have child items, so.. */
1880 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1881 gtk_tree_path_next (path);
1882 } while (gtk_tree_model_get_iter (model, &iter, path));
1884 gtk_tree_path_free (path);
1888 is_parent_of (TnyFolder *a, TnyFolder *b)
1891 gboolean retval = FALSE;
1893 a_id = tny_folder_get_id (a);
1895 gchar *string_to_match;
1898 string_to_match = g_strconcat (a_id, "/", NULL);
1899 b_id = tny_folder_get_id (b);
1900 retval = g_str_has_prefix (b_id, string_to_match);
1901 g_free (string_to_match);
1907 typedef struct _ForeachFolderInfo {
1910 } ForeachFolderInfo;
1913 foreach_folder_with_id (GtkTreeModel *model,
1918 ForeachFolderInfo *info;
1921 info = (ForeachFolderInfo *) data;
1922 gtk_tree_model_get (model, iter,
1923 INSTANCE_COLUMN, &instance,
1926 if (TNY_IS_FOLDER (instance)) {
1929 id = tny_folder_get_id (TNY_FOLDER (instance));
1931 collate = g_utf8_collate_key (id, -1);
1932 info->found = !strcmp (info->needle, collate);
1938 g_object_unref (instance);
1946 has_folder_with_id (ModestFolderView *self, const gchar *id)
1948 GtkTreeModel *model;
1949 ForeachFolderInfo info = {NULL, FALSE};
1951 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1952 info.needle = g_utf8_collate_key (id, -1);
1954 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1955 g_free (info.needle);
1961 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1964 gboolean retval = FALSE;
1966 a_id = tny_folder_get_id (a);
1969 b_id = tny_folder_get_id (b);
1972 const gchar *last_bar;
1973 gchar *string_to_match;
1974 last_bar = g_strrstr (b_id, "/");
1979 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1980 retval = has_folder_with_id (self, string_to_match);
1981 g_free (string_to_match);
1989 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1991 ModestFolderViewPrivate *priv;
1992 TnyIterator *iterator;
1993 gboolean retval = TRUE;
1995 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1996 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1998 for (iterator = tny_list_create_iterator (priv->list_to_move);
1999 retval && !tny_iterator_is_done (iterator);
2000 tny_iterator_next (iterator)) {
2002 instance = tny_iterator_get_current (iterator);
2003 if (instance == (GObject *) folder) {
2005 } else if (TNY_IS_FOLDER (instance)) {
2006 retval = !is_parent_of (TNY_FOLDER (instance), folder);
2008 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
2011 g_object_unref (instance);
2013 g_object_unref (iterator);
2020 * We use this function to implement the
2021 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
2022 * account in this case, and the local folders.
2025 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2027 ModestFolderViewPrivate *priv;
2028 gboolean retval = TRUE;
2029 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2030 GObject *instance = NULL;
2031 const gchar *id = NULL;
2033 gboolean found = FALSE;
2034 gboolean cleared = FALSE;
2035 ModestTnyFolderRules rules = 0;
2038 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2039 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2041 gtk_tree_model_get (model, iter,
2042 NAME_COLUMN, &fname,
2044 INSTANCE_COLUMN, &instance,
2047 /* Do not show if there is no instance, this could indeed
2048 happen when the model is being modified while it's being
2049 drawn. This could occur for example when moving folders
2056 if (TNY_IS_ACCOUNT (instance)) {
2057 TnyAccount *acc = TNY_ACCOUNT (instance);
2058 const gchar *account_id = tny_account_get_id (acc);
2060 /* If it isn't a special folder,
2061 * don't show it unless it is the visible account: */
2062 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2063 !modest_tny_account_is_virtual_local_folders (acc) &&
2064 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2066 /* Show only the visible account id */
2067 if (priv->visible_account_id) {
2068 if (strcmp (account_id, priv->visible_account_id))
2075 /* Never show these to the user. They are merged into one folder
2076 * in the local-folders account instead: */
2077 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2080 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2081 /* Only show special folders for current account if needed */
2082 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2083 TnyAccount *account;
2085 account = tny_folder_get_account (TNY_FOLDER (instance));
2087 if (TNY_IS_ACCOUNT (account)) {
2088 const gchar *account_id = tny_account_get_id (account);
2090 if (!modest_tny_account_is_virtual_local_folders (account) &&
2091 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2092 /* Show only the visible account id */
2093 if (priv->visible_account_id) {
2094 if (strcmp (account_id, priv->visible_account_id)) {
2096 } else if (priv->mailbox) {
2097 /* Filter mailboxes */
2098 if (!g_str_has_prefix (fname, priv->mailbox)) {
2100 } else if (!strcmp (fname, priv->mailbox)) {
2101 /* Hide mailbox parent */
2107 g_object_unref (account);
2114 /* Check hiding (if necessary) */
2115 cleared = modest_email_clipboard_cleared (priv->clipboard);
2116 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2117 id = tny_folder_get_id (TNY_FOLDER(instance));
2118 if (priv->hidding_ids != NULL)
2119 for (i=0; i < priv->n_selected && !found; i++)
2120 if (priv->hidding_ids[i] != NULL && id != NULL)
2121 found = (!strcmp (priv->hidding_ids[i], id));
2126 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2127 folder as no message can be move there according to UI specs */
2128 if (retval && !priv->show_non_move) {
2129 if (priv->list_to_move &&
2130 tny_list_get_length (priv->list_to_move) > 0 &&
2131 TNY_IS_FOLDER (instance)) {
2132 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2134 if (retval && TNY_IS_FOLDER (instance) &&
2135 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2137 case TNY_FOLDER_TYPE_OUTBOX:
2138 case TNY_FOLDER_TYPE_SENT:
2139 case TNY_FOLDER_TYPE_DRAFTS:
2142 case TNY_FOLDER_TYPE_UNKNOWN:
2143 case TNY_FOLDER_TYPE_NORMAL:
2144 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2145 if (type == TNY_FOLDER_TYPE_INVALID)
2146 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2148 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2149 type == TNY_FOLDER_TYPE_SENT
2150 || type == TNY_FOLDER_TYPE_DRAFTS)
2157 if (retval && TNY_IS_ACCOUNT (instance) &&
2158 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2159 ModestProtocolType protocol_type;
2161 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2162 retval = !modest_protocol_registry_protocol_type_has_tag
2163 (modest_runtime_get_protocol_registry (),
2165 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2169 /* apply special filters */
2170 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2171 if (TNY_IS_ACCOUNT (instance))
2175 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2176 if (TNY_IS_FOLDER (instance))
2180 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2181 if (TNY_IS_ACCOUNT (instance)) {
2182 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2184 } else if (TNY_IS_FOLDER (instance)) {
2185 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2190 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2191 if (TNY_IS_ACCOUNT (instance)) {
2192 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2194 } else if (TNY_IS_FOLDER (instance)) {
2195 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2200 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2201 /* A mailbox is a fake folder with an @ in the middle of the name */
2202 if (!TNY_IS_FOLDER (instance) ||
2203 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2206 const gchar *folder_name;
2207 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2208 if (!folder_name || strchr (folder_name, '@') == NULL)
2214 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2215 if (TNY_IS_FOLDER (instance)) {
2216 /* Check folder rules */
2217 ModestTnyFolderRules rules;
2219 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2220 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2221 } else if (TNY_IS_ACCOUNT (instance)) {
2222 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2230 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2231 if (TNY_IS_FOLDER (instance)) {
2232 TnyFolderType guess_type;
2234 if (TNY_FOLDER_TYPE_NORMAL) {
2235 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2241 case TNY_FOLDER_TYPE_OUTBOX:
2242 case TNY_FOLDER_TYPE_SENT:
2243 case TNY_FOLDER_TYPE_DRAFTS:
2244 case TNY_FOLDER_TYPE_ARCHIVE:
2245 case TNY_FOLDER_TYPE_INBOX:
2248 case TNY_FOLDER_TYPE_UNKNOWN:
2249 case TNY_FOLDER_TYPE_NORMAL:
2255 } else if (TNY_IS_ACCOUNT (instance)) {
2260 if (retval && TNY_IS_FOLDER (instance)) {
2261 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2264 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2265 if (TNY_IS_FOLDER (instance)) {
2266 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2267 } else if (TNY_IS_ACCOUNT (instance)) {
2272 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2273 if (TNY_IS_FOLDER (instance)) {
2274 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2275 } else if (TNY_IS_ACCOUNT (instance)) {
2280 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2281 if (TNY_IS_FOLDER (instance)) {
2282 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2283 } else if (TNY_IS_ACCOUNT (instance)) {
2289 g_object_unref (instance);
2297 modest_folder_view_update_model (ModestFolderView *self,
2298 TnyAccountStore *account_store)
2300 ModestFolderViewPrivate *priv;
2301 GtkTreeModel *model;
2302 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2304 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2305 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2308 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2310 /* Notify that there is no folder selected */
2311 g_signal_emit (G_OBJECT(self),
2312 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2314 if (priv->cur_folder_store) {
2315 g_object_unref (priv->cur_folder_store);
2316 priv->cur_folder_store = NULL;
2319 /* FIXME: the local accounts are not shown when the query
2320 selects only the subscribed folders */
2321 #ifdef MODEST_TOOLKIT_HILDON2
2322 TnyGtkFolderListStoreFlags flags;
2323 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2324 if (priv->do_refresh)
2325 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2327 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2328 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2330 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2331 MODEST_FOLDER_PATH_SEPARATOR);
2333 model = tny_gtk_folder_store_tree_model_new (NULL);
2336 /* When the model is a list store (plain representation) the
2337 outbox is not a child of any account so we have to manually
2338 delete it because removing the local folders account won't
2339 delete it (because tny_folder_get_account() is not defined
2340 for a merge folder */
2341 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2342 TnyAccount *account;
2343 ModestTnyAccountStore *acc_store;
2345 acc_store = modest_runtime_get_account_store ();
2346 account = modest_tny_account_store_get_local_folders_account (acc_store);
2348 if (g_signal_handler_is_connected (account,
2349 priv->outbox_deleted_handler))
2350 g_signal_handler_disconnect (account,
2351 priv->outbox_deleted_handler);
2353 priv->outbox_deleted_handler =
2354 g_signal_connect (account,
2356 G_CALLBACK (on_outbox_deleted_cb),
2358 g_object_unref (account);
2361 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2362 /* Get the accounts */
2363 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2365 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2367 if (priv->visible_account_id) {
2368 TnyAccount *account;
2370 /* Add local folders account */
2371 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2374 tny_list_append (TNY_LIST (model), (GObject *) account);
2375 g_object_unref (account);
2378 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2381 tny_list_append (TNY_LIST (model), (GObject *) account);
2382 g_object_unref (account);
2385 /* Add visible account */
2386 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2387 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2388 priv->visible_account_id);
2390 tny_list_append (TNY_LIST (model), (GObject *) account);
2391 g_object_unref (account);
2393 g_warning ("You need to set an account first");
2394 g_object_unref (model);
2398 g_warning ("You need to set an account first");
2399 g_object_unref (model);
2404 sortable = gtk_tree_model_sort_new_with_model (model);
2405 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2407 GTK_SORT_ASCENDING);
2408 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2410 cmp_rows, NULL, NULL);
2412 /* Create filter model */
2413 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2414 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2419 GtkTreeModel *old_tny_model = NULL;
2420 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2421 if (priv->signal_handlers > 0) {
2422 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2423 G_OBJECT (old_tny_model),
2424 "activity-changed");
2429 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2430 #ifndef MODEST_TOOLKIT_HILDON2
2431 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2432 (GCallback) on_row_inserted_maybe_select_folder, self);
2435 #ifdef MODEST_TOOLKIT_HILDON2
2436 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2439 G_CALLBACK (on_activity_changed),
2443 g_object_unref (model);
2444 g_object_unref (filter_model);
2445 g_object_unref (sortable);
2447 /* Force a reselection of the INBOX next time the widget is shown */
2448 priv->reselect = TRUE;
2455 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2457 GtkTreeModel *model = NULL;
2458 TnyFolderStore *folder = NULL;
2460 ModestFolderView *tree_view = NULL;
2461 ModestFolderViewPrivate *priv = NULL;
2462 gboolean selected = FALSE;
2464 g_return_if_fail (sel);
2465 g_return_if_fail (user_data);
2467 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2469 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2471 tree_view = MODEST_FOLDER_VIEW (user_data);
2474 gtk_tree_model_get (model, &iter,
2475 INSTANCE_COLUMN, &folder,
2478 /* If the folder is the same do not notify */
2479 if (folder && priv->cur_folder_store == folder) {
2480 g_object_unref (folder);
2485 /* Current folder was unselected */
2486 if (priv->cur_folder_store) {
2487 /* We must do this firstly because a libtinymail-camel
2488 implementation detail. If we issue the signal
2489 before doing the sync_async, then that signal could
2490 cause (and it actually does it) a free of the
2491 summary of the folder (because the main window will
2492 clear the headers view */
2493 #ifndef MODEST_TOOLKIT_HILDON2
2494 if (TNY_IS_FOLDER(priv->cur_folder_store))
2495 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2496 FALSE, NULL, NULL, NULL);
2499 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2500 priv->cur_folder_store, FALSE);
2502 g_object_unref (priv->cur_folder_store);
2503 priv->cur_folder_store = NULL;
2506 /* New current references */
2507 priv->cur_folder_store = folder;
2509 /* New folder has been selected. Do not notify if there is
2510 nothing new selected */
2512 g_signal_emit (G_OBJECT(tree_view),
2513 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2514 0, priv->cur_folder_store, TRUE);
2519 on_row_activated (GtkTreeView *treeview,
2520 GtkTreePath *treepath,
2521 GtkTreeViewColumn *column,
2524 GtkTreeModel *model = NULL;
2525 TnyFolderStore *folder = NULL;
2527 ModestFolderView *self = NULL;
2528 ModestFolderViewPrivate *priv = NULL;
2530 g_return_if_fail (treeview);
2531 g_return_if_fail (user_data);
2533 self = MODEST_FOLDER_VIEW (user_data);
2534 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2536 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2538 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2541 gtk_tree_model_get (model, &iter,
2542 INSTANCE_COLUMN, &folder,
2545 g_signal_emit (G_OBJECT(self),
2546 signals[FOLDER_ACTIVATED_SIGNAL],
2549 #ifdef MODEST_TOOLKIT_HILDON2
2550 HildonUIMode ui_mode;
2551 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2552 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2553 if (priv->cur_folder_store)
2554 g_object_unref (priv->cur_folder_store);
2555 priv->cur_folder_store = g_object_ref (folder);
2559 g_object_unref (folder);
2563 modest_folder_view_get_selected (ModestFolderView *self)
2565 ModestFolderViewPrivate *priv;
2567 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2569 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2570 if (priv->cur_folder_store)
2571 g_object_ref (priv->cur_folder_store);
2573 return priv->cur_folder_store;
2577 get_cmp_rows_type_pos (GObject *folder)
2579 /* Remote accounts -> Local account -> MMC account .*/
2582 if (TNY_IS_ACCOUNT (folder) &&
2583 modest_tny_account_is_virtual_local_folders (
2584 TNY_ACCOUNT (folder))) {
2586 } else if (TNY_IS_ACCOUNT (folder)) {
2587 TnyAccount *account = TNY_ACCOUNT (folder);
2588 const gchar *account_id = tny_account_get_id (account);
2589 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2595 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2596 return -1; /* Should never happen */
2601 inbox_is_special (TnyFolderStore *folder_store)
2603 gboolean is_special = TRUE;
2605 if (TNY_IS_FOLDER (folder_store)) {
2609 gchar *last_inbox_bar;
2611 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2612 downcase = g_utf8_strdown (id, -1);
2613 last_bar = g_strrstr (downcase, "/");
2615 last_inbox_bar = g_strrstr (downcase, "inbox/");
2616 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2627 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2629 TnyAccount *account;
2630 gboolean is_special;
2631 /* Inbox, Outbox, Drafts, Sent, User */
2634 if (!TNY_IS_FOLDER (folder_store))
2637 case TNY_FOLDER_TYPE_INBOX:
2639 account = tny_folder_get_account (folder_store);
2640 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2642 /* In inbox case we need to know if the inbox is really the top
2643 * inbox of the account, or if it's a submailbox inbox. To do
2644 * this we'll apply an heuristic rule: Find last "/" and check
2645 * if it's preceeded by another Inbox */
2646 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2647 g_object_unref (account);
2648 return is_special?0:4;
2651 case TNY_FOLDER_TYPE_OUTBOX:
2652 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2654 case TNY_FOLDER_TYPE_DRAFTS:
2656 account = tny_folder_get_account (folder_store);
2657 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2658 g_object_unref (account);
2659 return is_special?1:4;
2662 case TNY_FOLDER_TYPE_SENT:
2664 account = tny_folder_get_account (folder_store);
2665 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2666 g_object_unref (account);
2667 return is_special?3:4;
2676 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2678 const gchar *a1_name, *a2_name;
2680 a1_name = tny_account_get_name (a1);
2681 a2_name = tny_account_get_name (a2);
2683 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2687 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2689 TnyAccount *a1 = NULL, *a2 = NULL;
2692 if (TNY_IS_ACCOUNT (s1)) {
2693 a1 = TNY_ACCOUNT (g_object_ref (s1));
2694 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2695 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2698 if (TNY_IS_ACCOUNT (s2)) {
2699 a2 = TNY_ACCOUNT (g_object_ref (s2));
2700 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2701 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2718 /* First we sort with the type of account */
2719 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2723 cmp = compare_account_names (a1, a2);
2727 g_object_unref (a1);
2729 g_object_unref (a2);
2735 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2737 gint is_account1, is_account2;
2739 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2740 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2742 return is_account2 - is_account1;
2746 compare_folders (const gchar *name1, const gchar *name2)
2748 const gchar *separator1, *separator2;
2749 const gchar *next1, *next2;
2753 if (name1 == NULL || name1[0] == '\0')
2755 if (name2 == NULL || name2[0] == '\0')
2758 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2760 top1 = g_strndup (name1, separator1 - name1);
2762 top1 = g_strdup (name1);
2765 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2767 top2 = g_strndup (name2, separator2 - name2);
2769 top2 = g_strdup (name2);
2773 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2780 if (separator1 == NULL && separator2 == NULL)
2783 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2784 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2786 return compare_folders (next1, next2);
2791 * This function orders the mail accounts according to these rules:
2792 * 1st - remote accounts
2793 * 2nd - local account
2797 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2801 gchar *name1 = NULL;
2802 gchar *name2 = NULL;
2803 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2804 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2805 GObject *folder1 = NULL;
2806 GObject *folder2 = NULL;
2808 gtk_tree_model_get (tree_model, iter1,
2809 NAME_COLUMN, &name1,
2811 INSTANCE_COLUMN, &folder1,
2813 gtk_tree_model_get (tree_model, iter2,
2814 NAME_COLUMN, &name2,
2815 TYPE_COLUMN, &type2,
2816 INSTANCE_COLUMN, &folder2,
2819 /* Return if we get no folder. This could happen when folder
2820 operations are happening. The model is updated after the
2821 folder copy/move actually occurs, so there could be
2822 situations where the model to be drawn is not correct */
2823 if (!folder1 || !folder2)
2826 /* Sort by type. First the special folders, then the archives */
2827 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2831 /* Now we sort using the account of each folder */
2832 if (TNY_IS_FOLDER_STORE (folder1) &&
2833 TNY_IS_FOLDER_STORE (folder2)) {
2834 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2838 /* Each group is preceeded by its account */
2839 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2844 /* Pure sort by name */
2845 cmp = compare_folders (name1, name2);
2848 g_object_unref(G_OBJECT(folder1));
2850 g_object_unref(G_OBJECT(folder2));
2858 /*****************************************************************************/
2859 /* DRAG and DROP stuff */
2860 /*****************************************************************************/
2862 * This function fills the #GtkSelectionData with the row and the
2863 * model that has been dragged. It's called when this widget is a
2864 * source for dnd after the event drop happened
2867 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2868 guint info, guint time, gpointer data)
2870 GtkTreeSelection *selection;
2871 GtkTreeModel *model;
2873 GtkTreePath *source_row;
2875 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2876 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2878 source_row = gtk_tree_model_get_path (model, &iter);
2879 gtk_tree_set_row_drag_data (selection_data,
2883 gtk_tree_path_free (source_row);
2887 typedef struct _DndHelper {
2888 ModestFolderView *folder_view;
2889 gboolean delete_source;
2890 GtkTreePath *source_row;
2894 dnd_helper_destroyer (DndHelper *helper)
2896 /* Free the helper */
2897 gtk_tree_path_free (helper->source_row);
2898 g_slice_free (DndHelper, helper);
2902 xfer_folder_cb (ModestMailOperation *mail_op,
2903 TnyFolder *new_folder,
2907 /* Select the folder */
2908 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2914 /* get the folder for the row the treepath refers to. */
2915 /* folder must be unref'd */
2916 static TnyFolderStore *
2917 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2920 TnyFolderStore *folder = NULL;
2922 if (gtk_tree_model_get_iter (model,&iter, path))
2923 gtk_tree_model_get (model, &iter,
2924 INSTANCE_COLUMN, &folder,
2931 * This function is used by drag_data_received_cb to manage drag and
2932 * drop of a header, i.e, and drag from the header view to the folder
2936 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2937 GtkTreeModel *dest_model,
2938 GtkTreePath *dest_row,
2939 GtkSelectionData *selection_data)
2941 TnyList *headers = NULL;
2942 TnyFolder *folder = NULL, *src_folder = NULL;
2943 TnyFolderType folder_type;
2944 GtkTreeIter source_iter, dest_iter;
2945 ModestWindowMgr *mgr = NULL;
2946 ModestWindow *main_win = NULL;
2947 gchar **uris, **tmp;
2949 /* Build the list of headers */
2950 mgr = modest_runtime_get_window_mgr ();
2951 headers = tny_simple_list_new ();
2952 uris = modest_dnd_selection_data_get_paths (selection_data);
2955 while (*tmp != NULL) {
2958 gboolean first = TRUE;
2961 path = gtk_tree_path_new_from_string (*tmp);
2962 gtk_tree_model_get_iter (source_model, &source_iter, path);
2963 gtk_tree_model_get (source_model, &source_iter,
2964 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2967 /* Do not enable d&d of headers already opened */
2968 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2969 tny_list_append (headers, G_OBJECT (header));
2971 if (G_UNLIKELY (first)) {
2972 src_folder = tny_header_get_folder (header);
2976 /* Free and go on */
2977 gtk_tree_path_free (path);
2978 g_object_unref (header);
2983 /* This could happen ig we perform a d&d very quickly over the
2984 same row that row could dissapear because message is
2986 if (!TNY_IS_FOLDER (src_folder))
2989 /* Get the target folder */
2990 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2991 gtk_tree_model_get (dest_model, &dest_iter,
2995 if (!folder || !TNY_IS_FOLDER(folder)) {
2996 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
3000 folder_type = modest_tny_folder_guess_folder_type (folder);
3001 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
3002 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
3003 goto cleanup; /* cannot move messages there */
3006 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3007 /* g_warning ("folder not writable"); */
3008 goto cleanup; /* verboten! */
3011 /* Ask for confirmation to move */
3012 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
3014 g_warning ("%s: BUG: no main window found", __FUNCTION__);
3018 /* Transfer messages */
3019 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
3024 if (G_IS_OBJECT (src_folder))
3025 g_object_unref (src_folder);
3026 if (G_IS_OBJECT(folder))
3027 g_object_unref (G_OBJECT (folder));
3028 if (G_IS_OBJECT(headers))
3029 g_object_unref (headers);
3033 TnyFolderStore *src_folder;
3034 TnyFolderStore *dst_folder;
3035 ModestFolderView *folder_view;
3040 dnd_folder_info_destroyer (DndFolderInfo *info)
3042 if (info->src_folder)
3043 g_object_unref (info->src_folder);
3044 if (info->dst_folder)
3045 g_object_unref (info->dst_folder);
3046 g_slice_free (DndFolderInfo, info);
3050 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
3051 GtkWindow *parent_window,
3052 TnyAccount *account)
3055 modest_ui_actions_on_account_connection_error (parent_window, account);
3057 /* Free the helper & info */
3058 dnd_helper_destroyer (info->helper);
3059 dnd_folder_info_destroyer (info);
3063 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
3065 GtkWindow *parent_window,
3066 TnyAccount *account,
3069 DndFolderInfo *info = NULL;
3070 ModestMailOperation *mail_op;
3072 info = (DndFolderInfo *) user_data;
3074 if (err || canceled) {
3075 dnd_on_connection_failed_destroyer (info, parent_window, account);
3079 /* Do the mail operation */
3080 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
3081 modest_ui_actions_move_folder_error_handler,
3082 info->src_folder, NULL);
3084 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3087 /* Transfer the folder */
3088 modest_mail_operation_xfer_folder (mail_op,
3089 TNY_FOLDER (info->src_folder),
3091 info->helper->delete_source,
3093 info->helper->folder_view);
3096 g_object_unref (G_OBJECT (mail_op));
3097 dnd_helper_destroyer (info->helper);
3098 dnd_folder_info_destroyer (info);
3103 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3105 GtkWindow *parent_window,
3106 TnyAccount *account,
3109 DndFolderInfo *info = NULL;
3111 info = (DndFolderInfo *) user_data;
3113 if (err || canceled) {
3114 dnd_on_connection_failed_destroyer (info, parent_window, account);
3118 /* Connect to source folder and perform the copy/move */
3119 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3121 drag_and_drop_from_folder_view_src_folder_performer,
3126 * This function is used by drag_data_received_cb to manage drag and
3127 * drop of a folder, i.e, and drag from the folder view to the same
3131 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3132 GtkTreeModel *dest_model,
3133 GtkTreePath *dest_row,
3134 GtkSelectionData *selection_data,
3137 GtkTreeIter dest_iter, iter;
3138 TnyFolderStore *dest_folder = NULL;
3139 TnyFolderStore *folder = NULL;
3140 gboolean forbidden = FALSE;
3142 DndFolderInfo *info = NULL;
3144 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3146 g_warning ("%s: BUG: no main window", __FUNCTION__);
3147 dnd_helper_destroyer (helper);
3152 /* check the folder rules for the destination */
3153 folder = tree_path_to_folder (dest_model, dest_row);
3154 if (TNY_IS_FOLDER(folder)) {
3155 ModestTnyFolderRules rules =
3156 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3157 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3158 } else if (TNY_IS_FOLDER_STORE(folder)) {
3159 /* enable local root as destination for folders */
3160 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3161 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3164 g_object_unref (folder);
3167 /* check the folder rules for the source */
3168 folder = tree_path_to_folder (source_model, helper->source_row);
3169 if (TNY_IS_FOLDER(folder)) {
3170 ModestTnyFolderRules rules =
3171 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3172 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3175 g_object_unref (folder);
3179 /* Check if the drag is possible */
3180 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3182 modest_platform_run_information_dialog ((GtkWindow *) win,
3183 _("mail_in_ui_folder_move_target_error"),
3185 /* Restore the previous selection */
3186 folder = tree_path_to_folder (source_model, helper->source_row);
3188 if (TNY_IS_FOLDER (folder))
3189 modest_folder_view_select_folder (helper->folder_view,
3190 TNY_FOLDER (folder), FALSE);
3191 g_object_unref (folder);
3193 dnd_helper_destroyer (helper);
3198 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3199 gtk_tree_model_get (dest_model, &dest_iter,
3202 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3203 gtk_tree_model_get (source_model, &iter,
3207 /* Create the info for the performer */
3208 info = g_slice_new0 (DndFolderInfo);
3209 info->src_folder = g_object_ref (folder);
3210 info->dst_folder = g_object_ref (dest_folder);
3211 info->helper = helper;
3213 /* Connect to the destination folder and perform the copy/move */
3214 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3216 drag_and_drop_from_folder_view_dst_folder_performer,
3220 g_object_unref (dest_folder);
3221 g_object_unref (folder);
3225 * This function receives the data set by the "drag-data-get" signal
3226 * handler. This information comes within the #GtkSelectionData. This
3227 * function will manage both the drags of folders of the treeview and
3228 * drags of headers of the header view widget.
3231 on_drag_data_received (GtkWidget *widget,
3232 GdkDragContext *context,
3235 GtkSelectionData *selection_data,
3240 GtkWidget *source_widget;
3241 GtkTreeModel *dest_model, *source_model;
3242 GtkTreePath *source_row, *dest_row;
3243 GtkTreeViewDropPosition pos;
3244 gboolean delete_source = FALSE;
3245 gboolean success = FALSE;
3247 /* Do not allow further process */
3248 g_signal_stop_emission_by_name (widget, "drag-data-received");
3249 source_widget = gtk_drag_get_source_widget (context);
3251 /* Get the action */
3252 if (context->action == GDK_ACTION_MOVE) {
3253 delete_source = TRUE;
3255 /* Notify that there is no folder selected. We need to
3256 do this in order to update the headers view (and
3257 its monitors, because when moving, the old folder
3258 won't longer exist. We can not wait for the end of
3259 the operation, because the operation won't start if
3260 the folder is in use */
3261 if (source_widget == widget) {
3262 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3263 gtk_tree_selection_unselect_all (sel);
3267 /* Check if the get_data failed */
3268 if (selection_data == NULL || selection_data->length < 0)
3271 /* Select the destination model */
3272 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3274 /* Get the path to the destination row. Can not call
3275 gtk_tree_view_get_drag_dest_row() because the source row
3276 is not selected anymore */
3277 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3280 /* Only allow drops IN other rows */
3282 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3283 pos == GTK_TREE_VIEW_DROP_AFTER)
3287 /* Drags from the header view */
3288 if (source_widget != widget) {
3289 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3291 drag_and_drop_from_header_view (source_model,
3296 DndHelper *helper = NULL;
3298 /* Get the source model and row */
3299 gtk_tree_get_row_drag_data (selection_data,
3303 /* Create the helper */
3304 helper = g_slice_new0 (DndHelper);
3305 helper->delete_source = delete_source;
3306 helper->source_row = gtk_tree_path_copy (source_row);
3307 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3309 drag_and_drop_from_folder_view (source_model,
3315 gtk_tree_path_free (source_row);
3319 gtk_tree_path_free (dest_row);
3322 /* Finish the drag and drop */
3323 gtk_drag_finish (context, success, FALSE, time);
3327 * We define a "drag-drop" signal handler because we do not want to
3328 * use the default one, because the default one always calls
3329 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3330 * signal handler, because there we have all the information available
3331 * to know if the dnd was a success or not.
3334 drag_drop_cb (GtkWidget *widget,
3335 GdkDragContext *context,
3343 if (!context->targets)
3346 /* Check if we're dragging a folder row */
3347 target = gtk_drag_dest_find_target (widget, context, NULL);
3349 /* Request the data from the source. */
3350 gtk_drag_get_data(widget, context, target, time);
3356 * This function expands a node of a tree view if it's not expanded
3357 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3358 * does that, so that's why they're here.
3361 expand_row_timeout (gpointer data)
3363 GtkTreeView *tree_view = data;
3364 GtkTreePath *dest_path = NULL;
3365 GtkTreeViewDropPosition pos;
3366 gboolean result = FALSE;
3368 gdk_threads_enter ();
3370 gtk_tree_view_get_drag_dest_row (tree_view,
3375 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3376 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3377 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3378 gtk_tree_path_free (dest_path);
3382 gtk_tree_path_free (dest_path);
3387 gdk_threads_leave ();
3393 * This function is called whenever the pointer is moved over a widget
3394 * while dragging some data. It installs a timeout that will expand a
3395 * node of the treeview if not expanded yet. This function also calls
3396 * gdk_drag_status in order to set the suggested action that will be
3397 * used by the "drag-data-received" signal handler to know if we
3398 * should do a move or just a copy of the data.
3401 on_drag_motion (GtkWidget *widget,
3402 GdkDragContext *context,
3408 GtkTreeViewDropPosition pos;
3409 GtkTreePath *dest_row;
3410 GtkTreeModel *dest_model;
3411 ModestFolderViewPrivate *priv;
3412 GdkDragAction suggested_action;
3413 gboolean valid_location = FALSE;
3414 TnyFolderStore *folder = NULL;
3416 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3418 if (priv->timer_expander != 0) {
3419 g_source_remove (priv->timer_expander);
3420 priv->timer_expander = 0;
3423 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3428 /* Do not allow drops between folders */
3430 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3431 pos == GTK_TREE_VIEW_DROP_AFTER) {
3432 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3433 gdk_drag_status(context, 0, time);
3434 valid_location = FALSE;
3437 valid_location = TRUE;
3440 /* Check that the destination folder is writable */
3441 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3442 folder = tree_path_to_folder (dest_model, dest_row);
3443 if (folder && TNY_IS_FOLDER (folder)) {
3444 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3446 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3447 valid_location = FALSE;
3452 /* Expand the selected row after 1/2 second */
3453 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3454 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3456 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3458 /* Select the desired action. By default we pick MOVE */
3459 suggested_action = GDK_ACTION_MOVE;
3461 if (context->actions == GDK_ACTION_COPY)
3462 gdk_drag_status(context, GDK_ACTION_COPY, time);
3463 else if (context->actions == GDK_ACTION_MOVE)
3464 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3465 else if (context->actions & suggested_action)
3466 gdk_drag_status(context, suggested_action, time);
3468 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3472 g_object_unref (folder);
3474 gtk_tree_path_free (dest_row);
3476 g_signal_stop_emission_by_name (widget, "drag-motion");
3478 return valid_location;
3482 * This function sets the treeview as a source and a target for dnd
3483 * events. It also connects all the requirede signals.
3486 setup_drag_and_drop (GtkTreeView *self)
3488 /* Set up the folder view as a dnd destination. Set only the
3489 highlight flag, otherwise gtk will have a different
3491 #ifdef MODEST_TOOLKIT_HILDON2
3494 gtk_drag_dest_set (GTK_WIDGET (self),
3495 GTK_DEST_DEFAULT_HIGHLIGHT,
3496 folder_view_drag_types,
3497 G_N_ELEMENTS (folder_view_drag_types),
3498 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3500 g_signal_connect (G_OBJECT (self),
3501 "drag_data_received",
3502 G_CALLBACK (on_drag_data_received),
3506 /* Set up the treeview as a dnd source */
3507 gtk_drag_source_set (GTK_WIDGET (self),
3509 folder_view_drag_types,
3510 G_N_ELEMENTS (folder_view_drag_types),
3511 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3513 g_signal_connect (G_OBJECT (self),
3515 G_CALLBACK (on_drag_motion),
3518 g_signal_connect (G_OBJECT (self),
3520 G_CALLBACK (on_drag_data_get),
3523 g_signal_connect (G_OBJECT (self),
3525 G_CALLBACK (drag_drop_cb),
3530 * This function manages the navigation through the folders using the
3531 * keyboard or the hardware keys in the device
3534 on_key_pressed (GtkWidget *self,
3538 GtkTreeSelection *selection;
3540 GtkTreeModel *model;
3541 gboolean retval = FALSE;
3543 /* Up and Down are automatically managed by the treeview */
3544 if (event->keyval == GDK_Return) {
3545 /* Expand/Collapse the selected row */
3546 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3547 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3550 path = gtk_tree_model_get_path (model, &iter);
3552 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3553 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3555 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3556 gtk_tree_path_free (path);
3558 /* No further processing */
3566 * We listen to the changes in the local folder account name key,
3567 * because we want to show the right name in the view. The local
3568 * folder account name corresponds to the device name in the Maemo
3569 * version. We do this because we do not want to query gconf on each
3570 * tree view refresh. It's better to cache it and change whenever
3574 on_configuration_key_changed (ModestConf* conf,
3576 ModestConfEvent event,
3577 ModestConfNotificationId id,
3578 ModestFolderView *self)
3580 ModestFolderViewPrivate *priv;
3583 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3584 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3586 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3587 g_free (priv->local_account_name);
3589 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3590 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3592 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3593 MODEST_CONF_DEVICE_NAME, NULL);
3595 /* Force a redraw */
3596 #if GTK_CHECK_VERSION(2, 8, 0)
3597 GtkTreeViewColumn * tree_column;
3599 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3601 gtk_tree_view_column_queue_resize (tree_column);
3603 gtk_widget_queue_draw (GTK_WIDGET (self));
3609 modest_folder_view_set_style (ModestFolderView *self,
3610 ModestFolderViewStyle style)
3612 ModestFolderViewPrivate *priv;
3614 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3615 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3616 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3618 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3621 priv->style = style;
3625 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3626 const gchar *account_id)
3628 ModestFolderViewPrivate *priv;
3629 GtkTreeModel *model;
3631 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3633 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3635 /* This will be used by the filter_row callback,
3636 * to decided which rows to show: */
3637 if (priv->visible_account_id) {
3638 g_free (priv->visible_account_id);
3639 priv->visible_account_id = NULL;
3642 priv->visible_account_id = g_strdup (account_id);
3645 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3646 if (GTK_IS_TREE_MODEL_FILTER (model))
3647 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3649 modest_folder_view_update_model(self,
3650 (TnyAccountStore *) modest_runtime_get_account_store());
3652 /* Save settings to gconf */
3653 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3654 MODEST_CONF_FOLDER_VIEW_KEY);
3656 /* Notify observers */
3657 g_signal_emit (G_OBJECT(self),
3658 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3663 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3665 ModestFolderViewPrivate *priv;
3667 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3669 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3671 return (const gchar *) priv->visible_account_id;
3675 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3679 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3681 gtk_tree_model_get (model, iter,
3685 gboolean result = FALSE;
3686 if (type == TNY_FOLDER_TYPE_INBOX) {
3690 *inbox_iter = *iter;
3694 if (gtk_tree_model_iter_children (model, &child, iter)) {
3695 if (find_inbox_iter (model, &child, inbox_iter))
3699 } while (gtk_tree_model_iter_next (model, iter));
3708 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3710 #ifndef MODEST_TOOLKIT_HILDON2
3711 GtkTreeModel *model;
3712 GtkTreeIter iter, inbox_iter;
3713 GtkTreeSelection *sel;
3714 GtkTreePath *path = NULL;
3716 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3718 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3722 expand_root_items (self);
3723 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3725 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3726 g_warning ("%s: model is empty", __FUNCTION__);
3730 if (find_inbox_iter (model, &iter, &inbox_iter))
3731 path = gtk_tree_model_get_path (model, &inbox_iter);
3733 path = gtk_tree_path_new_first ();
3735 /* Select the row and free */
3736 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3737 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3738 gtk_tree_path_free (path);
3741 gtk_widget_grab_focus (GTK_WIDGET(self));
3748 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3753 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3754 TnyFolder* a_folder;
3757 gtk_tree_model_get (model, iter,
3758 INSTANCE_COLUMN, &a_folder,
3764 if (folder == a_folder) {
3765 g_object_unref (a_folder);
3766 *folder_iter = *iter;
3769 g_object_unref (a_folder);
3771 if (gtk_tree_model_iter_children (model, &child, iter)) {
3772 if (find_folder_iter (model, &child, folder_iter, folder))
3776 } while (gtk_tree_model_iter_next (model, iter));
3781 #ifndef MODEST_TOOLKIT_HILDON2
3783 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3786 ModestFolderView *self)
3788 ModestFolderViewPrivate *priv = NULL;
3789 GtkTreeSelection *sel;
3790 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3791 GObject *instance = NULL;
3793 if (!MODEST_IS_FOLDER_VIEW(self))
3796 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3798 priv->reexpand = TRUE;
3800 gtk_tree_model_get (tree_model, iter,
3802 INSTANCE_COLUMN, &instance,
3808 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3809 priv->folder_to_select = g_object_ref (instance);
3811 g_object_unref (instance);
3813 if (priv->folder_to_select) {
3815 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3818 path = gtk_tree_model_get_path (tree_model, iter);
3819 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3821 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3823 gtk_tree_selection_select_iter (sel, iter);
3824 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3826 gtk_tree_path_free (path);
3830 modest_folder_view_disable_next_folder_selection (self);
3832 /* Refilter the model */
3833 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3839 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3841 ModestFolderViewPrivate *priv;
3843 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3845 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3847 if (priv->folder_to_select)
3848 g_object_unref(priv->folder_to_select);
3850 priv->folder_to_select = NULL;
3854 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3855 gboolean after_change)
3857 GtkTreeModel *model;
3858 GtkTreeIter iter, folder_iter;
3859 GtkTreeSelection *sel;
3860 ModestFolderViewPrivate *priv = NULL;
3862 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3863 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3865 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3868 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3869 gtk_tree_selection_unselect_all (sel);
3871 if (priv->folder_to_select)
3872 g_object_unref(priv->folder_to_select);
3873 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3877 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3882 /* Refilter the model, before selecting the folder */
3883 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3885 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3886 g_warning ("%s: model is empty", __FUNCTION__);
3890 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3893 path = gtk_tree_model_get_path (model, &folder_iter);
3894 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3896 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3897 gtk_tree_selection_select_iter (sel, &folder_iter);
3898 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3900 gtk_tree_path_free (path);
3908 modest_folder_view_copy_selection (ModestFolderView *self)
3910 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3912 /* Copy selection */
3913 _clipboard_set_selected_data (self, FALSE);
3917 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3919 ModestFolderViewPrivate *priv = NULL;
3920 GtkTreeModel *model = NULL;
3921 const gchar **hidding = NULL;
3922 guint i, n_selected;
3924 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3925 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3927 /* Copy selection */
3928 if (!_clipboard_set_selected_data (folder_view, TRUE))
3931 /* Get hidding ids */
3932 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3934 /* Clear hidding array created by previous cut operation */
3935 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3937 /* Copy hidding array */
3938 priv->n_selected = n_selected;
3939 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3940 for (i=0; i < n_selected; i++)
3941 priv->hidding_ids[i] = g_strdup(hidding[i]);
3943 /* Hide cut folders */
3944 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3945 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3949 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3950 ModestFolderView *folder_view_dst)
3952 GtkTreeModel *filter_model = NULL;
3953 GtkTreeModel *model = NULL;
3954 GtkTreeModel *new_filter_model = NULL;
3955 GtkTreeModel *old_tny_model = NULL;
3956 GtkTreeModel *new_tny_model = NULL;
3957 ModestFolderViewPrivate *dst_priv;
3959 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3960 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3962 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3963 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3964 new_tny_model = NULL;
3967 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3968 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3969 G_OBJECT (old_tny_model),
3970 "activity-changed");
3972 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3973 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3975 /* Build new filter model */
3976 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3977 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3984 /* Set copied model */
3985 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3986 #ifndef MODEST_TOOLKIT_HILDON2
3987 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3988 G_OBJECT(new_filter_model), "row-inserted",
3989 (GCallback) on_row_inserted_maybe_select_folder,
3992 #ifdef MODEST_TOOLKIT_HILDON2
3993 if (new_tny_model) {
3994 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3995 G_OBJECT (new_tny_model),
3997 G_CALLBACK (on_activity_changed),
4003 g_object_unref (new_filter_model);
4007 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
4010 GtkTreeModel *model = NULL;
4011 ModestFolderViewPrivate* priv;
4013 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4015 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4016 priv->show_non_move = show;
4017 /* modest_folder_view_update_model(folder_view, */
4018 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
4020 /* Hide special folders */
4021 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
4022 if (GTK_IS_TREE_MODEL_FILTER (model)) {
4023 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
4028 modest_folder_view_show_message_count (ModestFolderView *folder_view,
4031 ModestFolderViewPrivate* priv;
4033 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4035 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4036 priv->show_message_count = show;
4038 g_object_set (G_OBJECT (priv->messages_renderer),
4039 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4043 /* Returns FALSE if it did not selected anything */
4045 _clipboard_set_selected_data (ModestFolderView *folder_view,
4048 ModestFolderViewPrivate *priv = NULL;
4049 TnyFolderStore *folder = NULL;
4050 gboolean retval = FALSE;
4052 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
4053 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4055 /* Set selected data on clipboard */
4056 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
4057 folder = modest_folder_view_get_selected (folder_view);
4059 /* Do not allow to select an account */
4060 if (TNY_IS_FOLDER (folder)) {
4061 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
4066 g_object_unref (folder);
4072 _clear_hidding_filter (ModestFolderView *folder_view)
4074 ModestFolderViewPrivate *priv;
4077 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4078 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4080 if (priv->hidding_ids != NULL) {
4081 for (i=0; i < priv->n_selected; i++)
4082 g_free (priv->hidding_ids[i]);
4083 g_free(priv->hidding_ids);
4089 on_display_name_changed (ModestAccountMgr *mgr,
4090 const gchar *account,
4093 ModestFolderView *self;
4095 self = MODEST_FOLDER_VIEW (user_data);
4097 /* Force a redraw */
4098 #if GTK_CHECK_VERSION(2, 8, 0)
4099 GtkTreeViewColumn * tree_column;
4101 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4103 gtk_tree_view_column_queue_resize (tree_column);
4105 gtk_widget_queue_draw (GTK_WIDGET (self));
4110 modest_folder_view_set_cell_style (ModestFolderView *self,
4111 ModestFolderViewCellStyle cell_style)
4113 ModestFolderViewPrivate *priv = NULL;
4115 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4116 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4118 priv->cell_style = cell_style;
4120 g_object_set (G_OBJECT (priv->messages_renderer),
4121 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4124 gtk_widget_queue_draw (GTK_WIDGET (self));
4128 update_style (ModestFolderView *self)
4130 ModestFolderViewPrivate *priv;
4131 GdkColor style_color, style_active_color;
4132 PangoAttrList *attr_list;
4134 PangoAttribute *attr;
4136 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4137 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4141 attr_list = pango_attr_list_new ();
4142 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4143 gdk_color_parse ("grey", &style_color);
4145 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4146 pango_attr_list_insert (attr_list, attr);
4149 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4151 "SmallSystemFont", NULL,
4154 attr = pango_attr_font_desc_new (pango_font_description_copy
4155 (style->font_desc));
4156 pango_attr_list_insert (attr_list, attr);
4158 g_object_set (G_OBJECT (priv->messages_renderer),
4159 "foreground-gdk", &style_color,
4160 "foreground-set", TRUE,
4161 "attributes", attr_list,
4163 pango_attr_list_unref (attr_list);
4166 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4167 priv->active_color = style_active_color;
4169 gdk_color_parse ("000", &(priv->active_color));
4174 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4176 if (strcmp ("style", spec->name) == 0) {
4177 update_style (MODEST_FOLDER_VIEW (obj));
4178 gtk_widget_queue_draw (GTK_WIDGET (obj));
4183 modest_folder_view_set_filter (ModestFolderView *self,
4184 ModestFolderViewFilter filter)
4186 ModestFolderViewPrivate *priv;
4187 GtkTreeModel *filter_model;
4189 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4190 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4192 priv->filter |= filter;
4194 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4195 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4196 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4201 modest_folder_view_unset_filter (ModestFolderView *self,
4202 ModestFolderViewFilter filter)
4204 ModestFolderViewPrivate *priv;
4205 GtkTreeModel *filter_model;
4207 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4208 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4210 priv->filter &= ~filter;
4212 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4213 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4214 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4219 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4220 ModestTnyFolderRules rules)
4222 GtkTreeModel *filter_model;
4224 gboolean fulfil = FALSE;
4226 if (!get_inner_models (self, &filter_model, NULL, NULL))
4229 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4233 TnyFolderStore *folder;
4235 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4237 if (TNY_IS_FOLDER (folder)) {
4238 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4239 /* Folder rules are negative: non_writable, non_deletable... */
4240 if (!(folder_rules & rules))
4243 g_object_unref (folder);
4246 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4252 modest_folder_view_set_list_to_move (ModestFolderView *self,
4255 ModestFolderViewPrivate *priv;
4257 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4258 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4260 if (priv->list_to_move)
4261 g_object_unref (priv->list_to_move);
4264 g_object_ref (list);
4266 priv->list_to_move = list;
4270 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4272 ModestFolderViewPrivate *priv;
4274 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4275 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4278 g_free (priv->mailbox);
4280 priv->mailbox = g_strdup (mailbox);
4282 /* Notify observers */
4283 g_signal_emit (G_OBJECT(self),
4284 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4285 priv->visible_account_id);
4289 modest_folder_view_get_mailbox (ModestFolderView *self)
4291 ModestFolderViewPrivate *priv;
4293 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4294 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4296 return (const gchar *) priv->mailbox;
4300 modest_folder_view_get_activity (ModestFolderView *self)
4302 ModestFolderViewPrivate *priv;
4303 GtkTreeModel *inner_model;
4305 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4306 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4307 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4309 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4310 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4316 #ifdef MODEST_TOOLKIT_HILDON2
4318 on_activity_changed (TnyGtkFolderListStore *store,
4320 ModestFolderView *folder_view)
4322 ModestFolderViewPrivate *priv;
4324 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4325 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4326 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4328 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
4334 modest_folder_view_get_model_tny_list (ModestFolderView *self)
4336 GtkTreeModel *model;
4342 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
4343 ret_value = TNY_LIST (model);
4344 g_object_ref (ret_value);