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->finalize = 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)
515 gchar *last_separator;
517 if (item_name == NULL)
520 for (c = *item_name; *c != '\0'; c++) {
521 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
526 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
527 if (last_separator != NULL) {
528 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
535 buffer = g_string_new ("");
536 for (i = 0; i < n_parents; i++) {
537 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
539 buffer = g_string_append (buffer, last_separator);
541 *item_name = g_string_free (buffer, FALSE);
547 format_compact_style (gchar **item_name,
549 const gchar *mailbox,
551 gboolean multiaccount,
552 gboolean *use_markup)
556 TnyFolderType folder_type;
558 if (!TNY_IS_FOLDER (instance))
561 folder = (TnyFolder *) instance;
563 folder_type = tny_folder_get_folder_type (folder);
564 is_special = (get_cmp_pos (folder_type, folder)!= 4);
567 /* Remove mailbox prefix if any */
568 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
569 if (g_str_has_prefix (*item_name, prefix)) {
570 gchar *new_item_name;
572 new_item_name = g_strdup (*item_name + strlen (prefix));
573 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
574 g_free (new_item_name);
575 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
578 *item_name = new_item_name;
580 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
583 *item_name = g_strdup (_("mcen_me_folder_inbox"));
586 if (!is_special || multiaccount) {
587 TnyAccount *account = tny_folder_get_account (folder);
588 const gchar *folder_name;
589 gboolean concat_folder_name = FALSE;
592 /* Should not happen */
596 /* convert parent folders to dots */
597 convert_parent_folders_to_dots (item_name);
599 folder_name = tny_folder_get_name (folder);
600 if (g_str_has_suffix (*item_name, folder_name)) {
601 gchar *offset = g_strrstr (*item_name, folder_name);
603 concat_folder_name = TRUE;
606 buffer = g_string_new ("");
608 buffer = g_string_append (buffer, *item_name);
609 if (concat_folder_name) {
610 buffer = g_string_append (buffer, folder_name);
613 g_object_unref (account);
615 *item_name = g_string_free (buffer, FALSE);
623 text_cell_data (GtkTreeViewColumn *column,
624 GtkCellRenderer *renderer,
625 GtkTreeModel *tree_model,
629 ModestFolderViewPrivate *priv;
630 GObject *rendobj = (GObject *) renderer;
632 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
633 GObject *instance = NULL;
634 gboolean use_markup = FALSE;
636 gtk_tree_model_get (tree_model, iter,
639 INSTANCE_COLUMN, &instance,
641 if (!fname || !instance)
644 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
645 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
647 gchar *item_name = NULL;
648 gint item_weight = 400;
650 if (type != TNY_FOLDER_TYPE_ROOT) {
655 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
656 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
659 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
660 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
662 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
665 /* Sometimes an special folder is reported by the server as
666 NORMAL, like some versions of Dovecot */
667 if (type == TNY_FOLDER_TYPE_NORMAL ||
668 type == TNY_FOLDER_TYPE_UNKNOWN) {
669 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
673 /* note: we cannot reliably get the counts from the
674 * tree model, we need to use explicit calls on
675 * tny_folder for some reason. Select the number to
676 * show: the unread or unsent messages. in case of
677 * outbox/drafts, show all */
678 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
679 (type == TNY_FOLDER_TYPE_OUTBOX) ||
680 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
681 number = tny_folder_get_all_count (TNY_FOLDER(instance));
684 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
688 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
689 item_name = g_strdup (fname);
696 /* Use bold font style if there are unread or unset messages */
698 if (priv->show_message_count) {
699 item_name = g_strdup_printf ("%s (%d)", fname, number);
701 item_name = g_strdup (fname);
705 item_name = g_strdup (fname);
710 } else if (TNY_IS_ACCOUNT (instance)) {
711 /* If it's a server account */
712 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
713 item_name = g_strdup (priv->local_account_name);
715 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
716 /* fname is only correct when the items are first
717 * added to the model, not when the account is
718 * changed later, so get the name from the account
720 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
723 item_name = g_strdup (fname);
729 item_name = g_strdup ("unknown");
731 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
732 gboolean multiaccount;
734 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
735 /* Convert item_name to markup */
736 format_compact_style (&item_name, instance, priv->mailbox,
738 multiaccount, &use_markup);
741 if (item_name && item_weight) {
742 /* Set the name in the treeview cell: */
743 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 && priv->active_color) {
744 g_object_set (rendobj,
747 "foreground-set", TRUE,
748 "foreground-gdk", priv->active_color,
751 g_object_set (rendobj,
753 "foreground-set", FALSE,
755 "weight", item_weight,
759 /* Notify display name observers */
760 /* TODO: What listens for this signal, and how can it use only the new name? */
761 if (((GObject *) priv->cur_folder_store) == instance) {
762 g_signal_emit (G_OBJECT(self),
763 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
770 /* If it is a Memory card account, make sure that we have the correct name.
771 * This function will be trigerred again when the name has been retrieved: */
772 if (TNY_IS_STORE_ACCOUNT (instance) &&
773 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
775 /* Get the account name asynchronously: */
776 GetMmcAccountNameData *callback_data =
777 g_slice_new0(GetMmcAccountNameData);
778 callback_data->self = self;
780 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
782 callback_data->previous_name = g_strdup (name);
784 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
785 on_get_mmc_account_name, callback_data);
789 g_object_unref (G_OBJECT (instance));
795 messages_cell_data (GtkTreeViewColumn *column,
796 GtkCellRenderer *renderer,
797 GtkTreeModel *tree_model,
801 ModestFolderView *self;
802 ModestFolderViewPrivate *priv;
803 GObject *rendobj = (GObject *) renderer;
804 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
805 GObject *instance = NULL;
806 gchar *item_name = NULL;
808 gtk_tree_model_get (tree_model, iter,
810 INSTANCE_COLUMN, &instance,
815 self = MODEST_FOLDER_VIEW (data);
816 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
819 if (type != TNY_FOLDER_TYPE_ROOT) {
824 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
825 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
828 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
830 /* Sometimes an special folder is reported by the server as
831 NORMAL, like some versions of Dovecot */
832 if (type == TNY_FOLDER_TYPE_NORMAL ||
833 type == TNY_FOLDER_TYPE_UNKNOWN) {
834 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
838 /* note: we cannot reliably get the counts from the tree model, we need
839 * to use explicit calls on tny_folder for some reason.
841 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
842 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
843 (type == TNY_FOLDER_TYPE_OUTBOX) ||
844 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
845 number = tny_folder_get_all_count (TNY_FOLDER(instance));
848 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
852 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
854 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_va_new_messages"),
856 } else if (number == 1) {
857 item_name = g_strdup_printf (drafts?_("mcen_ti_message"):_("mcen_va_new_message"),
865 item_name = g_strdup ("");
868 /* Set the name in the treeview cell: */
869 g_object_set (rendobj,"text", item_name, NULL);
877 g_object_unref (G_OBJECT (instance));
883 GdkPixbuf *pixbuf_open;
884 GdkPixbuf *pixbuf_close;
888 static inline GdkPixbuf *
889 get_composite_pixbuf (const gchar *icon_name,
891 GdkPixbuf *base_pixbuf)
893 GdkPixbuf *emblem, *retval = NULL;
895 emblem = modest_platform_get_icon (icon_name, size);
897 retval = gdk_pixbuf_copy (base_pixbuf);
898 gdk_pixbuf_composite (emblem, retval, 0, 0,
899 MIN (gdk_pixbuf_get_width (emblem),
900 gdk_pixbuf_get_width (retval)),
901 MIN (gdk_pixbuf_get_height (emblem),
902 gdk_pixbuf_get_height (retval)),
903 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
904 g_object_unref (emblem);
909 static inline ThreePixbufs *
910 get_composite_icons (const gchar *icon_code,
912 GdkPixbuf **pixbuf_open,
913 GdkPixbuf **pixbuf_close)
915 ThreePixbufs *retval;
918 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
921 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
926 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
930 retval = g_slice_new0 (ThreePixbufs);
932 retval->pixbuf = g_object_ref (*pixbuf);
934 retval->pixbuf_open = g_object_ref (*pixbuf_open);
936 retval->pixbuf_close = g_object_ref (*pixbuf_close);
941 static inline ThreePixbufs *
942 get_account_protocol_pixbufs (ModestFolderView *folder_view,
943 ModestProtocolType protocol_type,
946 ModestProtocol *protocol;
947 const GdkPixbuf *pixbuf = NULL;
948 ModestFolderViewPrivate *priv;
950 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
952 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
955 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
956 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
957 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
958 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
959 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
960 object, FOLDER_ICON_SIZE);
964 ThreePixbufs *retval;
965 retval = g_slice_new0 (ThreePixbufs);
966 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
967 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
968 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
975 static inline ThreePixbufs*
976 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
978 TnyAccount *account = NULL;
979 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
980 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
981 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
982 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
983 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
985 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
986 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
987 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
988 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
989 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
991 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
992 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
993 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
994 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
995 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
997 ThreePixbufs *retval = NULL;
999 if (TNY_IS_ACCOUNT (instance)) {
1000 account = g_object_ref (instance);
1001 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1002 account = tny_folder_get_account (TNY_FOLDER (instance));
1006 ModestProtocolType account_store_protocol;
1008 account_store_protocol = modest_tny_account_get_protocol_type (account);
1009 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1010 g_object_unref (account);
1016 /* Sometimes an special folder is reported by the server as
1017 NORMAL, like some versions of Dovecot */
1018 if (type == TNY_FOLDER_TYPE_NORMAL ||
1019 type == TNY_FOLDER_TYPE_UNKNOWN) {
1020 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1023 /* It's not enough with check the folder type. We need to
1024 ensure that we're not giving a special folder icon to a
1025 normal folder with the same name than a special folder */
1026 if (TNY_IS_FOLDER (instance) &&
1027 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1028 type = TNY_FOLDER_TYPE_NORMAL;
1030 /* Remote folders should not be treated as special folders */
1031 if (TNY_IS_FOLDER_STORE (instance) &&
1032 !TNY_IS_ACCOUNT (instance) &&
1033 type != TNY_FOLDER_TYPE_INBOX &&
1034 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1035 #ifdef MODEST_TOOLKIT_HILDON2
1036 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1039 &anorm_pixbuf_close);
1041 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1043 &normal_pixbuf_open,
1044 &normal_pixbuf_close);
1050 case TNY_FOLDER_TYPE_INVALID:
1051 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1054 case TNY_FOLDER_TYPE_ROOT:
1055 if (TNY_IS_ACCOUNT (instance)) {
1057 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1058 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1061 &avirt_pixbuf_close);
1063 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1065 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1066 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1069 &ammc_pixbuf_close);
1071 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1074 &anorm_pixbuf_close);
1079 case TNY_FOLDER_TYPE_INBOX:
1080 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1083 &inbox_pixbuf_close);
1085 case TNY_FOLDER_TYPE_OUTBOX:
1086 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1088 &outbox_pixbuf_open,
1089 &outbox_pixbuf_close);
1091 case TNY_FOLDER_TYPE_JUNK:
1092 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1095 &junk_pixbuf_close);
1097 case TNY_FOLDER_TYPE_SENT:
1098 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1101 &sent_pixbuf_close);
1103 case TNY_FOLDER_TYPE_TRASH:
1104 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1107 &trash_pixbuf_close);
1109 case TNY_FOLDER_TYPE_DRAFTS:
1110 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1113 &draft_pixbuf_close);
1115 case TNY_FOLDER_TYPE_ARCHIVE:
1116 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1121 case TNY_FOLDER_TYPE_NORMAL:
1123 /* Memory card folders could have an special icon */
1124 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1125 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1130 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1132 &normal_pixbuf_open,
1133 &normal_pixbuf_close);
1142 free_pixbufs (ThreePixbufs *pixbufs)
1144 if (pixbufs->pixbuf)
1145 g_object_unref (pixbufs->pixbuf);
1146 if (pixbufs->pixbuf_open)
1147 g_object_unref (pixbufs->pixbuf_open);
1148 if (pixbufs->pixbuf_close)
1149 g_object_unref (pixbufs->pixbuf_close);
1150 g_slice_free (ThreePixbufs, pixbufs);
1154 icon_cell_data (GtkTreeViewColumn *column,
1155 GtkCellRenderer *renderer,
1156 GtkTreeModel *tree_model,
1160 GObject *rendobj = NULL, *instance = NULL;
1161 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1162 gboolean has_children;
1163 ThreePixbufs *pixbufs;
1164 ModestFolderView *folder_view = (ModestFolderView *) data;
1166 rendobj = (GObject *) renderer;
1168 gtk_tree_model_get (tree_model, iter,
1170 INSTANCE_COLUMN, &instance,
1176 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1177 pixbufs = get_folder_icons (folder_view, type, instance);
1178 g_object_unref (instance);
1181 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1184 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1185 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1188 free_pixbufs (pixbufs);
1192 add_columns (GtkWidget *treeview)
1194 GtkTreeViewColumn *column;
1195 GtkCellRenderer *renderer;
1196 GtkTreeSelection *sel;
1197 ModestFolderViewPrivate *priv;
1199 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1202 column = gtk_tree_view_column_new ();
1204 /* Set icon and text render function */
1205 renderer = gtk_cell_renderer_pixbuf_new();
1206 #ifdef MODEST_TOOLKIT_HILDON2
1207 g_object_set (renderer,
1208 "xpad", MODEST_MARGIN_DEFAULT,
1209 "ypad", MODEST_MARGIN_DEFAULT,
1212 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1213 gtk_tree_view_column_set_cell_data_func(column, renderer,
1214 icon_cell_data, treeview, NULL);
1216 renderer = gtk_cell_renderer_text_new();
1217 g_object_set (renderer,
1218 #ifdef MODEST_TOOLKIT_HILDON2
1219 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1220 "ypad", MODEST_MARGIN_DEFAULT,
1221 "xpad", MODEST_MARGIN_DEFAULT,
1223 "ellipsize", PANGO_ELLIPSIZE_END,
1225 "ellipsize-set", TRUE, NULL);
1226 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1227 gtk_tree_view_column_set_cell_data_func(column, renderer,
1228 text_cell_data, treeview, NULL);
1230 priv->messages_renderer = gtk_cell_renderer_text_new ();
1231 g_object_set (priv->messages_renderer,
1232 #ifdef MODEST_TOOLKIT_HILDON2
1234 "ypad", MODEST_MARGIN_DEFAULT,
1235 "xpad", MODEST_MARGIN_DOUBLE,
1237 "scale", PANGO_SCALE_X_SMALL,
1240 "alignment", PANGO_ALIGN_RIGHT,
1244 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1245 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1246 messages_cell_data, treeview, NULL);
1248 /* Set selection mode */
1249 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1250 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1252 /* Set treeview appearance */
1253 gtk_tree_view_column_set_spacing (column, 2);
1254 gtk_tree_view_column_set_resizable (column, TRUE);
1255 gtk_tree_view_column_set_fixed_width (column, TRUE);
1256 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1257 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1260 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1264 modest_folder_view_init (ModestFolderView *obj)
1266 ModestFolderViewPrivate *priv;
1269 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1271 priv->timer_expander = 0;
1272 priv->account_store = NULL;
1274 priv->do_refresh = TRUE;
1275 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1276 priv->cur_folder_store = NULL;
1277 priv->visible_account_id = NULL;
1278 priv->mailbox = NULL;
1279 priv->folder_to_select = NULL;
1280 priv->outbox_deleted_handler = 0;
1281 priv->reexpand = TRUE;
1282 priv->signal_handlers = 0;
1284 /* Initialize the local account name */
1285 conf = modest_runtime_get_conf();
1286 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1288 /* Init email clipboard */
1289 priv->clipboard = modest_runtime_get_email_clipboard ();
1290 priv->hidding_ids = NULL;
1291 priv->n_selected = 0;
1292 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1293 priv->reselect = FALSE;
1294 priv->show_non_move = TRUE;
1295 priv->list_to_move = NULL;
1296 priv->show_message_count = TRUE;
1298 /* Build treeview */
1299 add_columns (GTK_WIDGET (obj));
1301 /* Setup drag and drop */
1302 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1304 /* Connect signals */
1305 g_signal_connect (G_OBJECT (obj),
1307 G_CALLBACK (on_key_pressed), NULL);
1309 priv->display_name_changed_signal =
1310 g_signal_connect (modest_runtime_get_account_mgr (),
1311 "display_name_changed",
1312 G_CALLBACK (on_display_name_changed),
1316 * Track changes in the local account name (in the device it
1317 * will be the device name)
1319 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1321 G_CALLBACK(on_configuration_key_changed),
1324 priv->active_color = NULL;
1327 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1333 tny_account_store_view_init (gpointer g, gpointer iface_data)
1335 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1337 klass->set_account_store = modest_folder_view_set_account_store;
1341 modest_folder_view_dispose (GObject *obj)
1343 static gboolean disposed = FALSE;
1344 ModestFolderViewPrivate *priv;
1349 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1351 #ifdef MODEST_TOOLKIT_HILDON2
1352 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1355 /* Free external references */
1356 if (priv->account_store) {
1357 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1358 priv->account_inserted_signal);
1359 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1360 priv->account_removed_signal);
1361 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1362 priv->account_changed_signal);
1363 g_object_unref (G_OBJECT(priv->account_store));
1364 priv->account_store = NULL;
1368 g_object_unref (G_OBJECT (priv->query));
1372 if (priv->folder_to_select) {
1373 g_object_unref (G_OBJECT(priv->folder_to_select));
1374 priv->folder_to_select = NULL;
1377 if (priv->cur_folder_store) {
1378 g_object_unref (priv->cur_folder_store);
1379 priv->cur_folder_store = NULL;
1382 if (priv->list_to_move) {
1383 g_object_unref (priv->list_to_move);
1384 priv->list_to_move = NULL;
1389 modest_folder_view_finalize (GObject *obj)
1391 ModestFolderViewPrivate *priv;
1392 GtkTreeSelection *sel;
1393 TnyAccount *local_account;
1395 g_return_if_fail (obj);
1397 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1399 if (priv->active_color) {
1400 gdk_color_free (priv->active_color);
1401 priv->active_color = NULL;
1404 if (priv->timer_expander != 0) {
1405 g_source_remove (priv->timer_expander);
1406 priv->timer_expander = 0;
1409 local_account = (TnyAccount *)
1410 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1411 if (local_account) {
1412 if (g_signal_handler_is_connected (local_account,
1413 priv->outbox_deleted_handler))
1414 g_signal_handler_disconnect (local_account,
1415 priv->outbox_deleted_handler);
1416 g_object_unref (local_account);
1419 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1420 priv->display_name_changed_signal)) {
1421 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1422 priv->display_name_changed_signal);
1423 priv->display_name_changed_signal = 0;
1426 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1428 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1430 g_free (priv->local_account_name);
1431 g_free (priv->visible_account_id);
1432 g_free (priv->mailbox);
1434 if (priv->conf_key_signal) {
1435 g_signal_handler_disconnect (modest_runtime_get_conf (),
1436 priv->conf_key_signal);
1437 priv->conf_key_signal = 0;
1440 /* Clear hidding array created by cut operation */
1441 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1443 G_OBJECT_CLASS(parent_class)->finalize (obj);
1448 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1450 ModestFolderViewPrivate *priv;
1453 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1454 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1456 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1457 device = tny_account_store_get_device (account_store);
1459 if (G_UNLIKELY (priv->account_store)) {
1461 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1462 priv->account_inserted_signal))
1463 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1464 priv->account_inserted_signal);
1465 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1466 priv->account_removed_signal))
1467 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1468 priv->account_removed_signal);
1469 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1470 priv->account_changed_signal))
1471 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1472 priv->account_changed_signal);
1473 g_object_unref (G_OBJECT (priv->account_store));
1476 priv->account_store = g_object_ref (G_OBJECT (account_store));
1478 priv->account_removed_signal =
1479 g_signal_connect (G_OBJECT(account_store), "account_removed",
1480 G_CALLBACK (on_account_removed), self);
1482 priv->account_inserted_signal =
1483 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1484 G_CALLBACK (on_account_inserted), self);
1486 priv->account_changed_signal =
1487 g_signal_connect (G_OBJECT(account_store), "account_changed",
1488 G_CALLBACK (on_account_changed), self);
1490 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1491 priv->reselect = FALSE;
1492 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1494 g_object_unref (G_OBJECT (device));
1498 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1501 ModestFolderView *self;
1502 GtkTreeModel *model, *filter_model;
1505 self = MODEST_FOLDER_VIEW (user_data);
1507 if (!get_inner_models (self, &filter_model, NULL, &model))
1510 /* Remove outbox from model */
1511 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1512 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1513 g_object_unref (outbox);
1516 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1520 on_account_inserted (TnyAccountStore *account_store,
1521 TnyAccount *account,
1524 ModestFolderViewPrivate *priv;
1525 GtkTreeModel *model, *filter_model;
1527 /* Ignore transport account insertions, we're not showing them
1528 in the folder view */
1529 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1532 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1535 /* If we're adding a new account, and there is no previous
1536 one, we need to select the visible server account */
1537 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1538 !priv->visible_account_id)
1539 modest_widget_memory_restore (modest_runtime_get_conf(),
1540 G_OBJECT (user_data),
1541 MODEST_CONF_FOLDER_VIEW_KEY);
1545 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1546 &filter_model, NULL, &model))
1549 /* Insert the account in the model */
1550 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1552 /* When the model is a list store (plain representation) the
1553 outbox is not a child of any account so we have to manually
1554 delete it because removing the local folders account won't
1555 delete it (because tny_folder_get_account() is not defined
1556 for a merge folder */
1557 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1558 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1560 priv->outbox_deleted_handler =
1561 g_signal_connect (account,
1563 G_CALLBACK (on_outbox_deleted_cb),
1567 /* Refilter the model */
1568 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1573 same_account_selected (ModestFolderView *self,
1574 TnyAccount *account)
1576 ModestFolderViewPrivate *priv;
1577 gboolean same_account = FALSE;
1579 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1581 if (priv->cur_folder_store) {
1582 TnyAccount *selected_folder_account = NULL;
1584 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1585 selected_folder_account =
1586 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1588 selected_folder_account =
1589 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1592 if (selected_folder_account == account)
1593 same_account = TRUE;
1595 g_object_unref (selected_folder_account);
1597 return same_account;
1602 * Selects the first inbox or the local account in an idle
1605 on_idle_select_first_inbox_or_local (gpointer user_data)
1607 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1609 gdk_threads_enter ();
1610 modest_folder_view_select_first_inbox_or_local (self);
1611 gdk_threads_leave ();
1617 on_account_changed (TnyAccountStore *account_store,
1618 TnyAccount *tny_account,
1621 ModestFolderView *self;
1622 ModestFolderViewPrivate *priv;
1623 GtkTreeModel *model, *filter_model;
1624 GtkTreeSelection *sel;
1625 gboolean same_account;
1627 /* Ignore transport account insertions, we're not showing them
1628 in the folder view */
1629 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1632 self = MODEST_FOLDER_VIEW (user_data);
1633 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1635 /* Get the inner model */
1636 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1637 &filter_model, NULL, &model))
1640 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1642 /* Invalidate the cur_folder_store only if the selected folder
1643 belongs to the account that is being removed */
1644 same_account = same_account_selected (self, tny_account);
1646 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1647 gtk_tree_selection_unselect_all (sel);
1650 /* Remove the account from the model */
1651 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1653 /* Insert the account in the model */
1654 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1656 /* Refilter the model */
1657 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1659 /* Select the first INBOX if the currently selected folder
1660 belongs to the account that is being deleted */
1661 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1662 g_idle_add (on_idle_select_first_inbox_or_local, self);
1666 on_account_removed (TnyAccountStore *account_store,
1667 TnyAccount *account,
1670 ModestFolderView *self = NULL;
1671 ModestFolderViewPrivate *priv;
1672 GtkTreeModel *model, *filter_model;
1673 GtkTreeSelection *sel = NULL;
1674 gboolean same_account = FALSE;
1676 /* Ignore transport account removals, we're not showing them
1677 in the folder view */
1678 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1681 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1682 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1686 self = MODEST_FOLDER_VIEW (user_data);
1687 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1689 /* Invalidate the cur_folder_store only if the selected folder
1690 belongs to the account that is being removed */
1691 same_account = same_account_selected (self, account);
1693 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1694 gtk_tree_selection_unselect_all (sel);
1697 /* Invalidate row to select only if the folder to select
1698 belongs to the account that is being removed*/
1699 if (priv->folder_to_select) {
1700 TnyAccount *folder_to_select_account = NULL;
1702 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1703 if (folder_to_select_account == account) {
1704 modest_folder_view_disable_next_folder_selection (self);
1705 g_object_unref (priv->folder_to_select);
1706 priv->folder_to_select = NULL;
1708 g_object_unref (folder_to_select_account);
1711 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1712 &filter_model, NULL, &model))
1715 /* Disconnect the signal handler */
1716 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1717 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1718 if (g_signal_handler_is_connected (account,
1719 priv->outbox_deleted_handler))
1720 g_signal_handler_disconnect (account,
1721 priv->outbox_deleted_handler);
1724 /* Remove the account from the model */
1725 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1727 /* If the removed account is the currently viewed one then
1728 clear the configuration value. The new visible account will be the default account */
1729 if (priv->visible_account_id &&
1730 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1732 /* Clear the current visible account_id */
1733 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1734 modest_folder_view_set_mailbox (self, NULL);
1736 /* Call the restore method, this will set the new visible account */
1737 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1738 MODEST_CONF_FOLDER_VIEW_KEY);
1741 /* Refilter the model */
1742 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1744 /* Select the first INBOX if the currently selected folder
1745 belongs to the account that is being deleted */
1747 g_idle_add (on_idle_select_first_inbox_or_local, self);
1751 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1753 GtkTreeViewColumn *col;
1755 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1757 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1759 g_printerr ("modest: failed get column for title\n");
1763 gtk_tree_view_column_set_title (col, title);
1764 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1769 modest_folder_view_on_map (ModestFolderView *self,
1770 GdkEventExpose *event,
1773 ModestFolderViewPrivate *priv;
1775 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1777 /* This won't happen often */
1778 if (G_UNLIKELY (priv->reselect)) {
1779 /* Select the first inbox or the local account if not found */
1781 /* TODO: this could cause a lock at startup, so we
1782 comment it for the moment. We know that this will
1783 be a bug, because the INBOX is not selected, but we
1784 need to rewrite some parts of Modest to avoid the
1785 deathlock situation */
1786 /* TODO: check if this is still the case */
1787 priv->reselect = FALSE;
1788 modest_folder_view_select_first_inbox_or_local (self);
1789 /* Notify the display name observers */
1790 g_signal_emit (G_OBJECT(self),
1791 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1795 if (priv->reexpand) {
1796 expand_root_items (self);
1797 priv->reexpand = FALSE;
1804 modest_folder_view_new (TnyFolderStoreQuery *query)
1806 return modest_folder_view_new_full (query, TRUE);
1810 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1813 ModestFolderViewPrivate *priv;
1814 GtkTreeSelection *sel;
1816 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1817 #ifdef MODEST_TOOLKIT_HILDON2
1818 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1821 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1824 priv->query = g_object_ref (query);
1826 priv->do_refresh = do_refresh;
1828 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1829 priv->changed_signal = g_signal_connect (sel, "changed",
1830 G_CALLBACK (on_selection_changed), self);
1832 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1834 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1836 return GTK_WIDGET(self);
1839 /* this feels dirty; any other way to expand all the root items? */
1841 expand_root_items (ModestFolderView *self)
1844 GtkTreeModel *model;
1847 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1848 path = gtk_tree_path_new_first ();
1850 /* all folders should have child items, so.. */
1852 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1853 gtk_tree_path_next (path);
1854 } while (gtk_tree_model_get_iter (model, &iter, path));
1856 gtk_tree_path_free (path);
1860 is_parent_of (TnyFolder *a, TnyFolder *b)
1863 gboolean retval = FALSE;
1865 a_id = tny_folder_get_id (a);
1867 gchar *string_to_match;
1870 string_to_match = g_strconcat (a_id, "/", NULL);
1871 b_id = tny_folder_get_id (b);
1872 retval = g_str_has_prefix (b_id, string_to_match);
1873 g_free (string_to_match);
1879 typedef struct _ForeachFolderInfo {
1882 } ForeachFolderInfo;
1885 foreach_folder_with_id (GtkTreeModel *model,
1890 ForeachFolderInfo *info;
1893 info = (ForeachFolderInfo *) data;
1894 gtk_tree_model_get (model, iter,
1895 INSTANCE_COLUMN, &instance,
1898 if (TNY_IS_FOLDER (instance)) {
1901 id = tny_folder_get_id (TNY_FOLDER (instance));
1903 collate = g_utf8_collate_key (id, -1);
1904 info->found = !strcmp (info->needle, collate);
1910 g_object_unref (instance);
1918 has_folder_with_id (ModestFolderView *self, const gchar *id)
1920 GtkTreeModel *model;
1921 ForeachFolderInfo info = {NULL, FALSE};
1923 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1924 info.needle = g_utf8_collate_key (id, -1);
1926 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1927 g_free (info.needle);
1933 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1936 gboolean retval = FALSE;
1938 a_id = tny_folder_get_id (a);
1941 b_id = tny_folder_get_id (b);
1944 const gchar *last_bar;
1945 gchar *string_to_match;
1946 last_bar = g_strrstr (b_id, "/");
1951 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1952 retval = has_folder_with_id (self, string_to_match);
1953 g_free (string_to_match);
1961 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1963 ModestFolderViewPrivate *priv;
1964 TnyIterator *iterator;
1965 gboolean retval = TRUE;
1967 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1968 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1970 for (iterator = tny_list_create_iterator (priv->list_to_move);
1971 retval && !tny_iterator_is_done (iterator);
1972 tny_iterator_next (iterator)) {
1974 instance = tny_iterator_get_current (iterator);
1975 if (instance == (GObject *) folder) {
1977 } else if (TNY_IS_FOLDER (instance)) {
1978 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1980 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1983 g_object_unref (instance);
1985 g_object_unref (iterator);
1992 * We use this function to implement the
1993 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1994 * account in this case, and the local folders.
1997 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1999 ModestFolderViewPrivate *priv;
2000 gboolean retval = TRUE;
2001 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2002 GObject *instance = NULL;
2003 const gchar *id = NULL;
2005 gboolean found = FALSE;
2006 gboolean cleared = FALSE;
2007 ModestTnyFolderRules rules = 0;
2010 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2011 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2013 gtk_tree_model_get (model, iter,
2014 NAME_COLUMN, &fname,
2016 INSTANCE_COLUMN, &instance,
2019 /* Do not show if there is no instance, this could indeed
2020 happen when the model is being modified while it's being
2021 drawn. This could occur for example when moving folders
2028 if (TNY_IS_ACCOUNT (instance)) {
2029 TnyAccount *acc = TNY_ACCOUNT (instance);
2030 const gchar *account_id = tny_account_get_id (acc);
2032 /* If it isn't a special folder,
2033 * don't show it unless it is the visible account: */
2034 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2035 !modest_tny_account_is_virtual_local_folders (acc) &&
2036 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2038 /* Show only the visible account id */
2039 if (priv->visible_account_id) {
2040 if (strcmp (account_id, priv->visible_account_id))
2047 /* Never show these to the user. They are merged into one folder
2048 * in the local-folders account instead: */
2049 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2052 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2053 /* Only show special folders for current account if needed */
2054 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2055 TnyAccount *account;
2057 account = tny_folder_get_account (TNY_FOLDER (instance));
2059 if (TNY_IS_ACCOUNT (account)) {
2060 const gchar *account_id = tny_account_get_id (account);
2062 if (!modest_tny_account_is_virtual_local_folders (account) &&
2063 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2064 /* Show only the visible account id */
2065 if (priv->visible_account_id) {
2066 if (strcmp (account_id, priv->visible_account_id)) {
2068 } else if (priv->mailbox) {
2069 /* Filter mailboxes */
2070 if (!g_str_has_prefix (fname, priv->mailbox)) {
2072 } else if (!strcmp (fname, priv->mailbox)) {
2073 /* Hide mailbox parent */
2079 g_object_unref (account);
2086 /* Check hiding (if necessary) */
2087 cleared = modest_email_clipboard_cleared (priv->clipboard);
2088 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2089 id = tny_folder_get_id (TNY_FOLDER(instance));
2090 if (priv->hidding_ids != NULL)
2091 for (i=0; i < priv->n_selected && !found; i++)
2092 if (priv->hidding_ids[i] != NULL && id != NULL)
2093 found = (!strcmp (priv->hidding_ids[i], id));
2098 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2099 folder as no message can be move there according to UI specs */
2100 if (retval && !priv->show_non_move) {
2101 if (priv->list_to_move &&
2102 tny_list_get_length (priv->list_to_move) > 0 &&
2103 TNY_IS_FOLDER (instance)) {
2104 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2106 if (retval && TNY_IS_FOLDER (instance) &&
2107 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2109 case TNY_FOLDER_TYPE_OUTBOX:
2110 case TNY_FOLDER_TYPE_SENT:
2111 case TNY_FOLDER_TYPE_DRAFTS:
2114 case TNY_FOLDER_TYPE_UNKNOWN:
2115 case TNY_FOLDER_TYPE_NORMAL:
2116 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2117 if (type == TNY_FOLDER_TYPE_INVALID)
2118 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2120 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2121 type == TNY_FOLDER_TYPE_SENT
2122 || type == TNY_FOLDER_TYPE_DRAFTS)
2129 if (retval && TNY_IS_ACCOUNT (instance) &&
2130 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2131 ModestProtocolType protocol_type;
2133 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2134 retval = !modest_protocol_registry_protocol_type_has_tag
2135 (modest_runtime_get_protocol_registry (),
2137 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2141 /* apply special filters */
2142 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2143 if (TNY_IS_ACCOUNT (instance))
2147 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2148 if (TNY_IS_FOLDER (instance))
2152 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2153 if (TNY_IS_ACCOUNT (instance)) {
2154 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2156 } else if (TNY_IS_FOLDER (instance)) {
2157 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2162 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2163 if (TNY_IS_ACCOUNT (instance)) {
2164 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2166 } else if (TNY_IS_FOLDER (instance)) {
2167 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2172 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2173 /* A mailbox is a fake folder with an @ in the middle of the name */
2174 if (!TNY_IS_FOLDER (instance) ||
2175 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2178 const gchar *folder_name;
2179 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2180 if (!folder_name || strchr (folder_name, '@') == NULL)
2186 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2187 if (TNY_IS_FOLDER (instance)) {
2188 /* Check folder rules */
2189 ModestTnyFolderRules rules;
2191 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2192 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2193 } else if (TNY_IS_ACCOUNT (instance)) {
2194 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2202 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2203 if (TNY_IS_FOLDER (instance)) {
2204 TnyFolderType guess_type;
2206 if (TNY_FOLDER_TYPE_NORMAL) {
2207 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2213 case TNY_FOLDER_TYPE_OUTBOX:
2214 case TNY_FOLDER_TYPE_SENT:
2215 case TNY_FOLDER_TYPE_DRAFTS:
2216 case TNY_FOLDER_TYPE_ARCHIVE:
2217 case TNY_FOLDER_TYPE_INBOX:
2220 case TNY_FOLDER_TYPE_UNKNOWN:
2221 case TNY_FOLDER_TYPE_NORMAL:
2227 } else if (TNY_IS_ACCOUNT (instance)) {
2232 if (retval && TNY_IS_FOLDER (instance)) {
2233 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2236 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2237 if (TNY_IS_FOLDER (instance)) {
2238 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2239 } else if (TNY_IS_ACCOUNT (instance)) {
2244 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2245 if (TNY_IS_FOLDER (instance)) {
2246 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2247 } else if (TNY_IS_ACCOUNT (instance)) {
2252 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2253 if (TNY_IS_FOLDER (instance)) {
2254 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2255 } else if (TNY_IS_ACCOUNT (instance)) {
2261 g_object_unref (instance);
2269 modest_folder_view_update_model (ModestFolderView *self,
2270 TnyAccountStore *account_store)
2272 ModestFolderViewPrivate *priv;
2273 GtkTreeModel *model;
2274 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2276 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2277 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2280 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2282 /* Notify that there is no folder selected */
2283 g_signal_emit (G_OBJECT(self),
2284 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2286 if (priv->cur_folder_store) {
2287 g_object_unref (priv->cur_folder_store);
2288 priv->cur_folder_store = NULL;
2291 /* FIXME: the local accounts are not shown when the query
2292 selects only the subscribed folders */
2293 #ifdef MODEST_TOOLKIT_HILDON2
2294 TnyGtkFolderListStoreFlags flags;
2295 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2296 if (!priv->do_refresh)
2297 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2298 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2300 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2301 MODEST_FOLDER_PATH_SEPARATOR);
2303 model = tny_gtk_folder_store_tree_model_new (NULL);
2306 /* When the model is a list store (plain representation) the
2307 outbox is not a child of any account so we have to manually
2308 delete it because removing the local folders account won't
2309 delete it (because tny_folder_get_account() is not defined
2310 for a merge folder */
2311 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2312 TnyAccount *account;
2313 ModestTnyAccountStore *acc_store;
2315 acc_store = modest_runtime_get_account_store ();
2316 account = modest_tny_account_store_get_local_folders_account (acc_store);
2318 if (g_signal_handler_is_connected (account,
2319 priv->outbox_deleted_handler))
2320 g_signal_handler_disconnect (account,
2321 priv->outbox_deleted_handler);
2323 priv->outbox_deleted_handler =
2324 g_signal_connect (account,
2326 G_CALLBACK (on_outbox_deleted_cb),
2328 g_object_unref (account);
2331 /* Get the accounts: */
2332 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2334 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2336 sortable = gtk_tree_model_sort_new_with_model (model);
2337 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2339 GTK_SORT_ASCENDING);
2340 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2342 cmp_rows, NULL, NULL);
2344 /* Create filter model */
2345 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2346 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2351 if (priv->signal_handlers > 0) {
2352 GtkTreeModel *old_tny_model;
2354 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2355 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2356 G_OBJECT (old_tny_model),
2357 "activity-changed");
2362 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2363 #ifndef MODEST_TOOLKIT_HILDON2
2364 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2365 (GCallback) on_row_inserted_maybe_select_folder, self);
2368 #ifdef MODEST_TOOLKIT_HILDON2
2369 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2372 G_CALLBACK (on_activity_changed),
2376 g_object_unref (model);
2377 g_object_unref (filter_model);
2378 g_object_unref (sortable);
2380 /* Force a reselection of the INBOX next time the widget is shown */
2381 priv->reselect = TRUE;
2388 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2390 GtkTreeModel *model = NULL;
2391 TnyFolderStore *folder = NULL;
2393 ModestFolderView *tree_view = NULL;
2394 ModestFolderViewPrivate *priv = NULL;
2395 gboolean selected = FALSE;
2397 g_return_if_fail (sel);
2398 g_return_if_fail (user_data);
2400 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2402 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2404 tree_view = MODEST_FOLDER_VIEW (user_data);
2407 gtk_tree_model_get (model, &iter,
2408 INSTANCE_COLUMN, &folder,
2411 /* If the folder is the same do not notify */
2412 if (folder && priv->cur_folder_store == folder) {
2413 g_object_unref (folder);
2418 /* Current folder was unselected */
2419 if (priv->cur_folder_store) {
2420 /* We must do this firstly because a libtinymail-camel
2421 implementation detail. If we issue the signal
2422 before doing the sync_async, then that signal could
2423 cause (and it actually does it) a free of the
2424 summary of the folder (because the main window will
2425 clear the headers view */
2426 #ifndef MODEST_TOOLKIT_HILDON2
2427 if (TNY_IS_FOLDER(priv->cur_folder_store))
2428 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2429 FALSE, NULL, NULL, NULL);
2432 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2433 priv->cur_folder_store, FALSE);
2435 g_object_unref (priv->cur_folder_store);
2436 priv->cur_folder_store = NULL;
2439 /* New current references */
2440 priv->cur_folder_store = folder;
2442 /* New folder has been selected. Do not notify if there is
2443 nothing new selected */
2445 g_signal_emit (G_OBJECT(tree_view),
2446 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2447 0, priv->cur_folder_store, TRUE);
2452 on_row_activated (GtkTreeView *treeview,
2453 GtkTreePath *treepath,
2454 GtkTreeViewColumn *column,
2457 GtkTreeModel *model = NULL;
2458 TnyFolderStore *folder = NULL;
2460 ModestFolderView *self = NULL;
2461 ModestFolderViewPrivate *priv = NULL;
2463 g_return_if_fail (treeview);
2464 g_return_if_fail (user_data);
2466 self = MODEST_FOLDER_VIEW (user_data);
2467 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2469 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2471 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2474 gtk_tree_model_get (model, &iter,
2475 INSTANCE_COLUMN, &folder,
2478 g_signal_emit (G_OBJECT(self),
2479 signals[FOLDER_ACTIVATED_SIGNAL],
2482 #ifdef MODEST_TOOLKIT_HILDON2
2483 HildonUIMode ui_mode;
2484 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2485 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2486 if (priv->cur_folder_store)
2487 g_object_unref (priv->cur_folder_store);
2488 priv->cur_folder_store = g_object_ref (folder);
2492 g_object_unref (folder);
2496 modest_folder_view_get_selected (ModestFolderView *self)
2498 ModestFolderViewPrivate *priv;
2500 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2502 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2503 if (priv->cur_folder_store)
2504 g_object_ref (priv->cur_folder_store);
2506 return priv->cur_folder_store;
2510 get_cmp_rows_type_pos (GObject *folder)
2512 /* Remote accounts -> Local account -> MMC account .*/
2515 if (TNY_IS_ACCOUNT (folder) &&
2516 modest_tny_account_is_virtual_local_folders (
2517 TNY_ACCOUNT (folder))) {
2519 } else if (TNY_IS_ACCOUNT (folder)) {
2520 TnyAccount *account = TNY_ACCOUNT (folder);
2521 const gchar *account_id = tny_account_get_id (account);
2522 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2528 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2529 return -1; /* Should never happen */
2534 inbox_is_special (TnyFolderStore *folder_store)
2536 gboolean is_special = TRUE;
2538 if (TNY_IS_FOLDER (folder_store)) {
2542 gchar *last_inbox_bar;
2544 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2545 downcase = g_utf8_strdown (id, -1);
2546 last_bar = g_strrstr (downcase, "/");
2548 last_inbox_bar = g_strrstr (downcase, "inbox/");
2549 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2560 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2562 TnyAccount *account;
2563 gboolean is_special;
2564 /* Inbox, Outbox, Drafts, Sent, User */
2567 if (!TNY_IS_FOLDER (folder_store))
2570 case TNY_FOLDER_TYPE_INBOX:
2572 account = tny_folder_get_account (folder_store);
2573 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2575 /* In inbox case we need to know if the inbox is really the top
2576 * inbox of the account, or if it's a submailbox inbox. To do
2577 * this we'll apply an heuristic rule: Find last "/" and check
2578 * if it's preceeded by another Inbox */
2579 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2580 g_object_unref (account);
2581 return is_special?0:4;
2584 case TNY_FOLDER_TYPE_OUTBOX:
2585 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2587 case TNY_FOLDER_TYPE_DRAFTS:
2589 account = tny_folder_get_account (folder_store);
2590 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2591 g_object_unref (account);
2592 return is_special?1:4;
2595 case TNY_FOLDER_TYPE_SENT:
2597 account = tny_folder_get_account (folder_store);
2598 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2599 g_object_unref (account);
2600 return is_special?3:4;
2609 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2611 const gchar *a1_name, *a2_name;
2613 a1_name = tny_account_get_name (a1);
2614 a2_name = tny_account_get_name (a2);
2616 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2620 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2622 TnyAccount *a1 = NULL, *a2 = NULL;
2625 if (TNY_IS_ACCOUNT (s1)) {
2626 a1 = TNY_ACCOUNT (g_object_ref (s1));
2627 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2628 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2631 if (TNY_IS_ACCOUNT (s2)) {
2632 a2 = TNY_ACCOUNT (g_object_ref (s2));
2633 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2634 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2651 /* First we sort with the type of account */
2652 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2656 cmp = compare_account_names (a1, a2);
2660 g_object_unref (a1);
2662 g_object_unref (a2);
2668 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2670 gint is_account1, is_account2;
2672 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2673 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2675 return is_account2 - is_account1;
2679 * This function orders the mail accounts according to these rules:
2680 * 1st - remote accounts
2681 * 2nd - local account
2685 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2689 gchar *name1 = NULL;
2690 gchar *name2 = NULL;
2691 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2692 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2693 GObject *folder1 = NULL;
2694 GObject *folder2 = NULL;
2696 gtk_tree_model_get (tree_model, iter1,
2697 NAME_COLUMN, &name1,
2699 INSTANCE_COLUMN, &folder1,
2701 gtk_tree_model_get (tree_model, iter2,
2702 NAME_COLUMN, &name2,
2703 TYPE_COLUMN, &type2,
2704 INSTANCE_COLUMN, &folder2,
2707 /* Return if we get no folder. This could happen when folder
2708 operations are happening. The model is updated after the
2709 folder copy/move actually occurs, so there could be
2710 situations where the model to be drawn is not correct */
2711 if (!folder1 || !folder2)
2714 /* Sort by type. First the special folders, then the archives */
2715 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2719 /* Now we sort using the account of each folder */
2720 if (TNY_IS_FOLDER_STORE (folder1) &&
2721 TNY_IS_FOLDER_STORE (folder2)) {
2722 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2726 /* Each group is preceeded by its account */
2727 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2732 /* Pure sort by name */
2733 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2736 g_object_unref(G_OBJECT(folder1));
2738 g_object_unref(G_OBJECT(folder2));
2746 /*****************************************************************************/
2747 /* DRAG and DROP stuff */
2748 /*****************************************************************************/
2750 * This function fills the #GtkSelectionData with the row and the
2751 * model that has been dragged. It's called when this widget is a
2752 * source for dnd after the event drop happened
2755 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2756 guint info, guint time, gpointer data)
2758 GtkTreeSelection *selection;
2759 GtkTreeModel *model;
2761 GtkTreePath *source_row;
2763 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2764 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2766 source_row = gtk_tree_model_get_path (model, &iter);
2767 gtk_tree_set_row_drag_data (selection_data,
2771 gtk_tree_path_free (source_row);
2775 typedef struct _DndHelper {
2776 ModestFolderView *folder_view;
2777 gboolean delete_source;
2778 GtkTreePath *source_row;
2782 dnd_helper_destroyer (DndHelper *helper)
2784 /* Free the helper */
2785 gtk_tree_path_free (helper->source_row);
2786 g_slice_free (DndHelper, helper);
2790 xfer_folder_cb (ModestMailOperation *mail_op,
2791 TnyFolder *new_folder,
2795 /* Select the folder */
2796 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2802 /* get the folder for the row the treepath refers to. */
2803 /* folder must be unref'd */
2804 static TnyFolderStore *
2805 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2808 TnyFolderStore *folder = NULL;
2810 if (gtk_tree_model_get_iter (model,&iter, path))
2811 gtk_tree_model_get (model, &iter,
2812 INSTANCE_COLUMN, &folder,
2819 * This function is used by drag_data_received_cb to manage drag and
2820 * drop of a header, i.e, and drag from the header view to the folder
2824 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2825 GtkTreeModel *dest_model,
2826 GtkTreePath *dest_row,
2827 GtkSelectionData *selection_data)
2829 TnyList *headers = NULL;
2830 TnyFolder *folder = NULL, *src_folder = NULL;
2831 TnyFolderType folder_type;
2832 GtkTreeIter source_iter, dest_iter;
2833 ModestWindowMgr *mgr = NULL;
2834 ModestWindow *main_win = NULL;
2835 gchar **uris, **tmp;
2837 /* Build the list of headers */
2838 mgr = modest_runtime_get_window_mgr ();
2839 headers = tny_simple_list_new ();
2840 uris = modest_dnd_selection_data_get_paths (selection_data);
2843 while (*tmp != NULL) {
2846 gboolean first = TRUE;
2849 path = gtk_tree_path_new_from_string (*tmp);
2850 gtk_tree_model_get_iter (source_model, &source_iter, path);
2851 gtk_tree_model_get (source_model, &source_iter,
2852 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2855 /* Do not enable d&d of headers already opened */
2856 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2857 tny_list_append (headers, G_OBJECT (header));
2859 if (G_UNLIKELY (first)) {
2860 src_folder = tny_header_get_folder (header);
2864 /* Free and go on */
2865 gtk_tree_path_free (path);
2866 g_object_unref (header);
2871 /* This could happen ig we perform a d&d very quickly over the
2872 same row that row could dissapear because message is
2874 if (!TNY_IS_FOLDER (src_folder))
2877 /* Get the target folder */
2878 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2879 gtk_tree_model_get (dest_model, &dest_iter,
2883 if (!folder || !TNY_IS_FOLDER(folder)) {
2884 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2888 folder_type = modest_tny_folder_guess_folder_type (folder);
2889 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2890 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2891 goto cleanup; /* cannot move messages there */
2894 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2895 /* g_warning ("folder not writable"); */
2896 goto cleanup; /* verboten! */
2899 /* Ask for confirmation to move */
2900 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2902 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2906 /* Transfer messages */
2907 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2912 if (G_IS_OBJECT (src_folder))
2913 g_object_unref (src_folder);
2914 if (G_IS_OBJECT(folder))
2915 g_object_unref (G_OBJECT (folder));
2916 if (G_IS_OBJECT(headers))
2917 g_object_unref (headers);
2921 TnyFolderStore *src_folder;
2922 TnyFolderStore *dst_folder;
2923 ModestFolderView *folder_view;
2928 dnd_folder_info_destroyer (DndFolderInfo *info)
2930 if (info->src_folder)
2931 g_object_unref (info->src_folder);
2932 if (info->dst_folder)
2933 g_object_unref (info->dst_folder);
2934 g_slice_free (DndFolderInfo, info);
2938 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2939 GtkWindow *parent_window,
2940 TnyAccount *account)
2943 modest_ui_actions_on_account_connection_error (parent_window, account);
2945 /* Free the helper & info */
2946 dnd_helper_destroyer (info->helper);
2947 dnd_folder_info_destroyer (info);
2951 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2953 GtkWindow *parent_window,
2954 TnyAccount *account,
2957 DndFolderInfo *info = NULL;
2958 ModestMailOperation *mail_op;
2960 info = (DndFolderInfo *) user_data;
2962 if (err || canceled) {
2963 dnd_on_connection_failed_destroyer (info, parent_window, account);
2967 /* Do the mail operation */
2968 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2969 modest_ui_actions_move_folder_error_handler,
2970 info->src_folder, NULL);
2972 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2975 /* Transfer the folder */
2976 modest_mail_operation_xfer_folder (mail_op,
2977 TNY_FOLDER (info->src_folder),
2979 info->helper->delete_source,
2981 info->helper->folder_view);
2984 g_object_unref (G_OBJECT (mail_op));
2985 dnd_helper_destroyer (info->helper);
2986 dnd_folder_info_destroyer (info);
2991 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2993 GtkWindow *parent_window,
2994 TnyAccount *account,
2997 DndFolderInfo *info = NULL;
2999 info = (DndFolderInfo *) user_data;
3001 if (err || canceled) {
3002 dnd_on_connection_failed_destroyer (info, parent_window, account);
3006 /* Connect to source folder and perform the copy/move */
3007 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3009 drag_and_drop_from_folder_view_src_folder_performer,
3014 * This function is used by drag_data_received_cb to manage drag and
3015 * drop of a folder, i.e, and drag from the folder view to the same
3019 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3020 GtkTreeModel *dest_model,
3021 GtkTreePath *dest_row,
3022 GtkSelectionData *selection_data,
3025 GtkTreeIter dest_iter, iter;
3026 TnyFolderStore *dest_folder = NULL;
3027 TnyFolderStore *folder = NULL;
3028 gboolean forbidden = FALSE;
3030 DndFolderInfo *info = NULL;
3032 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3034 g_warning ("%s: BUG: no main window", __FUNCTION__);
3035 dnd_helper_destroyer (helper);
3040 /* check the folder rules for the destination */
3041 folder = tree_path_to_folder (dest_model, dest_row);
3042 if (TNY_IS_FOLDER(folder)) {
3043 ModestTnyFolderRules rules =
3044 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3045 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3046 } else if (TNY_IS_FOLDER_STORE(folder)) {
3047 /* enable local root as destination for folders */
3048 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3049 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3052 g_object_unref (folder);
3055 /* check the folder rules for the source */
3056 folder = tree_path_to_folder (source_model, helper->source_row);
3057 if (TNY_IS_FOLDER(folder)) {
3058 ModestTnyFolderRules rules =
3059 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3060 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3063 g_object_unref (folder);
3067 /* Check if the drag is possible */
3068 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3070 modest_platform_run_information_dialog ((GtkWindow *) win,
3071 _("mail_in_ui_folder_move_target_error"),
3073 /* Restore the previous selection */
3074 folder = tree_path_to_folder (source_model, helper->source_row);
3076 if (TNY_IS_FOLDER (folder))
3077 modest_folder_view_select_folder (helper->folder_view,
3078 TNY_FOLDER (folder), FALSE);
3079 g_object_unref (folder);
3081 dnd_helper_destroyer (helper);
3086 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3087 gtk_tree_model_get (dest_model, &dest_iter,
3090 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3091 gtk_tree_model_get (source_model, &iter,
3095 /* Create the info for the performer */
3096 info = g_slice_new0 (DndFolderInfo);
3097 info->src_folder = g_object_ref (folder);
3098 info->dst_folder = g_object_ref (dest_folder);
3099 info->helper = helper;
3101 /* Connect to the destination folder and perform the copy/move */
3102 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3104 drag_and_drop_from_folder_view_dst_folder_performer,
3108 g_object_unref (dest_folder);
3109 g_object_unref (folder);
3113 * This function receives the data set by the "drag-data-get" signal
3114 * handler. This information comes within the #GtkSelectionData. This
3115 * function will manage both the drags of folders of the treeview and
3116 * drags of headers of the header view widget.
3119 on_drag_data_received (GtkWidget *widget,
3120 GdkDragContext *context,
3123 GtkSelectionData *selection_data,
3128 GtkWidget *source_widget;
3129 GtkTreeModel *dest_model, *source_model;
3130 GtkTreePath *source_row, *dest_row;
3131 GtkTreeViewDropPosition pos;
3132 gboolean delete_source = FALSE;
3133 gboolean success = FALSE;
3135 /* Do not allow further process */
3136 g_signal_stop_emission_by_name (widget, "drag-data-received");
3137 source_widget = gtk_drag_get_source_widget (context);
3139 /* Get the action */
3140 if (context->action == GDK_ACTION_MOVE) {
3141 delete_source = TRUE;
3143 /* Notify that there is no folder selected. We need to
3144 do this in order to update the headers view (and
3145 its monitors, because when moving, the old folder
3146 won't longer exist. We can not wait for the end of
3147 the operation, because the operation won't start if
3148 the folder is in use */
3149 if (source_widget == widget) {
3150 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3151 gtk_tree_selection_unselect_all (sel);
3155 /* Check if the get_data failed */
3156 if (selection_data == NULL || selection_data->length < 0)
3159 /* Select the destination model */
3160 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3162 /* Get the path to the destination row. Can not call
3163 gtk_tree_view_get_drag_dest_row() because the source row
3164 is not selected anymore */
3165 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3168 /* Only allow drops IN other rows */
3170 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3171 pos == GTK_TREE_VIEW_DROP_AFTER)
3175 /* Drags from the header view */
3176 if (source_widget != widget) {
3177 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3179 drag_and_drop_from_header_view (source_model,
3184 DndHelper *helper = NULL;
3186 /* Get the source model and row */
3187 gtk_tree_get_row_drag_data (selection_data,
3191 /* Create the helper */
3192 helper = g_slice_new0 (DndHelper);
3193 helper->delete_source = delete_source;
3194 helper->source_row = gtk_tree_path_copy (source_row);
3195 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3197 drag_and_drop_from_folder_view (source_model,
3203 gtk_tree_path_free (source_row);
3207 gtk_tree_path_free (dest_row);
3210 /* Finish the drag and drop */
3211 gtk_drag_finish (context, success, FALSE, time);
3215 * We define a "drag-drop" signal handler because we do not want to
3216 * use the default one, because the default one always calls
3217 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3218 * signal handler, because there we have all the information available
3219 * to know if the dnd was a success or not.
3222 drag_drop_cb (GtkWidget *widget,
3223 GdkDragContext *context,
3231 if (!context->targets)
3234 /* Check if we're dragging a folder row */
3235 target = gtk_drag_dest_find_target (widget, context, NULL);
3237 /* Request the data from the source. */
3238 gtk_drag_get_data(widget, context, target, time);
3244 * This function expands a node of a tree view if it's not expanded
3245 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3246 * does that, so that's why they're here.
3249 expand_row_timeout (gpointer data)
3251 GtkTreeView *tree_view = data;
3252 GtkTreePath *dest_path = NULL;
3253 GtkTreeViewDropPosition pos;
3254 gboolean result = FALSE;
3256 gdk_threads_enter ();
3258 gtk_tree_view_get_drag_dest_row (tree_view,
3263 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3264 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3265 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3266 gtk_tree_path_free (dest_path);
3270 gtk_tree_path_free (dest_path);
3275 gdk_threads_leave ();
3281 * This function is called whenever the pointer is moved over a widget
3282 * while dragging some data. It installs a timeout that will expand a
3283 * node of the treeview if not expanded yet. This function also calls
3284 * gdk_drag_status in order to set the suggested action that will be
3285 * used by the "drag-data-received" signal handler to know if we
3286 * should do a move or just a copy of the data.
3289 on_drag_motion (GtkWidget *widget,
3290 GdkDragContext *context,
3296 GtkTreeViewDropPosition pos;
3297 GtkTreePath *dest_row;
3298 GtkTreeModel *dest_model;
3299 ModestFolderViewPrivate *priv;
3300 GdkDragAction suggested_action;
3301 gboolean valid_location = FALSE;
3302 TnyFolderStore *folder = NULL;
3304 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3306 if (priv->timer_expander != 0) {
3307 g_source_remove (priv->timer_expander);
3308 priv->timer_expander = 0;
3311 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3316 /* Do not allow drops between folders */
3318 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3319 pos == GTK_TREE_VIEW_DROP_AFTER) {
3320 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3321 gdk_drag_status(context, 0, time);
3322 valid_location = FALSE;
3325 valid_location = TRUE;
3328 /* Check that the destination folder is writable */
3329 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3330 folder = tree_path_to_folder (dest_model, dest_row);
3331 if (folder && TNY_IS_FOLDER (folder)) {
3332 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3334 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3335 valid_location = FALSE;
3340 /* Expand the selected row after 1/2 second */
3341 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3342 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3344 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3346 /* Select the desired action. By default we pick MOVE */
3347 suggested_action = GDK_ACTION_MOVE;
3349 if (context->actions == GDK_ACTION_COPY)
3350 gdk_drag_status(context, GDK_ACTION_COPY, time);
3351 else if (context->actions == GDK_ACTION_MOVE)
3352 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3353 else if (context->actions & suggested_action)
3354 gdk_drag_status(context, suggested_action, time);
3356 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3360 g_object_unref (folder);
3362 gtk_tree_path_free (dest_row);
3364 g_signal_stop_emission_by_name (widget, "drag-motion");
3366 return valid_location;
3370 * This function sets the treeview as a source and a target for dnd
3371 * events. It also connects all the requirede signals.
3374 setup_drag_and_drop (GtkTreeView *self)
3376 /* Set up the folder view as a dnd destination. Set only the
3377 highlight flag, otherwise gtk will have a different
3379 #ifdef MODEST_TOOLKIT_HILDON2
3382 gtk_drag_dest_set (GTK_WIDGET (self),
3383 GTK_DEST_DEFAULT_HIGHLIGHT,
3384 folder_view_drag_types,
3385 G_N_ELEMENTS (folder_view_drag_types),
3386 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3388 g_signal_connect (G_OBJECT (self),
3389 "drag_data_received",
3390 G_CALLBACK (on_drag_data_received),
3394 /* Set up the treeview as a dnd source */
3395 gtk_drag_source_set (GTK_WIDGET (self),
3397 folder_view_drag_types,
3398 G_N_ELEMENTS (folder_view_drag_types),
3399 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3401 g_signal_connect (G_OBJECT (self),
3403 G_CALLBACK (on_drag_motion),
3406 g_signal_connect (G_OBJECT (self),
3408 G_CALLBACK (on_drag_data_get),
3411 g_signal_connect (G_OBJECT (self),
3413 G_CALLBACK (drag_drop_cb),
3418 * This function manages the navigation through the folders using the
3419 * keyboard or the hardware keys in the device
3422 on_key_pressed (GtkWidget *self,
3426 GtkTreeSelection *selection;
3428 GtkTreeModel *model;
3429 gboolean retval = FALSE;
3431 /* Up and Down are automatically managed by the treeview */
3432 if (event->keyval == GDK_Return) {
3433 /* Expand/Collapse the selected row */
3434 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3435 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3438 path = gtk_tree_model_get_path (model, &iter);
3440 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3441 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3443 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3444 gtk_tree_path_free (path);
3446 /* No further processing */
3454 * We listen to the changes in the local folder account name key,
3455 * because we want to show the right name in the view. The local
3456 * folder account name corresponds to the device name in the Maemo
3457 * version. We do this because we do not want to query gconf on each
3458 * tree view refresh. It's better to cache it and change whenever
3462 on_configuration_key_changed (ModestConf* conf,
3464 ModestConfEvent event,
3465 ModestConfNotificationId id,
3466 ModestFolderView *self)
3468 ModestFolderViewPrivate *priv;
3471 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3472 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3474 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3475 g_free (priv->local_account_name);
3477 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3478 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3480 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3481 MODEST_CONF_DEVICE_NAME, NULL);
3483 /* Force a redraw */
3484 #if GTK_CHECK_VERSION(2, 8, 0)
3485 GtkTreeViewColumn * tree_column;
3487 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3489 gtk_tree_view_column_queue_resize (tree_column);
3491 gtk_widget_queue_draw (GTK_WIDGET (self));
3497 modest_folder_view_set_style (ModestFolderView *self,
3498 ModestFolderViewStyle style)
3500 ModestFolderViewPrivate *priv;
3502 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3503 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3504 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3506 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3509 priv->style = style;
3513 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3514 const gchar *account_id)
3516 ModestFolderViewPrivate *priv;
3517 GtkTreeModel *model;
3519 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3521 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3523 /* This will be used by the filter_row callback,
3524 * to decided which rows to show: */
3525 if (priv->visible_account_id) {
3526 g_free (priv->visible_account_id);
3527 priv->visible_account_id = NULL;
3530 priv->visible_account_id = g_strdup (account_id);
3533 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3534 if (GTK_IS_TREE_MODEL_FILTER (model))
3535 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3537 /* Save settings to gconf */
3538 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3539 MODEST_CONF_FOLDER_VIEW_KEY);
3541 /* Notify observers */
3542 g_signal_emit (G_OBJECT(self),
3543 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3548 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3550 ModestFolderViewPrivate *priv;
3552 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3554 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3556 return (const gchar *) priv->visible_account_id;
3560 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3564 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3566 gtk_tree_model_get (model, iter,
3570 gboolean result = FALSE;
3571 if (type == TNY_FOLDER_TYPE_INBOX) {
3575 *inbox_iter = *iter;
3579 if (gtk_tree_model_iter_children (model, &child, iter)) {
3580 if (find_inbox_iter (model, &child, inbox_iter))
3584 } while (gtk_tree_model_iter_next (model, iter));
3593 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3595 #ifndef MODEST_TOOLKIT_HILDON2
3596 GtkTreeModel *model;
3597 GtkTreeIter iter, inbox_iter;
3598 GtkTreeSelection *sel;
3599 GtkTreePath *path = NULL;
3601 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3603 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3607 expand_root_items (self);
3608 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3610 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3611 g_warning ("%s: model is empty", __FUNCTION__);
3615 if (find_inbox_iter (model, &iter, &inbox_iter))
3616 path = gtk_tree_model_get_path (model, &inbox_iter);
3618 path = gtk_tree_path_new_first ();
3620 /* Select the row and free */
3621 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3622 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3623 gtk_tree_path_free (path);
3626 gtk_widget_grab_focus (GTK_WIDGET(self));
3633 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3638 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3639 TnyFolder* a_folder;
3642 gtk_tree_model_get (model, iter,
3643 INSTANCE_COLUMN, &a_folder,
3649 if (folder == a_folder) {
3650 g_object_unref (a_folder);
3651 *folder_iter = *iter;
3654 g_object_unref (a_folder);
3656 if (gtk_tree_model_iter_children (model, &child, iter)) {
3657 if (find_folder_iter (model, &child, folder_iter, folder))
3661 } while (gtk_tree_model_iter_next (model, iter));
3666 #ifndef MODEST_TOOLKIT_HILDON2
3668 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3671 ModestFolderView *self)
3673 ModestFolderViewPrivate *priv = NULL;
3674 GtkTreeSelection *sel;
3675 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3676 GObject *instance = NULL;
3678 if (!MODEST_IS_FOLDER_VIEW(self))
3681 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3683 priv->reexpand = TRUE;
3685 gtk_tree_model_get (tree_model, iter,
3687 INSTANCE_COLUMN, &instance,
3693 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3694 priv->folder_to_select = g_object_ref (instance);
3696 g_object_unref (instance);
3698 if (priv->folder_to_select) {
3700 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3703 path = gtk_tree_model_get_path (tree_model, iter);
3704 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3706 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3708 gtk_tree_selection_select_iter (sel, iter);
3709 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3711 gtk_tree_path_free (path);
3715 modest_folder_view_disable_next_folder_selection (self);
3717 /* Refilter the model */
3718 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3724 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3726 ModestFolderViewPrivate *priv;
3728 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3730 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3732 if (priv->folder_to_select)
3733 g_object_unref(priv->folder_to_select);
3735 priv->folder_to_select = NULL;
3739 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3740 gboolean after_change)
3742 GtkTreeModel *model;
3743 GtkTreeIter iter, folder_iter;
3744 GtkTreeSelection *sel;
3745 ModestFolderViewPrivate *priv = NULL;
3747 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3748 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3750 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3753 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3754 gtk_tree_selection_unselect_all (sel);
3756 if (priv->folder_to_select)
3757 g_object_unref(priv->folder_to_select);
3758 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3762 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3767 /* Refilter the model, before selecting the folder */
3768 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3770 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3771 g_warning ("%s: model is empty", __FUNCTION__);
3775 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3778 path = gtk_tree_model_get_path (model, &folder_iter);
3779 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3781 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3782 gtk_tree_selection_select_iter (sel, &folder_iter);
3783 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3785 gtk_tree_path_free (path);
3793 modest_folder_view_copy_selection (ModestFolderView *self)
3795 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3797 /* Copy selection */
3798 _clipboard_set_selected_data (self, FALSE);
3802 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3804 ModestFolderViewPrivate *priv = NULL;
3805 GtkTreeModel *model = NULL;
3806 const gchar **hidding = NULL;
3807 guint i, n_selected;
3809 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3810 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3812 /* Copy selection */
3813 if (!_clipboard_set_selected_data (folder_view, TRUE))
3816 /* Get hidding ids */
3817 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3819 /* Clear hidding array created by previous cut operation */
3820 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3822 /* Copy hidding array */
3823 priv->n_selected = n_selected;
3824 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3825 for (i=0; i < n_selected; i++)
3826 priv->hidding_ids[i] = g_strdup(hidding[i]);
3828 /* Hide cut folders */
3829 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3830 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3834 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3835 ModestFolderView *folder_view_dst)
3837 GtkTreeModel *filter_model = NULL;
3838 GtkTreeModel *model = NULL;
3839 GtkTreeModel *new_filter_model = NULL;
3840 GtkTreeModel *old_tny_model = NULL;
3841 GtkTreeModel *new_tny_model = NULL;
3842 ModestFolderViewPrivate *dst_priv;
3844 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3845 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3847 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3848 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3849 new_tny_model = NULL;
3852 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3853 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3854 G_OBJECT (old_tny_model),
3855 "activity-changed");
3857 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3858 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3860 /* Build new filter model */
3861 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3862 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3869 /* Set copied model */
3870 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3871 #ifndef MODEST_TOOLKIT_HILDON2
3872 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3873 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3875 #ifdef MODEST_TOOLKIT_HILDON2
3876 if (new_tny_model) {
3877 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3878 G_OBJECT (new_tny_model),
3880 G_CALLBACK (on_activity_changed),
3886 g_object_unref (new_filter_model);
3890 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3893 GtkTreeModel *model = NULL;
3894 ModestFolderViewPrivate* priv;
3896 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3898 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3899 priv->show_non_move = show;
3900 /* modest_folder_view_update_model(folder_view, */
3901 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3903 /* Hide special folders */
3904 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3905 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3906 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3911 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3914 ModestFolderViewPrivate* priv;
3916 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3918 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3919 priv->show_message_count = show;
3921 g_object_set (G_OBJECT (priv->messages_renderer),
3922 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3926 /* Returns FALSE if it did not selected anything */
3928 _clipboard_set_selected_data (ModestFolderView *folder_view,
3931 ModestFolderViewPrivate *priv = NULL;
3932 TnyFolderStore *folder = NULL;
3933 gboolean retval = FALSE;
3935 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3936 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3938 /* Set selected data on clipboard */
3939 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3940 folder = modest_folder_view_get_selected (folder_view);
3942 /* Do not allow to select an account */
3943 if (TNY_IS_FOLDER (folder)) {
3944 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3949 g_object_unref (folder);
3955 _clear_hidding_filter (ModestFolderView *folder_view)
3957 ModestFolderViewPrivate *priv;
3960 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3961 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3963 if (priv->hidding_ids != NULL) {
3964 for (i=0; i < priv->n_selected; i++)
3965 g_free (priv->hidding_ids[i]);
3966 g_free(priv->hidding_ids);
3972 on_display_name_changed (ModestAccountMgr *mgr,
3973 const gchar *account,
3976 ModestFolderView *self;
3978 self = MODEST_FOLDER_VIEW (user_data);
3980 /* Force a redraw */
3981 #if GTK_CHECK_VERSION(2, 8, 0)
3982 GtkTreeViewColumn * tree_column;
3984 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3986 gtk_tree_view_column_queue_resize (tree_column);
3988 gtk_widget_queue_draw (GTK_WIDGET (self));
3993 modest_folder_view_set_cell_style (ModestFolderView *self,
3994 ModestFolderViewCellStyle cell_style)
3996 ModestFolderViewPrivate *priv = NULL;
3998 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3999 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4001 priv->cell_style = cell_style;
4003 g_object_set (G_OBJECT (priv->messages_renderer),
4004 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4007 gtk_widget_queue_draw (GTK_WIDGET (self));
4011 update_style (ModestFolderView *self)
4013 ModestFolderViewPrivate *priv;
4014 GdkColor style_color, style_active_color;
4015 PangoAttrList *attr_list;
4017 PangoAttribute *attr;
4019 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4020 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4024 attr_list = pango_attr_list_new ();
4025 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4026 gdk_color_parse ("grey", &style_color);
4028 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4029 pango_attr_list_insert (attr_list, attr);
4032 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4034 "SmallSystemFont", NULL,
4037 attr = pango_attr_font_desc_new (pango_font_description_copy
4038 (style->font_desc));
4039 pango_attr_list_insert (attr_list, attr);
4041 g_object_set (G_OBJECT (priv->messages_renderer),
4042 "foreground-gdk", &style_color,
4043 "foreground-set", TRUE,
4044 "attributes", attr_list,
4046 pango_attr_list_unref (attr_list);
4048 if (priv->active_color)
4049 gdk_color_free (priv->active_color);
4051 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4052 priv->active_color = gdk_color_copy (&style_active_color);
4057 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4059 if (strcmp ("style", spec->name) == 0) {
4060 update_style (MODEST_FOLDER_VIEW (obj));
4061 gtk_widget_queue_draw (GTK_WIDGET (obj));
4066 modest_folder_view_set_filter (ModestFolderView *self,
4067 ModestFolderViewFilter filter)
4069 ModestFolderViewPrivate *priv;
4070 GtkTreeModel *filter_model;
4072 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4073 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4075 priv->filter |= filter;
4077 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4078 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4079 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4084 modest_folder_view_unset_filter (ModestFolderView *self,
4085 ModestFolderViewFilter filter)
4087 ModestFolderViewPrivate *priv;
4088 GtkTreeModel *filter_model;
4090 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4091 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4093 priv->filter &= ~filter;
4095 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4096 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4097 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4102 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4103 ModestTnyFolderRules rules)
4105 GtkTreeModel *filter_model;
4107 gboolean fulfil = FALSE;
4109 if (!get_inner_models (self, &filter_model, NULL, NULL))
4112 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4116 TnyFolderStore *folder;
4118 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4120 if (TNY_IS_FOLDER (folder)) {
4121 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4122 /* Folder rules are negative: non_writable, non_deletable... */
4123 if (!(folder_rules & rules))
4126 g_object_unref (folder);
4129 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4135 modest_folder_view_set_list_to_move (ModestFolderView *self,
4138 ModestFolderViewPrivate *priv;
4140 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4141 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4143 if (priv->list_to_move)
4144 g_object_unref (priv->list_to_move);
4147 g_object_ref (list);
4149 priv->list_to_move = list;
4153 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4155 ModestFolderViewPrivate *priv;
4157 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4158 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4161 g_free (priv->mailbox);
4163 priv->mailbox = g_strdup (mailbox);
4165 /* Notify observers */
4166 g_signal_emit (G_OBJECT(self),
4167 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4168 priv->visible_account_id);
4172 modest_folder_view_get_mailbox (ModestFolderView *self)
4174 ModestFolderViewPrivate *priv;
4176 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4177 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4179 return (const gchar *) priv->mailbox;
4183 modest_folder_view_get_activity (ModestFolderView *self)
4185 ModestFolderViewPrivate *priv;
4186 GtkTreeModel *inner_model;
4188 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4189 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4190 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4192 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4193 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4199 #ifdef MODEST_TOOLKIT_HILDON2
4201 on_activity_changed (TnyGtkFolderListStore *store,
4203 ModestFolderView *folder_view)
4205 ModestFolderViewPrivate *priv;
4207 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4208 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4209 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4211 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,