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);
101 static void tny_account_store_view_init (gpointer g,
102 gpointer iface_data);
104 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
105 TnyAccountStore *account_store);
107 static void on_selection_changed (GtkTreeSelection *sel,
110 static void on_row_activated (GtkTreeView *treeview,
112 GtkTreeViewColumn *column,
115 static void on_account_removed (TnyAccountStore *self,
119 static void on_account_inserted (TnyAccountStore *self,
123 static void on_account_changed (TnyAccountStore *self,
127 static gint cmp_rows (GtkTreeModel *tree_model,
132 static gboolean filter_row (GtkTreeModel *model,
136 static gboolean on_key_pressed (GtkWidget *self,
140 static void on_configuration_key_changed (ModestConf* conf,
142 ModestConfEvent event,
143 ModestConfNotificationId notification_id,
144 ModestFolderView *self);
147 static void on_drag_data_get (GtkWidget *widget,
148 GdkDragContext *context,
149 GtkSelectionData *selection_data,
154 static void on_drag_data_received (GtkWidget *widget,
155 GdkDragContext *context,
158 GtkSelectionData *selection_data,
163 static gboolean on_drag_motion (GtkWidget *widget,
164 GdkDragContext *context,
170 static void expand_root_items (ModestFolderView *self);
172 static gint expand_row_timeout (gpointer data);
174 static void setup_drag_and_drop (GtkTreeView *self);
176 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
179 static void _clear_hidding_filter (ModestFolderView *folder_view);
181 #ifndef MODEST_TOOLKIT_HILDON2
182 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
185 ModestFolderView *self);
188 static void on_display_name_changed (ModestAccountMgr *self,
189 const gchar *account,
191 static void update_style (ModestFolderView *self);
192 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
193 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
194 static gboolean inbox_is_special (TnyFolderStore *folder_store);
196 static gboolean get_inner_models (ModestFolderView *self,
197 GtkTreeModel **filter_model,
198 GtkTreeModel **sort_model,
199 GtkTreeModel **tny_model);
200 #ifdef MODEST_TOOLKIT_HILDON2
202 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;
239 guint timer_expander;
241 gchar *local_account_name;
242 gchar *visible_account_id;
244 ModestFolderViewStyle style;
245 ModestFolderViewCellStyle cell_style;
246 gboolean show_message_count;
248 gboolean reselect; /* we use this to force a reselection of the INBOX */
249 gboolean show_non_move;
250 TnyList *list_to_move;
251 gboolean reexpand; /* next time we expose, we'll expand all root folders */
253 GtkCellRenderer *messages_renderer;
255 gulong outbox_deleted_handler;
257 guint activity_changed_handler;
259 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
260 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
261 MODEST_TYPE_FOLDER_VIEW, \
262 ModestFolderViewPrivate))
264 static GObjectClass *parent_class = NULL;
266 static guint signals[LAST_SIGNAL] = {0};
269 modest_folder_view_get_type (void)
271 static GType my_type = 0;
273 static const GTypeInfo my_info = {
274 sizeof(ModestFolderViewClass),
275 NULL, /* base init */
276 NULL, /* base finalize */
277 (GClassInitFunc) modest_folder_view_class_init,
278 NULL, /* class finalize */
279 NULL, /* class data */
280 sizeof(ModestFolderView),
282 (GInstanceInitFunc) modest_folder_view_init,
286 static const GInterfaceInfo tny_account_store_view_info = {
287 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
288 NULL, /* interface_finalize */
289 NULL /* interface_data */
293 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
297 g_type_add_interface_static (my_type,
298 TNY_TYPE_ACCOUNT_STORE_VIEW,
299 &tny_account_store_view_info);
305 modest_folder_view_class_init (ModestFolderViewClass *klass)
307 GObjectClass *gobject_class;
308 GtkTreeViewClass *treeview_class;
309 gobject_class = (GObjectClass*) klass;
310 treeview_class = (GtkTreeViewClass*) klass;
312 parent_class = g_type_class_peek_parent (klass);
313 gobject_class->finalize = modest_folder_view_finalize;
315 g_type_class_add_private (gobject_class,
316 sizeof(ModestFolderViewPrivate));
318 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
319 g_signal_new ("folder_selection_changed",
320 G_TYPE_FROM_CLASS (gobject_class),
322 G_STRUCT_OFFSET (ModestFolderViewClass,
323 folder_selection_changed),
325 modest_marshal_VOID__POINTER_BOOLEAN,
326 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
329 * This signal is emitted whenever the currently selected
330 * folder display name is computed. Note that the name could
331 * be different to the folder name, because we could append
332 * the unread messages count to the folder name to build the
333 * folder display name
335 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
336 g_signal_new ("folder-display-name-changed",
337 G_TYPE_FROM_CLASS (gobject_class),
339 G_STRUCT_OFFSET (ModestFolderViewClass,
340 folder_display_name_changed),
342 g_cclosure_marshal_VOID__STRING,
343 G_TYPE_NONE, 1, G_TYPE_STRING);
345 signals[FOLDER_ACTIVATED_SIGNAL] =
346 g_signal_new ("folder_activated",
347 G_TYPE_FROM_CLASS (gobject_class),
349 G_STRUCT_OFFSET (ModestFolderViewClass,
352 g_cclosure_marshal_VOID__POINTER,
353 G_TYPE_NONE, 1, G_TYPE_POINTER);
356 * Emitted whenever the visible account changes
358 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
359 g_signal_new ("visible-account-changed",
360 G_TYPE_FROM_CLASS (gobject_class),
362 G_STRUCT_OFFSET (ModestFolderViewClass,
363 visible_account_changed),
365 g_cclosure_marshal_VOID__STRING,
366 G_TYPE_NONE, 1, G_TYPE_STRING);
369 * Emitted when the underlying GtkListStore is updating data
371 signals[ACTIVITY_CHANGED_SIGNAL] =
372 g_signal_new ("activity-changed",
373 G_TYPE_FROM_CLASS (gobject_class),
375 G_STRUCT_OFFSET (ModestFolderViewClass,
378 g_cclosure_marshal_VOID__BOOLEAN,
379 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
381 treeview_class->select_cursor_parent = NULL;
383 #ifdef MODEST_TOOLKIT_HILDON2
384 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
390 /* Retrieves the filter, sort and tny models of the folder view. If
391 any of these does not exist then it returns FALSE */
393 get_inner_models (ModestFolderView *self,
394 GtkTreeModel **filter_model,
395 GtkTreeModel **sort_model,
396 GtkTreeModel **tny_model)
398 GtkTreeModel *s_model, *f_model, *t_model;
400 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
401 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
402 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
406 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
407 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
408 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
412 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
416 *filter_model = f_model;
418 *sort_model = s_model;
420 *tny_model = t_model;
425 /* Simplify checks for NULLs: */
427 strings_are_equal (const gchar *a, const gchar *b)
433 return (strcmp (a, b) == 0);
440 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
442 GObject *instance = NULL;
444 gtk_tree_model_get (model, iter,
445 INSTANCE_COLUMN, &instance,
449 return FALSE; /* keep walking */
451 if (!TNY_IS_ACCOUNT (instance)) {
452 g_object_unref (instance);
453 return FALSE; /* keep walking */
456 /* Check if this is the looked-for account: */
457 TnyAccount *this_account = TNY_ACCOUNT (instance);
458 TnyAccount *account = TNY_ACCOUNT (data);
460 const gchar *this_account_id = tny_account_get_id(this_account);
461 const gchar *account_id = tny_account_get_id(account);
462 g_object_unref (instance);
465 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
466 if (strings_are_equal(this_account_id, account_id)) {
467 /* Tell the model that the data has changed, so that
468 * it calls the cell_data_func callbacks again: */
469 /* TODO: This does not seem to actually cause the new string to be shown: */
470 gtk_tree_model_row_changed (model, path, iter);
472 return TRUE; /* stop walking */
475 return FALSE; /* keep walking */
480 ModestFolderView *self;
481 gchar *previous_name;
482 } GetMmcAccountNameData;
485 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
487 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
489 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
491 if (!strings_are_equal (
492 tny_account_get_name(TNY_ACCOUNT(account)),
493 data->previous_name)) {
495 /* Tell the model that the data has changed, so that
496 * it calls the cell_data_func callbacks again: */
497 ModestFolderView *self = data->self;
498 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
500 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
503 g_free (data->previous_name);
504 g_slice_free (GetMmcAccountNameData, data);
508 convert_parent_folders_to_dots (gchar **item_name)
512 gchar *last_separator;
514 if (item_name == NULL)
517 for (c = *item_name; *c != '\0'; c++) {
518 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
523 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
524 if (last_separator != NULL) {
525 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
532 buffer = g_string_new ("");
533 for (i = 0; i < n_parents; i++) {
534 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
536 buffer = g_string_append (buffer, last_separator);
538 *item_name = g_string_free (buffer, FALSE);
544 format_compact_style (gchar **item_name,
546 const gchar *mailbox,
548 gboolean multiaccount,
549 gboolean *use_markup)
553 TnyFolderType folder_type;
555 if (!TNY_IS_FOLDER (instance))
558 folder = (TnyFolder *) instance;
560 folder_type = tny_folder_get_folder_type (folder);
561 is_special = (get_cmp_pos (folder_type, folder)!= 4);
564 /* Remove mailbox prefix if any */
565 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
566 if (g_str_has_prefix (*item_name, prefix)) {
567 gchar *new_item_name;
569 new_item_name = g_strdup (*item_name + strlen (prefix));
570 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
571 g_free (new_item_name);
572 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
575 *item_name = new_item_name;
577 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
580 *item_name = g_strdup (_("mcen_me_folder_inbox"));
583 if (!is_special || multiaccount) {
584 TnyAccount *account = tny_folder_get_account (folder);
585 const gchar *folder_name;
586 gboolean concat_folder_name = FALSE;
589 /* Should not happen */
593 /* convert parent folders to dots */
594 convert_parent_folders_to_dots (item_name);
596 folder_name = tny_folder_get_name (folder);
597 if (g_str_has_suffix (*item_name, folder_name)) {
598 gchar *offset = g_strrstr (*item_name, folder_name);
600 concat_folder_name = TRUE;
603 buffer = g_string_new ("");
605 buffer = g_string_append (buffer, *item_name);
606 if (concat_folder_name) {
607 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
608 buffer = g_string_append (buffer, folder_name);
609 if (bold) buffer = g_string_append (buffer, "</span>");
612 g_object_unref (account);
614 *item_name = g_string_free (buffer, FALSE);
622 text_cell_data (GtkTreeViewColumn *column,
623 GtkCellRenderer *renderer,
624 GtkTreeModel *tree_model,
628 ModestFolderViewPrivate *priv;
629 GObject *rendobj = (GObject *) renderer;
631 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
632 GObject *instance = NULL;
633 gboolean use_markup = FALSE;
635 gtk_tree_model_get (tree_model, iter,
638 INSTANCE_COLUMN, &instance,
640 if (!fname || !instance)
643 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
644 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
646 gchar *item_name = NULL;
647 gint item_weight = 400;
649 if (type != TNY_FOLDER_TYPE_ROOT) {
654 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
655 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
658 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
659 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
661 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
664 /* Sometimes an special folder is reported by the server as
665 NORMAL, like some versions of Dovecot */
666 if (type == TNY_FOLDER_TYPE_NORMAL ||
667 type == TNY_FOLDER_TYPE_UNKNOWN) {
668 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
672 /* note: we cannot reliably get the counts from the
673 * tree model, we need to use explicit calls on
674 * tny_folder for some reason. Select the number to
675 * show: the unread or unsent messages. in case of
676 * outbox/drafts, show all */
677 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
678 (type == TNY_FOLDER_TYPE_OUTBOX) ||
679 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
680 number = tny_folder_get_all_count (TNY_FOLDER(instance));
683 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
687 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
688 item_name = g_strdup (fname);
695 /* Use bold font style if there are unread or unset messages */
697 if (priv->show_message_count) {
698 item_name = g_strdup_printf ("%s (%d)", fname, number);
700 item_name = g_strdup (fname);
704 item_name = g_strdup (fname);
709 } else if (TNY_IS_ACCOUNT (instance)) {
710 /* If it's a server account */
711 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
712 item_name = g_strdup (priv->local_account_name);
714 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
715 /* fname is only correct when the items are first
716 * added to the model, not when the account is
717 * changed later, so get the name from the account
719 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
722 item_name = g_strdup (fname);
728 item_name = g_strdup ("unknown");
730 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
731 gboolean multiaccount;
733 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
734 /* Convert item_name to markup */
735 format_compact_style (&item_name, instance, priv->mailbox,
737 multiaccount, &use_markup);
740 if (item_name && item_weight) {
741 /* Set the name in the treeview cell: */
743 g_object_set (rendobj, "markup", item_name, "weight-set", FALSE, NULL);
745 g_object_set (rendobj, "text", item_name, "weight-set", TRUE, "weight", item_weight, NULL);
747 /* Notify display name observers */
748 /* TODO: What listens for this signal, and how can it use only the new name? */
749 if (((GObject *) priv->cur_folder_store) == instance) {
750 g_signal_emit (G_OBJECT(self),
751 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
758 /* If it is a Memory card account, make sure that we have the correct name.
759 * This function will be trigerred again when the name has been retrieved: */
760 if (TNY_IS_STORE_ACCOUNT (instance) &&
761 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
763 /* Get the account name asynchronously: */
764 GetMmcAccountNameData *callback_data =
765 g_slice_new0(GetMmcAccountNameData);
766 callback_data->self = self;
768 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
770 callback_data->previous_name = g_strdup (name);
772 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
773 on_get_mmc_account_name, callback_data);
777 g_object_unref (G_OBJECT (instance));
783 messages_cell_data (GtkTreeViewColumn *column,
784 GtkCellRenderer *renderer,
785 GtkTreeModel *tree_model,
789 ModestFolderView *self;
790 ModestFolderViewPrivate *priv;
791 GObject *rendobj = (GObject *) renderer;
792 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
793 GObject *instance = NULL;
794 gchar *item_name = NULL;
796 gtk_tree_model_get (tree_model, iter,
798 INSTANCE_COLUMN, &instance,
803 self = MODEST_FOLDER_VIEW (data);
804 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
807 if (type != TNY_FOLDER_TYPE_ROOT) {
812 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
813 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
816 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
818 /* Sometimes an special folder is reported by the server as
819 NORMAL, like some versions of Dovecot */
820 if (type == TNY_FOLDER_TYPE_NORMAL ||
821 type == TNY_FOLDER_TYPE_UNKNOWN) {
822 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
826 /* note: we cannot reliably get the counts from the tree model, we need
827 * to use explicit calls on tny_folder for some reason.
829 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
830 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
831 (type == TNY_FOLDER_TYPE_OUTBOX) ||
832 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
833 number = tny_folder_get_all_count (TNY_FOLDER(instance));
836 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
840 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
842 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
850 item_name = g_strdup ("");
853 /* Set the name in the treeview cell: */
854 g_object_set (rendobj,"text", item_name, NULL);
862 g_object_unref (G_OBJECT (instance));
868 GdkPixbuf *pixbuf_open;
869 GdkPixbuf *pixbuf_close;
873 static inline GdkPixbuf *
874 get_composite_pixbuf (const gchar *icon_name,
876 GdkPixbuf *base_pixbuf)
878 GdkPixbuf *emblem, *retval = NULL;
880 emblem = modest_platform_get_icon (icon_name, size);
882 retval = gdk_pixbuf_copy (base_pixbuf);
883 gdk_pixbuf_composite (emblem, retval, 0, 0,
884 MIN (gdk_pixbuf_get_width (emblem),
885 gdk_pixbuf_get_width (retval)),
886 MIN (gdk_pixbuf_get_height (emblem),
887 gdk_pixbuf_get_height (retval)),
888 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
889 g_object_unref (emblem);
894 static inline ThreePixbufs *
895 get_composite_icons (const gchar *icon_code,
897 GdkPixbuf **pixbuf_open,
898 GdkPixbuf **pixbuf_close)
900 ThreePixbufs *retval;
903 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
906 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
911 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
915 retval = g_slice_new0 (ThreePixbufs);
917 retval->pixbuf = g_object_ref (*pixbuf);
919 retval->pixbuf_open = g_object_ref (*pixbuf_open);
921 retval->pixbuf_close = g_object_ref (*pixbuf_close);
926 static inline ThreePixbufs *
927 get_account_protocol_pixbufs (ModestFolderView *folder_view,
928 ModestProtocolType protocol_type,
931 ModestProtocol *protocol;
932 const GdkPixbuf *pixbuf = NULL;
933 ModestFolderViewPrivate *priv;
935 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
937 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
940 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
941 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
942 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
943 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
944 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
945 object, FOLDER_ICON_SIZE);
949 ThreePixbufs *retval;
950 retval = g_slice_new0 (ThreePixbufs);
951 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
952 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
953 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
960 static inline ThreePixbufs*
961 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
963 TnyAccount *account = NULL;
964 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
965 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
966 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
967 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
968 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
970 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
971 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
972 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
973 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
974 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
976 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
977 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
978 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
979 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
980 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
982 ThreePixbufs *retval = NULL;
984 if (TNY_IS_ACCOUNT (instance)) {
985 account = g_object_ref (instance);
986 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
987 account = tny_folder_get_account (TNY_FOLDER (instance));
991 ModestProtocolType account_store_protocol;
993 account_store_protocol = modest_tny_account_get_protocol_type (account);
994 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
995 g_object_unref (account);
1001 /* Sometimes an special folder is reported by the server as
1002 NORMAL, like some versions of Dovecot */
1003 if (type == TNY_FOLDER_TYPE_NORMAL ||
1004 type == TNY_FOLDER_TYPE_UNKNOWN) {
1005 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1008 /* It's not enough with check the folder type. We need to
1009 ensure that we're not giving a special folder icon to a
1010 normal folder with the same name than a special folder */
1011 if (TNY_IS_FOLDER (instance) &&
1012 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1013 type = TNY_FOLDER_TYPE_NORMAL;
1015 /* Remote folders should not be treated as special folders */
1016 if (TNY_IS_FOLDER_STORE (instance) &&
1017 !TNY_IS_ACCOUNT (instance) &&
1018 type != TNY_FOLDER_TYPE_INBOX &&
1019 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1020 #ifdef MODEST_TOOLKIT_HILDON2
1021 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1024 &anorm_pixbuf_close);
1026 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1028 &normal_pixbuf_open,
1029 &normal_pixbuf_close);
1035 case TNY_FOLDER_TYPE_INVALID:
1036 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1039 case TNY_FOLDER_TYPE_ROOT:
1040 if (TNY_IS_ACCOUNT (instance)) {
1042 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1043 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1046 &avirt_pixbuf_close);
1048 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1050 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1051 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1054 &ammc_pixbuf_close);
1056 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1059 &anorm_pixbuf_close);
1064 case TNY_FOLDER_TYPE_INBOX:
1065 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1068 &inbox_pixbuf_close);
1070 case TNY_FOLDER_TYPE_OUTBOX:
1071 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1073 &outbox_pixbuf_open,
1074 &outbox_pixbuf_close);
1076 case TNY_FOLDER_TYPE_JUNK:
1077 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1080 &junk_pixbuf_close);
1082 case TNY_FOLDER_TYPE_SENT:
1083 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1086 &sent_pixbuf_close);
1088 case TNY_FOLDER_TYPE_TRASH:
1089 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1092 &trash_pixbuf_close);
1094 case TNY_FOLDER_TYPE_DRAFTS:
1095 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1098 &draft_pixbuf_close);
1100 case TNY_FOLDER_TYPE_ARCHIVE:
1101 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1106 case TNY_FOLDER_TYPE_NORMAL:
1108 /* Memory card folders could have an special icon */
1109 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1110 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1115 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1117 &normal_pixbuf_open,
1118 &normal_pixbuf_close);
1127 free_pixbufs (ThreePixbufs *pixbufs)
1129 if (pixbufs->pixbuf)
1130 g_object_unref (pixbufs->pixbuf);
1131 if (pixbufs->pixbuf_open)
1132 g_object_unref (pixbufs->pixbuf_open);
1133 if (pixbufs->pixbuf_close)
1134 g_object_unref (pixbufs->pixbuf_close);
1135 g_slice_free (ThreePixbufs, pixbufs);
1139 icon_cell_data (GtkTreeViewColumn *column,
1140 GtkCellRenderer *renderer,
1141 GtkTreeModel *tree_model,
1145 GObject *rendobj = NULL, *instance = NULL;
1146 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1147 gboolean has_children;
1148 ThreePixbufs *pixbufs;
1149 ModestFolderView *folder_view = (ModestFolderView *) data;
1151 rendobj = (GObject *) renderer;
1153 gtk_tree_model_get (tree_model, iter,
1155 INSTANCE_COLUMN, &instance,
1161 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1162 pixbufs = get_folder_icons (folder_view, type, instance);
1163 g_object_unref (instance);
1166 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1169 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1170 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1173 free_pixbufs (pixbufs);
1177 add_columns (GtkWidget *treeview)
1179 GtkTreeViewColumn *column;
1180 GtkCellRenderer *renderer;
1181 GtkTreeSelection *sel;
1182 ModestFolderViewPrivate *priv;
1184 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1187 column = gtk_tree_view_column_new ();
1189 /* Set icon and text render function */
1190 renderer = gtk_cell_renderer_pixbuf_new();
1191 #ifdef MODEST_TOOLKIT_HILDON2
1192 g_object_set (renderer,
1193 "xpad", MODEST_MARGIN_DEFAULT,
1194 "ypad", MODEST_MARGIN_DEFAULT,
1197 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1198 gtk_tree_view_column_set_cell_data_func(column, renderer,
1199 icon_cell_data, treeview, NULL);
1201 renderer = gtk_cell_renderer_text_new();
1202 g_object_set (renderer,
1203 #ifdef MODEST_TOOLKIT_HILDON2
1204 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1205 "ypad", MODEST_MARGIN_DEFAULT,
1206 "xpad", MODEST_MARGIN_DEFAULT,
1208 "ellipsize", PANGO_ELLIPSIZE_END,
1210 "ellipsize-set", TRUE, NULL);
1211 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1212 gtk_tree_view_column_set_cell_data_func(column, renderer,
1213 text_cell_data, treeview, NULL);
1215 priv->messages_renderer = gtk_cell_renderer_text_new ();
1216 g_object_set (priv->messages_renderer,
1217 #ifdef MODEST_TOOLKIT_HILDON2
1219 "ypad", MODEST_MARGIN_DEFAULT,
1220 "xpad", MODEST_MARGIN_DOUBLE,
1222 "scale", PANGO_SCALE_X_SMALL,
1225 "alignment", PANGO_ALIGN_RIGHT,
1229 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1230 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1231 messages_cell_data, treeview, NULL);
1233 /* Set selection mode */
1234 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1235 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1237 /* Set treeview appearance */
1238 gtk_tree_view_column_set_spacing (column, 2);
1239 gtk_tree_view_column_set_resizable (column, TRUE);
1240 gtk_tree_view_column_set_fixed_width (column, TRUE);
1241 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1242 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1245 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1249 modest_folder_view_init (ModestFolderView *obj)
1251 ModestFolderViewPrivate *priv;
1254 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1256 priv->timer_expander = 0;
1257 priv->account_store = NULL;
1259 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1260 priv->cur_folder_store = NULL;
1261 priv->visible_account_id = NULL;
1262 priv->mailbox = NULL;
1263 priv->folder_to_select = NULL;
1264 priv->outbox_deleted_handler = 0;
1265 priv->reexpand = TRUE;
1266 priv->activity_changed_handler = 0;
1268 /* Initialize the local account name */
1269 conf = modest_runtime_get_conf();
1270 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1272 /* Init email clipboard */
1273 priv->clipboard = modest_runtime_get_email_clipboard ();
1274 priv->hidding_ids = NULL;
1275 priv->n_selected = 0;
1276 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1277 priv->reselect = FALSE;
1278 priv->show_non_move = TRUE;
1279 priv->list_to_move = NULL;
1280 priv->show_message_count = TRUE;
1282 /* Build treeview */
1283 add_columns (GTK_WIDGET (obj));
1285 /* Setup drag and drop */
1286 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1288 /* Connect signals */
1289 g_signal_connect (G_OBJECT (obj),
1291 G_CALLBACK (on_key_pressed), NULL);
1293 priv->display_name_changed_signal =
1294 g_signal_connect (modest_runtime_get_account_mgr (),
1295 "display_name_changed",
1296 G_CALLBACK (on_display_name_changed),
1300 * Track changes in the local account name (in the device it
1301 * will be the device name)
1303 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1305 G_CALLBACK(on_configuration_key_changed),
1309 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1315 tny_account_store_view_init (gpointer g, gpointer iface_data)
1317 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1319 klass->set_account_store = modest_folder_view_set_account_store;
1323 modest_folder_view_finalize (GObject *obj)
1325 ModestFolderViewPrivate *priv;
1326 GtkTreeSelection *sel;
1327 TnyAccount *local_account;
1329 g_return_if_fail (obj);
1331 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1333 if (priv->timer_expander != 0) {
1334 g_source_remove (priv->timer_expander);
1335 priv->timer_expander = 0;
1338 local_account = (TnyAccount *)
1339 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1340 if (local_account) {
1341 if (g_signal_handler_is_connected (local_account,
1342 priv->outbox_deleted_handler))
1343 g_signal_handler_disconnect (local_account,
1344 priv->outbox_deleted_handler);
1345 g_object_unref (local_account);
1348 if (priv->account_store) {
1349 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1350 priv->account_inserted_signal);
1351 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1352 priv->account_removed_signal);
1353 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1354 priv->account_changed_signal);
1355 g_object_unref (G_OBJECT(priv->account_store));
1356 priv->account_store = NULL;
1359 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1360 priv->display_name_changed_signal)) {
1361 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1362 priv->display_name_changed_signal);
1363 priv->display_name_changed_signal = 0;
1367 g_object_unref (G_OBJECT (priv->query));
1371 if (priv->folder_to_select) {
1372 g_object_unref (G_OBJECT(priv->folder_to_select));
1373 priv->folder_to_select = NULL;
1376 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1378 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1380 g_free (priv->local_account_name);
1381 g_free (priv->visible_account_id);
1382 g_free (priv->mailbox);
1384 if (priv->conf_key_signal) {
1385 g_signal_handler_disconnect (modest_runtime_get_conf (),
1386 priv->conf_key_signal);
1387 priv->conf_key_signal = 0;
1390 if (priv->cur_folder_store) {
1391 g_object_unref (priv->cur_folder_store);
1392 priv->cur_folder_store = NULL;
1395 if (priv->list_to_move) {
1396 g_object_unref (priv->list_to_move);
1397 priv->list_to_move = NULL;
1400 /* Clear hidding array created by cut operation */
1401 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1403 G_OBJECT_CLASS(parent_class)->finalize (obj);
1408 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1410 ModestFolderViewPrivate *priv;
1413 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1414 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1416 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1417 device = tny_account_store_get_device (account_store);
1419 if (G_UNLIKELY (priv->account_store)) {
1421 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1422 priv->account_inserted_signal))
1423 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1424 priv->account_inserted_signal);
1425 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1426 priv->account_removed_signal))
1427 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1428 priv->account_removed_signal);
1429 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1430 priv->account_changed_signal))
1431 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1432 priv->account_changed_signal);
1433 g_object_unref (G_OBJECT (priv->account_store));
1436 priv->account_store = g_object_ref (G_OBJECT (account_store));
1438 priv->account_removed_signal =
1439 g_signal_connect (G_OBJECT(account_store), "account_removed",
1440 G_CALLBACK (on_account_removed), self);
1442 priv->account_inserted_signal =
1443 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1444 G_CALLBACK (on_account_inserted), self);
1446 priv->account_changed_signal =
1447 g_signal_connect (G_OBJECT(account_store), "account_changed",
1448 G_CALLBACK (on_account_changed), self);
1450 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1451 priv->reselect = FALSE;
1452 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1454 g_object_unref (G_OBJECT (device));
1458 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1461 ModestFolderView *self;
1462 GtkTreeModel *model, *filter_model;
1465 self = MODEST_FOLDER_VIEW (user_data);
1467 if (!get_inner_models (self, &filter_model, NULL, &model))
1470 /* Remove outbox from model */
1471 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1472 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1473 g_object_unref (outbox);
1476 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1480 on_account_inserted (TnyAccountStore *account_store,
1481 TnyAccount *account,
1484 ModestFolderViewPrivate *priv;
1485 GtkTreeModel *model, *filter_model;
1487 /* Ignore transport account insertions, we're not showing them
1488 in the folder view */
1489 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1492 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1495 /* If we're adding a new account, and there is no previous
1496 one, we need to select the visible server account */
1497 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1498 !priv->visible_account_id)
1499 modest_widget_memory_restore (modest_runtime_get_conf(),
1500 G_OBJECT (user_data),
1501 MODEST_CONF_FOLDER_VIEW_KEY);
1505 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1506 &filter_model, NULL, &model))
1509 /* Insert the account in the model */
1510 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1512 /* When the model is a list store (plain representation) the
1513 outbox is not a child of any account so we have to manually
1514 delete it because removing the local folders account won't
1515 delete it (because tny_folder_get_account() is not defined
1516 for a merge folder */
1517 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1518 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1520 priv->outbox_deleted_handler =
1521 g_signal_connect (account,
1523 G_CALLBACK (on_outbox_deleted_cb),
1527 /* Refilter the model */
1528 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1533 same_account_selected (ModestFolderView *self,
1534 TnyAccount *account)
1536 ModestFolderViewPrivate *priv;
1537 gboolean same_account = FALSE;
1539 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1541 if (priv->cur_folder_store) {
1542 TnyAccount *selected_folder_account = NULL;
1544 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1545 selected_folder_account =
1546 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1548 selected_folder_account =
1549 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1552 if (selected_folder_account == account)
1553 same_account = TRUE;
1555 g_object_unref (selected_folder_account);
1557 return same_account;
1562 * Selects the first inbox or the local account in an idle
1565 on_idle_select_first_inbox_or_local (gpointer user_data)
1567 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1569 gdk_threads_enter ();
1570 modest_folder_view_select_first_inbox_or_local (self);
1571 gdk_threads_leave ();
1577 on_account_changed (TnyAccountStore *account_store,
1578 TnyAccount *tny_account,
1581 ModestFolderView *self;
1582 ModestFolderViewPrivate *priv;
1583 GtkTreeModel *model, *filter_model;
1584 GtkTreeSelection *sel;
1585 gboolean same_account;
1587 /* Ignore transport account insertions, we're not showing them
1588 in the folder view */
1589 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1592 self = MODEST_FOLDER_VIEW (user_data);
1593 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1595 /* Get the inner model */
1596 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1597 &filter_model, NULL, &model))
1600 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1602 /* Invalidate the cur_folder_store only if the selected folder
1603 belongs to the account that is being removed */
1604 same_account = same_account_selected (self, tny_account);
1606 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1607 gtk_tree_selection_unselect_all (sel);
1610 /* Remove the account from the model */
1611 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1613 /* Insert the account in the model */
1614 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1616 /* Refilter the model */
1617 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1619 /* Select the first INBOX if the currently selected folder
1620 belongs to the account that is being deleted */
1621 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1622 g_idle_add (on_idle_select_first_inbox_or_local, self);
1626 on_account_removed (TnyAccountStore *account_store,
1627 TnyAccount *account,
1630 ModestFolderView *self = NULL;
1631 ModestFolderViewPrivate *priv;
1632 GtkTreeModel *model, *filter_model;
1633 GtkTreeSelection *sel = NULL;
1634 gboolean same_account = FALSE;
1636 /* Ignore transport account removals, we're not showing them
1637 in the folder view */
1638 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1641 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1642 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1646 self = MODEST_FOLDER_VIEW (user_data);
1647 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1649 /* Invalidate the cur_folder_store only if the selected folder
1650 belongs to the account that is being removed */
1651 same_account = same_account_selected (self, account);
1653 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1654 gtk_tree_selection_unselect_all (sel);
1657 /* Invalidate row to select only if the folder to select
1658 belongs to the account that is being removed*/
1659 if (priv->folder_to_select) {
1660 TnyAccount *folder_to_select_account = NULL;
1662 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1663 if (folder_to_select_account == account) {
1664 modest_folder_view_disable_next_folder_selection (self);
1665 g_object_unref (priv->folder_to_select);
1666 priv->folder_to_select = NULL;
1668 g_object_unref (folder_to_select_account);
1671 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1672 &filter_model, NULL, &model))
1675 /* Disconnect the signal handler */
1676 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1677 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1678 if (g_signal_handler_is_connected (account,
1679 priv->outbox_deleted_handler))
1680 g_signal_handler_disconnect (account,
1681 priv->outbox_deleted_handler);
1684 /* Remove the account from the model */
1685 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1687 /* If the removed account is the currently viewed one then
1688 clear the configuration value. The new visible account will be the default account */
1689 if (priv->visible_account_id &&
1690 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1692 /* Clear the current visible account_id */
1693 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1694 modest_folder_view_set_mailbox (self, NULL);
1696 /* Call the restore method, this will set the new visible account */
1697 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1698 MODEST_CONF_FOLDER_VIEW_KEY);
1701 /* Refilter the model */
1702 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1704 /* Select the first INBOX if the currently selected folder
1705 belongs to the account that is being deleted */
1707 g_idle_add (on_idle_select_first_inbox_or_local, self);
1711 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1713 GtkTreeViewColumn *col;
1715 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1717 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1719 g_printerr ("modest: failed get column for title\n");
1723 gtk_tree_view_column_set_title (col, title);
1724 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1729 modest_folder_view_on_map (ModestFolderView *self,
1730 GdkEventExpose *event,
1733 ModestFolderViewPrivate *priv;
1735 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1737 /* This won't happen often */
1738 if (G_UNLIKELY (priv->reselect)) {
1739 /* Select the first inbox or the local account if not found */
1741 /* TODO: this could cause a lock at startup, so we
1742 comment it for the moment. We know that this will
1743 be a bug, because the INBOX is not selected, but we
1744 need to rewrite some parts of Modest to avoid the
1745 deathlock situation */
1746 /* TODO: check if this is still the case */
1747 priv->reselect = FALSE;
1748 modest_folder_view_select_first_inbox_or_local (self);
1749 /* Notify the display name observers */
1750 g_signal_emit (G_OBJECT(self),
1751 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1755 if (priv->reexpand) {
1756 expand_root_items (self);
1757 priv->reexpand = FALSE;
1764 modest_folder_view_new (TnyFolderStoreQuery *query)
1767 ModestFolderViewPrivate *priv;
1768 GtkTreeSelection *sel;
1770 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1771 #ifdef MODEST_TOOLKIT_HILDON2
1772 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1775 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1778 priv->query = g_object_ref (query);
1780 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1781 priv->changed_signal = g_signal_connect (sel, "changed",
1782 G_CALLBACK (on_selection_changed), self);
1784 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1786 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1788 return GTK_WIDGET(self);
1791 /* this feels dirty; any other way to expand all the root items? */
1793 expand_root_items (ModestFolderView *self)
1796 GtkTreeModel *model;
1799 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1800 path = gtk_tree_path_new_first ();
1802 /* all folders should have child items, so.. */
1804 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1805 gtk_tree_path_next (path);
1806 } while (gtk_tree_model_get_iter (model, &iter, path));
1808 gtk_tree_path_free (path);
1812 is_parent_of (TnyFolder *a, TnyFolder *b)
1815 gboolean retval = FALSE;
1817 a_id = tny_folder_get_id (a);
1819 gchar *string_to_match;
1822 string_to_match = g_strconcat (a_id, "/", NULL);
1823 b_id = tny_folder_get_id (b);
1824 retval = g_str_has_prefix (b_id, string_to_match);
1825 g_free (string_to_match);
1831 typedef struct _ForeachFolderInfo {
1834 } ForeachFolderInfo;
1837 foreach_folder_with_id (GtkTreeModel *model,
1842 ForeachFolderInfo *info;
1845 info = (ForeachFolderInfo *) data;
1846 gtk_tree_model_get (model, iter,
1847 INSTANCE_COLUMN, &instance,
1850 if (TNY_IS_FOLDER (instance)) {
1853 id = tny_folder_get_id (TNY_FOLDER (instance));
1855 collate = g_utf8_collate_key (id, -1);
1856 info->found = !strcmp (info->needle, collate);
1862 g_object_unref (instance);
1870 has_folder_with_id (ModestFolderView *self, const gchar *id)
1872 GtkTreeModel *model;
1873 ForeachFolderInfo info = {NULL, FALSE};
1875 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1876 info.needle = g_utf8_collate_key (id, -1);
1878 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1879 g_free (info.needle);
1885 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1888 gboolean retval = FALSE;
1890 a_id = tny_folder_get_id (a);
1893 b_id = tny_folder_get_id (b);
1896 const gchar *last_bar;
1897 gchar *string_to_match;
1898 last_bar = g_strrstr (b_id, "/");
1903 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1904 retval = has_folder_with_id (self, string_to_match);
1905 g_free (string_to_match);
1913 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1915 ModestFolderViewPrivate *priv;
1916 TnyIterator *iterator;
1917 gboolean retval = TRUE;
1919 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1920 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1922 for (iterator = tny_list_create_iterator (priv->list_to_move);
1923 retval && !tny_iterator_is_done (iterator);
1924 tny_iterator_next (iterator)) {
1926 instance = tny_iterator_get_current (iterator);
1927 if (instance == (GObject *) folder) {
1929 } else if (TNY_IS_FOLDER (instance)) {
1930 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1932 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1935 g_object_unref (instance);
1937 g_object_unref (iterator);
1944 * We use this function to implement the
1945 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1946 * account in this case, and the local folders.
1949 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1951 ModestFolderViewPrivate *priv;
1952 gboolean retval = TRUE;
1953 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1954 GObject *instance = NULL;
1955 const gchar *id = NULL;
1957 gboolean found = FALSE;
1958 gboolean cleared = FALSE;
1959 ModestTnyFolderRules rules = 0;
1962 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1963 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1965 gtk_tree_model_get (model, iter,
1966 NAME_COLUMN, &fname,
1968 INSTANCE_COLUMN, &instance,
1971 /* Do not show if there is no instance, this could indeed
1972 happen when the model is being modified while it's being
1973 drawn. This could occur for example when moving folders
1980 if (TNY_IS_ACCOUNT (instance)) {
1981 TnyAccount *acc = TNY_ACCOUNT (instance);
1982 const gchar *account_id = tny_account_get_id (acc);
1984 /* If it isn't a special folder,
1985 * don't show it unless it is the visible account: */
1986 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1987 !modest_tny_account_is_virtual_local_folders (acc) &&
1988 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1990 /* Show only the visible account id */
1991 if (priv->visible_account_id) {
1992 if (strcmp (account_id, priv->visible_account_id))
1999 /* Never show these to the user. They are merged into one folder
2000 * in the local-folders account instead: */
2001 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2004 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2005 /* Only show special folders for current account if needed */
2006 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2007 TnyAccount *account;
2009 account = tny_folder_get_account (TNY_FOLDER (instance));
2011 if (TNY_IS_ACCOUNT (account)) {
2012 const gchar *account_id = tny_account_get_id (account);
2014 if (!modest_tny_account_is_virtual_local_folders (account) &&
2015 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2016 /* Show only the visible account id */
2017 if (priv->visible_account_id) {
2018 if (strcmp (account_id, priv->visible_account_id)) {
2020 } else if (priv->mailbox) {
2021 /* Filter mailboxes */
2022 if (!g_str_has_prefix (fname, priv->mailbox)) {
2024 } else if (!strcmp (fname, priv->mailbox)) {
2025 /* Hide mailbox parent */
2031 g_object_unref (account);
2038 /* Check hiding (if necessary) */
2039 cleared = modest_email_clipboard_cleared (priv->clipboard);
2040 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2041 id = tny_folder_get_id (TNY_FOLDER(instance));
2042 if (priv->hidding_ids != NULL)
2043 for (i=0; i < priv->n_selected && !found; i++)
2044 if (priv->hidding_ids[i] != NULL && id != NULL)
2045 found = (!strcmp (priv->hidding_ids[i], id));
2050 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2051 folder as no message can be move there according to UI specs */
2052 if (retval && !priv->show_non_move) {
2053 if (priv->list_to_move &&
2054 tny_list_get_length (priv->list_to_move) > 0 &&
2055 TNY_IS_FOLDER (instance)) {
2056 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2058 if (retval && TNY_IS_FOLDER (instance) &&
2059 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2061 case TNY_FOLDER_TYPE_OUTBOX:
2062 case TNY_FOLDER_TYPE_SENT:
2063 case TNY_FOLDER_TYPE_DRAFTS:
2066 case TNY_FOLDER_TYPE_UNKNOWN:
2067 case TNY_FOLDER_TYPE_NORMAL:
2068 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2069 if (type == TNY_FOLDER_TYPE_INVALID)
2070 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2072 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2073 type == TNY_FOLDER_TYPE_SENT
2074 || type == TNY_FOLDER_TYPE_DRAFTS)
2081 if (retval && TNY_IS_ACCOUNT (instance) &&
2082 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2083 ModestProtocolType protocol_type;
2085 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2086 retval = !modest_protocol_registry_protocol_type_has_tag
2087 (modest_runtime_get_protocol_registry (),
2089 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2093 /* apply special filters */
2094 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2095 if (TNY_IS_ACCOUNT (instance))
2099 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2100 if (TNY_IS_FOLDER (instance))
2104 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2105 if (TNY_IS_ACCOUNT (instance)) {
2106 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2108 } else if (TNY_IS_FOLDER (instance)) {
2109 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2114 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2115 if (TNY_IS_ACCOUNT (instance)) {
2116 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2118 } else if (TNY_IS_FOLDER (instance)) {
2119 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2124 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2125 /* A mailbox is a fake folder with an @ in the middle of the name */
2126 if (!TNY_IS_FOLDER (instance) ||
2127 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2130 const gchar *folder_name;
2131 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2132 if (!folder_name || strchr (folder_name, '@') == NULL)
2138 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2139 if (TNY_IS_FOLDER (instance)) {
2140 /* Check folder rules */
2141 ModestTnyFolderRules rules;
2143 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2144 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2145 } else if (TNY_IS_ACCOUNT (instance)) {
2146 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2154 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2155 if (TNY_IS_FOLDER (instance)) {
2156 TnyFolderType guess_type;
2158 if (TNY_FOLDER_TYPE_NORMAL) {
2159 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2165 case TNY_FOLDER_TYPE_OUTBOX:
2166 case TNY_FOLDER_TYPE_SENT:
2167 case TNY_FOLDER_TYPE_DRAFTS:
2168 case TNY_FOLDER_TYPE_ARCHIVE:
2169 case TNY_FOLDER_TYPE_INBOX:
2172 case TNY_FOLDER_TYPE_UNKNOWN:
2173 case TNY_FOLDER_TYPE_NORMAL:
2179 } else if (TNY_IS_ACCOUNT (instance)) {
2184 if (retval && TNY_IS_FOLDER (instance)) {
2185 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2188 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2189 if (TNY_IS_FOLDER (instance)) {
2190 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2191 } else if (TNY_IS_ACCOUNT (instance)) {
2196 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2197 if (TNY_IS_FOLDER (instance)) {
2198 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2199 } else if (TNY_IS_ACCOUNT (instance)) {
2204 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2205 if (TNY_IS_FOLDER (instance)) {
2206 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2207 } else if (TNY_IS_ACCOUNT (instance)) {
2213 g_object_unref (instance);
2221 modest_folder_view_update_model (ModestFolderView *self,
2222 TnyAccountStore *account_store)
2224 ModestFolderViewPrivate *priv;
2225 GtkTreeModel *model;
2226 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2228 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2229 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2232 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2234 /* Notify that there is no folder selected */
2235 g_signal_emit (G_OBJECT(self),
2236 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2238 if (priv->cur_folder_store) {
2239 g_object_unref (priv->cur_folder_store);
2240 priv->cur_folder_store = NULL;
2243 /* FIXME: the local accounts are not shown when the query
2244 selects only the subscribed folders */
2245 #ifdef MODEST_TOOLKIT_HILDON2
2246 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2247 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2248 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2249 MODEST_FOLDER_PATH_SEPARATOR);
2251 model = tny_gtk_folder_store_tree_model_new (NULL);
2254 /* When the model is a list store (plain representation) the
2255 outbox is not a child of any account so we have to manually
2256 delete it because removing the local folders account won't
2257 delete it (because tny_folder_get_account() is not defined
2258 for a merge folder */
2259 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2260 TnyAccount *account;
2261 ModestTnyAccountStore *acc_store;
2263 acc_store = modest_runtime_get_account_store ();
2264 account = modest_tny_account_store_get_local_folders_account (acc_store);
2266 if (g_signal_handler_is_connected (account,
2267 priv->outbox_deleted_handler))
2268 g_signal_handler_disconnect (account,
2269 priv->outbox_deleted_handler);
2271 priv->outbox_deleted_handler =
2272 g_signal_connect (account,
2274 G_CALLBACK (on_outbox_deleted_cb),
2276 g_object_unref (account);
2279 /* Get the accounts: */
2280 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2282 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2284 sortable = gtk_tree_model_sort_new_with_model (model);
2285 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2287 GTK_SORT_ASCENDING);
2288 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2290 cmp_rows, NULL, NULL);
2292 /* Create filter model */
2293 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2294 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2299 if (priv->activity_changed_handler > 0) {
2300 GtkTreeModel *old_tny_model;
2302 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2303 g_signal_handler_disconnect (G_OBJECT (old_tny_model), priv->activity_changed_handler);
2305 priv->activity_changed_handler = 0;
2309 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2310 #ifndef MODEST_TOOLKIT_HILDON2
2311 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2312 (GCallback) on_row_inserted_maybe_select_folder, self);
2315 #ifdef MODEST_TOOLKIT_HILDON2
2316 priv->activity_changed_handler =
2317 g_signal_connect (G_OBJECT (model), "activity-changed", G_CALLBACK (on_activity_changed), self);
2320 g_object_unref (model);
2321 g_object_unref (filter_model);
2322 g_object_unref (sortable);
2324 /* Force a reselection of the INBOX next time the widget is shown */
2325 priv->reselect = TRUE;
2332 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2334 GtkTreeModel *model = NULL;
2335 TnyFolderStore *folder = NULL;
2337 ModestFolderView *tree_view = NULL;
2338 ModestFolderViewPrivate *priv = NULL;
2339 gboolean selected = FALSE;
2341 g_return_if_fail (sel);
2342 g_return_if_fail (user_data);
2344 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2346 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2348 tree_view = MODEST_FOLDER_VIEW (user_data);
2351 gtk_tree_model_get (model, &iter,
2352 INSTANCE_COLUMN, &folder,
2355 /* If the folder is the same do not notify */
2356 if (folder && priv->cur_folder_store == folder) {
2357 g_object_unref (folder);
2362 /* Current folder was unselected */
2363 if (priv->cur_folder_store) {
2364 /* We must do this firstly because a libtinymail-camel
2365 implementation detail. If we issue the signal
2366 before doing the sync_async, then that signal could
2367 cause (and it actually does it) a free of the
2368 summary of the folder (because the main window will
2369 clear the headers view */
2370 if (TNY_IS_FOLDER(priv->cur_folder_store))
2371 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2372 FALSE, NULL, NULL, NULL);
2374 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2375 priv->cur_folder_store, FALSE);
2377 g_object_unref (priv->cur_folder_store);
2378 priv->cur_folder_store = NULL;
2381 /* New current references */
2382 priv->cur_folder_store = folder;
2384 /* New folder has been selected. Do not notify if there is
2385 nothing new selected */
2387 g_signal_emit (G_OBJECT(tree_view),
2388 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2389 0, priv->cur_folder_store, TRUE);
2394 on_row_activated (GtkTreeView *treeview,
2395 GtkTreePath *treepath,
2396 GtkTreeViewColumn *column,
2399 GtkTreeModel *model = NULL;
2400 TnyFolderStore *folder = NULL;
2402 ModestFolderView *self = NULL;
2403 ModestFolderViewPrivate *priv = NULL;
2405 g_return_if_fail (treeview);
2406 g_return_if_fail (user_data);
2408 self = MODEST_FOLDER_VIEW (user_data);
2409 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2411 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2413 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2416 gtk_tree_model_get (model, &iter,
2417 INSTANCE_COLUMN, &folder,
2420 g_signal_emit (G_OBJECT(self),
2421 signals[FOLDER_ACTIVATED_SIGNAL],
2424 #ifdef MODEST_TOOLKIT_HILDON2
2425 HildonUIMode ui_mode;
2426 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2427 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2428 if (priv->cur_folder_store)
2429 g_object_unref (priv->cur_folder_store);
2430 priv->cur_folder_store = g_object_ref (folder);
2434 g_object_unref (folder);
2438 modest_folder_view_get_selected (ModestFolderView *self)
2440 ModestFolderViewPrivate *priv;
2442 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2444 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2445 if (priv->cur_folder_store)
2446 g_object_ref (priv->cur_folder_store);
2448 return priv->cur_folder_store;
2452 get_cmp_rows_type_pos (GObject *folder)
2454 /* Remote accounts -> Local account -> MMC account .*/
2457 if (TNY_IS_ACCOUNT (folder) &&
2458 modest_tny_account_is_virtual_local_folders (
2459 TNY_ACCOUNT (folder))) {
2461 } else if (TNY_IS_ACCOUNT (folder)) {
2462 TnyAccount *account = TNY_ACCOUNT (folder);
2463 const gchar *account_id = tny_account_get_id (account);
2464 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2470 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2471 return -1; /* Should never happen */
2476 inbox_is_special (TnyFolderStore *folder_store)
2478 gboolean is_special = TRUE;
2480 if (TNY_IS_FOLDER (folder_store)) {
2484 gchar *last_inbox_bar;
2486 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2487 downcase = g_utf8_strdown (id, -1);
2488 last_bar = g_strrstr (downcase, "/");
2490 last_inbox_bar = g_strrstr (downcase, "inbox/");
2491 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2502 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2504 TnyAccount *account;
2505 gboolean is_special;
2506 /* Inbox, Outbox, Drafts, Sent, User */
2509 if (!TNY_IS_FOLDER (folder_store))
2512 case TNY_FOLDER_TYPE_INBOX:
2514 account = tny_folder_get_account (folder_store);
2515 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2517 /* In inbox case we need to know if the inbox is really the top
2518 * inbox of the account, or if it's a submailbox inbox. To do
2519 * this we'll apply an heuristic rule: Find last "/" and check
2520 * if it's preceeded by another Inbox */
2521 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2522 g_object_unref (account);
2523 return is_special?0:4;
2526 case TNY_FOLDER_TYPE_OUTBOX:
2527 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2529 case TNY_FOLDER_TYPE_DRAFTS:
2531 account = tny_folder_get_account (folder_store);
2532 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2533 g_object_unref (account);
2534 return is_special?1:4;
2537 case TNY_FOLDER_TYPE_SENT:
2539 account = tny_folder_get_account (folder_store);
2540 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2541 g_object_unref (account);
2542 return is_special?3:4;
2551 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2553 const gchar *a1_name, *a2_name;
2555 a1_name = tny_account_get_name (a1);
2556 a2_name = tny_account_get_name (a2);
2558 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2562 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2564 TnyAccount *a1 = NULL, *a2 = NULL;
2567 if (TNY_IS_ACCOUNT (s1)) {
2568 a1 = TNY_ACCOUNT (g_object_ref (s1));
2569 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2570 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2573 if (TNY_IS_ACCOUNT (s2)) {
2574 a2 = TNY_ACCOUNT (g_object_ref (s2));
2575 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2576 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2593 /* First we sort with the type of account */
2594 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2598 cmp = compare_account_names (a1, a2);
2602 g_object_unref (a1);
2604 g_object_unref (a2);
2610 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2612 gint is_account1, is_account2;
2614 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2615 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2617 return is_account2 - is_account1;
2621 * This function orders the mail accounts according to these rules:
2622 * 1st - remote accounts
2623 * 2nd - local account
2627 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2631 gchar *name1 = NULL;
2632 gchar *name2 = NULL;
2633 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2634 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2635 GObject *folder1 = NULL;
2636 GObject *folder2 = NULL;
2638 gtk_tree_model_get (tree_model, iter1,
2639 NAME_COLUMN, &name1,
2641 INSTANCE_COLUMN, &folder1,
2643 gtk_tree_model_get (tree_model, iter2,
2644 NAME_COLUMN, &name2,
2645 TYPE_COLUMN, &type2,
2646 INSTANCE_COLUMN, &folder2,
2649 /* Return if we get no folder. This could happen when folder
2650 operations are happening. The model is updated after the
2651 folder copy/move actually occurs, so there could be
2652 situations where the model to be drawn is not correct */
2653 if (!folder1 || !folder2)
2656 /* Sort by type. First the special folders, then the archives */
2657 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2661 /* Now we sort using the account of each folder */
2662 if (TNY_IS_FOLDER_STORE (folder1) &&
2663 TNY_IS_FOLDER_STORE (folder2)) {
2664 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2668 /* Each group is preceeded by its account */
2669 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2674 /* Pure sort by name */
2675 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2678 g_object_unref(G_OBJECT(folder1));
2680 g_object_unref(G_OBJECT(folder2));
2688 /*****************************************************************************/
2689 /* DRAG and DROP stuff */
2690 /*****************************************************************************/
2692 * This function fills the #GtkSelectionData with the row and the
2693 * model that has been dragged. It's called when this widget is a
2694 * source for dnd after the event drop happened
2697 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2698 guint info, guint time, gpointer data)
2700 GtkTreeSelection *selection;
2701 GtkTreeModel *model;
2703 GtkTreePath *source_row;
2705 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2706 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2708 source_row = gtk_tree_model_get_path (model, &iter);
2709 gtk_tree_set_row_drag_data (selection_data,
2713 gtk_tree_path_free (source_row);
2717 typedef struct _DndHelper {
2718 ModestFolderView *folder_view;
2719 gboolean delete_source;
2720 GtkTreePath *source_row;
2724 dnd_helper_destroyer (DndHelper *helper)
2726 /* Free the helper */
2727 gtk_tree_path_free (helper->source_row);
2728 g_slice_free (DndHelper, helper);
2732 xfer_folder_cb (ModestMailOperation *mail_op,
2733 TnyFolder *new_folder,
2737 /* Select the folder */
2738 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2744 /* get the folder for the row the treepath refers to. */
2745 /* folder must be unref'd */
2746 static TnyFolderStore *
2747 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2750 TnyFolderStore *folder = NULL;
2752 if (gtk_tree_model_get_iter (model,&iter, path))
2753 gtk_tree_model_get (model, &iter,
2754 INSTANCE_COLUMN, &folder,
2761 * This function is used by drag_data_received_cb to manage drag and
2762 * drop of a header, i.e, and drag from the header view to the folder
2766 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2767 GtkTreeModel *dest_model,
2768 GtkTreePath *dest_row,
2769 GtkSelectionData *selection_data)
2771 TnyList *headers = NULL;
2772 TnyFolder *folder = NULL, *src_folder = NULL;
2773 TnyFolderType folder_type;
2774 GtkTreeIter source_iter, dest_iter;
2775 ModestWindowMgr *mgr = NULL;
2776 ModestWindow *main_win = NULL;
2777 gchar **uris, **tmp;
2779 /* Build the list of headers */
2780 mgr = modest_runtime_get_window_mgr ();
2781 headers = tny_simple_list_new ();
2782 uris = modest_dnd_selection_data_get_paths (selection_data);
2785 while (*tmp != NULL) {
2788 gboolean first = TRUE;
2791 path = gtk_tree_path_new_from_string (*tmp);
2792 gtk_tree_model_get_iter (source_model, &source_iter, path);
2793 gtk_tree_model_get (source_model, &source_iter,
2794 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2797 /* Do not enable d&d of headers already opened */
2798 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2799 tny_list_append (headers, G_OBJECT (header));
2801 if (G_UNLIKELY (first)) {
2802 src_folder = tny_header_get_folder (header);
2806 /* Free and go on */
2807 gtk_tree_path_free (path);
2808 g_object_unref (header);
2813 /* This could happen ig we perform a d&d very quickly over the
2814 same row that row could dissapear because message is
2816 if (!TNY_IS_FOLDER (src_folder))
2819 /* Get the target folder */
2820 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2821 gtk_tree_model_get (dest_model, &dest_iter,
2825 if (!folder || !TNY_IS_FOLDER(folder)) {
2826 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2830 folder_type = modest_tny_folder_guess_folder_type (folder);
2831 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2832 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2833 goto cleanup; /* cannot move messages there */
2836 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2837 /* g_warning ("folder not writable"); */
2838 goto cleanup; /* verboten! */
2841 /* Ask for confirmation to move */
2842 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2844 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2848 /* Transfer messages */
2849 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2854 if (G_IS_OBJECT (src_folder))
2855 g_object_unref (src_folder);
2856 if (G_IS_OBJECT(folder))
2857 g_object_unref (G_OBJECT (folder));
2858 if (G_IS_OBJECT(headers))
2859 g_object_unref (headers);
2863 TnyFolderStore *src_folder;
2864 TnyFolderStore *dst_folder;
2865 ModestFolderView *folder_view;
2870 dnd_folder_info_destroyer (DndFolderInfo *info)
2872 if (info->src_folder)
2873 g_object_unref (info->src_folder);
2874 if (info->dst_folder)
2875 g_object_unref (info->dst_folder);
2876 g_slice_free (DndFolderInfo, info);
2880 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2881 GtkWindow *parent_window,
2882 TnyAccount *account)
2885 modest_ui_actions_on_account_connection_error (parent_window, account);
2887 /* Free the helper & info */
2888 dnd_helper_destroyer (info->helper);
2889 dnd_folder_info_destroyer (info);
2893 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2895 GtkWindow *parent_window,
2896 TnyAccount *account,
2899 DndFolderInfo *info = NULL;
2900 ModestMailOperation *mail_op;
2902 info = (DndFolderInfo *) user_data;
2904 if (err || canceled) {
2905 dnd_on_connection_failed_destroyer (info, parent_window, account);
2909 /* Do the mail operation */
2910 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2911 modest_ui_actions_move_folder_error_handler,
2912 info->src_folder, NULL);
2914 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2917 /* Transfer the folder */
2918 modest_mail_operation_xfer_folder (mail_op,
2919 TNY_FOLDER (info->src_folder),
2921 info->helper->delete_source,
2923 info->helper->folder_view);
2926 g_object_unref (G_OBJECT (mail_op));
2927 dnd_helper_destroyer (info->helper);
2928 dnd_folder_info_destroyer (info);
2933 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2935 GtkWindow *parent_window,
2936 TnyAccount *account,
2939 DndFolderInfo *info = NULL;
2941 info = (DndFolderInfo *) user_data;
2943 if (err || canceled) {
2944 dnd_on_connection_failed_destroyer (info, parent_window, account);
2948 /* Connect to source folder and perform the copy/move */
2949 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2951 drag_and_drop_from_folder_view_src_folder_performer,
2956 * This function is used by drag_data_received_cb to manage drag and
2957 * drop of a folder, i.e, and drag from the folder view to the same
2961 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2962 GtkTreeModel *dest_model,
2963 GtkTreePath *dest_row,
2964 GtkSelectionData *selection_data,
2967 GtkTreeIter dest_iter, iter;
2968 TnyFolderStore *dest_folder = NULL;
2969 TnyFolderStore *folder = NULL;
2970 gboolean forbidden = FALSE;
2972 DndFolderInfo *info = NULL;
2974 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2976 g_warning ("%s: BUG: no main window", __FUNCTION__);
2977 dnd_helper_destroyer (helper);
2982 /* check the folder rules for the destination */
2983 folder = tree_path_to_folder (dest_model, dest_row);
2984 if (TNY_IS_FOLDER(folder)) {
2985 ModestTnyFolderRules rules =
2986 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2987 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2988 } else if (TNY_IS_FOLDER_STORE(folder)) {
2989 /* enable local root as destination for folders */
2990 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2991 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2994 g_object_unref (folder);
2997 /* check the folder rules for the source */
2998 folder = tree_path_to_folder (source_model, helper->source_row);
2999 if (TNY_IS_FOLDER(folder)) {
3000 ModestTnyFolderRules rules =
3001 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3002 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3005 g_object_unref (folder);
3009 /* Check if the drag is possible */
3010 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3012 modest_platform_run_information_dialog ((GtkWindow *) win,
3013 _("mail_in_ui_folder_move_target_error"),
3015 /* Restore the previous selection */
3016 folder = tree_path_to_folder (source_model, helper->source_row);
3018 if (TNY_IS_FOLDER (folder))
3019 modest_folder_view_select_folder (helper->folder_view,
3020 TNY_FOLDER (folder), FALSE);
3021 g_object_unref (folder);
3023 dnd_helper_destroyer (helper);
3028 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3029 gtk_tree_model_get (dest_model, &dest_iter,
3032 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3033 gtk_tree_model_get (source_model, &iter,
3037 /* Create the info for the performer */
3038 info = g_slice_new0 (DndFolderInfo);
3039 info->src_folder = g_object_ref (folder);
3040 info->dst_folder = g_object_ref (dest_folder);
3041 info->helper = helper;
3043 /* Connect to the destination folder and perform the copy/move */
3044 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3046 drag_and_drop_from_folder_view_dst_folder_performer,
3050 g_object_unref (dest_folder);
3051 g_object_unref (folder);
3055 * This function receives the data set by the "drag-data-get" signal
3056 * handler. This information comes within the #GtkSelectionData. This
3057 * function will manage both the drags of folders of the treeview and
3058 * drags of headers of the header view widget.
3061 on_drag_data_received (GtkWidget *widget,
3062 GdkDragContext *context,
3065 GtkSelectionData *selection_data,
3070 GtkWidget *source_widget;
3071 GtkTreeModel *dest_model, *source_model;
3072 GtkTreePath *source_row, *dest_row;
3073 GtkTreeViewDropPosition pos;
3074 gboolean delete_source = FALSE;
3075 gboolean success = FALSE;
3077 /* Do not allow further process */
3078 g_signal_stop_emission_by_name (widget, "drag-data-received");
3079 source_widget = gtk_drag_get_source_widget (context);
3081 /* Get the action */
3082 if (context->action == GDK_ACTION_MOVE) {
3083 delete_source = TRUE;
3085 /* Notify that there is no folder selected. We need to
3086 do this in order to update the headers view (and
3087 its monitors, because when moving, the old folder
3088 won't longer exist. We can not wait for the end of
3089 the operation, because the operation won't start if
3090 the folder is in use */
3091 if (source_widget == widget) {
3092 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3093 gtk_tree_selection_unselect_all (sel);
3097 /* Check if the get_data failed */
3098 if (selection_data == NULL || selection_data->length < 0)
3101 /* Select the destination model */
3102 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3104 /* Get the path to the destination row. Can not call
3105 gtk_tree_view_get_drag_dest_row() because the source row
3106 is not selected anymore */
3107 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3110 /* Only allow drops IN other rows */
3112 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3113 pos == GTK_TREE_VIEW_DROP_AFTER)
3117 /* Drags from the header view */
3118 if (source_widget != widget) {
3119 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3121 drag_and_drop_from_header_view (source_model,
3126 DndHelper *helper = NULL;
3128 /* Get the source model and row */
3129 gtk_tree_get_row_drag_data (selection_data,
3133 /* Create the helper */
3134 helper = g_slice_new0 (DndHelper);
3135 helper->delete_source = delete_source;
3136 helper->source_row = gtk_tree_path_copy (source_row);
3137 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3139 drag_and_drop_from_folder_view (source_model,
3145 gtk_tree_path_free (source_row);
3149 gtk_tree_path_free (dest_row);
3152 /* Finish the drag and drop */
3153 gtk_drag_finish (context, success, FALSE, time);
3157 * We define a "drag-drop" signal handler because we do not want to
3158 * use the default one, because the default one always calls
3159 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3160 * signal handler, because there we have all the information available
3161 * to know if the dnd was a success or not.
3164 drag_drop_cb (GtkWidget *widget,
3165 GdkDragContext *context,
3173 if (!context->targets)
3176 /* Check if we're dragging a folder row */
3177 target = gtk_drag_dest_find_target (widget, context, NULL);
3179 /* Request the data from the source. */
3180 gtk_drag_get_data(widget, context, target, time);
3186 * This function expands a node of a tree view if it's not expanded
3187 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3188 * does that, so that's why they're here.
3191 expand_row_timeout (gpointer data)
3193 GtkTreeView *tree_view = data;
3194 GtkTreePath *dest_path = NULL;
3195 GtkTreeViewDropPosition pos;
3196 gboolean result = FALSE;
3198 gdk_threads_enter ();
3200 gtk_tree_view_get_drag_dest_row (tree_view,
3205 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3206 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3207 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3208 gtk_tree_path_free (dest_path);
3212 gtk_tree_path_free (dest_path);
3217 gdk_threads_leave ();
3223 * This function is called whenever the pointer is moved over a widget
3224 * while dragging some data. It installs a timeout that will expand a
3225 * node of the treeview if not expanded yet. This function also calls
3226 * gdk_drag_status in order to set the suggested action that will be
3227 * used by the "drag-data-received" signal handler to know if we
3228 * should do a move or just a copy of the data.
3231 on_drag_motion (GtkWidget *widget,
3232 GdkDragContext *context,
3238 GtkTreeViewDropPosition pos;
3239 GtkTreePath *dest_row;
3240 GtkTreeModel *dest_model;
3241 ModestFolderViewPrivate *priv;
3242 GdkDragAction suggested_action;
3243 gboolean valid_location = FALSE;
3244 TnyFolderStore *folder = NULL;
3246 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3248 if (priv->timer_expander != 0) {
3249 g_source_remove (priv->timer_expander);
3250 priv->timer_expander = 0;
3253 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3258 /* Do not allow drops between folders */
3260 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3261 pos == GTK_TREE_VIEW_DROP_AFTER) {
3262 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3263 gdk_drag_status(context, 0, time);
3264 valid_location = FALSE;
3267 valid_location = TRUE;
3270 /* Check that the destination folder is writable */
3271 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3272 folder = tree_path_to_folder (dest_model, dest_row);
3273 if (folder && TNY_IS_FOLDER (folder)) {
3274 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3276 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3277 valid_location = FALSE;
3282 /* Expand the selected row after 1/2 second */
3283 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3284 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3286 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3288 /* Select the desired action. By default we pick MOVE */
3289 suggested_action = GDK_ACTION_MOVE;
3291 if (context->actions == GDK_ACTION_COPY)
3292 gdk_drag_status(context, GDK_ACTION_COPY, time);
3293 else if (context->actions == GDK_ACTION_MOVE)
3294 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3295 else if (context->actions & suggested_action)
3296 gdk_drag_status(context, suggested_action, time);
3298 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3302 g_object_unref (folder);
3304 gtk_tree_path_free (dest_row);
3306 g_signal_stop_emission_by_name (widget, "drag-motion");
3308 return valid_location;
3312 * This function sets the treeview as a source and a target for dnd
3313 * events. It also connects all the requirede signals.
3316 setup_drag_and_drop (GtkTreeView *self)
3318 /* Set up the folder view as a dnd destination. Set only the
3319 highlight flag, otherwise gtk will have a different
3321 #ifdef MODEST_TOOLKIT_HILDON2
3324 gtk_drag_dest_set (GTK_WIDGET (self),
3325 GTK_DEST_DEFAULT_HIGHLIGHT,
3326 folder_view_drag_types,
3327 G_N_ELEMENTS (folder_view_drag_types),
3328 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3330 g_signal_connect (G_OBJECT (self),
3331 "drag_data_received",
3332 G_CALLBACK (on_drag_data_received),
3336 /* Set up the treeview as a dnd source */
3337 gtk_drag_source_set (GTK_WIDGET (self),
3339 folder_view_drag_types,
3340 G_N_ELEMENTS (folder_view_drag_types),
3341 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3343 g_signal_connect (G_OBJECT (self),
3345 G_CALLBACK (on_drag_motion),
3348 g_signal_connect (G_OBJECT (self),
3350 G_CALLBACK (on_drag_data_get),
3353 g_signal_connect (G_OBJECT (self),
3355 G_CALLBACK (drag_drop_cb),
3360 * This function manages the navigation through the folders using the
3361 * keyboard or the hardware keys in the device
3364 on_key_pressed (GtkWidget *self,
3368 GtkTreeSelection *selection;
3370 GtkTreeModel *model;
3371 gboolean retval = FALSE;
3373 /* Up and Down are automatically managed by the treeview */
3374 if (event->keyval == GDK_Return) {
3375 /* Expand/Collapse the selected row */
3376 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3377 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3380 path = gtk_tree_model_get_path (model, &iter);
3382 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3383 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3385 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3386 gtk_tree_path_free (path);
3388 /* No further processing */
3396 * We listen to the changes in the local folder account name key,
3397 * because we want to show the right name in the view. The local
3398 * folder account name corresponds to the device name in the Maemo
3399 * version. We do this because we do not want to query gconf on each
3400 * tree view refresh. It's better to cache it and change whenever
3404 on_configuration_key_changed (ModestConf* conf,
3406 ModestConfEvent event,
3407 ModestConfNotificationId id,
3408 ModestFolderView *self)
3410 ModestFolderViewPrivate *priv;
3413 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3414 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3416 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3417 g_free (priv->local_account_name);
3419 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3420 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3422 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3423 MODEST_CONF_DEVICE_NAME, NULL);
3425 /* Force a redraw */
3426 #if GTK_CHECK_VERSION(2, 8, 0)
3427 GtkTreeViewColumn * tree_column;
3429 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3431 gtk_tree_view_column_queue_resize (tree_column);
3433 gtk_widget_queue_draw (GTK_WIDGET (self));
3439 modest_folder_view_set_style (ModestFolderView *self,
3440 ModestFolderViewStyle style)
3442 ModestFolderViewPrivate *priv;
3444 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3445 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3446 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3448 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3451 priv->style = style;
3455 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3456 const gchar *account_id)
3458 ModestFolderViewPrivate *priv;
3459 GtkTreeModel *model;
3461 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3463 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3465 /* This will be used by the filter_row callback,
3466 * to decided which rows to show: */
3467 if (priv->visible_account_id) {
3468 g_free (priv->visible_account_id);
3469 priv->visible_account_id = NULL;
3472 priv->visible_account_id = g_strdup (account_id);
3475 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3476 if (GTK_IS_TREE_MODEL_FILTER (model))
3477 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3479 /* Save settings to gconf */
3480 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3481 MODEST_CONF_FOLDER_VIEW_KEY);
3483 /* Notify observers */
3484 g_signal_emit (G_OBJECT(self),
3485 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3490 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3492 ModestFolderViewPrivate *priv;
3494 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3496 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3498 return (const gchar *) priv->visible_account_id;
3502 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3506 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3508 gtk_tree_model_get (model, iter,
3512 gboolean result = FALSE;
3513 if (type == TNY_FOLDER_TYPE_INBOX) {
3517 *inbox_iter = *iter;
3521 if (gtk_tree_model_iter_children (model, &child, iter)) {
3522 if (find_inbox_iter (model, &child, inbox_iter))
3526 } while (gtk_tree_model_iter_next (model, iter));
3535 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3537 #ifndef MODEST_TOOLKIT_HILDON2
3538 GtkTreeModel *model;
3539 GtkTreeIter iter, inbox_iter;
3540 GtkTreeSelection *sel;
3541 GtkTreePath *path = NULL;
3543 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3545 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3549 expand_root_items (self);
3550 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3552 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3553 g_warning ("%s: model is empty", __FUNCTION__);
3557 if (find_inbox_iter (model, &iter, &inbox_iter))
3558 path = gtk_tree_model_get_path (model, &inbox_iter);
3560 path = gtk_tree_path_new_first ();
3562 /* Select the row and free */
3563 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3564 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3565 gtk_tree_path_free (path);
3568 gtk_widget_grab_focus (GTK_WIDGET(self));
3575 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3580 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3581 TnyFolder* a_folder;
3584 gtk_tree_model_get (model, iter,
3585 INSTANCE_COLUMN, &a_folder,
3591 if (folder == a_folder) {
3592 g_object_unref (a_folder);
3593 *folder_iter = *iter;
3596 g_object_unref (a_folder);
3598 if (gtk_tree_model_iter_children (model, &child, iter)) {
3599 if (find_folder_iter (model, &child, folder_iter, folder))
3603 } while (gtk_tree_model_iter_next (model, iter));
3608 #ifndef MODEST_TOOLKIT_HILDON2
3610 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3613 ModestFolderView *self)
3615 ModestFolderViewPrivate *priv = NULL;
3616 GtkTreeSelection *sel;
3617 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3618 GObject *instance = NULL;
3620 if (!MODEST_IS_FOLDER_VIEW(self))
3623 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3625 priv->reexpand = TRUE;
3627 gtk_tree_model_get (tree_model, iter,
3629 INSTANCE_COLUMN, &instance,
3635 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3636 priv->folder_to_select = g_object_ref (instance);
3638 g_object_unref (instance);
3640 if (priv->folder_to_select) {
3642 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3645 path = gtk_tree_model_get_path (tree_model, iter);
3646 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3648 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3650 gtk_tree_selection_select_iter (sel, iter);
3651 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3653 gtk_tree_path_free (path);
3657 modest_folder_view_disable_next_folder_selection (self);
3659 /* Refilter the model */
3660 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3666 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3668 ModestFolderViewPrivate *priv;
3670 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3672 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3674 if (priv->folder_to_select)
3675 g_object_unref(priv->folder_to_select);
3677 priv->folder_to_select = NULL;
3681 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3682 gboolean after_change)
3684 GtkTreeModel *model;
3685 GtkTreeIter iter, folder_iter;
3686 GtkTreeSelection *sel;
3687 ModestFolderViewPrivate *priv = NULL;
3689 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3690 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3692 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3695 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3696 gtk_tree_selection_unselect_all (sel);
3698 if (priv->folder_to_select)
3699 g_object_unref(priv->folder_to_select);
3700 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3704 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3709 /* Refilter the model, before selecting the folder */
3710 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3712 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3713 g_warning ("%s: model is empty", __FUNCTION__);
3717 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3720 path = gtk_tree_model_get_path (model, &folder_iter);
3721 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3723 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3724 gtk_tree_selection_select_iter (sel, &folder_iter);
3725 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3727 gtk_tree_path_free (path);
3735 modest_folder_view_copy_selection (ModestFolderView *self)
3737 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3739 /* Copy selection */
3740 _clipboard_set_selected_data (self, FALSE);
3744 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3746 ModestFolderViewPrivate *priv = NULL;
3747 GtkTreeModel *model = NULL;
3748 const gchar **hidding = NULL;
3749 guint i, n_selected;
3751 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3752 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3754 /* Copy selection */
3755 if (!_clipboard_set_selected_data (folder_view, TRUE))
3758 /* Get hidding ids */
3759 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3761 /* Clear hidding array created by previous cut operation */
3762 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3764 /* Copy hidding array */
3765 priv->n_selected = n_selected;
3766 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3767 for (i=0; i < n_selected; i++)
3768 priv->hidding_ids[i] = g_strdup(hidding[i]);
3770 /* Hide cut folders */
3771 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3772 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3776 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3777 ModestFolderView *folder_view_dst)
3779 GtkTreeModel *filter_model = NULL;
3780 GtkTreeModel *model = NULL;
3781 GtkTreeModel *new_filter_model = NULL;
3782 GtkTreeModel *old_tny_model = NULL;
3783 GtkTreeModel *new_tny_model = NULL;
3784 ModestFolderViewPrivate *dst_priv;
3786 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3787 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3789 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3790 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3791 new_tny_model = NULL;
3794 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3795 g_signal_handler_disconnect (G_OBJECT (old_tny_model), dst_priv->activity_changed_handler);
3796 dst_priv->activity_changed_handler = 0;
3798 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3799 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3801 /* Build new filter model */
3802 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3803 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3810 /* Set copied model */
3811 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3812 #ifndef MODEST_TOOLKIT_HILDON2
3813 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3814 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3816 #ifdef MODEST_TOOLKIT_HILDON2
3818 dst_priv->activity_changed_handler = g_signal_connect (G_OBJECT (new_tny_model), "activity-changed",
3819 G_CALLBACK (on_activity_changed), folder_view_dst);
3823 g_object_unref (new_filter_model);
3827 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3830 GtkTreeModel *model = NULL;
3831 ModestFolderViewPrivate* priv;
3833 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3835 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3836 priv->show_non_move = show;
3837 /* modest_folder_view_update_model(folder_view, */
3838 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3840 /* Hide special folders */
3841 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3842 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3843 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3848 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3851 ModestFolderViewPrivate* priv;
3853 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3855 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3856 priv->show_message_count = show;
3858 g_object_set (G_OBJECT (priv->messages_renderer),
3859 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3863 /* Returns FALSE if it did not selected anything */
3865 _clipboard_set_selected_data (ModestFolderView *folder_view,
3868 ModestFolderViewPrivate *priv = NULL;
3869 TnyFolderStore *folder = NULL;
3870 gboolean retval = FALSE;
3872 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3873 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3875 /* Set selected data on clipboard */
3876 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3877 folder = modest_folder_view_get_selected (folder_view);
3879 /* Do not allow to select an account */
3880 if (TNY_IS_FOLDER (folder)) {
3881 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3886 g_object_unref (folder);
3892 _clear_hidding_filter (ModestFolderView *folder_view)
3894 ModestFolderViewPrivate *priv;
3897 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3898 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3900 if (priv->hidding_ids != NULL) {
3901 for (i=0; i < priv->n_selected; i++)
3902 g_free (priv->hidding_ids[i]);
3903 g_free(priv->hidding_ids);
3909 on_display_name_changed (ModestAccountMgr *mgr,
3910 const gchar *account,
3913 ModestFolderView *self;
3915 self = MODEST_FOLDER_VIEW (user_data);
3917 /* Force a redraw */
3918 #if GTK_CHECK_VERSION(2, 8, 0)
3919 GtkTreeViewColumn * tree_column;
3921 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3923 gtk_tree_view_column_queue_resize (tree_column);
3925 gtk_widget_queue_draw (GTK_WIDGET (self));
3930 modest_folder_view_set_cell_style (ModestFolderView *self,
3931 ModestFolderViewCellStyle cell_style)
3933 ModestFolderViewPrivate *priv = NULL;
3935 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3936 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3938 priv->cell_style = cell_style;
3940 g_object_set (G_OBJECT (priv->messages_renderer),
3941 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3944 gtk_widget_queue_draw (GTK_WIDGET (self));
3948 update_style (ModestFolderView *self)
3950 ModestFolderViewPrivate *priv;
3951 GdkColor style_color;
3952 PangoAttrList *attr_list;
3954 PangoAttribute *attr;
3956 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3957 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3961 attr_list = pango_attr_list_new ();
3962 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3963 gdk_color_parse ("grey", &style_color);
3965 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3966 pango_attr_list_insert (attr_list, attr);
3969 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3971 "SmallSystemFont", NULL,
3974 attr = pango_attr_font_desc_new (pango_font_description_copy
3975 (style->font_desc));
3976 pango_attr_list_insert (attr_list, attr);
3978 g_object_set (G_OBJECT (priv->messages_renderer),
3979 "foreground-gdk", &style_color,
3980 "foreground-set", TRUE,
3981 "attributes", attr_list,
3983 pango_attr_list_unref (attr_list);
3988 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3990 if (strcmp ("style", spec->name) == 0) {
3991 update_style (MODEST_FOLDER_VIEW (obj));
3992 gtk_widget_queue_draw (GTK_WIDGET (obj));
3997 modest_folder_view_set_filter (ModestFolderView *self,
3998 ModestFolderViewFilter filter)
4000 ModestFolderViewPrivate *priv;
4001 GtkTreeModel *filter_model;
4003 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4004 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4006 priv->filter |= filter;
4008 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4009 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4010 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4015 modest_folder_view_unset_filter (ModestFolderView *self,
4016 ModestFolderViewFilter filter)
4018 ModestFolderViewPrivate *priv;
4019 GtkTreeModel *filter_model;
4021 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4022 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4024 priv->filter &= ~filter;
4026 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4027 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4028 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4033 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4034 ModestTnyFolderRules rules)
4036 GtkTreeModel *filter_model;
4038 gboolean fulfil = FALSE;
4040 if (!get_inner_models (self, &filter_model, NULL, NULL))
4043 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4047 TnyFolderStore *folder;
4049 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4051 if (TNY_IS_FOLDER (folder)) {
4052 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4053 /* Folder rules are negative: non_writable, non_deletable... */
4054 if (!(folder_rules & rules))
4057 g_object_unref (folder);
4060 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4066 modest_folder_view_set_list_to_move (ModestFolderView *self,
4069 ModestFolderViewPrivate *priv;
4071 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4072 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4074 if (priv->list_to_move)
4075 g_object_unref (priv->list_to_move);
4078 g_object_ref (list);
4080 priv->list_to_move = list;
4084 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4086 ModestFolderViewPrivate *priv;
4088 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4089 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4092 g_free (priv->mailbox);
4094 priv->mailbox = g_strdup (mailbox);
4096 /* Notify observers */
4097 g_signal_emit (G_OBJECT(self),
4098 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4099 priv->visible_account_id);
4103 modest_folder_view_get_mailbox (ModestFolderView *self)
4105 ModestFolderViewPrivate *priv;
4107 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4108 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4110 return (const gchar *) priv->mailbox;
4114 modest_folder_view_get_activity (ModestFolderView *self)
4116 ModestFolderViewPrivate *priv;
4117 GtkTreeModel *inner_model;
4119 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4120 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4121 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4123 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4124 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4130 #ifdef MODEST_TOOLKIT_HILDON2
4132 on_activity_changed (TnyGtkFolderListStore *store,
4134 ModestFolderView *folder_view)
4136 ModestFolderViewPrivate *priv;
4138 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4139 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4140 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4142 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,