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;
1371 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1373 #ifdef MODEST_TOOLKIT_HILDON2
1374 if (priv->signal_handlers) {
1375 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1376 priv->signal_handlers = NULL;
1380 /* Free external references */
1381 if (priv->account_store) {
1382 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1383 priv->account_inserted_signal);
1384 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1385 priv->account_removed_signal);
1386 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1387 priv->account_changed_signal);
1388 g_object_unref (G_OBJECT(priv->account_store));
1389 priv->account_store = NULL;
1393 g_object_unref (G_OBJECT (priv->query));
1397 if (priv->folder_to_select) {
1398 g_object_unref (G_OBJECT(priv->folder_to_select));
1399 priv->folder_to_select = NULL;
1402 if (priv->cur_folder_store) {
1403 g_object_unref (priv->cur_folder_store);
1404 priv->cur_folder_store = NULL;
1407 if (priv->list_to_move) {
1408 g_object_unref (priv->list_to_move);
1409 priv->list_to_move = NULL;
1412 G_OBJECT_CLASS(parent_class)->dispose (obj);
1416 modest_folder_view_finalize (GObject *obj)
1418 ModestFolderViewPrivate *priv;
1419 GtkTreeSelection *sel;
1420 TnyAccount *local_account;
1422 g_return_if_fail (obj);
1424 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1426 if (priv->timer_expander != 0) {
1427 g_source_remove (priv->timer_expander);
1428 priv->timer_expander = 0;
1431 local_account = (TnyAccount *)
1432 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1433 if (local_account) {
1434 if (g_signal_handler_is_connected (local_account,
1435 priv->outbox_deleted_handler))
1436 g_signal_handler_disconnect (local_account,
1437 priv->outbox_deleted_handler);
1438 g_object_unref (local_account);
1441 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1442 priv->display_name_changed_signal)) {
1443 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1444 priv->display_name_changed_signal);
1445 priv->display_name_changed_signal = 0;
1448 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1450 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1452 g_free (priv->local_account_name);
1453 g_free (priv->visible_account_id);
1454 g_free (priv->mailbox);
1456 if (priv->conf_key_signal) {
1457 g_signal_handler_disconnect (modest_runtime_get_conf (),
1458 priv->conf_key_signal);
1459 priv->conf_key_signal = 0;
1462 /* Clear hidding array created by cut operation */
1463 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1465 gdk_color_parse ("000", &priv->active_color);
1467 G_OBJECT_CLASS(parent_class)->finalize (obj);
1472 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1474 ModestFolderViewPrivate *priv;
1477 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1478 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1480 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1481 device = tny_account_store_get_device (account_store);
1483 if (G_UNLIKELY (priv->account_store)) {
1485 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1486 priv->account_inserted_signal))
1487 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1488 priv->account_inserted_signal);
1489 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1490 priv->account_removed_signal))
1491 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1492 priv->account_removed_signal);
1493 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1494 priv->account_changed_signal))
1495 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1496 priv->account_changed_signal);
1497 g_object_unref (G_OBJECT (priv->account_store));
1500 priv->account_store = g_object_ref (G_OBJECT (account_store));
1502 priv->account_removed_signal =
1503 g_signal_connect (G_OBJECT(account_store), "account_removed",
1504 G_CALLBACK (on_account_removed), self);
1506 priv->account_inserted_signal =
1507 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1508 G_CALLBACK (on_account_inserted), self);
1510 priv->account_changed_signal =
1511 g_signal_connect (G_OBJECT(account_store), "account_changed",
1512 G_CALLBACK (on_account_changed), self);
1514 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1515 priv->reselect = FALSE;
1516 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1518 g_object_unref (G_OBJECT (device));
1522 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1525 ModestFolderView *self;
1526 GtkTreeModel *model, *filter_model;
1529 self = MODEST_FOLDER_VIEW (user_data);
1531 if (!get_inner_models (self, &filter_model, NULL, &model))
1534 /* Remove outbox from model */
1535 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1536 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1537 g_object_unref (outbox);
1540 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1544 on_account_inserted (TnyAccountStore *account_store,
1545 TnyAccount *account,
1548 ModestFolderViewPrivate *priv;
1549 GtkTreeModel *model, *filter_model;
1551 /* Ignore transport account insertions, we're not showing them
1552 in the folder view */
1553 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1556 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1559 /* If we're adding a new account, and there is no previous
1560 one, we need to select the visible server account */
1561 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1562 !priv->visible_account_id)
1563 modest_widget_memory_restore (modest_runtime_get_conf(),
1564 G_OBJECT (user_data),
1565 MODEST_CONF_FOLDER_VIEW_KEY);
1569 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1570 &filter_model, NULL, &model))
1573 /* Insert the account in the model */
1574 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1576 /* When the model is a list store (plain representation) the
1577 outbox is not a child of any account so we have to manually
1578 delete it because removing the local folders account won't
1579 delete it (because tny_folder_get_account() is not defined
1580 for a merge folder */
1581 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1582 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1584 priv->outbox_deleted_handler =
1585 g_signal_connect (account,
1587 G_CALLBACK (on_outbox_deleted_cb),
1591 /* Refilter the model */
1592 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1597 same_account_selected (ModestFolderView *self,
1598 TnyAccount *account)
1600 ModestFolderViewPrivate *priv;
1601 gboolean same_account = FALSE;
1603 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1605 if (priv->cur_folder_store) {
1606 TnyAccount *selected_folder_account = NULL;
1608 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1609 selected_folder_account =
1610 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1612 selected_folder_account =
1613 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1616 if (selected_folder_account == account)
1617 same_account = TRUE;
1619 g_object_unref (selected_folder_account);
1621 return same_account;
1626 * Selects the first inbox or the local account in an idle
1629 on_idle_select_first_inbox_or_local (gpointer user_data)
1631 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1633 gdk_threads_enter ();
1634 modest_folder_view_select_first_inbox_or_local (self);
1635 gdk_threads_leave ();
1641 on_account_changed (TnyAccountStore *account_store,
1642 TnyAccount *tny_account,
1645 ModestFolderView *self;
1646 ModestFolderViewPrivate *priv;
1647 GtkTreeModel *model, *filter_model;
1648 GtkTreeSelection *sel;
1649 gboolean same_account;
1651 /* Ignore transport account insertions, we're not showing them
1652 in the folder view */
1653 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1656 self = MODEST_FOLDER_VIEW (user_data);
1657 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1659 /* Get the inner model */
1660 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1661 &filter_model, NULL, &model))
1664 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1666 /* Invalidate the cur_folder_store only if the selected folder
1667 belongs to the account that is being removed */
1668 same_account = same_account_selected (self, tny_account);
1670 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1671 gtk_tree_selection_unselect_all (sel);
1674 /* Remove the account from the model */
1675 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1677 /* Insert the account in the model */
1678 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1680 /* Refilter the model */
1681 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1683 /* Select the first INBOX if the currently selected folder
1684 belongs to the account that is being deleted */
1685 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1686 g_idle_add (on_idle_select_first_inbox_or_local, self);
1690 on_account_removed (TnyAccountStore *account_store,
1691 TnyAccount *account,
1694 ModestFolderView *self = NULL;
1695 ModestFolderViewPrivate *priv;
1696 GtkTreeModel *model, *filter_model;
1697 GtkTreeSelection *sel = NULL;
1698 gboolean same_account = FALSE;
1700 /* Ignore transport account removals, we're not showing them
1701 in the folder view */
1702 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1705 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1706 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1710 self = MODEST_FOLDER_VIEW (user_data);
1711 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1713 /* Invalidate the cur_folder_store only if the selected folder
1714 belongs to the account that is being removed */
1715 same_account = same_account_selected (self, account);
1717 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1718 gtk_tree_selection_unselect_all (sel);
1721 /* Invalidate row to select only if the folder to select
1722 belongs to the account that is being removed*/
1723 if (priv->folder_to_select) {
1724 TnyAccount *folder_to_select_account = NULL;
1726 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1727 if (folder_to_select_account == account) {
1728 modest_folder_view_disable_next_folder_selection (self);
1729 g_object_unref (priv->folder_to_select);
1730 priv->folder_to_select = NULL;
1732 g_object_unref (folder_to_select_account);
1735 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1736 &filter_model, NULL, &model))
1739 /* Disconnect the signal handler */
1740 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1741 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1742 if (g_signal_handler_is_connected (account,
1743 priv->outbox_deleted_handler))
1744 g_signal_handler_disconnect (account,
1745 priv->outbox_deleted_handler);
1748 /* Remove the account from the model */
1749 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1751 /* If the removed account is the currently viewed one then
1752 clear the configuration value. The new visible account will be the default account */
1753 if (priv->visible_account_id &&
1754 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1756 /* Clear the current visible account_id */
1757 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1758 modest_folder_view_set_mailbox (self, NULL);
1760 /* Call the restore method, this will set the new visible account */
1761 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1762 MODEST_CONF_FOLDER_VIEW_KEY);
1765 /* Refilter the model */
1766 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1768 /* Select the first INBOX if the currently selected folder
1769 belongs to the account that is being deleted */
1771 g_idle_add (on_idle_select_first_inbox_or_local, self);
1775 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1777 GtkTreeViewColumn *col;
1779 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1781 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1783 g_printerr ("modest: failed get column for title\n");
1787 gtk_tree_view_column_set_title (col, title);
1788 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1793 modest_folder_view_on_map (ModestFolderView *self,
1794 GdkEventExpose *event,
1797 ModestFolderViewPrivate *priv;
1799 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1801 /* This won't happen often */
1802 if (G_UNLIKELY (priv->reselect)) {
1803 /* Select the first inbox or the local account if not found */
1805 /* TODO: this could cause a lock at startup, so we
1806 comment it for the moment. We know that this will
1807 be a bug, because the INBOX is not selected, but we
1808 need to rewrite some parts of Modest to avoid the
1809 deathlock situation */
1810 /* TODO: check if this is still the case */
1811 priv->reselect = FALSE;
1812 modest_folder_view_select_first_inbox_or_local (self);
1813 /* Notify the display name observers */
1814 g_signal_emit (G_OBJECT(self),
1815 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1819 if (priv->reexpand) {
1820 expand_root_items (self);
1821 priv->reexpand = FALSE;
1828 modest_folder_view_new (TnyFolderStoreQuery *query)
1830 return modest_folder_view_new_full (query, TRUE);
1834 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1837 ModestFolderViewPrivate *priv;
1838 GtkTreeSelection *sel;
1840 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1841 #ifdef MODEST_TOOLKIT_HILDON2
1842 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1845 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1848 priv->query = g_object_ref (query);
1850 priv->do_refresh = do_refresh;
1852 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1853 priv->changed_signal = g_signal_connect (sel, "changed",
1854 G_CALLBACK (on_selection_changed), self);
1856 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1858 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1860 return GTK_WIDGET(self);
1863 /* this feels dirty; any other way to expand all the root items? */
1865 expand_root_items (ModestFolderView *self)
1868 GtkTreeModel *model;
1871 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1872 path = gtk_tree_path_new_first ();
1874 /* all folders should have child items, so.. */
1876 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1877 gtk_tree_path_next (path);
1878 } while (gtk_tree_model_get_iter (model, &iter, path));
1880 gtk_tree_path_free (path);
1884 is_parent_of (TnyFolder *a, TnyFolder *b)
1887 gboolean retval = FALSE;
1889 a_id = tny_folder_get_id (a);
1891 gchar *string_to_match;
1894 string_to_match = g_strconcat (a_id, "/", NULL);
1895 b_id = tny_folder_get_id (b);
1896 retval = g_str_has_prefix (b_id, string_to_match);
1897 g_free (string_to_match);
1903 typedef struct _ForeachFolderInfo {
1906 } ForeachFolderInfo;
1909 foreach_folder_with_id (GtkTreeModel *model,
1914 ForeachFolderInfo *info;
1917 info = (ForeachFolderInfo *) data;
1918 gtk_tree_model_get (model, iter,
1919 INSTANCE_COLUMN, &instance,
1922 if (TNY_IS_FOLDER (instance)) {
1925 id = tny_folder_get_id (TNY_FOLDER (instance));
1927 collate = g_utf8_collate_key (id, -1);
1928 info->found = !strcmp (info->needle, collate);
1934 g_object_unref (instance);
1942 has_folder_with_id (ModestFolderView *self, const gchar *id)
1944 GtkTreeModel *model;
1945 ForeachFolderInfo info = {NULL, FALSE};
1947 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1948 info.needle = g_utf8_collate_key (id, -1);
1950 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1951 g_free (info.needle);
1957 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1960 gboolean retval = FALSE;
1962 a_id = tny_folder_get_id (a);
1965 b_id = tny_folder_get_id (b);
1968 const gchar *last_bar;
1969 gchar *string_to_match;
1970 last_bar = g_strrstr (b_id, "/");
1975 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1976 retval = has_folder_with_id (self, string_to_match);
1977 g_free (string_to_match);
1985 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1987 ModestFolderViewPrivate *priv;
1988 TnyIterator *iterator;
1989 gboolean retval = TRUE;
1991 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1994 for (iterator = tny_list_create_iterator (priv->list_to_move);
1995 retval && !tny_iterator_is_done (iterator);
1996 tny_iterator_next (iterator)) {
1998 instance = tny_iterator_get_current (iterator);
1999 if (instance == (GObject *) folder) {
2001 } else if (TNY_IS_FOLDER (instance)) {
2002 retval = !is_parent_of (TNY_FOLDER (instance), folder);
2004 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
2007 g_object_unref (instance);
2009 g_object_unref (iterator);
2016 * We use this function to implement the
2017 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
2018 * account in this case, and the local folders.
2021 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2023 ModestFolderViewPrivate *priv;
2024 gboolean retval = TRUE;
2025 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2026 GObject *instance = NULL;
2027 const gchar *id = NULL;
2029 gboolean found = FALSE;
2030 gboolean cleared = FALSE;
2031 ModestTnyFolderRules rules = 0;
2034 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2035 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2037 gtk_tree_model_get (model, iter,
2038 NAME_COLUMN, &fname,
2040 INSTANCE_COLUMN, &instance,
2043 /* Do not show if there is no instance, this could indeed
2044 happen when the model is being modified while it's being
2045 drawn. This could occur for example when moving folders
2052 if (TNY_IS_ACCOUNT (instance)) {
2053 TnyAccount *acc = TNY_ACCOUNT (instance);
2054 const gchar *account_id = tny_account_get_id (acc);
2056 /* If it isn't a special folder,
2057 * don't show it unless it is the visible account: */
2058 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2059 !modest_tny_account_is_virtual_local_folders (acc) &&
2060 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2062 /* Show only the visible account id */
2063 if (priv->visible_account_id) {
2064 if (strcmp (account_id, priv->visible_account_id))
2071 /* Never show these to the user. They are merged into one folder
2072 * in the local-folders account instead: */
2073 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2076 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2077 /* Only show special folders for current account if needed */
2078 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2079 TnyAccount *account;
2081 account = tny_folder_get_account (TNY_FOLDER (instance));
2083 if (TNY_IS_ACCOUNT (account)) {
2084 const gchar *account_id = tny_account_get_id (account);
2086 if (!modest_tny_account_is_virtual_local_folders (account) &&
2087 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2088 /* Show only the visible account id */
2089 if (priv->visible_account_id) {
2090 if (strcmp (account_id, priv->visible_account_id)) {
2092 } else if (priv->mailbox) {
2093 /* Filter mailboxes */
2094 if (!g_str_has_prefix (fname, priv->mailbox)) {
2096 } else if (!strcmp (fname, priv->mailbox)) {
2097 /* Hide mailbox parent */
2103 g_object_unref (account);
2110 /* Check hiding (if necessary) */
2111 cleared = modest_email_clipboard_cleared (priv->clipboard);
2112 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2113 id = tny_folder_get_id (TNY_FOLDER(instance));
2114 if (priv->hidding_ids != NULL)
2115 for (i=0; i < priv->n_selected && !found; i++)
2116 if (priv->hidding_ids[i] != NULL && id != NULL)
2117 found = (!strcmp (priv->hidding_ids[i], id));
2122 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2123 folder as no message can be move there according to UI specs */
2124 if (retval && !priv->show_non_move) {
2125 if (priv->list_to_move &&
2126 tny_list_get_length (priv->list_to_move) > 0 &&
2127 TNY_IS_FOLDER (instance)) {
2128 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2130 if (retval && TNY_IS_FOLDER (instance) &&
2131 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2133 case TNY_FOLDER_TYPE_OUTBOX:
2134 case TNY_FOLDER_TYPE_SENT:
2135 case TNY_FOLDER_TYPE_DRAFTS:
2138 case TNY_FOLDER_TYPE_UNKNOWN:
2139 case TNY_FOLDER_TYPE_NORMAL:
2140 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2141 if (type == TNY_FOLDER_TYPE_INVALID)
2142 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2144 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2145 type == TNY_FOLDER_TYPE_SENT
2146 || type == TNY_FOLDER_TYPE_DRAFTS)
2153 if (retval && TNY_IS_ACCOUNT (instance) &&
2154 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2155 ModestProtocolType protocol_type;
2157 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2158 retval = !modest_protocol_registry_protocol_type_has_tag
2159 (modest_runtime_get_protocol_registry (),
2161 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2165 /* apply special filters */
2166 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2167 if (TNY_IS_ACCOUNT (instance))
2171 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2172 if (TNY_IS_FOLDER (instance))
2176 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2177 if (TNY_IS_ACCOUNT (instance)) {
2178 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2180 } else if (TNY_IS_FOLDER (instance)) {
2181 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2186 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2187 if (TNY_IS_ACCOUNT (instance)) {
2188 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2190 } else if (TNY_IS_FOLDER (instance)) {
2191 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2196 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2197 /* A mailbox is a fake folder with an @ in the middle of the name */
2198 if (!TNY_IS_FOLDER (instance) ||
2199 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2202 const gchar *folder_name;
2203 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2204 if (!folder_name || strchr (folder_name, '@') == NULL)
2210 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2211 if (TNY_IS_FOLDER (instance)) {
2212 /* Check folder rules */
2213 ModestTnyFolderRules rules;
2215 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2216 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2217 } else if (TNY_IS_ACCOUNT (instance)) {
2218 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2226 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2227 if (TNY_IS_FOLDER (instance)) {
2228 TnyFolderType guess_type;
2230 if (TNY_FOLDER_TYPE_NORMAL) {
2231 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2237 case TNY_FOLDER_TYPE_OUTBOX:
2238 case TNY_FOLDER_TYPE_SENT:
2239 case TNY_FOLDER_TYPE_DRAFTS:
2240 case TNY_FOLDER_TYPE_ARCHIVE:
2241 case TNY_FOLDER_TYPE_INBOX:
2244 case TNY_FOLDER_TYPE_UNKNOWN:
2245 case TNY_FOLDER_TYPE_NORMAL:
2251 } else if (TNY_IS_ACCOUNT (instance)) {
2256 if (retval && TNY_IS_FOLDER (instance)) {
2257 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2260 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2261 if (TNY_IS_FOLDER (instance)) {
2262 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2263 } else if (TNY_IS_ACCOUNT (instance)) {
2268 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2269 if (TNY_IS_FOLDER (instance)) {
2270 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2271 } else if (TNY_IS_ACCOUNT (instance)) {
2276 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2277 if (TNY_IS_FOLDER (instance)) {
2278 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2279 } else if (TNY_IS_ACCOUNT (instance)) {
2285 g_object_unref (instance);
2293 modest_folder_view_update_model (ModestFolderView *self,
2294 TnyAccountStore *account_store)
2296 ModestFolderViewPrivate *priv;
2297 GtkTreeModel *model;
2298 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2300 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2301 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2304 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2306 /* Notify that there is no folder selected */
2307 g_signal_emit (G_OBJECT(self),
2308 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2310 if (priv->cur_folder_store) {
2311 g_object_unref (priv->cur_folder_store);
2312 priv->cur_folder_store = NULL;
2315 /* FIXME: the local accounts are not shown when the query
2316 selects only the subscribed folders */
2317 #ifdef MODEST_TOOLKIT_HILDON2
2318 TnyGtkFolderListStoreFlags flags;
2319 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2320 if (priv->do_refresh)
2321 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2323 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2324 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2326 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2327 MODEST_FOLDER_PATH_SEPARATOR);
2329 model = tny_gtk_folder_store_tree_model_new (NULL);
2332 /* When the model is a list store (plain representation) the
2333 outbox is not a child of any account so we have to manually
2334 delete it because removing the local folders account won't
2335 delete it (because tny_folder_get_account() is not defined
2336 for a merge folder */
2337 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2338 TnyAccount *account;
2339 ModestTnyAccountStore *acc_store;
2341 acc_store = modest_runtime_get_account_store ();
2342 account = modest_tny_account_store_get_local_folders_account (acc_store);
2344 if (g_signal_handler_is_connected (account,
2345 priv->outbox_deleted_handler))
2346 g_signal_handler_disconnect (account,
2347 priv->outbox_deleted_handler);
2349 priv->outbox_deleted_handler =
2350 g_signal_connect (account,
2352 G_CALLBACK (on_outbox_deleted_cb),
2354 g_object_unref (account);
2357 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2358 /* Get the accounts */
2359 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2361 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2363 if (priv->visible_account_id) {
2364 TnyAccount *account;
2366 /* Add local folders account */
2367 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2370 tny_list_append (TNY_LIST (model), (GObject *) account);
2371 g_object_unref (account);
2374 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2377 tny_list_append (TNY_LIST (model), (GObject *) account);
2378 g_object_unref (account);
2381 /* Add visible account */
2382 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2383 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2384 priv->visible_account_id);
2386 tny_list_append (TNY_LIST (model), (GObject *) account);
2387 g_object_unref (account);
2389 g_warning ("You need to set an account first");
2390 g_object_unref (model);
2394 g_warning ("You need to set an account first");
2395 g_object_unref (model);
2400 sortable = gtk_tree_model_sort_new_with_model (model);
2401 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2403 GTK_SORT_ASCENDING);
2404 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2406 cmp_rows, NULL, NULL);
2408 /* Create filter model */
2409 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2410 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2415 GtkTreeModel *old_tny_model = NULL;
2416 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2417 if (priv->signal_handlers > 0) {
2418 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2419 G_OBJECT (old_tny_model),
2420 "activity-changed");
2425 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2426 #ifndef MODEST_TOOLKIT_HILDON2
2427 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2428 (GCallback) on_row_inserted_maybe_select_folder, self);
2431 #ifdef MODEST_TOOLKIT_HILDON2
2432 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2435 G_CALLBACK (on_activity_changed),
2439 g_object_unref (model);
2440 g_object_unref (filter_model);
2441 g_object_unref (sortable);
2443 /* Force a reselection of the INBOX next time the widget is shown */
2444 priv->reselect = TRUE;
2451 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2453 GtkTreeModel *model = NULL;
2454 TnyFolderStore *folder = NULL;
2456 ModestFolderView *tree_view = NULL;
2457 ModestFolderViewPrivate *priv = NULL;
2458 gboolean selected = FALSE;
2460 g_return_if_fail (sel);
2461 g_return_if_fail (user_data);
2463 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2465 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2467 tree_view = MODEST_FOLDER_VIEW (user_data);
2470 gtk_tree_model_get (model, &iter,
2471 INSTANCE_COLUMN, &folder,
2474 /* If the folder is the same do not notify */
2475 if (folder && priv->cur_folder_store == folder) {
2476 g_object_unref (folder);
2481 /* Current folder was unselected */
2482 if (priv->cur_folder_store) {
2483 /* We must do this firstly because a libtinymail-camel
2484 implementation detail. If we issue the signal
2485 before doing the sync_async, then that signal could
2486 cause (and it actually does it) a free of the
2487 summary of the folder (because the main window will
2488 clear the headers view */
2489 #ifndef MODEST_TOOLKIT_HILDON2
2490 if (TNY_IS_FOLDER(priv->cur_folder_store))
2491 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2492 FALSE, NULL, NULL, NULL);
2495 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2496 priv->cur_folder_store, FALSE);
2498 g_object_unref (priv->cur_folder_store);
2499 priv->cur_folder_store = NULL;
2502 /* New current references */
2503 priv->cur_folder_store = folder;
2505 /* New folder has been selected. Do not notify if there is
2506 nothing new selected */
2508 g_signal_emit (G_OBJECT(tree_view),
2509 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2510 0, priv->cur_folder_store, TRUE);
2515 on_row_activated (GtkTreeView *treeview,
2516 GtkTreePath *treepath,
2517 GtkTreeViewColumn *column,
2520 GtkTreeModel *model = NULL;
2521 TnyFolderStore *folder = NULL;
2523 ModestFolderView *self = NULL;
2524 ModestFolderViewPrivate *priv = NULL;
2526 g_return_if_fail (treeview);
2527 g_return_if_fail (user_data);
2529 self = MODEST_FOLDER_VIEW (user_data);
2530 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2532 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2534 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2537 gtk_tree_model_get (model, &iter,
2538 INSTANCE_COLUMN, &folder,
2541 g_signal_emit (G_OBJECT(self),
2542 signals[FOLDER_ACTIVATED_SIGNAL],
2545 #ifdef MODEST_TOOLKIT_HILDON2
2546 HildonUIMode ui_mode;
2547 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2548 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2549 if (priv->cur_folder_store)
2550 g_object_unref (priv->cur_folder_store);
2551 priv->cur_folder_store = g_object_ref (folder);
2555 g_object_unref (folder);
2559 modest_folder_view_get_selected (ModestFolderView *self)
2561 ModestFolderViewPrivate *priv;
2563 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2565 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2566 if (priv->cur_folder_store)
2567 g_object_ref (priv->cur_folder_store);
2569 return priv->cur_folder_store;
2573 get_cmp_rows_type_pos (GObject *folder)
2575 /* Remote accounts -> Local account -> MMC account .*/
2578 if (TNY_IS_ACCOUNT (folder) &&
2579 modest_tny_account_is_virtual_local_folders (
2580 TNY_ACCOUNT (folder))) {
2582 } else if (TNY_IS_ACCOUNT (folder)) {
2583 TnyAccount *account = TNY_ACCOUNT (folder);
2584 const gchar *account_id = tny_account_get_id (account);
2585 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2591 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2592 return -1; /* Should never happen */
2597 inbox_is_special (TnyFolderStore *folder_store)
2599 gboolean is_special = TRUE;
2601 if (TNY_IS_FOLDER (folder_store)) {
2605 gchar *last_inbox_bar;
2607 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2608 downcase = g_utf8_strdown (id, -1);
2609 last_bar = g_strrstr (downcase, "/");
2611 last_inbox_bar = g_strrstr (downcase, "inbox/");
2612 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2623 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2625 TnyAccount *account;
2626 gboolean is_special;
2627 /* Inbox, Outbox, Drafts, Sent, User */
2630 if (!TNY_IS_FOLDER (folder_store))
2633 case TNY_FOLDER_TYPE_INBOX:
2635 account = tny_folder_get_account (folder_store);
2636 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2638 /* In inbox case we need to know if the inbox is really the top
2639 * inbox of the account, or if it's a submailbox inbox. To do
2640 * this we'll apply an heuristic rule: Find last "/" and check
2641 * if it's preceeded by another Inbox */
2642 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2643 g_object_unref (account);
2644 return is_special?0:4;
2647 case TNY_FOLDER_TYPE_OUTBOX:
2648 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2650 case TNY_FOLDER_TYPE_DRAFTS:
2652 account = tny_folder_get_account (folder_store);
2653 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2654 g_object_unref (account);
2655 return is_special?1:4;
2658 case TNY_FOLDER_TYPE_SENT:
2660 account = tny_folder_get_account (folder_store);
2661 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2662 g_object_unref (account);
2663 return is_special?3:4;
2672 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2674 const gchar *a1_name, *a2_name;
2676 a1_name = tny_account_get_name (a1);
2677 a2_name = tny_account_get_name (a2);
2679 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2683 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2685 TnyAccount *a1 = NULL, *a2 = NULL;
2688 if (TNY_IS_ACCOUNT (s1)) {
2689 a1 = TNY_ACCOUNT (g_object_ref (s1));
2690 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2691 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2694 if (TNY_IS_ACCOUNT (s2)) {
2695 a2 = TNY_ACCOUNT (g_object_ref (s2));
2696 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2697 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2714 /* First we sort with the type of account */
2715 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2719 cmp = compare_account_names (a1, a2);
2723 g_object_unref (a1);
2725 g_object_unref (a2);
2731 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2733 gint is_account1, is_account2;
2735 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2736 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2738 return is_account2 - is_account1;
2742 compare_folders (const gchar *name1, const gchar *name2)
2744 const gchar *separator1, *separator2;
2745 const gchar *next1, *next2;
2749 if (name1 == NULL || name1[0] == '\0')
2751 if (name2 == NULL || name2[0] == '\0')
2754 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2756 top1 = g_strndup (name1, separator1 - name1);
2758 top1 = g_strdup (name1);
2761 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2763 top2 = g_strndup (name2, separator2 - name2);
2765 top2 = g_strdup (name2);
2769 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2776 if (separator1 == NULL && separator2 == NULL)
2779 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2780 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2782 return compare_folders (next1, next2);
2787 * This function orders the mail accounts according to these rules:
2788 * 1st - remote accounts
2789 * 2nd - local account
2793 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2797 gchar *name1 = NULL;
2798 gchar *name2 = NULL;
2799 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2800 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2801 GObject *folder1 = NULL;
2802 GObject *folder2 = NULL;
2804 gtk_tree_model_get (tree_model, iter1,
2805 NAME_COLUMN, &name1,
2807 INSTANCE_COLUMN, &folder1,
2809 gtk_tree_model_get (tree_model, iter2,
2810 NAME_COLUMN, &name2,
2811 TYPE_COLUMN, &type2,
2812 INSTANCE_COLUMN, &folder2,
2815 /* Return if we get no folder. This could happen when folder
2816 operations are happening. The model is updated after the
2817 folder copy/move actually occurs, so there could be
2818 situations where the model to be drawn is not correct */
2819 if (!folder1 || !folder2)
2822 /* Sort by type. First the special folders, then the archives */
2823 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2827 /* Now we sort using the account of each folder */
2828 if (TNY_IS_FOLDER_STORE (folder1) &&
2829 TNY_IS_FOLDER_STORE (folder2)) {
2830 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2834 /* Each group is preceeded by its account */
2835 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2840 /* Pure sort by name */
2841 cmp = compare_folders (name1, name2);
2844 g_object_unref(G_OBJECT(folder1));
2846 g_object_unref(G_OBJECT(folder2));
2854 /*****************************************************************************/
2855 /* DRAG and DROP stuff */
2856 /*****************************************************************************/
2858 * This function fills the #GtkSelectionData with the row and the
2859 * model that has been dragged. It's called when this widget is a
2860 * source for dnd after the event drop happened
2863 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2864 guint info, guint time, gpointer data)
2866 GtkTreeSelection *selection;
2867 GtkTreeModel *model;
2869 GtkTreePath *source_row;
2871 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2872 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2874 source_row = gtk_tree_model_get_path (model, &iter);
2875 gtk_tree_set_row_drag_data (selection_data,
2879 gtk_tree_path_free (source_row);
2883 typedef struct _DndHelper {
2884 ModestFolderView *folder_view;
2885 gboolean delete_source;
2886 GtkTreePath *source_row;
2890 dnd_helper_destroyer (DndHelper *helper)
2892 /* Free the helper */
2893 gtk_tree_path_free (helper->source_row);
2894 g_slice_free (DndHelper, helper);
2898 xfer_folder_cb (ModestMailOperation *mail_op,
2899 TnyFolder *new_folder,
2903 /* Select the folder */
2904 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2910 /* get the folder for the row the treepath refers to. */
2911 /* folder must be unref'd */
2912 static TnyFolderStore *
2913 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2916 TnyFolderStore *folder = NULL;
2918 if (gtk_tree_model_get_iter (model,&iter, path))
2919 gtk_tree_model_get (model, &iter,
2920 INSTANCE_COLUMN, &folder,
2927 * This function is used by drag_data_received_cb to manage drag and
2928 * drop of a header, i.e, and drag from the header view to the folder
2932 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2933 GtkTreeModel *dest_model,
2934 GtkTreePath *dest_row,
2935 GtkSelectionData *selection_data)
2937 TnyList *headers = NULL;
2938 TnyFolder *folder = NULL, *src_folder = NULL;
2939 TnyFolderType folder_type;
2940 GtkTreeIter source_iter, dest_iter;
2941 ModestWindowMgr *mgr = NULL;
2942 ModestWindow *main_win = NULL;
2943 gchar **uris, **tmp;
2945 /* Build the list of headers */
2946 mgr = modest_runtime_get_window_mgr ();
2947 headers = tny_simple_list_new ();
2948 uris = modest_dnd_selection_data_get_paths (selection_data);
2951 while (*tmp != NULL) {
2954 gboolean first = TRUE;
2957 path = gtk_tree_path_new_from_string (*tmp);
2958 gtk_tree_model_get_iter (source_model, &source_iter, path);
2959 gtk_tree_model_get (source_model, &source_iter,
2960 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2963 /* Do not enable d&d of headers already opened */
2964 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2965 tny_list_append (headers, G_OBJECT (header));
2967 if (G_UNLIKELY (first)) {
2968 src_folder = tny_header_get_folder (header);
2972 /* Free and go on */
2973 gtk_tree_path_free (path);
2974 g_object_unref (header);
2979 /* This could happen ig we perform a d&d very quickly over the
2980 same row that row could dissapear because message is
2982 if (!TNY_IS_FOLDER (src_folder))
2985 /* Get the target folder */
2986 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2987 gtk_tree_model_get (dest_model, &dest_iter,
2991 if (!folder || !TNY_IS_FOLDER(folder)) {
2992 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2996 folder_type = modest_tny_folder_guess_folder_type (folder);
2997 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2998 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2999 goto cleanup; /* cannot move messages there */
3002 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3003 /* g_warning ("folder not writable"); */
3004 goto cleanup; /* verboten! */
3007 /* Ask for confirmation to move */
3008 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
3010 g_warning ("%s: BUG: no main window found", __FUNCTION__);
3014 /* Transfer messages */
3015 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
3020 if (G_IS_OBJECT (src_folder))
3021 g_object_unref (src_folder);
3022 if (G_IS_OBJECT(folder))
3023 g_object_unref (G_OBJECT (folder));
3024 if (G_IS_OBJECT(headers))
3025 g_object_unref (headers);
3029 TnyFolderStore *src_folder;
3030 TnyFolderStore *dst_folder;
3031 ModestFolderView *folder_view;
3036 dnd_folder_info_destroyer (DndFolderInfo *info)
3038 if (info->src_folder)
3039 g_object_unref (info->src_folder);
3040 if (info->dst_folder)
3041 g_object_unref (info->dst_folder);
3042 g_slice_free (DndFolderInfo, info);
3046 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
3047 GtkWindow *parent_window,
3048 TnyAccount *account)
3051 modest_ui_actions_on_account_connection_error (parent_window, account);
3053 /* Free the helper & info */
3054 dnd_helper_destroyer (info->helper);
3055 dnd_folder_info_destroyer (info);
3059 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
3061 GtkWindow *parent_window,
3062 TnyAccount *account,
3065 DndFolderInfo *info = NULL;
3066 ModestMailOperation *mail_op;
3068 info = (DndFolderInfo *) user_data;
3070 if (err || canceled) {
3071 dnd_on_connection_failed_destroyer (info, parent_window, account);
3075 /* Do the mail operation */
3076 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
3077 modest_ui_actions_move_folder_error_handler,
3078 info->src_folder, NULL);
3080 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3083 /* Transfer the folder */
3084 modest_mail_operation_xfer_folder (mail_op,
3085 TNY_FOLDER (info->src_folder),
3087 info->helper->delete_source,
3089 info->helper->folder_view);
3092 g_object_unref (G_OBJECT (mail_op));
3093 dnd_helper_destroyer (info->helper);
3094 dnd_folder_info_destroyer (info);
3099 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3101 GtkWindow *parent_window,
3102 TnyAccount *account,
3105 DndFolderInfo *info = NULL;
3107 info = (DndFolderInfo *) user_data;
3109 if (err || canceled) {
3110 dnd_on_connection_failed_destroyer (info, parent_window, account);
3114 /* Connect to source folder and perform the copy/move */
3115 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3117 drag_and_drop_from_folder_view_src_folder_performer,
3122 * This function is used by drag_data_received_cb to manage drag and
3123 * drop of a folder, i.e, and drag from the folder view to the same
3127 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3128 GtkTreeModel *dest_model,
3129 GtkTreePath *dest_row,
3130 GtkSelectionData *selection_data,
3133 GtkTreeIter dest_iter, iter;
3134 TnyFolderStore *dest_folder = NULL;
3135 TnyFolderStore *folder = NULL;
3136 gboolean forbidden = FALSE;
3138 DndFolderInfo *info = NULL;
3140 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3142 g_warning ("%s: BUG: no main window", __FUNCTION__);
3143 dnd_helper_destroyer (helper);
3148 /* check the folder rules for the destination */
3149 folder = tree_path_to_folder (dest_model, dest_row);
3150 if (TNY_IS_FOLDER(folder)) {
3151 ModestTnyFolderRules rules =
3152 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3153 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3154 } else if (TNY_IS_FOLDER_STORE(folder)) {
3155 /* enable local root as destination for folders */
3156 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3157 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3160 g_object_unref (folder);
3163 /* check the folder rules for the source */
3164 folder = tree_path_to_folder (source_model, helper->source_row);
3165 if (TNY_IS_FOLDER(folder)) {
3166 ModestTnyFolderRules rules =
3167 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3168 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3171 g_object_unref (folder);
3175 /* Check if the drag is possible */
3176 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3178 modest_platform_run_information_dialog ((GtkWindow *) win,
3179 _("mail_in_ui_folder_move_target_error"),
3181 /* Restore the previous selection */
3182 folder = tree_path_to_folder (source_model, helper->source_row);
3184 if (TNY_IS_FOLDER (folder))
3185 modest_folder_view_select_folder (helper->folder_view,
3186 TNY_FOLDER (folder), FALSE);
3187 g_object_unref (folder);
3189 dnd_helper_destroyer (helper);
3194 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3195 gtk_tree_model_get (dest_model, &dest_iter,
3198 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3199 gtk_tree_model_get (source_model, &iter,
3203 /* Create the info for the performer */
3204 info = g_slice_new0 (DndFolderInfo);
3205 info->src_folder = g_object_ref (folder);
3206 info->dst_folder = g_object_ref (dest_folder);
3207 info->helper = helper;
3209 /* Connect to the destination folder and perform the copy/move */
3210 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3212 drag_and_drop_from_folder_view_dst_folder_performer,
3216 g_object_unref (dest_folder);
3217 g_object_unref (folder);
3221 * This function receives the data set by the "drag-data-get" signal
3222 * handler. This information comes within the #GtkSelectionData. This
3223 * function will manage both the drags of folders of the treeview and
3224 * drags of headers of the header view widget.
3227 on_drag_data_received (GtkWidget *widget,
3228 GdkDragContext *context,
3231 GtkSelectionData *selection_data,
3236 GtkWidget *source_widget;
3237 GtkTreeModel *dest_model, *source_model;
3238 GtkTreePath *source_row, *dest_row;
3239 GtkTreeViewDropPosition pos;
3240 gboolean delete_source = FALSE;
3241 gboolean success = FALSE;
3243 /* Do not allow further process */
3244 g_signal_stop_emission_by_name (widget, "drag-data-received");
3245 source_widget = gtk_drag_get_source_widget (context);
3247 /* Get the action */
3248 if (context->action == GDK_ACTION_MOVE) {
3249 delete_source = TRUE;
3251 /* Notify that there is no folder selected. We need to
3252 do this in order to update the headers view (and
3253 its monitors, because when moving, the old folder
3254 won't longer exist. We can not wait for the end of
3255 the operation, because the operation won't start if
3256 the folder is in use */
3257 if (source_widget == widget) {
3258 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3259 gtk_tree_selection_unselect_all (sel);
3263 /* Check if the get_data failed */
3264 if (selection_data == NULL || selection_data->length < 0)
3267 /* Select the destination model */
3268 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3270 /* Get the path to the destination row. Can not call
3271 gtk_tree_view_get_drag_dest_row() because the source row
3272 is not selected anymore */
3273 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3276 /* Only allow drops IN other rows */
3278 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3279 pos == GTK_TREE_VIEW_DROP_AFTER)
3283 /* Drags from the header view */
3284 if (source_widget != widget) {
3285 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3287 drag_and_drop_from_header_view (source_model,
3292 DndHelper *helper = NULL;
3294 /* Get the source model and row */
3295 gtk_tree_get_row_drag_data (selection_data,
3299 /* Create the helper */
3300 helper = g_slice_new0 (DndHelper);
3301 helper->delete_source = delete_source;
3302 helper->source_row = gtk_tree_path_copy (source_row);
3303 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3305 drag_and_drop_from_folder_view (source_model,
3311 gtk_tree_path_free (source_row);
3315 gtk_tree_path_free (dest_row);
3318 /* Finish the drag and drop */
3319 gtk_drag_finish (context, success, FALSE, time);
3323 * We define a "drag-drop" signal handler because we do not want to
3324 * use the default one, because the default one always calls
3325 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3326 * signal handler, because there we have all the information available
3327 * to know if the dnd was a success or not.
3330 drag_drop_cb (GtkWidget *widget,
3331 GdkDragContext *context,
3339 if (!context->targets)
3342 /* Check if we're dragging a folder row */
3343 target = gtk_drag_dest_find_target (widget, context, NULL);
3345 /* Request the data from the source. */
3346 gtk_drag_get_data(widget, context, target, time);
3352 * This function expands a node of a tree view if it's not expanded
3353 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3354 * does that, so that's why they're here.
3357 expand_row_timeout (gpointer data)
3359 GtkTreeView *tree_view = data;
3360 GtkTreePath *dest_path = NULL;
3361 GtkTreeViewDropPosition pos;
3362 gboolean result = FALSE;
3364 gdk_threads_enter ();
3366 gtk_tree_view_get_drag_dest_row (tree_view,
3371 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3372 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3373 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3374 gtk_tree_path_free (dest_path);
3378 gtk_tree_path_free (dest_path);
3383 gdk_threads_leave ();
3389 * This function is called whenever the pointer is moved over a widget
3390 * while dragging some data. It installs a timeout that will expand a
3391 * node of the treeview if not expanded yet. This function also calls
3392 * gdk_drag_status in order to set the suggested action that will be
3393 * used by the "drag-data-received" signal handler to know if we
3394 * should do a move or just a copy of the data.
3397 on_drag_motion (GtkWidget *widget,
3398 GdkDragContext *context,
3404 GtkTreeViewDropPosition pos;
3405 GtkTreePath *dest_row;
3406 GtkTreeModel *dest_model;
3407 ModestFolderViewPrivate *priv;
3408 GdkDragAction suggested_action;
3409 gboolean valid_location = FALSE;
3410 TnyFolderStore *folder = NULL;
3412 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3414 if (priv->timer_expander != 0) {
3415 g_source_remove (priv->timer_expander);
3416 priv->timer_expander = 0;
3419 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3424 /* Do not allow drops between folders */
3426 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3427 pos == GTK_TREE_VIEW_DROP_AFTER) {
3428 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3429 gdk_drag_status(context, 0, time);
3430 valid_location = FALSE;
3433 valid_location = TRUE;
3436 /* Check that the destination folder is writable */
3437 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3438 folder = tree_path_to_folder (dest_model, dest_row);
3439 if (folder && TNY_IS_FOLDER (folder)) {
3440 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3442 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3443 valid_location = FALSE;
3448 /* Expand the selected row after 1/2 second */
3449 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3450 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3452 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3454 /* Select the desired action. By default we pick MOVE */
3455 suggested_action = GDK_ACTION_MOVE;
3457 if (context->actions == GDK_ACTION_COPY)
3458 gdk_drag_status(context, GDK_ACTION_COPY, time);
3459 else if (context->actions == GDK_ACTION_MOVE)
3460 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3461 else if (context->actions & suggested_action)
3462 gdk_drag_status(context, suggested_action, time);
3464 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3468 g_object_unref (folder);
3470 gtk_tree_path_free (dest_row);
3472 g_signal_stop_emission_by_name (widget, "drag-motion");
3474 return valid_location;
3478 * This function sets the treeview as a source and a target for dnd
3479 * events. It also connects all the requirede signals.
3482 setup_drag_and_drop (GtkTreeView *self)
3484 /* Set up the folder view as a dnd destination. Set only the
3485 highlight flag, otherwise gtk will have a different
3487 #ifdef MODEST_TOOLKIT_HILDON2
3490 gtk_drag_dest_set (GTK_WIDGET (self),
3491 GTK_DEST_DEFAULT_HIGHLIGHT,
3492 folder_view_drag_types,
3493 G_N_ELEMENTS (folder_view_drag_types),
3494 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3496 g_signal_connect (G_OBJECT (self),
3497 "drag_data_received",
3498 G_CALLBACK (on_drag_data_received),
3502 /* Set up the treeview as a dnd source */
3503 gtk_drag_source_set (GTK_WIDGET (self),
3505 folder_view_drag_types,
3506 G_N_ELEMENTS (folder_view_drag_types),
3507 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3509 g_signal_connect (G_OBJECT (self),
3511 G_CALLBACK (on_drag_motion),
3514 g_signal_connect (G_OBJECT (self),
3516 G_CALLBACK (on_drag_data_get),
3519 g_signal_connect (G_OBJECT (self),
3521 G_CALLBACK (drag_drop_cb),
3526 * This function manages the navigation through the folders using the
3527 * keyboard or the hardware keys in the device
3530 on_key_pressed (GtkWidget *self,
3534 GtkTreeSelection *selection;
3536 GtkTreeModel *model;
3537 gboolean retval = FALSE;
3539 /* Up and Down are automatically managed by the treeview */
3540 if (event->keyval == GDK_Return) {
3541 /* Expand/Collapse the selected row */
3542 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3543 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3546 path = gtk_tree_model_get_path (model, &iter);
3548 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3549 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3551 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3552 gtk_tree_path_free (path);
3554 /* No further processing */
3562 * We listen to the changes in the local folder account name key,
3563 * because we want to show the right name in the view. The local
3564 * folder account name corresponds to the device name in the Maemo
3565 * version. We do this because we do not want to query gconf on each
3566 * tree view refresh. It's better to cache it and change whenever
3570 on_configuration_key_changed (ModestConf* conf,
3572 ModestConfEvent event,
3573 ModestConfNotificationId id,
3574 ModestFolderView *self)
3576 ModestFolderViewPrivate *priv;
3579 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3580 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3582 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3583 g_free (priv->local_account_name);
3585 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3586 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3588 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3589 MODEST_CONF_DEVICE_NAME, NULL);
3591 /* Force a redraw */
3592 #if GTK_CHECK_VERSION(2, 8, 0)
3593 GtkTreeViewColumn * tree_column;
3595 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3597 gtk_tree_view_column_queue_resize (tree_column);
3599 gtk_widget_queue_draw (GTK_WIDGET (self));
3605 modest_folder_view_set_style (ModestFolderView *self,
3606 ModestFolderViewStyle style)
3608 ModestFolderViewPrivate *priv;
3610 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3611 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3612 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3614 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3617 priv->style = style;
3621 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3622 const gchar *account_id)
3624 ModestFolderViewPrivate *priv;
3625 GtkTreeModel *model;
3627 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3629 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3631 /* This will be used by the filter_row callback,
3632 * to decided which rows to show: */
3633 if (priv->visible_account_id) {
3634 g_free (priv->visible_account_id);
3635 priv->visible_account_id = NULL;
3638 priv->visible_account_id = g_strdup (account_id);
3641 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3642 if (GTK_IS_TREE_MODEL_FILTER (model))
3643 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3645 modest_folder_view_update_model(self,
3646 (TnyAccountStore *) modest_runtime_get_account_store());
3648 /* Save settings to gconf */
3649 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3650 MODEST_CONF_FOLDER_VIEW_KEY);
3652 /* Notify observers */
3653 g_signal_emit (G_OBJECT(self),
3654 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3659 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3661 ModestFolderViewPrivate *priv;
3663 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3665 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3667 return (const gchar *) priv->visible_account_id;
3671 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3675 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3677 gtk_tree_model_get (model, iter,
3681 gboolean result = FALSE;
3682 if (type == TNY_FOLDER_TYPE_INBOX) {
3686 *inbox_iter = *iter;
3690 if (gtk_tree_model_iter_children (model, &child, iter)) {
3691 if (find_inbox_iter (model, &child, inbox_iter))
3695 } while (gtk_tree_model_iter_next (model, iter));
3704 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3706 #ifndef MODEST_TOOLKIT_HILDON2
3707 GtkTreeModel *model;
3708 GtkTreeIter iter, inbox_iter;
3709 GtkTreeSelection *sel;
3710 GtkTreePath *path = NULL;
3712 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3714 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3718 expand_root_items (self);
3719 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3721 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3722 g_warning ("%s: model is empty", __FUNCTION__);
3726 if (find_inbox_iter (model, &iter, &inbox_iter))
3727 path = gtk_tree_model_get_path (model, &inbox_iter);
3729 path = gtk_tree_path_new_first ();
3731 /* Select the row and free */
3732 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3733 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3734 gtk_tree_path_free (path);
3737 gtk_widget_grab_focus (GTK_WIDGET(self));
3744 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3749 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3750 TnyFolder* a_folder;
3753 gtk_tree_model_get (model, iter,
3754 INSTANCE_COLUMN, &a_folder,
3760 if (folder == a_folder) {
3761 g_object_unref (a_folder);
3762 *folder_iter = *iter;
3765 g_object_unref (a_folder);
3767 if (gtk_tree_model_iter_children (model, &child, iter)) {
3768 if (find_folder_iter (model, &child, folder_iter, folder))
3772 } while (gtk_tree_model_iter_next (model, iter));
3777 #ifndef MODEST_TOOLKIT_HILDON2
3779 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3782 ModestFolderView *self)
3784 ModestFolderViewPrivate *priv = NULL;
3785 GtkTreeSelection *sel;
3786 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3787 GObject *instance = NULL;
3789 if (!MODEST_IS_FOLDER_VIEW(self))
3792 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3794 priv->reexpand = TRUE;
3796 gtk_tree_model_get (tree_model, iter,
3798 INSTANCE_COLUMN, &instance,
3804 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3805 priv->folder_to_select = g_object_ref (instance);
3807 g_object_unref (instance);
3809 if (priv->folder_to_select) {
3811 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3814 path = gtk_tree_model_get_path (tree_model, iter);
3815 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3817 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3819 gtk_tree_selection_select_iter (sel, iter);
3820 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3822 gtk_tree_path_free (path);
3826 modest_folder_view_disable_next_folder_selection (self);
3828 /* Refilter the model */
3829 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3835 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3837 ModestFolderViewPrivate *priv;
3839 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3841 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3843 if (priv->folder_to_select)
3844 g_object_unref(priv->folder_to_select);
3846 priv->folder_to_select = NULL;
3850 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3851 gboolean after_change)
3853 GtkTreeModel *model;
3854 GtkTreeIter iter, folder_iter;
3855 GtkTreeSelection *sel;
3856 ModestFolderViewPrivate *priv = NULL;
3858 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3859 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3861 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3864 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3865 gtk_tree_selection_unselect_all (sel);
3867 if (priv->folder_to_select)
3868 g_object_unref(priv->folder_to_select);
3869 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3873 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3878 /* Refilter the model, before selecting the folder */
3879 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3881 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3882 g_warning ("%s: model is empty", __FUNCTION__);
3886 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3889 path = gtk_tree_model_get_path (model, &folder_iter);
3890 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3892 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3893 gtk_tree_selection_select_iter (sel, &folder_iter);
3894 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3896 gtk_tree_path_free (path);
3904 modest_folder_view_copy_selection (ModestFolderView *self)
3906 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3908 /* Copy selection */
3909 _clipboard_set_selected_data (self, FALSE);
3913 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3915 ModestFolderViewPrivate *priv = NULL;
3916 GtkTreeModel *model = NULL;
3917 const gchar **hidding = NULL;
3918 guint i, n_selected;
3920 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3921 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3923 /* Copy selection */
3924 if (!_clipboard_set_selected_data (folder_view, TRUE))
3927 /* Get hidding ids */
3928 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3930 /* Clear hidding array created by previous cut operation */
3931 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3933 /* Copy hidding array */
3934 priv->n_selected = n_selected;
3935 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3936 for (i=0; i < n_selected; i++)
3937 priv->hidding_ids[i] = g_strdup(hidding[i]);
3939 /* Hide cut folders */
3940 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3941 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3945 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3946 ModestFolderView *folder_view_dst)
3948 GtkTreeModel *filter_model = NULL;
3949 GtkTreeModel *model = NULL;
3950 GtkTreeModel *new_filter_model = NULL;
3951 GtkTreeModel *old_tny_model = NULL;
3952 GtkTreeModel *new_tny_model = NULL;
3953 ModestFolderViewPrivate *dst_priv;
3955 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3956 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3958 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3959 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3960 new_tny_model = NULL;
3963 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3964 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3965 G_OBJECT (old_tny_model),
3966 "activity-changed");
3968 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3969 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3971 /* Build new filter model */
3972 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3973 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3980 /* Set copied model */
3981 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3982 #ifndef MODEST_TOOLKIT_HILDON2
3983 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3984 G_OBJECT(new_filter_model), "row-inserted",
3985 (GCallback) on_row_inserted_maybe_select_folder,
3988 #ifdef MODEST_TOOLKIT_HILDON2
3989 if (new_tny_model) {
3990 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3991 G_OBJECT (new_tny_model),
3993 G_CALLBACK (on_activity_changed),
3999 g_object_unref (new_filter_model);
4003 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
4006 GtkTreeModel *model = NULL;
4007 ModestFolderViewPrivate* priv;
4009 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4011 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4012 priv->show_non_move = show;
4013 /* modest_folder_view_update_model(folder_view, */
4014 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
4016 /* Hide special folders */
4017 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
4018 if (GTK_IS_TREE_MODEL_FILTER (model)) {
4019 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
4024 modest_folder_view_show_message_count (ModestFolderView *folder_view,
4027 ModestFolderViewPrivate* priv;
4029 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4031 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4032 priv->show_message_count = show;
4034 g_object_set (G_OBJECT (priv->messages_renderer),
4035 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4039 /* Returns FALSE if it did not selected anything */
4041 _clipboard_set_selected_data (ModestFolderView *folder_view,
4044 ModestFolderViewPrivate *priv = NULL;
4045 TnyFolderStore *folder = NULL;
4046 gboolean retval = FALSE;
4048 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
4049 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4051 /* Set selected data on clipboard */
4052 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
4053 folder = modest_folder_view_get_selected (folder_view);
4055 /* Do not allow to select an account */
4056 if (TNY_IS_FOLDER (folder)) {
4057 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
4062 g_object_unref (folder);
4068 _clear_hidding_filter (ModestFolderView *folder_view)
4070 ModestFolderViewPrivate *priv;
4073 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4074 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4076 if (priv->hidding_ids != NULL) {
4077 for (i=0; i < priv->n_selected; i++)
4078 g_free (priv->hidding_ids[i]);
4079 g_free(priv->hidding_ids);
4085 on_display_name_changed (ModestAccountMgr *mgr,
4086 const gchar *account,
4089 ModestFolderView *self;
4091 self = MODEST_FOLDER_VIEW (user_data);
4093 /* Force a redraw */
4094 #if GTK_CHECK_VERSION(2, 8, 0)
4095 GtkTreeViewColumn * tree_column;
4097 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4099 gtk_tree_view_column_queue_resize (tree_column);
4101 gtk_widget_queue_draw (GTK_WIDGET (self));
4106 modest_folder_view_set_cell_style (ModestFolderView *self,
4107 ModestFolderViewCellStyle cell_style)
4109 ModestFolderViewPrivate *priv = NULL;
4111 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4112 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4114 priv->cell_style = cell_style;
4116 g_object_set (G_OBJECT (priv->messages_renderer),
4117 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4120 gtk_widget_queue_draw (GTK_WIDGET (self));
4124 update_style (ModestFolderView *self)
4126 ModestFolderViewPrivate *priv;
4127 GdkColor style_color, style_active_color;
4128 PangoAttrList *attr_list;
4130 PangoAttribute *attr;
4132 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4133 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4137 attr_list = pango_attr_list_new ();
4138 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4139 gdk_color_parse ("grey", &style_color);
4141 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4142 pango_attr_list_insert (attr_list, attr);
4145 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4147 "SmallSystemFont", NULL,
4150 attr = pango_attr_font_desc_new (pango_font_description_copy
4151 (style->font_desc));
4152 pango_attr_list_insert (attr_list, attr);
4154 g_object_set (G_OBJECT (priv->messages_renderer),
4155 "foreground-gdk", &style_color,
4156 "foreground-set", TRUE,
4157 "attributes", attr_list,
4159 pango_attr_list_unref (attr_list);
4162 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4163 priv->active_color = style_active_color;
4165 gdk_color_parse ("000", &(priv->active_color));
4170 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4172 if (strcmp ("style", spec->name) == 0) {
4173 update_style (MODEST_FOLDER_VIEW (obj));
4174 gtk_widget_queue_draw (GTK_WIDGET (obj));
4179 modest_folder_view_set_filter (ModestFolderView *self,
4180 ModestFolderViewFilter filter)
4182 ModestFolderViewPrivate *priv;
4183 GtkTreeModel *filter_model;
4185 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4186 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4188 priv->filter |= filter;
4190 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4191 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4192 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4197 modest_folder_view_unset_filter (ModestFolderView *self,
4198 ModestFolderViewFilter filter)
4200 ModestFolderViewPrivate *priv;
4201 GtkTreeModel *filter_model;
4203 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4204 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4206 priv->filter &= ~filter;
4208 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4209 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4210 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4215 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4216 ModestTnyFolderRules rules)
4218 GtkTreeModel *filter_model;
4220 gboolean fulfil = FALSE;
4222 if (!get_inner_models (self, &filter_model, NULL, NULL))
4225 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4229 TnyFolderStore *folder;
4231 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4233 if (TNY_IS_FOLDER (folder)) {
4234 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4235 /* Folder rules are negative: non_writable, non_deletable... */
4236 if (!(folder_rules & rules))
4239 g_object_unref (folder);
4242 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4248 modest_folder_view_set_list_to_move (ModestFolderView *self,
4251 ModestFolderViewPrivate *priv;
4253 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4254 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4256 if (priv->list_to_move)
4257 g_object_unref (priv->list_to_move);
4260 g_object_ref (list);
4262 priv->list_to_move = list;
4266 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4268 ModestFolderViewPrivate *priv;
4270 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4271 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4274 g_free (priv->mailbox);
4276 priv->mailbox = g_strdup (mailbox);
4278 /* Notify observers */
4279 g_signal_emit (G_OBJECT(self),
4280 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4281 priv->visible_account_id);
4285 modest_folder_view_get_mailbox (ModestFolderView *self)
4287 ModestFolderViewPrivate *priv;
4289 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4290 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4292 return (const gchar *) priv->mailbox;
4296 modest_folder_view_get_activity (ModestFolderView *self)
4298 ModestFolderViewPrivate *priv;
4299 GtkTreeModel *inner_model;
4301 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4302 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4303 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4305 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4306 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4312 #ifdef MODEST_TOOLKIT_HILDON2
4314 on_activity_changed (TnyGtkFolderListStore *store,
4316 ModestFolderView *folder_view)
4318 ModestFolderViewPrivate *priv;
4320 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4321 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4322 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4324 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
4330 modest_folder_view_get_model_tny_list (ModestFolderView *self)
4332 GtkTreeModel *model;
4338 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
4339 ret_value = TNY_LIST (model);
4340 g_object_ref (ret_value);