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_ascii_strcasecmp (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;
933 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
935 *pixbuf = gdk_pixbuf_copy (icon);
941 if (!*pixbuf_open && pixbuf && *pixbuf)
942 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
946 if (!*pixbuf_close && pixbuf && *pixbuf)
947 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
951 retval = g_slice_new0 (ThreePixbufs);
953 retval->pixbuf = g_object_ref (*pixbuf);
955 retval->pixbuf_open = g_object_ref (*pixbuf_open);
957 retval->pixbuf_close = g_object_ref (*pixbuf_close);
962 static inline ThreePixbufs *
963 get_account_protocol_pixbufs (ModestFolderView *folder_view,
964 ModestProtocolType protocol_type,
967 ModestProtocol *protocol;
968 const GdkPixbuf *pixbuf = NULL;
969 ModestFolderViewPrivate *priv;
971 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
973 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
976 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
977 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
978 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
979 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
980 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
981 object, FOLDER_ICON_SIZE);
985 ThreePixbufs *retval;
986 retval = g_slice_new0 (ThreePixbufs);
987 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
988 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
989 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
996 static inline ThreePixbufs*
997 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
999 TnyAccount *account = NULL;
1000 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
1001 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
1002 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
1003 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
1004 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
1006 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1007 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1008 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1009 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1010 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1012 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1013 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1014 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1015 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1016 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1018 ThreePixbufs *retval = NULL;
1020 if (TNY_IS_ACCOUNT (instance)) {
1021 account = g_object_ref (instance);
1022 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1023 account = tny_folder_get_account (TNY_FOLDER (instance));
1027 ModestProtocolType account_store_protocol;
1029 account_store_protocol = modest_tny_account_get_protocol_type (account);
1030 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1031 g_object_unref (account);
1037 /* Sometimes an special folder is reported by the server as
1038 NORMAL, like some versions of Dovecot */
1039 if (type == TNY_FOLDER_TYPE_NORMAL ||
1040 type == TNY_FOLDER_TYPE_UNKNOWN) {
1041 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1044 /* It's not enough with check the folder type. We need to
1045 ensure that we're not giving a special folder icon to a
1046 normal folder with the same name than a special folder */
1047 if (TNY_IS_FOLDER (instance) &&
1048 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1049 type = TNY_FOLDER_TYPE_NORMAL;
1051 /* Remote folders should not be treated as special folders */
1052 if (TNY_IS_FOLDER_STORE (instance) &&
1053 !TNY_IS_ACCOUNT (instance) &&
1054 type != TNY_FOLDER_TYPE_INBOX &&
1055 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1056 #ifdef MODEST_TOOLKIT_HILDON2
1057 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1060 &anorm_pixbuf_close);
1062 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1064 &normal_pixbuf_open,
1065 &normal_pixbuf_close);
1071 case TNY_FOLDER_TYPE_INVALID:
1072 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1075 case TNY_FOLDER_TYPE_ROOT:
1076 if (TNY_IS_ACCOUNT (instance)) {
1078 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1079 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1082 &avirt_pixbuf_close);
1084 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1086 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1087 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1090 &ammc_pixbuf_close);
1092 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1095 &anorm_pixbuf_close);
1100 case TNY_FOLDER_TYPE_INBOX:
1101 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1104 &inbox_pixbuf_close);
1106 case TNY_FOLDER_TYPE_OUTBOX:
1107 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1109 &outbox_pixbuf_open,
1110 &outbox_pixbuf_close);
1112 case TNY_FOLDER_TYPE_JUNK:
1113 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1116 &junk_pixbuf_close);
1118 case TNY_FOLDER_TYPE_SENT:
1119 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1122 &sent_pixbuf_close);
1124 case TNY_FOLDER_TYPE_TRASH:
1125 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1128 &trash_pixbuf_close);
1130 case TNY_FOLDER_TYPE_DRAFTS:
1131 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1134 &draft_pixbuf_close);
1136 case TNY_FOLDER_TYPE_ARCHIVE:
1137 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1142 case TNY_FOLDER_TYPE_NORMAL:
1144 /* Memory card folders could have an special icon */
1145 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1146 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1151 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1153 &normal_pixbuf_open,
1154 &normal_pixbuf_close);
1163 free_pixbufs (ThreePixbufs *pixbufs)
1165 if (pixbufs->pixbuf)
1166 g_object_unref (pixbufs->pixbuf);
1167 if (pixbufs->pixbuf_open)
1168 g_object_unref (pixbufs->pixbuf_open);
1169 if (pixbufs->pixbuf_close)
1170 g_object_unref (pixbufs->pixbuf_close);
1171 g_slice_free (ThreePixbufs, pixbufs);
1175 icon_cell_data (GtkTreeViewColumn *column,
1176 GtkCellRenderer *renderer,
1177 GtkTreeModel *tree_model,
1181 GObject *rendobj = NULL, *instance = NULL;
1182 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1183 gboolean has_children;
1184 ThreePixbufs *pixbufs;
1185 ModestFolderView *folder_view = (ModestFolderView *) data;
1187 rendobj = (GObject *) renderer;
1189 gtk_tree_model_get (tree_model, iter,
1191 INSTANCE_COLUMN, &instance,
1197 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1198 pixbufs = get_folder_icons (folder_view, type, instance);
1199 g_object_unref (instance);
1202 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1205 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1206 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1209 free_pixbufs (pixbufs);
1213 add_columns (GtkWidget *treeview)
1215 GtkTreeViewColumn *column;
1216 GtkCellRenderer *renderer;
1217 GtkTreeSelection *sel;
1218 ModestFolderViewPrivate *priv;
1220 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1223 column = gtk_tree_view_column_new ();
1225 /* Set icon and text render function */
1226 renderer = gtk_cell_renderer_pixbuf_new();
1227 #ifdef MODEST_TOOLKIT_HILDON2
1228 g_object_set (renderer,
1229 "xpad", MODEST_MARGIN_DEFAULT,
1230 "ypad", MODEST_MARGIN_DEFAULT,
1233 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1234 gtk_tree_view_column_set_cell_data_func(column, renderer,
1235 icon_cell_data, treeview, NULL);
1237 renderer = gtk_cell_renderer_text_new();
1238 g_object_set (renderer,
1239 #ifdef MODEST_TOOLKIT_HILDON2
1240 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1241 "ypad", MODEST_MARGIN_DEFAULT,
1242 "xpad", MODEST_MARGIN_DEFAULT,
1244 "ellipsize", PANGO_ELLIPSIZE_END,
1246 "ellipsize-set", TRUE, NULL);
1247 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1248 gtk_tree_view_column_set_cell_data_func(column, renderer,
1249 text_cell_data, treeview, NULL);
1251 priv->messages_renderer = gtk_cell_renderer_text_new ();
1252 g_object_set (priv->messages_renderer,
1253 #ifdef MODEST_TOOLKIT_HILDON2
1255 "ypad", MODEST_MARGIN_DEFAULT,
1256 "xpad", MODEST_MARGIN_DOUBLE,
1258 "scale", PANGO_SCALE_X_SMALL,
1261 "alignment", PANGO_ALIGN_RIGHT,
1265 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1266 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1267 messages_cell_data, treeview, NULL);
1269 /* Set selection mode */
1270 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1271 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1273 /* Set treeview appearance */
1274 gtk_tree_view_column_set_spacing (column, 2);
1275 gtk_tree_view_column_set_resizable (column, TRUE);
1276 gtk_tree_view_column_set_fixed_width (column, TRUE);
1277 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1278 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1281 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1285 modest_folder_view_init (ModestFolderView *obj)
1287 ModestFolderViewPrivate *priv;
1290 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1292 priv->timer_expander = 0;
1293 priv->account_store = NULL;
1295 priv->do_refresh = TRUE;
1296 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1297 priv->cur_folder_store = NULL;
1298 priv->visible_account_id = NULL;
1299 priv->mailbox = NULL;
1300 priv->folder_to_select = NULL;
1301 priv->outbox_deleted_handler = 0;
1302 priv->reexpand = TRUE;
1303 priv->signal_handlers = 0;
1305 /* Initialize the local account name */
1306 conf = modest_runtime_get_conf();
1307 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1309 /* Init email clipboard */
1310 priv->clipboard = modest_runtime_get_email_clipboard ();
1311 priv->hidding_ids = NULL;
1312 priv->n_selected = 0;
1313 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1314 priv->reselect = FALSE;
1315 priv->show_non_move = TRUE;
1316 priv->list_to_move = NULL;
1317 priv->show_message_count = TRUE;
1319 /* Build treeview */
1320 add_columns (GTK_WIDGET (obj));
1322 /* Setup drag and drop */
1323 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1325 /* Connect signals */
1326 g_signal_connect (G_OBJECT (obj),
1328 G_CALLBACK (on_key_pressed), NULL);
1330 priv->display_name_changed_signal =
1331 g_signal_connect (modest_runtime_get_account_mgr (),
1332 "display_name_changed",
1333 G_CALLBACK (on_display_name_changed),
1337 * Track changes in the local account name (in the device it
1338 * will be the device name)
1340 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1342 G_CALLBACK(on_configuration_key_changed),
1345 gdk_color_parse ("000", &priv->active_color);
1348 g_signal_connect (G_OBJECT (obj), "notify::style",
1349 G_CALLBACK (on_notify_style), (gpointer) obj);
1353 tny_account_store_view_init (gpointer g, gpointer iface_data)
1355 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1357 klass->set_account_store = modest_folder_view_set_account_store;
1361 modest_folder_view_dispose (GObject *obj)
1363 ModestFolderViewPrivate *priv;
1365 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1367 #ifdef MODEST_TOOLKIT_HILDON2
1368 if (priv->signal_handlers) {
1369 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1370 priv->signal_handlers = NULL;
1374 /* Free external references */
1375 if (priv->account_store) {
1376 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1377 priv->account_inserted_signal);
1378 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1379 priv->account_removed_signal);
1380 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1381 priv->account_changed_signal);
1382 g_object_unref (G_OBJECT(priv->account_store));
1383 priv->account_store = NULL;
1387 g_object_unref (G_OBJECT (priv->query));
1391 if (priv->folder_to_select) {
1392 g_object_unref (G_OBJECT(priv->folder_to_select));
1393 priv->folder_to_select = NULL;
1396 if (priv->cur_folder_store) {
1397 g_object_unref (priv->cur_folder_store);
1398 priv->cur_folder_store = NULL;
1401 if (priv->list_to_move) {
1402 g_object_unref (priv->list_to_move);
1403 priv->list_to_move = NULL;
1406 G_OBJECT_CLASS(parent_class)->dispose (obj);
1410 modest_folder_view_finalize (GObject *obj)
1412 ModestFolderViewPrivate *priv;
1413 GtkTreeSelection *sel;
1414 TnyAccount *local_account;
1416 g_return_if_fail (obj);
1418 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1420 if (priv->timer_expander != 0) {
1421 g_source_remove (priv->timer_expander);
1422 priv->timer_expander = 0;
1425 local_account = (TnyAccount *)
1426 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1427 if (local_account) {
1428 if (g_signal_handler_is_connected (local_account,
1429 priv->outbox_deleted_handler))
1430 g_signal_handler_disconnect (local_account,
1431 priv->outbox_deleted_handler);
1432 g_object_unref (local_account);
1435 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1436 priv->display_name_changed_signal)) {
1437 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1438 priv->display_name_changed_signal);
1439 priv->display_name_changed_signal = 0;
1442 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1444 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1446 g_free (priv->local_account_name);
1447 g_free (priv->visible_account_id);
1448 g_free (priv->mailbox);
1450 if (priv->conf_key_signal) {
1451 g_signal_handler_disconnect (modest_runtime_get_conf (),
1452 priv->conf_key_signal);
1453 priv->conf_key_signal = 0;
1456 /* Clear hidding array created by cut operation */
1457 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1459 gdk_color_parse ("000", &priv->active_color);
1461 G_OBJECT_CLASS(parent_class)->finalize (obj);
1466 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1468 ModestFolderViewPrivate *priv;
1471 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1472 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1474 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1475 device = tny_account_store_get_device (account_store);
1477 if (G_UNLIKELY (priv->account_store)) {
1479 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1480 priv->account_inserted_signal))
1481 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1482 priv->account_inserted_signal);
1483 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1484 priv->account_removed_signal))
1485 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1486 priv->account_removed_signal);
1487 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1488 priv->account_changed_signal))
1489 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1490 priv->account_changed_signal);
1491 g_object_unref (G_OBJECT (priv->account_store));
1494 priv->account_store = g_object_ref (G_OBJECT (account_store));
1496 priv->account_removed_signal =
1497 g_signal_connect (G_OBJECT(account_store), "account_removed",
1498 G_CALLBACK (on_account_removed), self);
1500 priv->account_inserted_signal =
1501 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1502 G_CALLBACK (on_account_inserted), self);
1504 priv->account_changed_signal =
1505 g_signal_connect (G_OBJECT(account_store), "account_changed",
1506 G_CALLBACK (on_account_changed), self);
1508 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1509 priv->reselect = FALSE;
1510 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1512 g_object_unref (G_OBJECT (device));
1516 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1519 ModestFolderView *self;
1520 GtkTreeModel *model, *filter_model;
1523 self = MODEST_FOLDER_VIEW (user_data);
1525 if (!get_inner_models (self, &filter_model, NULL, &model))
1528 /* Remove outbox from model */
1529 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1530 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1531 g_object_unref (outbox);
1534 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1538 on_account_inserted (TnyAccountStore *account_store,
1539 TnyAccount *account,
1542 ModestFolderViewPrivate *priv;
1543 GtkTreeModel *model, *filter_model;
1545 /* Ignore transport account insertions, we're not showing them
1546 in the folder view */
1547 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1550 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1553 /* If we're adding a new account, and there is no previous
1554 one, we need to select the visible server account */
1555 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1556 !priv->visible_account_id)
1557 modest_widget_memory_restore (modest_runtime_get_conf(),
1558 G_OBJECT (user_data),
1559 MODEST_CONF_FOLDER_VIEW_KEY);
1563 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1564 &filter_model, NULL, &model))
1567 /* Insert the account in the model */
1568 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1570 /* When the model is a list store (plain representation) the
1571 outbox is not a child of any account so we have to manually
1572 delete it because removing the local folders account won't
1573 delete it (because tny_folder_get_account() is not defined
1574 for a merge folder */
1575 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1576 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1578 priv->outbox_deleted_handler =
1579 g_signal_connect (account,
1581 G_CALLBACK (on_outbox_deleted_cb),
1585 /* Refilter the model */
1586 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1591 same_account_selected (ModestFolderView *self,
1592 TnyAccount *account)
1594 ModestFolderViewPrivate *priv;
1595 gboolean same_account = FALSE;
1597 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1599 if (priv->cur_folder_store) {
1600 TnyAccount *selected_folder_account = NULL;
1602 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1603 selected_folder_account =
1604 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1606 selected_folder_account =
1607 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1610 if (selected_folder_account == account)
1611 same_account = TRUE;
1613 g_object_unref (selected_folder_account);
1615 return same_account;
1620 * Selects the first inbox or the local account in an idle
1623 on_idle_select_first_inbox_or_local (gpointer user_data)
1625 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1627 gdk_threads_enter ();
1628 modest_folder_view_select_first_inbox_or_local (self);
1629 gdk_threads_leave ();
1635 on_account_changed (TnyAccountStore *account_store,
1636 TnyAccount *tny_account,
1639 ModestFolderView *self;
1640 ModestFolderViewPrivate *priv;
1641 GtkTreeModel *model, *filter_model;
1642 GtkTreeSelection *sel;
1643 gboolean same_account;
1645 /* Ignore transport account insertions, we're not showing them
1646 in the folder view */
1647 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1650 self = MODEST_FOLDER_VIEW (user_data);
1651 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1653 /* Get the inner model */
1654 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1655 &filter_model, NULL, &model))
1658 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1660 /* Invalidate the cur_folder_store only if the selected folder
1661 belongs to the account that is being removed */
1662 same_account = same_account_selected (self, tny_account);
1664 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1665 gtk_tree_selection_unselect_all (sel);
1668 /* Remove the account from the model */
1669 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1671 /* Insert the account in the model */
1672 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1674 /* Refilter the model */
1675 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1677 /* Select the first INBOX if the currently selected folder
1678 belongs to the account that is being deleted */
1679 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1680 g_idle_add (on_idle_select_first_inbox_or_local, self);
1684 on_account_removed (TnyAccountStore *account_store,
1685 TnyAccount *account,
1688 ModestFolderView *self = NULL;
1689 ModestFolderViewPrivate *priv;
1690 GtkTreeModel *model, *filter_model;
1691 GtkTreeSelection *sel = NULL;
1692 gboolean same_account = FALSE;
1694 /* Ignore transport account removals, we're not showing them
1695 in the folder view */
1696 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1699 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1700 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1704 self = MODEST_FOLDER_VIEW (user_data);
1705 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1707 /* Invalidate the cur_folder_store only if the selected folder
1708 belongs to the account that is being removed */
1709 same_account = same_account_selected (self, account);
1711 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1712 gtk_tree_selection_unselect_all (sel);
1715 /* Invalidate row to select only if the folder to select
1716 belongs to the account that is being removed*/
1717 if (priv->folder_to_select) {
1718 TnyAccount *folder_to_select_account = NULL;
1720 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1721 if (folder_to_select_account == account) {
1722 modest_folder_view_disable_next_folder_selection (self);
1723 g_object_unref (priv->folder_to_select);
1724 priv->folder_to_select = NULL;
1726 g_object_unref (folder_to_select_account);
1729 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1730 &filter_model, NULL, &model))
1733 /* Disconnect the signal handler */
1734 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1735 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1736 if (g_signal_handler_is_connected (account,
1737 priv->outbox_deleted_handler))
1738 g_signal_handler_disconnect (account,
1739 priv->outbox_deleted_handler);
1742 /* Remove the account from the model */
1743 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1745 /* If the removed account is the currently viewed one then
1746 clear the configuration value. The new visible account will be the default account */
1747 if (priv->visible_account_id &&
1748 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1750 /* Clear the current visible account_id */
1751 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1752 modest_folder_view_set_mailbox (self, NULL);
1754 /* Call the restore method, this will set the new visible account */
1755 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1756 MODEST_CONF_FOLDER_VIEW_KEY);
1759 /* Refilter the model */
1760 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1762 /* Select the first INBOX if the currently selected folder
1763 belongs to the account that is being deleted */
1765 g_idle_add (on_idle_select_first_inbox_or_local, self);
1769 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1771 GtkTreeViewColumn *col;
1773 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1775 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1777 g_printerr ("modest: failed get column for title\n");
1781 gtk_tree_view_column_set_title (col, title);
1782 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1787 modest_folder_view_on_map (ModestFolderView *self,
1788 GdkEventExpose *event,
1791 ModestFolderViewPrivate *priv;
1793 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1795 /* This won't happen often */
1796 if (G_UNLIKELY (priv->reselect)) {
1797 /* Select the first inbox or the local account if not found */
1799 /* TODO: this could cause a lock at startup, so we
1800 comment it for the moment. We know that this will
1801 be a bug, because the INBOX is not selected, but we
1802 need to rewrite some parts of Modest to avoid the
1803 deathlock situation */
1804 /* TODO: check if this is still the case */
1805 priv->reselect = FALSE;
1806 modest_folder_view_select_first_inbox_or_local (self);
1807 /* Notify the display name observers */
1808 g_signal_emit (G_OBJECT(self),
1809 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1813 if (priv->reexpand) {
1814 expand_root_items (self);
1815 priv->reexpand = FALSE;
1822 modest_folder_view_new (TnyFolderStoreQuery *query)
1824 return modest_folder_view_new_full (query, TRUE);
1828 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1831 ModestFolderViewPrivate *priv;
1832 GtkTreeSelection *sel;
1834 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1835 #ifdef MODEST_TOOLKIT_HILDON2
1836 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1839 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1842 priv->query = g_object_ref (query);
1844 priv->do_refresh = do_refresh;
1846 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1847 priv->changed_signal = g_signal_connect (sel, "changed",
1848 G_CALLBACK (on_selection_changed), self);
1850 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1852 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1854 return GTK_WIDGET(self);
1857 /* this feels dirty; any other way to expand all the root items? */
1859 expand_root_items (ModestFolderView *self)
1862 GtkTreeModel *model;
1865 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1866 path = gtk_tree_path_new_first ();
1868 /* all folders should have child items, so.. */
1870 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1871 gtk_tree_path_next (path);
1872 } while (gtk_tree_model_get_iter (model, &iter, path));
1874 gtk_tree_path_free (path);
1878 is_parent_of (TnyFolder *a, TnyFolder *b)
1881 gboolean retval = FALSE;
1883 a_id = tny_folder_get_id (a);
1885 gchar *string_to_match;
1888 string_to_match = g_strconcat (a_id, "/", NULL);
1889 b_id = tny_folder_get_id (b);
1890 retval = g_str_has_prefix (b_id, string_to_match);
1891 g_free (string_to_match);
1897 typedef struct _ForeachFolderInfo {
1900 } ForeachFolderInfo;
1903 foreach_folder_with_id (GtkTreeModel *model,
1908 ForeachFolderInfo *info;
1911 info = (ForeachFolderInfo *) data;
1912 gtk_tree_model_get (model, iter,
1913 INSTANCE_COLUMN, &instance,
1916 if (TNY_IS_FOLDER (instance)) {
1919 id = tny_folder_get_id (TNY_FOLDER (instance));
1921 collate = g_utf8_collate_key (id, -1);
1922 info->found = !strcmp (info->needle, collate);
1928 g_object_unref (instance);
1936 has_folder_with_id (ModestFolderView *self, const gchar *id)
1938 GtkTreeModel *model;
1939 ForeachFolderInfo info = {NULL, FALSE};
1941 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1942 info.needle = g_utf8_collate_key (id, -1);
1944 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1945 g_free (info.needle);
1951 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1954 gboolean retval = FALSE;
1956 a_id = tny_folder_get_id (a);
1959 b_id = tny_folder_get_id (b);
1962 const gchar *last_bar;
1963 gchar *string_to_match;
1964 last_bar = g_strrstr (b_id, "/");
1969 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1970 retval = has_folder_with_id (self, string_to_match);
1971 g_free (string_to_match);
1979 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1981 ModestFolderViewPrivate *priv;
1982 TnyIterator *iterator;
1983 gboolean retval = TRUE;
1985 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1986 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1988 for (iterator = tny_list_create_iterator (priv->list_to_move);
1989 retval && !tny_iterator_is_done (iterator);
1990 tny_iterator_next (iterator)) {
1992 instance = tny_iterator_get_current (iterator);
1993 if (instance == (GObject *) folder) {
1995 } else if (TNY_IS_FOLDER (instance)) {
1996 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1998 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
2001 g_object_unref (instance);
2003 g_object_unref (iterator);
2010 * We use this function to implement the
2011 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
2012 * account in this case, and the local folders.
2015 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2017 ModestFolderViewPrivate *priv;
2018 gboolean retval = TRUE;
2019 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2020 GObject *instance = NULL;
2021 const gchar *id = NULL;
2023 gboolean found = FALSE;
2024 gboolean cleared = FALSE;
2025 ModestTnyFolderRules rules = 0;
2028 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2029 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2031 gtk_tree_model_get (model, iter,
2032 NAME_COLUMN, &fname,
2034 INSTANCE_COLUMN, &instance,
2037 /* Do not show if there is no instance, this could indeed
2038 happen when the model is being modified while it's being
2039 drawn. This could occur for example when moving folders
2046 if (TNY_IS_ACCOUNT (instance)) {
2047 TnyAccount *acc = TNY_ACCOUNT (instance);
2048 const gchar *account_id = tny_account_get_id (acc);
2050 /* If it isn't a special folder,
2051 * don't show it unless it is the visible account: */
2052 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2053 !modest_tny_account_is_virtual_local_folders (acc) &&
2054 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2056 /* Show only the visible account id */
2057 if (priv->visible_account_id) {
2058 if (strcmp (account_id, priv->visible_account_id))
2065 /* Never show these to the user. They are merged into one folder
2066 * in the local-folders account instead: */
2067 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2070 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2071 /* Only show special folders for current account if needed */
2072 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2073 TnyAccount *account;
2075 account = tny_folder_get_account (TNY_FOLDER (instance));
2077 if (TNY_IS_ACCOUNT (account)) {
2078 const gchar *account_id = tny_account_get_id (account);
2080 if (!modest_tny_account_is_virtual_local_folders (account) &&
2081 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2082 /* Show only the visible account id */
2083 if (priv->visible_account_id) {
2084 if (strcmp (account_id, priv->visible_account_id)) {
2086 } else if (priv->mailbox) {
2087 /* Filter mailboxes */
2088 if (!g_str_has_prefix (fname, priv->mailbox)) {
2090 } else if (!strcmp (fname, priv->mailbox)) {
2091 /* Hide mailbox parent */
2097 g_object_unref (account);
2104 /* Check hiding (if necessary) */
2105 cleared = modest_email_clipboard_cleared (priv->clipboard);
2106 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2107 id = tny_folder_get_id (TNY_FOLDER(instance));
2108 if (priv->hidding_ids != NULL)
2109 for (i=0; i < priv->n_selected && !found; i++)
2110 if (priv->hidding_ids[i] != NULL && id != NULL)
2111 found = (!strcmp (priv->hidding_ids[i], id));
2116 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2117 folder as no message can be move there according to UI specs */
2118 if (retval && !priv->show_non_move) {
2119 if (priv->list_to_move &&
2120 tny_list_get_length (priv->list_to_move) > 0 &&
2121 TNY_IS_FOLDER (instance)) {
2122 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2124 if (retval && TNY_IS_FOLDER (instance) &&
2125 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2127 case TNY_FOLDER_TYPE_OUTBOX:
2128 case TNY_FOLDER_TYPE_SENT:
2129 case TNY_FOLDER_TYPE_DRAFTS:
2132 case TNY_FOLDER_TYPE_UNKNOWN:
2133 case TNY_FOLDER_TYPE_NORMAL:
2134 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2135 if (type == TNY_FOLDER_TYPE_INVALID)
2136 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2138 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2139 type == TNY_FOLDER_TYPE_SENT
2140 || type == TNY_FOLDER_TYPE_DRAFTS)
2147 if (retval && TNY_IS_ACCOUNT (instance) &&
2148 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2149 ModestProtocolType protocol_type;
2151 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2152 retval = !modest_protocol_registry_protocol_type_has_tag
2153 (modest_runtime_get_protocol_registry (),
2155 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2159 /* apply special filters */
2160 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2161 if (TNY_IS_ACCOUNT (instance))
2165 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2166 if (TNY_IS_FOLDER (instance))
2170 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2171 if (TNY_IS_ACCOUNT (instance)) {
2172 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2174 } else if (TNY_IS_FOLDER (instance)) {
2175 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2180 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2181 if (TNY_IS_ACCOUNT (instance)) {
2182 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2184 } else if (TNY_IS_FOLDER (instance)) {
2185 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2190 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2191 /* A mailbox is a fake folder with an @ in the middle of the name */
2192 if (!TNY_IS_FOLDER (instance) ||
2193 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2196 const gchar *folder_name;
2197 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2198 if (!folder_name || strchr (folder_name, '@') == NULL)
2204 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2205 if (TNY_IS_FOLDER (instance)) {
2206 /* Check folder rules */
2207 ModestTnyFolderRules rules;
2209 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2210 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2211 } else if (TNY_IS_ACCOUNT (instance)) {
2212 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2220 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2221 if (TNY_IS_FOLDER (instance)) {
2222 TnyFolderType guess_type;
2224 if (TNY_FOLDER_TYPE_NORMAL) {
2225 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2231 case TNY_FOLDER_TYPE_OUTBOX:
2232 case TNY_FOLDER_TYPE_SENT:
2233 case TNY_FOLDER_TYPE_DRAFTS:
2234 case TNY_FOLDER_TYPE_ARCHIVE:
2235 case TNY_FOLDER_TYPE_INBOX:
2238 case TNY_FOLDER_TYPE_UNKNOWN:
2239 case TNY_FOLDER_TYPE_NORMAL:
2245 } else if (TNY_IS_ACCOUNT (instance)) {
2250 if (retval && TNY_IS_FOLDER (instance)) {
2251 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2254 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2255 if (TNY_IS_FOLDER (instance)) {
2256 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2257 } else if (TNY_IS_ACCOUNT (instance)) {
2262 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2263 if (TNY_IS_FOLDER (instance)) {
2264 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2265 } else if (TNY_IS_ACCOUNT (instance)) {
2270 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2271 if (TNY_IS_FOLDER (instance)) {
2272 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2273 } else if (TNY_IS_ACCOUNT (instance)) {
2279 g_object_unref (instance);
2287 modest_folder_view_update_model (ModestFolderView *self,
2288 TnyAccountStore *account_store)
2290 ModestFolderViewPrivate *priv;
2291 GtkTreeModel *model;
2292 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2294 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2295 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2298 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2300 /* Notify that there is no folder selected */
2301 g_signal_emit (G_OBJECT(self),
2302 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2304 if (priv->cur_folder_store) {
2305 g_object_unref (priv->cur_folder_store);
2306 priv->cur_folder_store = NULL;
2309 /* FIXME: the local accounts are not shown when the query
2310 selects only the subscribed folders */
2311 #ifdef MODEST_TOOLKIT_HILDON2
2312 TnyGtkFolderListStoreFlags flags;
2313 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2314 if (priv->do_refresh)
2315 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2317 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2318 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2320 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2321 MODEST_FOLDER_PATH_SEPARATOR);
2323 model = tny_gtk_folder_store_tree_model_new (NULL);
2326 /* When the model is a list store (plain representation) the
2327 outbox is not a child of any account so we have to manually
2328 delete it because removing the local folders account won't
2329 delete it (because tny_folder_get_account() is not defined
2330 for a merge folder */
2331 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2332 TnyAccount *account;
2333 ModestTnyAccountStore *acc_store;
2335 acc_store = modest_runtime_get_account_store ();
2336 account = modest_tny_account_store_get_local_folders_account (acc_store);
2338 if (g_signal_handler_is_connected (account,
2339 priv->outbox_deleted_handler))
2340 g_signal_handler_disconnect (account,
2341 priv->outbox_deleted_handler);
2343 priv->outbox_deleted_handler =
2344 g_signal_connect (account,
2346 G_CALLBACK (on_outbox_deleted_cb),
2348 g_object_unref (account);
2351 /* Get the accounts: */
2352 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2354 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2356 sortable = gtk_tree_model_sort_new_with_model (model);
2357 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2359 GTK_SORT_ASCENDING);
2360 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2362 cmp_rows, NULL, NULL);
2364 /* Create filter model */
2365 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2366 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2371 GtkTreeModel *old_tny_model;
2372 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2373 if (priv->signal_handlers > 0) {
2374 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2375 G_OBJECT (old_tny_model),
2376 "activity-changed");
2381 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2382 #ifndef MODEST_TOOLKIT_HILDON2
2383 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2384 (GCallback) on_row_inserted_maybe_select_folder, self);
2387 #ifdef MODEST_TOOLKIT_HILDON2
2388 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2391 G_CALLBACK (on_activity_changed),
2395 g_object_unref (model);
2396 g_object_unref (filter_model);
2397 g_object_unref (sortable);
2399 /* Force a reselection of the INBOX next time the widget is shown */
2400 priv->reselect = TRUE;
2407 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2409 GtkTreeModel *model = NULL;
2410 TnyFolderStore *folder = NULL;
2412 ModestFolderView *tree_view = NULL;
2413 ModestFolderViewPrivate *priv = NULL;
2414 gboolean selected = FALSE;
2416 g_return_if_fail (sel);
2417 g_return_if_fail (user_data);
2419 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2421 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2423 tree_view = MODEST_FOLDER_VIEW (user_data);
2426 gtk_tree_model_get (model, &iter,
2427 INSTANCE_COLUMN, &folder,
2430 /* If the folder is the same do not notify */
2431 if (folder && priv->cur_folder_store == folder) {
2432 g_object_unref (folder);
2437 /* Current folder was unselected */
2438 if (priv->cur_folder_store) {
2439 /* We must do this firstly because a libtinymail-camel
2440 implementation detail. If we issue the signal
2441 before doing the sync_async, then that signal could
2442 cause (and it actually does it) a free of the
2443 summary of the folder (because the main window will
2444 clear the headers view */
2445 #ifndef MODEST_TOOLKIT_HILDON2
2446 if (TNY_IS_FOLDER(priv->cur_folder_store))
2447 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2448 FALSE, NULL, NULL, NULL);
2451 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2452 priv->cur_folder_store, FALSE);
2454 g_object_unref (priv->cur_folder_store);
2455 priv->cur_folder_store = NULL;
2458 /* New current references */
2459 priv->cur_folder_store = folder;
2461 /* New folder has been selected. Do not notify if there is
2462 nothing new selected */
2464 g_signal_emit (G_OBJECT(tree_view),
2465 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2466 0, priv->cur_folder_store, TRUE);
2471 on_row_activated (GtkTreeView *treeview,
2472 GtkTreePath *treepath,
2473 GtkTreeViewColumn *column,
2476 GtkTreeModel *model = NULL;
2477 TnyFolderStore *folder = NULL;
2479 ModestFolderView *self = NULL;
2480 ModestFolderViewPrivate *priv = NULL;
2482 g_return_if_fail (treeview);
2483 g_return_if_fail (user_data);
2485 self = MODEST_FOLDER_VIEW (user_data);
2486 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2488 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2490 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2493 gtk_tree_model_get (model, &iter,
2494 INSTANCE_COLUMN, &folder,
2497 g_signal_emit (G_OBJECT(self),
2498 signals[FOLDER_ACTIVATED_SIGNAL],
2501 #ifdef MODEST_TOOLKIT_HILDON2
2502 HildonUIMode ui_mode;
2503 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2504 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2505 if (priv->cur_folder_store)
2506 g_object_unref (priv->cur_folder_store);
2507 priv->cur_folder_store = g_object_ref (folder);
2511 g_object_unref (folder);
2515 modest_folder_view_get_selected (ModestFolderView *self)
2517 ModestFolderViewPrivate *priv;
2519 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2521 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2522 if (priv->cur_folder_store)
2523 g_object_ref (priv->cur_folder_store);
2525 return priv->cur_folder_store;
2529 get_cmp_rows_type_pos (GObject *folder)
2531 /* Remote accounts -> Local account -> MMC account .*/
2534 if (TNY_IS_ACCOUNT (folder) &&
2535 modest_tny_account_is_virtual_local_folders (
2536 TNY_ACCOUNT (folder))) {
2538 } else if (TNY_IS_ACCOUNT (folder)) {
2539 TnyAccount *account = TNY_ACCOUNT (folder);
2540 const gchar *account_id = tny_account_get_id (account);
2541 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2547 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2548 return -1; /* Should never happen */
2553 inbox_is_special (TnyFolderStore *folder_store)
2555 gboolean is_special = TRUE;
2557 if (TNY_IS_FOLDER (folder_store)) {
2561 gchar *last_inbox_bar;
2563 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2564 downcase = g_utf8_strdown (id, -1);
2565 last_bar = g_strrstr (downcase, "/");
2567 last_inbox_bar = g_strrstr (downcase, "inbox/");
2568 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2579 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2581 TnyAccount *account;
2582 gboolean is_special;
2583 /* Inbox, Outbox, Drafts, Sent, User */
2586 if (!TNY_IS_FOLDER (folder_store))
2589 case TNY_FOLDER_TYPE_INBOX:
2591 account = tny_folder_get_account (folder_store);
2592 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2594 /* In inbox case we need to know if the inbox is really the top
2595 * inbox of the account, or if it's a submailbox inbox. To do
2596 * this we'll apply an heuristic rule: Find last "/" and check
2597 * if it's preceeded by another Inbox */
2598 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2599 g_object_unref (account);
2600 return is_special?0:4;
2603 case TNY_FOLDER_TYPE_OUTBOX:
2604 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2606 case TNY_FOLDER_TYPE_DRAFTS:
2608 account = tny_folder_get_account (folder_store);
2609 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2610 g_object_unref (account);
2611 return is_special?1:4;
2614 case TNY_FOLDER_TYPE_SENT:
2616 account = tny_folder_get_account (folder_store);
2617 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2618 g_object_unref (account);
2619 return is_special?3:4;
2628 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2630 const gchar *a1_name, *a2_name;
2632 a1_name = tny_account_get_name (a1);
2633 a2_name = tny_account_get_name (a2);
2635 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2639 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2641 TnyAccount *a1 = NULL, *a2 = NULL;
2644 if (TNY_IS_ACCOUNT (s1)) {
2645 a1 = TNY_ACCOUNT (g_object_ref (s1));
2646 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2647 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2650 if (TNY_IS_ACCOUNT (s2)) {
2651 a2 = TNY_ACCOUNT (g_object_ref (s2));
2652 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2653 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2670 /* First we sort with the type of account */
2671 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2675 cmp = compare_account_names (a1, a2);
2679 g_object_unref (a1);
2681 g_object_unref (a2);
2687 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2689 gint is_account1, is_account2;
2691 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2692 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2694 return is_account2 - is_account1;
2698 compare_folders (const gchar *name1, const gchar *name2)
2700 const gchar *separator1, *separator2;
2701 const gchar *next1, *next2;
2705 if (name1 == NULL || name1[0] == '\0')
2707 if (name2 == NULL || name2[0] == '\0')
2710 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2712 top1 = g_strndup (name1, separator1 - name1);
2714 top1 = g_strdup (name1);
2717 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2719 top2 = g_strndup (name2, separator2 - name2);
2721 top2 = g_strdup (name2);
2725 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2732 if (separator1 == NULL && separator2 == NULL)
2735 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2736 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2738 return compare_folders (next1, next2);
2743 * This function orders the mail accounts according to these rules:
2744 * 1st - remote accounts
2745 * 2nd - local account
2749 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2753 gchar *name1 = NULL;
2754 gchar *name2 = NULL;
2755 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2756 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2757 GObject *folder1 = NULL;
2758 GObject *folder2 = NULL;
2760 gtk_tree_model_get (tree_model, iter1,
2761 NAME_COLUMN, &name1,
2763 INSTANCE_COLUMN, &folder1,
2765 gtk_tree_model_get (tree_model, iter2,
2766 NAME_COLUMN, &name2,
2767 TYPE_COLUMN, &type2,
2768 INSTANCE_COLUMN, &folder2,
2771 /* Return if we get no folder. This could happen when folder
2772 operations are happening. The model is updated after the
2773 folder copy/move actually occurs, so there could be
2774 situations where the model to be drawn is not correct */
2775 if (!folder1 || !folder2)
2778 /* Sort by type. First the special folders, then the archives */
2779 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2783 /* Now we sort using the account of each folder */
2784 if (TNY_IS_FOLDER_STORE (folder1) &&
2785 TNY_IS_FOLDER_STORE (folder2)) {
2786 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2790 /* Each group is preceeded by its account */
2791 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2796 /* Pure sort by name */
2797 cmp = compare_folders (name1, name2);
2800 g_object_unref(G_OBJECT(folder1));
2802 g_object_unref(G_OBJECT(folder2));
2810 /*****************************************************************************/
2811 /* DRAG and DROP stuff */
2812 /*****************************************************************************/
2814 * This function fills the #GtkSelectionData with the row and the
2815 * model that has been dragged. It's called when this widget is a
2816 * source for dnd after the event drop happened
2819 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2820 guint info, guint time, gpointer data)
2822 GtkTreeSelection *selection;
2823 GtkTreeModel *model;
2825 GtkTreePath *source_row;
2827 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2828 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2830 source_row = gtk_tree_model_get_path (model, &iter);
2831 gtk_tree_set_row_drag_data (selection_data,
2835 gtk_tree_path_free (source_row);
2839 typedef struct _DndHelper {
2840 ModestFolderView *folder_view;
2841 gboolean delete_source;
2842 GtkTreePath *source_row;
2846 dnd_helper_destroyer (DndHelper *helper)
2848 /* Free the helper */
2849 gtk_tree_path_free (helper->source_row);
2850 g_slice_free (DndHelper, helper);
2854 xfer_folder_cb (ModestMailOperation *mail_op,
2855 TnyFolder *new_folder,
2859 /* Select the folder */
2860 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2866 /* get the folder for the row the treepath refers to. */
2867 /* folder must be unref'd */
2868 static TnyFolderStore *
2869 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2872 TnyFolderStore *folder = NULL;
2874 if (gtk_tree_model_get_iter (model,&iter, path))
2875 gtk_tree_model_get (model, &iter,
2876 INSTANCE_COLUMN, &folder,
2883 * This function is used by drag_data_received_cb to manage drag and
2884 * drop of a header, i.e, and drag from the header view to the folder
2888 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2889 GtkTreeModel *dest_model,
2890 GtkTreePath *dest_row,
2891 GtkSelectionData *selection_data)
2893 TnyList *headers = NULL;
2894 TnyFolder *folder = NULL, *src_folder = NULL;
2895 TnyFolderType folder_type;
2896 GtkTreeIter source_iter, dest_iter;
2897 ModestWindowMgr *mgr = NULL;
2898 ModestWindow *main_win = NULL;
2899 gchar **uris, **tmp;
2901 /* Build the list of headers */
2902 mgr = modest_runtime_get_window_mgr ();
2903 headers = tny_simple_list_new ();
2904 uris = modest_dnd_selection_data_get_paths (selection_data);
2907 while (*tmp != NULL) {
2910 gboolean first = TRUE;
2913 path = gtk_tree_path_new_from_string (*tmp);
2914 gtk_tree_model_get_iter (source_model, &source_iter, path);
2915 gtk_tree_model_get (source_model, &source_iter,
2916 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2919 /* Do not enable d&d of headers already opened */
2920 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2921 tny_list_append (headers, G_OBJECT (header));
2923 if (G_UNLIKELY (first)) {
2924 src_folder = tny_header_get_folder (header);
2928 /* Free and go on */
2929 gtk_tree_path_free (path);
2930 g_object_unref (header);
2935 /* This could happen ig we perform a d&d very quickly over the
2936 same row that row could dissapear because message is
2938 if (!TNY_IS_FOLDER (src_folder))
2941 /* Get the target folder */
2942 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2943 gtk_tree_model_get (dest_model, &dest_iter,
2947 if (!folder || !TNY_IS_FOLDER(folder)) {
2948 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2952 folder_type = modest_tny_folder_guess_folder_type (folder);
2953 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2954 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2955 goto cleanup; /* cannot move messages there */
2958 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2959 /* g_warning ("folder not writable"); */
2960 goto cleanup; /* verboten! */
2963 /* Ask for confirmation to move */
2964 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2966 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2970 /* Transfer messages */
2971 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2976 if (G_IS_OBJECT (src_folder))
2977 g_object_unref (src_folder);
2978 if (G_IS_OBJECT(folder))
2979 g_object_unref (G_OBJECT (folder));
2980 if (G_IS_OBJECT(headers))
2981 g_object_unref (headers);
2985 TnyFolderStore *src_folder;
2986 TnyFolderStore *dst_folder;
2987 ModestFolderView *folder_view;
2992 dnd_folder_info_destroyer (DndFolderInfo *info)
2994 if (info->src_folder)
2995 g_object_unref (info->src_folder);
2996 if (info->dst_folder)
2997 g_object_unref (info->dst_folder);
2998 g_slice_free (DndFolderInfo, info);
3002 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
3003 GtkWindow *parent_window,
3004 TnyAccount *account)
3007 modest_ui_actions_on_account_connection_error (parent_window, account);
3009 /* Free the helper & info */
3010 dnd_helper_destroyer (info->helper);
3011 dnd_folder_info_destroyer (info);
3015 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
3017 GtkWindow *parent_window,
3018 TnyAccount *account,
3021 DndFolderInfo *info = NULL;
3022 ModestMailOperation *mail_op;
3024 info = (DndFolderInfo *) user_data;
3026 if (err || canceled) {
3027 dnd_on_connection_failed_destroyer (info, parent_window, account);
3031 /* Do the mail operation */
3032 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
3033 modest_ui_actions_move_folder_error_handler,
3034 info->src_folder, NULL);
3036 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3039 /* Transfer the folder */
3040 modest_mail_operation_xfer_folder (mail_op,
3041 TNY_FOLDER (info->src_folder),
3043 info->helper->delete_source,
3045 info->helper->folder_view);
3048 g_object_unref (G_OBJECT (mail_op));
3049 dnd_helper_destroyer (info->helper);
3050 dnd_folder_info_destroyer (info);
3055 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3057 GtkWindow *parent_window,
3058 TnyAccount *account,
3061 DndFolderInfo *info = NULL;
3063 info = (DndFolderInfo *) user_data;
3065 if (err || canceled) {
3066 dnd_on_connection_failed_destroyer (info, parent_window, account);
3070 /* Connect to source folder and perform the copy/move */
3071 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3073 drag_and_drop_from_folder_view_src_folder_performer,
3078 * This function is used by drag_data_received_cb to manage drag and
3079 * drop of a folder, i.e, and drag from the folder view to the same
3083 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3084 GtkTreeModel *dest_model,
3085 GtkTreePath *dest_row,
3086 GtkSelectionData *selection_data,
3089 GtkTreeIter dest_iter, iter;
3090 TnyFolderStore *dest_folder = NULL;
3091 TnyFolderStore *folder = NULL;
3092 gboolean forbidden = FALSE;
3094 DndFolderInfo *info = NULL;
3096 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3098 g_warning ("%s: BUG: no main window", __FUNCTION__);
3099 dnd_helper_destroyer (helper);
3104 /* check the folder rules for the destination */
3105 folder = tree_path_to_folder (dest_model, dest_row);
3106 if (TNY_IS_FOLDER(folder)) {
3107 ModestTnyFolderRules rules =
3108 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3109 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3110 } else if (TNY_IS_FOLDER_STORE(folder)) {
3111 /* enable local root as destination for folders */
3112 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3113 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3116 g_object_unref (folder);
3119 /* check the folder rules for the source */
3120 folder = tree_path_to_folder (source_model, helper->source_row);
3121 if (TNY_IS_FOLDER(folder)) {
3122 ModestTnyFolderRules rules =
3123 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3124 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3127 g_object_unref (folder);
3131 /* Check if the drag is possible */
3132 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3134 modest_platform_run_information_dialog ((GtkWindow *) win,
3135 _("mail_in_ui_folder_move_target_error"),
3137 /* Restore the previous selection */
3138 folder = tree_path_to_folder (source_model, helper->source_row);
3140 if (TNY_IS_FOLDER (folder))
3141 modest_folder_view_select_folder (helper->folder_view,
3142 TNY_FOLDER (folder), FALSE);
3143 g_object_unref (folder);
3145 dnd_helper_destroyer (helper);
3150 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3151 gtk_tree_model_get (dest_model, &dest_iter,
3154 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3155 gtk_tree_model_get (source_model, &iter,
3159 /* Create the info for the performer */
3160 info = g_slice_new0 (DndFolderInfo);
3161 info->src_folder = g_object_ref (folder);
3162 info->dst_folder = g_object_ref (dest_folder);
3163 info->helper = helper;
3165 /* Connect to the destination folder and perform the copy/move */
3166 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3168 drag_and_drop_from_folder_view_dst_folder_performer,
3172 g_object_unref (dest_folder);
3173 g_object_unref (folder);
3177 * This function receives the data set by the "drag-data-get" signal
3178 * handler. This information comes within the #GtkSelectionData. This
3179 * function will manage both the drags of folders of the treeview and
3180 * drags of headers of the header view widget.
3183 on_drag_data_received (GtkWidget *widget,
3184 GdkDragContext *context,
3187 GtkSelectionData *selection_data,
3192 GtkWidget *source_widget;
3193 GtkTreeModel *dest_model, *source_model;
3194 GtkTreePath *source_row, *dest_row;
3195 GtkTreeViewDropPosition pos;
3196 gboolean delete_source = FALSE;
3197 gboolean success = FALSE;
3199 /* Do not allow further process */
3200 g_signal_stop_emission_by_name (widget, "drag-data-received");
3201 source_widget = gtk_drag_get_source_widget (context);
3203 /* Get the action */
3204 if (context->action == GDK_ACTION_MOVE) {
3205 delete_source = TRUE;
3207 /* Notify that there is no folder selected. We need to
3208 do this in order to update the headers view (and
3209 its monitors, because when moving, the old folder
3210 won't longer exist. We can not wait for the end of
3211 the operation, because the operation won't start if
3212 the folder is in use */
3213 if (source_widget == widget) {
3214 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3215 gtk_tree_selection_unselect_all (sel);
3219 /* Check if the get_data failed */
3220 if (selection_data == NULL || selection_data->length < 0)
3223 /* Select the destination model */
3224 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3226 /* Get the path to the destination row. Can not call
3227 gtk_tree_view_get_drag_dest_row() because the source row
3228 is not selected anymore */
3229 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3232 /* Only allow drops IN other rows */
3234 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3235 pos == GTK_TREE_VIEW_DROP_AFTER)
3239 /* Drags from the header view */
3240 if (source_widget != widget) {
3241 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3243 drag_and_drop_from_header_view (source_model,
3248 DndHelper *helper = NULL;
3250 /* Get the source model and row */
3251 gtk_tree_get_row_drag_data (selection_data,
3255 /* Create the helper */
3256 helper = g_slice_new0 (DndHelper);
3257 helper->delete_source = delete_source;
3258 helper->source_row = gtk_tree_path_copy (source_row);
3259 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3261 drag_and_drop_from_folder_view (source_model,
3267 gtk_tree_path_free (source_row);
3271 gtk_tree_path_free (dest_row);
3274 /* Finish the drag and drop */
3275 gtk_drag_finish (context, success, FALSE, time);
3279 * We define a "drag-drop" signal handler because we do not want to
3280 * use the default one, because the default one always calls
3281 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3282 * signal handler, because there we have all the information available
3283 * to know if the dnd was a success or not.
3286 drag_drop_cb (GtkWidget *widget,
3287 GdkDragContext *context,
3295 if (!context->targets)
3298 /* Check if we're dragging a folder row */
3299 target = gtk_drag_dest_find_target (widget, context, NULL);
3301 /* Request the data from the source. */
3302 gtk_drag_get_data(widget, context, target, time);
3308 * This function expands a node of a tree view if it's not expanded
3309 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3310 * does that, so that's why they're here.
3313 expand_row_timeout (gpointer data)
3315 GtkTreeView *tree_view = data;
3316 GtkTreePath *dest_path = NULL;
3317 GtkTreeViewDropPosition pos;
3318 gboolean result = FALSE;
3320 gdk_threads_enter ();
3322 gtk_tree_view_get_drag_dest_row (tree_view,
3327 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3328 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3329 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3330 gtk_tree_path_free (dest_path);
3334 gtk_tree_path_free (dest_path);
3339 gdk_threads_leave ();
3345 * This function is called whenever the pointer is moved over a widget
3346 * while dragging some data. It installs a timeout that will expand a
3347 * node of the treeview if not expanded yet. This function also calls
3348 * gdk_drag_status in order to set the suggested action that will be
3349 * used by the "drag-data-received" signal handler to know if we
3350 * should do a move or just a copy of the data.
3353 on_drag_motion (GtkWidget *widget,
3354 GdkDragContext *context,
3360 GtkTreeViewDropPosition pos;
3361 GtkTreePath *dest_row;
3362 GtkTreeModel *dest_model;
3363 ModestFolderViewPrivate *priv;
3364 GdkDragAction suggested_action;
3365 gboolean valid_location = FALSE;
3366 TnyFolderStore *folder = NULL;
3368 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3370 if (priv->timer_expander != 0) {
3371 g_source_remove (priv->timer_expander);
3372 priv->timer_expander = 0;
3375 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3380 /* Do not allow drops between folders */
3382 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3383 pos == GTK_TREE_VIEW_DROP_AFTER) {
3384 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3385 gdk_drag_status(context, 0, time);
3386 valid_location = FALSE;
3389 valid_location = TRUE;
3392 /* Check that the destination folder is writable */
3393 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3394 folder = tree_path_to_folder (dest_model, dest_row);
3395 if (folder && TNY_IS_FOLDER (folder)) {
3396 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3398 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3399 valid_location = FALSE;
3404 /* Expand the selected row after 1/2 second */
3405 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3406 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3408 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3410 /* Select the desired action. By default we pick MOVE */
3411 suggested_action = GDK_ACTION_MOVE;
3413 if (context->actions == GDK_ACTION_COPY)
3414 gdk_drag_status(context, GDK_ACTION_COPY, time);
3415 else if (context->actions == GDK_ACTION_MOVE)
3416 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3417 else if (context->actions & suggested_action)
3418 gdk_drag_status(context, suggested_action, time);
3420 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3424 g_object_unref (folder);
3426 gtk_tree_path_free (dest_row);
3428 g_signal_stop_emission_by_name (widget, "drag-motion");
3430 return valid_location;
3434 * This function sets the treeview as a source and a target for dnd
3435 * events. It also connects all the requirede signals.
3438 setup_drag_and_drop (GtkTreeView *self)
3440 /* Set up the folder view as a dnd destination. Set only the
3441 highlight flag, otherwise gtk will have a different
3443 #ifdef MODEST_TOOLKIT_HILDON2
3446 gtk_drag_dest_set (GTK_WIDGET (self),
3447 GTK_DEST_DEFAULT_HIGHLIGHT,
3448 folder_view_drag_types,
3449 G_N_ELEMENTS (folder_view_drag_types),
3450 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3452 g_signal_connect (G_OBJECT (self),
3453 "drag_data_received",
3454 G_CALLBACK (on_drag_data_received),
3458 /* Set up the treeview as a dnd source */
3459 gtk_drag_source_set (GTK_WIDGET (self),
3461 folder_view_drag_types,
3462 G_N_ELEMENTS (folder_view_drag_types),
3463 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3465 g_signal_connect (G_OBJECT (self),
3467 G_CALLBACK (on_drag_motion),
3470 g_signal_connect (G_OBJECT (self),
3472 G_CALLBACK (on_drag_data_get),
3475 g_signal_connect (G_OBJECT (self),
3477 G_CALLBACK (drag_drop_cb),
3482 * This function manages the navigation through the folders using the
3483 * keyboard or the hardware keys in the device
3486 on_key_pressed (GtkWidget *self,
3490 GtkTreeSelection *selection;
3492 GtkTreeModel *model;
3493 gboolean retval = FALSE;
3495 /* Up and Down are automatically managed by the treeview */
3496 if (event->keyval == GDK_Return) {
3497 /* Expand/Collapse the selected row */
3498 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3499 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3502 path = gtk_tree_model_get_path (model, &iter);
3504 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3505 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3507 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3508 gtk_tree_path_free (path);
3510 /* No further processing */
3518 * We listen to the changes in the local folder account name key,
3519 * because we want to show the right name in the view. The local
3520 * folder account name corresponds to the device name in the Maemo
3521 * version. We do this because we do not want to query gconf on each
3522 * tree view refresh. It's better to cache it and change whenever
3526 on_configuration_key_changed (ModestConf* conf,
3528 ModestConfEvent event,
3529 ModestConfNotificationId id,
3530 ModestFolderView *self)
3532 ModestFolderViewPrivate *priv;
3535 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3536 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3538 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3539 g_free (priv->local_account_name);
3541 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3542 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3544 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3545 MODEST_CONF_DEVICE_NAME, NULL);
3547 /* Force a redraw */
3548 #if GTK_CHECK_VERSION(2, 8, 0)
3549 GtkTreeViewColumn * tree_column;
3551 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3553 gtk_tree_view_column_queue_resize (tree_column);
3555 gtk_widget_queue_draw (GTK_WIDGET (self));
3561 modest_folder_view_set_style (ModestFolderView *self,
3562 ModestFolderViewStyle style)
3564 ModestFolderViewPrivate *priv;
3566 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3567 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3568 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3570 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3573 priv->style = style;
3577 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3578 const gchar *account_id)
3580 ModestFolderViewPrivate *priv;
3581 GtkTreeModel *model;
3583 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3585 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3587 /* This will be used by the filter_row callback,
3588 * to decided which rows to show: */
3589 if (priv->visible_account_id) {
3590 g_free (priv->visible_account_id);
3591 priv->visible_account_id = NULL;
3594 priv->visible_account_id = g_strdup (account_id);
3597 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3598 if (GTK_IS_TREE_MODEL_FILTER (model))
3599 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3601 /* Save settings to gconf */
3602 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3603 MODEST_CONF_FOLDER_VIEW_KEY);
3605 /* Notify observers */
3606 g_signal_emit (G_OBJECT(self),
3607 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3612 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3614 ModestFolderViewPrivate *priv;
3616 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3618 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3620 return (const gchar *) priv->visible_account_id;
3624 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3628 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3630 gtk_tree_model_get (model, iter,
3634 gboolean result = FALSE;
3635 if (type == TNY_FOLDER_TYPE_INBOX) {
3639 *inbox_iter = *iter;
3643 if (gtk_tree_model_iter_children (model, &child, iter)) {
3644 if (find_inbox_iter (model, &child, inbox_iter))
3648 } while (gtk_tree_model_iter_next (model, iter));
3657 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3659 #ifndef MODEST_TOOLKIT_HILDON2
3660 GtkTreeModel *model;
3661 GtkTreeIter iter, inbox_iter;
3662 GtkTreeSelection *sel;
3663 GtkTreePath *path = NULL;
3665 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3667 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3671 expand_root_items (self);
3672 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3674 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3675 g_warning ("%s: model is empty", __FUNCTION__);
3679 if (find_inbox_iter (model, &iter, &inbox_iter))
3680 path = gtk_tree_model_get_path (model, &inbox_iter);
3682 path = gtk_tree_path_new_first ();
3684 /* Select the row and free */
3685 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3686 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3687 gtk_tree_path_free (path);
3690 gtk_widget_grab_focus (GTK_WIDGET(self));
3697 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3702 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3703 TnyFolder* a_folder;
3706 gtk_tree_model_get (model, iter,
3707 INSTANCE_COLUMN, &a_folder,
3713 if (folder == a_folder) {
3714 g_object_unref (a_folder);
3715 *folder_iter = *iter;
3718 g_object_unref (a_folder);
3720 if (gtk_tree_model_iter_children (model, &child, iter)) {
3721 if (find_folder_iter (model, &child, folder_iter, folder))
3725 } while (gtk_tree_model_iter_next (model, iter));
3730 #ifndef MODEST_TOOLKIT_HILDON2
3732 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3735 ModestFolderView *self)
3737 ModestFolderViewPrivate *priv = NULL;
3738 GtkTreeSelection *sel;
3739 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3740 GObject *instance = NULL;
3742 if (!MODEST_IS_FOLDER_VIEW(self))
3745 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3747 priv->reexpand = TRUE;
3749 gtk_tree_model_get (tree_model, iter,
3751 INSTANCE_COLUMN, &instance,
3757 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3758 priv->folder_to_select = g_object_ref (instance);
3760 g_object_unref (instance);
3762 if (priv->folder_to_select) {
3764 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3767 path = gtk_tree_model_get_path (tree_model, iter);
3768 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3770 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3772 gtk_tree_selection_select_iter (sel, iter);
3773 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3775 gtk_tree_path_free (path);
3779 modest_folder_view_disable_next_folder_selection (self);
3781 /* Refilter the model */
3782 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3788 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3790 ModestFolderViewPrivate *priv;
3792 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3794 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3796 if (priv->folder_to_select)
3797 g_object_unref(priv->folder_to_select);
3799 priv->folder_to_select = NULL;
3803 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3804 gboolean after_change)
3806 GtkTreeModel *model;
3807 GtkTreeIter iter, folder_iter;
3808 GtkTreeSelection *sel;
3809 ModestFolderViewPrivate *priv = NULL;
3811 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3812 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3814 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3817 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3818 gtk_tree_selection_unselect_all (sel);
3820 if (priv->folder_to_select)
3821 g_object_unref(priv->folder_to_select);
3822 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3826 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3831 /* Refilter the model, before selecting the folder */
3832 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3834 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3835 g_warning ("%s: model is empty", __FUNCTION__);
3839 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3842 path = gtk_tree_model_get_path (model, &folder_iter);
3843 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3845 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3846 gtk_tree_selection_select_iter (sel, &folder_iter);
3847 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3849 gtk_tree_path_free (path);
3857 modest_folder_view_copy_selection (ModestFolderView *self)
3859 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3861 /* Copy selection */
3862 _clipboard_set_selected_data (self, FALSE);
3866 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3868 ModestFolderViewPrivate *priv = NULL;
3869 GtkTreeModel *model = NULL;
3870 const gchar **hidding = NULL;
3871 guint i, n_selected;
3873 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3874 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3876 /* Copy selection */
3877 if (!_clipboard_set_selected_data (folder_view, TRUE))
3880 /* Get hidding ids */
3881 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3883 /* Clear hidding array created by previous cut operation */
3884 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3886 /* Copy hidding array */
3887 priv->n_selected = n_selected;
3888 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3889 for (i=0; i < n_selected; i++)
3890 priv->hidding_ids[i] = g_strdup(hidding[i]);
3892 /* Hide cut folders */
3893 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3894 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3898 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3899 ModestFolderView *folder_view_dst)
3901 GtkTreeModel *filter_model = NULL;
3902 GtkTreeModel *model = NULL;
3903 GtkTreeModel *new_filter_model = NULL;
3904 GtkTreeModel *old_tny_model = NULL;
3905 GtkTreeModel *new_tny_model = NULL;
3906 ModestFolderViewPrivate *dst_priv;
3908 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3909 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3911 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3912 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3913 new_tny_model = NULL;
3916 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3917 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3918 G_OBJECT (old_tny_model),
3919 "activity-changed");
3921 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3922 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3924 /* Build new filter model */
3925 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3926 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3933 /* Set copied model */
3934 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3935 #ifndef MODEST_TOOLKIT_HILDON2
3936 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3937 G_OBJECT(new_filter_model), "row-inserted",
3938 (GCallback) on_row_inserted_maybe_select_folder,
3941 #ifdef MODEST_TOOLKIT_HILDON2
3942 if (new_tny_model) {
3943 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3944 G_OBJECT (new_tny_model),
3946 G_CALLBACK (on_activity_changed),
3952 g_object_unref (new_filter_model);
3956 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3959 GtkTreeModel *model = NULL;
3960 ModestFolderViewPrivate* priv;
3962 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3964 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3965 priv->show_non_move = show;
3966 /* modest_folder_view_update_model(folder_view, */
3967 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3969 /* Hide special folders */
3970 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3971 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3972 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3977 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3980 ModestFolderViewPrivate* priv;
3982 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3984 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3985 priv->show_message_count = show;
3987 g_object_set (G_OBJECT (priv->messages_renderer),
3988 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3992 /* Returns FALSE if it did not selected anything */
3994 _clipboard_set_selected_data (ModestFolderView *folder_view,
3997 ModestFolderViewPrivate *priv = NULL;
3998 TnyFolderStore *folder = NULL;
3999 gboolean retval = FALSE;
4001 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
4002 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4004 /* Set selected data on clipboard */
4005 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
4006 folder = modest_folder_view_get_selected (folder_view);
4008 /* Do not allow to select an account */
4009 if (TNY_IS_FOLDER (folder)) {
4010 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
4015 g_object_unref (folder);
4021 _clear_hidding_filter (ModestFolderView *folder_view)
4023 ModestFolderViewPrivate *priv;
4026 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4027 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4029 if (priv->hidding_ids != NULL) {
4030 for (i=0; i < priv->n_selected; i++)
4031 g_free (priv->hidding_ids[i]);
4032 g_free(priv->hidding_ids);
4038 on_display_name_changed (ModestAccountMgr *mgr,
4039 const gchar *account,
4042 ModestFolderView *self;
4044 self = MODEST_FOLDER_VIEW (user_data);
4046 /* Force a redraw */
4047 #if GTK_CHECK_VERSION(2, 8, 0)
4048 GtkTreeViewColumn * tree_column;
4050 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4052 gtk_tree_view_column_queue_resize (tree_column);
4054 gtk_widget_queue_draw (GTK_WIDGET (self));
4059 modest_folder_view_set_cell_style (ModestFolderView *self,
4060 ModestFolderViewCellStyle cell_style)
4062 ModestFolderViewPrivate *priv = NULL;
4064 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4065 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4067 priv->cell_style = cell_style;
4069 g_object_set (G_OBJECT (priv->messages_renderer),
4070 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4073 gtk_widget_queue_draw (GTK_WIDGET (self));
4077 update_style (ModestFolderView *self)
4079 ModestFolderViewPrivate *priv;
4080 GdkColor style_color, style_active_color;
4081 PangoAttrList *attr_list;
4083 PangoAttribute *attr;
4085 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4086 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4090 attr_list = pango_attr_list_new ();
4091 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4092 gdk_color_parse ("grey", &style_color);
4094 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4095 pango_attr_list_insert (attr_list, attr);
4098 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4100 "SmallSystemFont", NULL,
4103 attr = pango_attr_font_desc_new (pango_font_description_copy
4104 (style->font_desc));
4105 pango_attr_list_insert (attr_list, attr);
4107 g_object_set (G_OBJECT (priv->messages_renderer),
4108 "foreground-gdk", &style_color,
4109 "foreground-set", TRUE,
4110 "attributes", attr_list,
4112 pango_attr_list_unref (attr_list);
4115 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4116 priv->active_color = style_active_color;
4118 gdk_color_parse ("000", &(priv->active_color));
4123 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4125 if (strcmp ("style", spec->name) == 0) {
4126 update_style (MODEST_FOLDER_VIEW (obj));
4127 gtk_widget_queue_draw (GTK_WIDGET (obj));
4132 modest_folder_view_set_filter (ModestFolderView *self,
4133 ModestFolderViewFilter filter)
4135 ModestFolderViewPrivate *priv;
4136 GtkTreeModel *filter_model;
4138 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4139 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4141 priv->filter |= filter;
4143 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4144 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4145 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4150 modest_folder_view_unset_filter (ModestFolderView *self,
4151 ModestFolderViewFilter filter)
4153 ModestFolderViewPrivate *priv;
4154 GtkTreeModel *filter_model;
4156 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4157 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4159 priv->filter &= ~filter;
4161 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4162 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4163 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4168 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4169 ModestTnyFolderRules rules)
4171 GtkTreeModel *filter_model;
4173 gboolean fulfil = FALSE;
4175 if (!get_inner_models (self, &filter_model, NULL, NULL))
4178 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4182 TnyFolderStore *folder;
4184 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4186 if (TNY_IS_FOLDER (folder)) {
4187 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4188 /* Folder rules are negative: non_writable, non_deletable... */
4189 if (!(folder_rules & rules))
4192 g_object_unref (folder);
4195 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4201 modest_folder_view_set_list_to_move (ModestFolderView *self,
4204 ModestFolderViewPrivate *priv;
4206 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4207 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4209 if (priv->list_to_move)
4210 g_object_unref (priv->list_to_move);
4213 g_object_ref (list);
4215 priv->list_to_move = list;
4219 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4221 ModestFolderViewPrivate *priv;
4223 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4224 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4227 g_free (priv->mailbox);
4229 priv->mailbox = g_strdup (mailbox);
4231 /* Notify observers */
4232 g_signal_emit (G_OBJECT(self),
4233 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4234 priv->visible_account_id);
4238 modest_folder_view_get_mailbox (ModestFolderView *self)
4240 ModestFolderViewPrivate *priv;
4242 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4243 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4245 return (const gchar *) priv->mailbox;
4249 modest_folder_view_get_activity (ModestFolderView *self)
4251 ModestFolderViewPrivate *priv;
4252 GtkTreeModel *inner_model;
4254 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4255 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4256 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4258 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4259 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4265 #ifdef MODEST_TOOLKIT_HILDON2
4267 on_activity_changed (TnyGtkFolderListStore *store,
4269 ModestFolderView *folder_view)
4271 ModestFolderViewPrivate *priv;
4273 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4274 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4275 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4277 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,