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"),
844 } else if (number == 1) {
845 item_name = g_strdup_printf (drafts?_("mcen_ti_message"):_("mcen_ti_new_message"),
853 item_name = g_strdup ("");
856 /* Set the name in the treeview cell: */
857 g_object_set (rendobj,"text", item_name, NULL);
865 g_object_unref (G_OBJECT (instance));
871 GdkPixbuf *pixbuf_open;
872 GdkPixbuf *pixbuf_close;
876 static inline GdkPixbuf *
877 get_composite_pixbuf (const gchar *icon_name,
879 GdkPixbuf *base_pixbuf)
881 GdkPixbuf *emblem, *retval = NULL;
883 emblem = modest_platform_get_icon (icon_name, size);
885 retval = gdk_pixbuf_copy (base_pixbuf);
886 gdk_pixbuf_composite (emblem, retval, 0, 0,
887 MIN (gdk_pixbuf_get_width (emblem),
888 gdk_pixbuf_get_width (retval)),
889 MIN (gdk_pixbuf_get_height (emblem),
890 gdk_pixbuf_get_height (retval)),
891 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
892 g_object_unref (emblem);
897 static inline ThreePixbufs *
898 get_composite_icons (const gchar *icon_code,
900 GdkPixbuf **pixbuf_open,
901 GdkPixbuf **pixbuf_close)
903 ThreePixbufs *retval;
906 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
909 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
914 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
918 retval = g_slice_new0 (ThreePixbufs);
920 retval->pixbuf = g_object_ref (*pixbuf);
922 retval->pixbuf_open = g_object_ref (*pixbuf_open);
924 retval->pixbuf_close = g_object_ref (*pixbuf_close);
929 static inline ThreePixbufs *
930 get_account_protocol_pixbufs (ModestFolderView *folder_view,
931 ModestProtocolType protocol_type,
934 ModestProtocol *protocol;
935 const GdkPixbuf *pixbuf = NULL;
936 ModestFolderViewPrivate *priv;
938 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
940 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
943 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
944 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
945 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
946 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
947 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
948 object, FOLDER_ICON_SIZE);
952 ThreePixbufs *retval;
953 retval = g_slice_new0 (ThreePixbufs);
954 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
955 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
956 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
963 static inline ThreePixbufs*
964 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
966 TnyAccount *account = NULL;
967 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
968 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
969 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
970 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
971 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
973 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
974 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
975 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
976 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
977 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
979 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
980 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
981 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
982 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
983 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
985 ThreePixbufs *retval = NULL;
987 if (TNY_IS_ACCOUNT (instance)) {
988 account = g_object_ref (instance);
989 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
990 account = tny_folder_get_account (TNY_FOLDER (instance));
994 ModestProtocolType account_store_protocol;
996 account_store_protocol = modest_tny_account_get_protocol_type (account);
997 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
998 g_object_unref (account);
1004 /* Sometimes an special folder is reported by the server as
1005 NORMAL, like some versions of Dovecot */
1006 if (type == TNY_FOLDER_TYPE_NORMAL ||
1007 type == TNY_FOLDER_TYPE_UNKNOWN) {
1008 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1011 /* It's not enough with check the folder type. We need to
1012 ensure that we're not giving a special folder icon to a
1013 normal folder with the same name than a special folder */
1014 if (TNY_IS_FOLDER (instance) &&
1015 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1016 type = TNY_FOLDER_TYPE_NORMAL;
1018 /* Remote folders should not be treated as special folders */
1019 if (TNY_IS_FOLDER_STORE (instance) &&
1020 !TNY_IS_ACCOUNT (instance) &&
1021 type != TNY_FOLDER_TYPE_INBOX &&
1022 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1023 #ifdef MODEST_TOOLKIT_HILDON2
1024 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1027 &anorm_pixbuf_close);
1029 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1031 &normal_pixbuf_open,
1032 &normal_pixbuf_close);
1038 case TNY_FOLDER_TYPE_INVALID:
1039 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1042 case TNY_FOLDER_TYPE_ROOT:
1043 if (TNY_IS_ACCOUNT (instance)) {
1045 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1046 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1049 &avirt_pixbuf_close);
1051 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1053 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1054 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1057 &ammc_pixbuf_close);
1059 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1062 &anorm_pixbuf_close);
1067 case TNY_FOLDER_TYPE_INBOX:
1068 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1071 &inbox_pixbuf_close);
1073 case TNY_FOLDER_TYPE_OUTBOX:
1074 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1076 &outbox_pixbuf_open,
1077 &outbox_pixbuf_close);
1079 case TNY_FOLDER_TYPE_JUNK:
1080 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1083 &junk_pixbuf_close);
1085 case TNY_FOLDER_TYPE_SENT:
1086 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1089 &sent_pixbuf_close);
1091 case TNY_FOLDER_TYPE_TRASH:
1092 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1095 &trash_pixbuf_close);
1097 case TNY_FOLDER_TYPE_DRAFTS:
1098 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1101 &draft_pixbuf_close);
1103 case TNY_FOLDER_TYPE_ARCHIVE:
1104 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1109 case TNY_FOLDER_TYPE_NORMAL:
1111 /* Memory card folders could have an special icon */
1112 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1113 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1118 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1120 &normal_pixbuf_open,
1121 &normal_pixbuf_close);
1130 free_pixbufs (ThreePixbufs *pixbufs)
1132 if (pixbufs->pixbuf)
1133 g_object_unref (pixbufs->pixbuf);
1134 if (pixbufs->pixbuf_open)
1135 g_object_unref (pixbufs->pixbuf_open);
1136 if (pixbufs->pixbuf_close)
1137 g_object_unref (pixbufs->pixbuf_close);
1138 g_slice_free (ThreePixbufs, pixbufs);
1142 icon_cell_data (GtkTreeViewColumn *column,
1143 GtkCellRenderer *renderer,
1144 GtkTreeModel *tree_model,
1148 GObject *rendobj = NULL, *instance = NULL;
1149 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1150 gboolean has_children;
1151 ThreePixbufs *pixbufs;
1152 ModestFolderView *folder_view = (ModestFolderView *) data;
1154 rendobj = (GObject *) renderer;
1156 gtk_tree_model_get (tree_model, iter,
1158 INSTANCE_COLUMN, &instance,
1164 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1165 pixbufs = get_folder_icons (folder_view, type, instance);
1166 g_object_unref (instance);
1169 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1172 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1173 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1176 free_pixbufs (pixbufs);
1180 add_columns (GtkWidget *treeview)
1182 GtkTreeViewColumn *column;
1183 GtkCellRenderer *renderer;
1184 GtkTreeSelection *sel;
1185 ModestFolderViewPrivate *priv;
1187 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1190 column = gtk_tree_view_column_new ();
1192 /* Set icon and text render function */
1193 renderer = gtk_cell_renderer_pixbuf_new();
1194 #ifdef MODEST_TOOLKIT_HILDON2
1195 g_object_set (renderer,
1196 "xpad", MODEST_MARGIN_DEFAULT,
1197 "ypad", MODEST_MARGIN_DEFAULT,
1200 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1201 gtk_tree_view_column_set_cell_data_func(column, renderer,
1202 icon_cell_data, treeview, NULL);
1204 renderer = gtk_cell_renderer_text_new();
1205 g_object_set (renderer,
1206 #ifdef MODEST_TOOLKIT_HILDON2
1207 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1208 "ypad", MODEST_MARGIN_DEFAULT,
1209 "xpad", MODEST_MARGIN_DEFAULT,
1211 "ellipsize", PANGO_ELLIPSIZE_END,
1213 "ellipsize-set", TRUE, NULL);
1214 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1215 gtk_tree_view_column_set_cell_data_func(column, renderer,
1216 text_cell_data, treeview, NULL);
1218 priv->messages_renderer = gtk_cell_renderer_text_new ();
1219 g_object_set (priv->messages_renderer,
1220 #ifdef MODEST_TOOLKIT_HILDON2
1222 "ypad", MODEST_MARGIN_DEFAULT,
1223 "xpad", MODEST_MARGIN_DOUBLE,
1225 "scale", PANGO_SCALE_X_SMALL,
1228 "alignment", PANGO_ALIGN_RIGHT,
1232 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1233 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1234 messages_cell_data, treeview, NULL);
1236 /* Set selection mode */
1237 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1238 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1240 /* Set treeview appearance */
1241 gtk_tree_view_column_set_spacing (column, 2);
1242 gtk_tree_view_column_set_resizable (column, TRUE);
1243 gtk_tree_view_column_set_fixed_width (column, TRUE);
1244 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1245 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1248 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1252 modest_folder_view_init (ModestFolderView *obj)
1254 ModestFolderViewPrivate *priv;
1257 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1259 priv->timer_expander = 0;
1260 priv->account_store = NULL;
1262 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1263 priv->cur_folder_store = NULL;
1264 priv->visible_account_id = NULL;
1265 priv->mailbox = NULL;
1266 priv->folder_to_select = NULL;
1267 priv->outbox_deleted_handler = 0;
1268 priv->reexpand = TRUE;
1269 priv->activity_changed_handler = 0;
1271 /* Initialize the local account name */
1272 conf = modest_runtime_get_conf();
1273 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1275 /* Init email clipboard */
1276 priv->clipboard = modest_runtime_get_email_clipboard ();
1277 priv->hidding_ids = NULL;
1278 priv->n_selected = 0;
1279 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1280 priv->reselect = FALSE;
1281 priv->show_non_move = TRUE;
1282 priv->list_to_move = NULL;
1283 priv->show_message_count = TRUE;
1285 /* Build treeview */
1286 add_columns (GTK_WIDGET (obj));
1288 /* Setup drag and drop */
1289 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1291 /* Connect signals */
1292 g_signal_connect (G_OBJECT (obj),
1294 G_CALLBACK (on_key_pressed), NULL);
1296 priv->display_name_changed_signal =
1297 g_signal_connect (modest_runtime_get_account_mgr (),
1298 "display_name_changed",
1299 G_CALLBACK (on_display_name_changed),
1303 * Track changes in the local account name (in the device it
1304 * will be the device name)
1306 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1308 G_CALLBACK(on_configuration_key_changed),
1312 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1318 tny_account_store_view_init (gpointer g, gpointer iface_data)
1320 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1322 klass->set_account_store = modest_folder_view_set_account_store;
1326 modest_folder_view_finalize (GObject *obj)
1328 ModestFolderViewPrivate *priv;
1329 GtkTreeSelection *sel;
1330 TnyAccount *local_account;
1332 g_return_if_fail (obj);
1334 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1336 if (priv->timer_expander != 0) {
1337 g_source_remove (priv->timer_expander);
1338 priv->timer_expander = 0;
1341 local_account = (TnyAccount *)
1342 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1343 if (local_account) {
1344 if (g_signal_handler_is_connected (local_account,
1345 priv->outbox_deleted_handler))
1346 g_signal_handler_disconnect (local_account,
1347 priv->outbox_deleted_handler);
1348 g_object_unref (local_account);
1351 if (priv->account_store) {
1352 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1353 priv->account_inserted_signal);
1354 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1355 priv->account_removed_signal);
1356 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1357 priv->account_changed_signal);
1358 g_object_unref (G_OBJECT(priv->account_store));
1359 priv->account_store = NULL;
1362 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1363 priv->display_name_changed_signal)) {
1364 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1365 priv->display_name_changed_signal);
1366 priv->display_name_changed_signal = 0;
1370 g_object_unref (G_OBJECT (priv->query));
1374 if (priv->folder_to_select) {
1375 g_object_unref (G_OBJECT(priv->folder_to_select));
1376 priv->folder_to_select = NULL;
1379 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1381 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1383 g_free (priv->local_account_name);
1384 g_free (priv->visible_account_id);
1385 g_free (priv->mailbox);
1387 if (priv->conf_key_signal) {
1388 g_signal_handler_disconnect (modest_runtime_get_conf (),
1389 priv->conf_key_signal);
1390 priv->conf_key_signal = 0;
1393 if (priv->cur_folder_store) {
1394 g_object_unref (priv->cur_folder_store);
1395 priv->cur_folder_store = NULL;
1398 if (priv->list_to_move) {
1399 g_object_unref (priv->list_to_move);
1400 priv->list_to_move = NULL;
1403 /* Clear hidding array created by cut operation */
1404 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1406 G_OBJECT_CLASS(parent_class)->finalize (obj);
1411 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1413 ModestFolderViewPrivate *priv;
1416 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1417 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1419 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1420 device = tny_account_store_get_device (account_store);
1422 if (G_UNLIKELY (priv->account_store)) {
1424 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1425 priv->account_inserted_signal))
1426 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1427 priv->account_inserted_signal);
1428 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1429 priv->account_removed_signal))
1430 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1431 priv->account_removed_signal);
1432 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1433 priv->account_changed_signal))
1434 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1435 priv->account_changed_signal);
1436 g_object_unref (G_OBJECT (priv->account_store));
1439 priv->account_store = g_object_ref (G_OBJECT (account_store));
1441 priv->account_removed_signal =
1442 g_signal_connect (G_OBJECT(account_store), "account_removed",
1443 G_CALLBACK (on_account_removed), self);
1445 priv->account_inserted_signal =
1446 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1447 G_CALLBACK (on_account_inserted), self);
1449 priv->account_changed_signal =
1450 g_signal_connect (G_OBJECT(account_store), "account_changed",
1451 G_CALLBACK (on_account_changed), self);
1453 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1454 priv->reselect = FALSE;
1455 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1457 g_object_unref (G_OBJECT (device));
1461 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1464 ModestFolderView *self;
1465 GtkTreeModel *model, *filter_model;
1468 self = MODEST_FOLDER_VIEW (user_data);
1470 if (!get_inner_models (self, &filter_model, NULL, &model))
1473 /* Remove outbox from model */
1474 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1475 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1476 g_object_unref (outbox);
1479 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1483 on_account_inserted (TnyAccountStore *account_store,
1484 TnyAccount *account,
1487 ModestFolderViewPrivate *priv;
1488 GtkTreeModel *model, *filter_model;
1490 /* Ignore transport account insertions, we're not showing them
1491 in the folder view */
1492 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1495 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1498 /* If we're adding a new account, and there is no previous
1499 one, we need to select the visible server account */
1500 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1501 !priv->visible_account_id)
1502 modest_widget_memory_restore (modest_runtime_get_conf(),
1503 G_OBJECT (user_data),
1504 MODEST_CONF_FOLDER_VIEW_KEY);
1508 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1509 &filter_model, NULL, &model))
1512 /* Insert the account in the model */
1513 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1515 /* When the model is a list store (plain representation) the
1516 outbox is not a child of any account so we have to manually
1517 delete it because removing the local folders account won't
1518 delete it (because tny_folder_get_account() is not defined
1519 for a merge folder */
1520 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1521 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1523 priv->outbox_deleted_handler =
1524 g_signal_connect (account,
1526 G_CALLBACK (on_outbox_deleted_cb),
1530 /* Refilter the model */
1531 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1536 same_account_selected (ModestFolderView *self,
1537 TnyAccount *account)
1539 ModestFolderViewPrivate *priv;
1540 gboolean same_account = FALSE;
1542 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1544 if (priv->cur_folder_store) {
1545 TnyAccount *selected_folder_account = NULL;
1547 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1548 selected_folder_account =
1549 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1551 selected_folder_account =
1552 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1555 if (selected_folder_account == account)
1556 same_account = TRUE;
1558 g_object_unref (selected_folder_account);
1560 return same_account;
1565 * Selects the first inbox or the local account in an idle
1568 on_idle_select_first_inbox_or_local (gpointer user_data)
1570 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1572 gdk_threads_enter ();
1573 modest_folder_view_select_first_inbox_or_local (self);
1574 gdk_threads_leave ();
1580 on_account_changed (TnyAccountStore *account_store,
1581 TnyAccount *tny_account,
1584 ModestFolderView *self;
1585 ModestFolderViewPrivate *priv;
1586 GtkTreeModel *model, *filter_model;
1587 GtkTreeSelection *sel;
1588 gboolean same_account;
1590 /* Ignore transport account insertions, we're not showing them
1591 in the folder view */
1592 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1595 self = MODEST_FOLDER_VIEW (user_data);
1596 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1598 /* Get the inner model */
1599 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1600 &filter_model, NULL, &model))
1603 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1605 /* Invalidate the cur_folder_store only if the selected folder
1606 belongs to the account that is being removed */
1607 same_account = same_account_selected (self, tny_account);
1609 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1610 gtk_tree_selection_unselect_all (sel);
1613 /* Remove the account from the model */
1614 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1616 /* Insert the account in the model */
1617 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1619 /* Refilter the model */
1620 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1622 /* Select the first INBOX if the currently selected folder
1623 belongs to the account that is being deleted */
1624 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1625 g_idle_add (on_idle_select_first_inbox_or_local, self);
1629 on_account_removed (TnyAccountStore *account_store,
1630 TnyAccount *account,
1633 ModestFolderView *self = NULL;
1634 ModestFolderViewPrivate *priv;
1635 GtkTreeModel *model, *filter_model;
1636 GtkTreeSelection *sel = NULL;
1637 gboolean same_account = FALSE;
1639 /* Ignore transport account removals, we're not showing them
1640 in the folder view */
1641 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1644 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1645 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1649 self = MODEST_FOLDER_VIEW (user_data);
1650 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1652 /* Invalidate the cur_folder_store only if the selected folder
1653 belongs to the account that is being removed */
1654 same_account = same_account_selected (self, account);
1656 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1657 gtk_tree_selection_unselect_all (sel);
1660 /* Invalidate row to select only if the folder to select
1661 belongs to the account that is being removed*/
1662 if (priv->folder_to_select) {
1663 TnyAccount *folder_to_select_account = NULL;
1665 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1666 if (folder_to_select_account == account) {
1667 modest_folder_view_disable_next_folder_selection (self);
1668 g_object_unref (priv->folder_to_select);
1669 priv->folder_to_select = NULL;
1671 g_object_unref (folder_to_select_account);
1674 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1675 &filter_model, NULL, &model))
1678 /* Disconnect the signal handler */
1679 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1680 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1681 if (g_signal_handler_is_connected (account,
1682 priv->outbox_deleted_handler))
1683 g_signal_handler_disconnect (account,
1684 priv->outbox_deleted_handler);
1687 /* Remove the account from the model */
1688 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1690 /* If the removed account is the currently viewed one then
1691 clear the configuration value. The new visible account will be the default account */
1692 if (priv->visible_account_id &&
1693 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1695 /* Clear the current visible account_id */
1696 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1697 modest_folder_view_set_mailbox (self, NULL);
1699 /* Call the restore method, this will set the new visible account */
1700 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1701 MODEST_CONF_FOLDER_VIEW_KEY);
1704 /* Refilter the model */
1705 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1707 /* Select the first INBOX if the currently selected folder
1708 belongs to the account that is being deleted */
1710 g_idle_add (on_idle_select_first_inbox_or_local, self);
1714 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1716 GtkTreeViewColumn *col;
1718 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1720 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1722 g_printerr ("modest: failed get column for title\n");
1726 gtk_tree_view_column_set_title (col, title);
1727 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1732 modest_folder_view_on_map (ModestFolderView *self,
1733 GdkEventExpose *event,
1736 ModestFolderViewPrivate *priv;
1738 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1740 /* This won't happen often */
1741 if (G_UNLIKELY (priv->reselect)) {
1742 /* Select the first inbox or the local account if not found */
1744 /* TODO: this could cause a lock at startup, so we
1745 comment it for the moment. We know that this will
1746 be a bug, because the INBOX is not selected, but we
1747 need to rewrite some parts of Modest to avoid the
1748 deathlock situation */
1749 /* TODO: check if this is still the case */
1750 priv->reselect = FALSE;
1751 modest_folder_view_select_first_inbox_or_local (self);
1752 /* Notify the display name observers */
1753 g_signal_emit (G_OBJECT(self),
1754 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1758 if (priv->reexpand) {
1759 expand_root_items (self);
1760 priv->reexpand = FALSE;
1767 modest_folder_view_new (TnyFolderStoreQuery *query)
1770 ModestFolderViewPrivate *priv;
1771 GtkTreeSelection *sel;
1773 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1774 #ifdef MODEST_TOOLKIT_HILDON2
1775 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1778 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1781 priv->query = g_object_ref (query);
1783 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1784 priv->changed_signal = g_signal_connect (sel, "changed",
1785 G_CALLBACK (on_selection_changed), self);
1787 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1789 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1791 return GTK_WIDGET(self);
1794 /* this feels dirty; any other way to expand all the root items? */
1796 expand_root_items (ModestFolderView *self)
1799 GtkTreeModel *model;
1802 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1803 path = gtk_tree_path_new_first ();
1805 /* all folders should have child items, so.. */
1807 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1808 gtk_tree_path_next (path);
1809 } while (gtk_tree_model_get_iter (model, &iter, path));
1811 gtk_tree_path_free (path);
1815 is_parent_of (TnyFolder *a, TnyFolder *b)
1818 gboolean retval = FALSE;
1820 a_id = tny_folder_get_id (a);
1822 gchar *string_to_match;
1825 string_to_match = g_strconcat (a_id, "/", NULL);
1826 b_id = tny_folder_get_id (b);
1827 retval = g_str_has_prefix (b_id, string_to_match);
1828 g_free (string_to_match);
1834 typedef struct _ForeachFolderInfo {
1837 } ForeachFolderInfo;
1840 foreach_folder_with_id (GtkTreeModel *model,
1845 ForeachFolderInfo *info;
1848 info = (ForeachFolderInfo *) data;
1849 gtk_tree_model_get (model, iter,
1850 INSTANCE_COLUMN, &instance,
1853 if (TNY_IS_FOLDER (instance)) {
1856 id = tny_folder_get_id (TNY_FOLDER (instance));
1858 collate = g_utf8_collate_key (id, -1);
1859 info->found = !strcmp (info->needle, collate);
1865 g_object_unref (instance);
1873 has_folder_with_id (ModestFolderView *self, const gchar *id)
1875 GtkTreeModel *model;
1876 ForeachFolderInfo info = {NULL, FALSE};
1878 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1879 info.needle = g_utf8_collate_key (id, -1);
1881 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1882 g_free (info.needle);
1888 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1891 gboolean retval = FALSE;
1893 a_id = tny_folder_get_id (a);
1896 b_id = tny_folder_get_id (b);
1899 const gchar *last_bar;
1900 gchar *string_to_match;
1901 last_bar = g_strrstr (b_id, "/");
1906 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1907 retval = has_folder_with_id (self, string_to_match);
1908 g_free (string_to_match);
1916 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1918 ModestFolderViewPrivate *priv;
1919 TnyIterator *iterator;
1920 gboolean retval = TRUE;
1922 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1923 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1925 for (iterator = tny_list_create_iterator (priv->list_to_move);
1926 retval && !tny_iterator_is_done (iterator);
1927 tny_iterator_next (iterator)) {
1929 instance = tny_iterator_get_current (iterator);
1930 if (instance == (GObject *) folder) {
1932 } else if (TNY_IS_FOLDER (instance)) {
1933 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1935 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1938 g_object_unref (instance);
1940 g_object_unref (iterator);
1947 * We use this function to implement the
1948 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1949 * account in this case, and the local folders.
1952 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1954 ModestFolderViewPrivate *priv;
1955 gboolean retval = TRUE;
1956 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1957 GObject *instance = NULL;
1958 const gchar *id = NULL;
1960 gboolean found = FALSE;
1961 gboolean cleared = FALSE;
1962 ModestTnyFolderRules rules = 0;
1965 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1966 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1968 gtk_tree_model_get (model, iter,
1969 NAME_COLUMN, &fname,
1971 INSTANCE_COLUMN, &instance,
1974 /* Do not show if there is no instance, this could indeed
1975 happen when the model is being modified while it's being
1976 drawn. This could occur for example when moving folders
1983 if (TNY_IS_ACCOUNT (instance)) {
1984 TnyAccount *acc = TNY_ACCOUNT (instance);
1985 const gchar *account_id = tny_account_get_id (acc);
1987 /* If it isn't a special folder,
1988 * don't show it unless it is the visible account: */
1989 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1990 !modest_tny_account_is_virtual_local_folders (acc) &&
1991 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1993 /* Show only the visible account id */
1994 if (priv->visible_account_id) {
1995 if (strcmp (account_id, priv->visible_account_id))
2002 /* Never show these to the user. They are merged into one folder
2003 * in the local-folders account instead: */
2004 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2007 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2008 /* Only show special folders for current account if needed */
2009 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2010 TnyAccount *account;
2012 account = tny_folder_get_account (TNY_FOLDER (instance));
2014 if (TNY_IS_ACCOUNT (account)) {
2015 const gchar *account_id = tny_account_get_id (account);
2017 if (!modest_tny_account_is_virtual_local_folders (account) &&
2018 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2019 /* Show only the visible account id */
2020 if (priv->visible_account_id) {
2021 if (strcmp (account_id, priv->visible_account_id)) {
2023 } else if (priv->mailbox) {
2024 /* Filter mailboxes */
2025 if (!g_str_has_prefix (fname, priv->mailbox)) {
2027 } else if (!strcmp (fname, priv->mailbox)) {
2028 /* Hide mailbox parent */
2034 g_object_unref (account);
2041 /* Check hiding (if necessary) */
2042 cleared = modest_email_clipboard_cleared (priv->clipboard);
2043 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2044 id = tny_folder_get_id (TNY_FOLDER(instance));
2045 if (priv->hidding_ids != NULL)
2046 for (i=0; i < priv->n_selected && !found; i++)
2047 if (priv->hidding_ids[i] != NULL && id != NULL)
2048 found = (!strcmp (priv->hidding_ids[i], id));
2053 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2054 folder as no message can be move there according to UI specs */
2055 if (retval && !priv->show_non_move) {
2056 if (priv->list_to_move &&
2057 tny_list_get_length (priv->list_to_move) > 0 &&
2058 TNY_IS_FOLDER (instance)) {
2059 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2061 if (retval && TNY_IS_FOLDER (instance) &&
2062 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2064 case TNY_FOLDER_TYPE_OUTBOX:
2065 case TNY_FOLDER_TYPE_SENT:
2066 case TNY_FOLDER_TYPE_DRAFTS:
2069 case TNY_FOLDER_TYPE_UNKNOWN:
2070 case TNY_FOLDER_TYPE_NORMAL:
2071 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2072 if (type == TNY_FOLDER_TYPE_INVALID)
2073 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2075 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2076 type == TNY_FOLDER_TYPE_SENT
2077 || type == TNY_FOLDER_TYPE_DRAFTS)
2084 if (retval && TNY_IS_ACCOUNT (instance) &&
2085 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2086 ModestProtocolType protocol_type;
2088 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2089 retval = !modest_protocol_registry_protocol_type_has_tag
2090 (modest_runtime_get_protocol_registry (),
2092 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2096 /* apply special filters */
2097 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2098 if (TNY_IS_ACCOUNT (instance))
2102 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2103 if (TNY_IS_FOLDER (instance))
2107 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2108 if (TNY_IS_ACCOUNT (instance)) {
2109 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2111 } else if (TNY_IS_FOLDER (instance)) {
2112 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2117 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2118 if (TNY_IS_ACCOUNT (instance)) {
2119 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2121 } else if (TNY_IS_FOLDER (instance)) {
2122 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2127 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2128 /* A mailbox is a fake folder with an @ in the middle of the name */
2129 if (!TNY_IS_FOLDER (instance) ||
2130 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2133 const gchar *folder_name;
2134 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2135 if (!folder_name || strchr (folder_name, '@') == NULL)
2141 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2142 if (TNY_IS_FOLDER (instance)) {
2143 /* Check folder rules */
2144 ModestTnyFolderRules rules;
2146 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2147 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2148 } else if (TNY_IS_ACCOUNT (instance)) {
2149 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2157 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2158 if (TNY_IS_FOLDER (instance)) {
2159 TnyFolderType guess_type;
2161 if (TNY_FOLDER_TYPE_NORMAL) {
2162 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2168 case TNY_FOLDER_TYPE_OUTBOX:
2169 case TNY_FOLDER_TYPE_SENT:
2170 case TNY_FOLDER_TYPE_DRAFTS:
2171 case TNY_FOLDER_TYPE_ARCHIVE:
2172 case TNY_FOLDER_TYPE_INBOX:
2175 case TNY_FOLDER_TYPE_UNKNOWN:
2176 case TNY_FOLDER_TYPE_NORMAL:
2182 } else if (TNY_IS_ACCOUNT (instance)) {
2187 if (retval && TNY_IS_FOLDER (instance)) {
2188 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2191 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2192 if (TNY_IS_FOLDER (instance)) {
2193 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2194 } else if (TNY_IS_ACCOUNT (instance)) {
2199 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2200 if (TNY_IS_FOLDER (instance)) {
2201 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2202 } else if (TNY_IS_ACCOUNT (instance)) {
2207 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2208 if (TNY_IS_FOLDER (instance)) {
2209 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2210 } else if (TNY_IS_ACCOUNT (instance)) {
2216 g_object_unref (instance);
2224 modest_folder_view_update_model (ModestFolderView *self,
2225 TnyAccountStore *account_store)
2227 ModestFolderViewPrivate *priv;
2228 GtkTreeModel *model;
2229 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2231 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2232 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2235 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2237 /* Notify that there is no folder selected */
2238 g_signal_emit (G_OBJECT(self),
2239 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2241 if (priv->cur_folder_store) {
2242 g_object_unref (priv->cur_folder_store);
2243 priv->cur_folder_store = NULL;
2246 /* FIXME: the local accounts are not shown when the query
2247 selects only the subscribed folders */
2248 #ifdef MODEST_TOOLKIT_HILDON2
2249 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2250 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2251 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2252 MODEST_FOLDER_PATH_SEPARATOR);
2254 model = tny_gtk_folder_store_tree_model_new (NULL);
2257 /* When the model is a list store (plain representation) the
2258 outbox is not a child of any account so we have to manually
2259 delete it because removing the local folders account won't
2260 delete it (because tny_folder_get_account() is not defined
2261 for a merge folder */
2262 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2263 TnyAccount *account;
2264 ModestTnyAccountStore *acc_store;
2266 acc_store = modest_runtime_get_account_store ();
2267 account = modest_tny_account_store_get_local_folders_account (acc_store);
2269 if (g_signal_handler_is_connected (account,
2270 priv->outbox_deleted_handler))
2271 g_signal_handler_disconnect (account,
2272 priv->outbox_deleted_handler);
2274 priv->outbox_deleted_handler =
2275 g_signal_connect (account,
2277 G_CALLBACK (on_outbox_deleted_cb),
2279 g_object_unref (account);
2282 /* Get the accounts: */
2283 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2285 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2287 sortable = gtk_tree_model_sort_new_with_model (model);
2288 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2290 GTK_SORT_ASCENDING);
2291 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2293 cmp_rows, NULL, NULL);
2295 /* Create filter model */
2296 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2297 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2302 if (priv->activity_changed_handler > 0) {
2303 GtkTreeModel *old_tny_model;
2305 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2306 g_signal_handler_disconnect (G_OBJECT (old_tny_model), priv->activity_changed_handler);
2308 priv->activity_changed_handler = 0;
2312 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2313 #ifndef MODEST_TOOLKIT_HILDON2
2314 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2315 (GCallback) on_row_inserted_maybe_select_folder, self);
2318 #ifdef MODEST_TOOLKIT_HILDON2
2319 priv->activity_changed_handler =
2320 g_signal_connect (G_OBJECT (model), "activity-changed", G_CALLBACK (on_activity_changed), self);
2323 g_object_unref (model);
2324 g_object_unref (filter_model);
2325 g_object_unref (sortable);
2327 /* Force a reselection of the INBOX next time the widget is shown */
2328 priv->reselect = TRUE;
2335 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2337 GtkTreeModel *model = NULL;
2338 TnyFolderStore *folder = NULL;
2340 ModestFolderView *tree_view = NULL;
2341 ModestFolderViewPrivate *priv = NULL;
2342 gboolean selected = FALSE;
2344 g_return_if_fail (sel);
2345 g_return_if_fail (user_data);
2347 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2349 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2351 tree_view = MODEST_FOLDER_VIEW (user_data);
2354 gtk_tree_model_get (model, &iter,
2355 INSTANCE_COLUMN, &folder,
2358 /* If the folder is the same do not notify */
2359 if (folder && priv->cur_folder_store == folder) {
2360 g_object_unref (folder);
2365 /* Current folder was unselected */
2366 if (priv->cur_folder_store) {
2367 /* We must do this firstly because a libtinymail-camel
2368 implementation detail. If we issue the signal
2369 before doing the sync_async, then that signal could
2370 cause (and it actually does it) a free of the
2371 summary of the folder (because the main window will
2372 clear the headers view */
2373 if (TNY_IS_FOLDER(priv->cur_folder_store))
2374 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2375 FALSE, NULL, NULL, NULL);
2377 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2378 priv->cur_folder_store, FALSE);
2380 g_object_unref (priv->cur_folder_store);
2381 priv->cur_folder_store = NULL;
2384 /* New current references */
2385 priv->cur_folder_store = folder;
2387 /* New folder has been selected. Do not notify if there is
2388 nothing new selected */
2390 g_signal_emit (G_OBJECT(tree_view),
2391 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2392 0, priv->cur_folder_store, TRUE);
2397 on_row_activated (GtkTreeView *treeview,
2398 GtkTreePath *treepath,
2399 GtkTreeViewColumn *column,
2402 GtkTreeModel *model = NULL;
2403 TnyFolderStore *folder = NULL;
2405 ModestFolderView *self = NULL;
2406 ModestFolderViewPrivate *priv = NULL;
2408 g_return_if_fail (treeview);
2409 g_return_if_fail (user_data);
2411 self = MODEST_FOLDER_VIEW (user_data);
2412 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2414 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2416 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2419 gtk_tree_model_get (model, &iter,
2420 INSTANCE_COLUMN, &folder,
2423 g_signal_emit (G_OBJECT(self),
2424 signals[FOLDER_ACTIVATED_SIGNAL],
2427 #ifdef MODEST_TOOLKIT_HILDON2
2428 HildonUIMode ui_mode;
2429 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2430 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2431 if (priv->cur_folder_store)
2432 g_object_unref (priv->cur_folder_store);
2433 priv->cur_folder_store = g_object_ref (folder);
2437 g_object_unref (folder);
2441 modest_folder_view_get_selected (ModestFolderView *self)
2443 ModestFolderViewPrivate *priv;
2445 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2447 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2448 if (priv->cur_folder_store)
2449 g_object_ref (priv->cur_folder_store);
2451 return priv->cur_folder_store;
2455 get_cmp_rows_type_pos (GObject *folder)
2457 /* Remote accounts -> Local account -> MMC account .*/
2460 if (TNY_IS_ACCOUNT (folder) &&
2461 modest_tny_account_is_virtual_local_folders (
2462 TNY_ACCOUNT (folder))) {
2464 } else if (TNY_IS_ACCOUNT (folder)) {
2465 TnyAccount *account = TNY_ACCOUNT (folder);
2466 const gchar *account_id = tny_account_get_id (account);
2467 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2473 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2474 return -1; /* Should never happen */
2479 inbox_is_special (TnyFolderStore *folder_store)
2481 gboolean is_special = TRUE;
2483 if (TNY_IS_FOLDER (folder_store)) {
2487 gchar *last_inbox_bar;
2489 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2490 downcase = g_utf8_strdown (id, -1);
2491 last_bar = g_strrstr (downcase, "/");
2493 last_inbox_bar = g_strrstr (downcase, "inbox/");
2494 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2505 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2507 TnyAccount *account;
2508 gboolean is_special;
2509 /* Inbox, Outbox, Drafts, Sent, User */
2512 if (!TNY_IS_FOLDER (folder_store))
2515 case TNY_FOLDER_TYPE_INBOX:
2517 account = tny_folder_get_account (folder_store);
2518 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2520 /* In inbox case we need to know if the inbox is really the top
2521 * inbox of the account, or if it's a submailbox inbox. To do
2522 * this we'll apply an heuristic rule: Find last "/" and check
2523 * if it's preceeded by another Inbox */
2524 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2525 g_object_unref (account);
2526 return is_special?0:4;
2529 case TNY_FOLDER_TYPE_OUTBOX:
2530 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2532 case TNY_FOLDER_TYPE_DRAFTS:
2534 account = tny_folder_get_account (folder_store);
2535 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2536 g_object_unref (account);
2537 return is_special?1:4;
2540 case TNY_FOLDER_TYPE_SENT:
2542 account = tny_folder_get_account (folder_store);
2543 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2544 g_object_unref (account);
2545 return is_special?3:4;
2554 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2556 const gchar *a1_name, *a2_name;
2558 a1_name = tny_account_get_name (a1);
2559 a2_name = tny_account_get_name (a2);
2561 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2565 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2567 TnyAccount *a1 = NULL, *a2 = NULL;
2570 if (TNY_IS_ACCOUNT (s1)) {
2571 a1 = TNY_ACCOUNT (g_object_ref (s1));
2572 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2573 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2576 if (TNY_IS_ACCOUNT (s2)) {
2577 a2 = TNY_ACCOUNT (g_object_ref (s2));
2578 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2579 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2596 /* First we sort with the type of account */
2597 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2601 cmp = compare_account_names (a1, a2);
2605 g_object_unref (a1);
2607 g_object_unref (a2);
2613 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2615 gint is_account1, is_account2;
2617 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2618 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2620 return is_account2 - is_account1;
2624 * This function orders the mail accounts according to these rules:
2625 * 1st - remote accounts
2626 * 2nd - local account
2630 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2634 gchar *name1 = NULL;
2635 gchar *name2 = NULL;
2636 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2637 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2638 GObject *folder1 = NULL;
2639 GObject *folder2 = NULL;
2641 gtk_tree_model_get (tree_model, iter1,
2642 NAME_COLUMN, &name1,
2644 INSTANCE_COLUMN, &folder1,
2646 gtk_tree_model_get (tree_model, iter2,
2647 NAME_COLUMN, &name2,
2648 TYPE_COLUMN, &type2,
2649 INSTANCE_COLUMN, &folder2,
2652 /* Return if we get no folder. This could happen when folder
2653 operations are happening. The model is updated after the
2654 folder copy/move actually occurs, so there could be
2655 situations where the model to be drawn is not correct */
2656 if (!folder1 || !folder2)
2659 /* Sort by type. First the special folders, then the archives */
2660 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2664 /* Now we sort using the account of each folder */
2665 if (TNY_IS_FOLDER_STORE (folder1) &&
2666 TNY_IS_FOLDER_STORE (folder2)) {
2667 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2671 /* Each group is preceeded by its account */
2672 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2677 /* Pure sort by name */
2678 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2681 g_object_unref(G_OBJECT(folder1));
2683 g_object_unref(G_OBJECT(folder2));
2691 /*****************************************************************************/
2692 /* DRAG and DROP stuff */
2693 /*****************************************************************************/
2695 * This function fills the #GtkSelectionData with the row and the
2696 * model that has been dragged. It's called when this widget is a
2697 * source for dnd after the event drop happened
2700 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2701 guint info, guint time, gpointer data)
2703 GtkTreeSelection *selection;
2704 GtkTreeModel *model;
2706 GtkTreePath *source_row;
2708 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2709 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2711 source_row = gtk_tree_model_get_path (model, &iter);
2712 gtk_tree_set_row_drag_data (selection_data,
2716 gtk_tree_path_free (source_row);
2720 typedef struct _DndHelper {
2721 ModestFolderView *folder_view;
2722 gboolean delete_source;
2723 GtkTreePath *source_row;
2727 dnd_helper_destroyer (DndHelper *helper)
2729 /* Free the helper */
2730 gtk_tree_path_free (helper->source_row);
2731 g_slice_free (DndHelper, helper);
2735 xfer_folder_cb (ModestMailOperation *mail_op,
2736 TnyFolder *new_folder,
2740 /* Select the folder */
2741 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2747 /* get the folder for the row the treepath refers to. */
2748 /* folder must be unref'd */
2749 static TnyFolderStore *
2750 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2753 TnyFolderStore *folder = NULL;
2755 if (gtk_tree_model_get_iter (model,&iter, path))
2756 gtk_tree_model_get (model, &iter,
2757 INSTANCE_COLUMN, &folder,
2764 * This function is used by drag_data_received_cb to manage drag and
2765 * drop of a header, i.e, and drag from the header view to the folder
2769 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2770 GtkTreeModel *dest_model,
2771 GtkTreePath *dest_row,
2772 GtkSelectionData *selection_data)
2774 TnyList *headers = NULL;
2775 TnyFolder *folder = NULL, *src_folder = NULL;
2776 TnyFolderType folder_type;
2777 GtkTreeIter source_iter, dest_iter;
2778 ModestWindowMgr *mgr = NULL;
2779 ModestWindow *main_win = NULL;
2780 gchar **uris, **tmp;
2782 /* Build the list of headers */
2783 mgr = modest_runtime_get_window_mgr ();
2784 headers = tny_simple_list_new ();
2785 uris = modest_dnd_selection_data_get_paths (selection_data);
2788 while (*tmp != NULL) {
2791 gboolean first = TRUE;
2794 path = gtk_tree_path_new_from_string (*tmp);
2795 gtk_tree_model_get_iter (source_model, &source_iter, path);
2796 gtk_tree_model_get (source_model, &source_iter,
2797 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2800 /* Do not enable d&d of headers already opened */
2801 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2802 tny_list_append (headers, G_OBJECT (header));
2804 if (G_UNLIKELY (first)) {
2805 src_folder = tny_header_get_folder (header);
2809 /* Free and go on */
2810 gtk_tree_path_free (path);
2811 g_object_unref (header);
2816 /* This could happen ig we perform a d&d very quickly over the
2817 same row that row could dissapear because message is
2819 if (!TNY_IS_FOLDER (src_folder))
2822 /* Get the target folder */
2823 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2824 gtk_tree_model_get (dest_model, &dest_iter,
2828 if (!folder || !TNY_IS_FOLDER(folder)) {
2829 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2833 folder_type = modest_tny_folder_guess_folder_type (folder);
2834 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2835 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2836 goto cleanup; /* cannot move messages there */
2839 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2840 /* g_warning ("folder not writable"); */
2841 goto cleanup; /* verboten! */
2844 /* Ask for confirmation to move */
2845 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2847 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2851 /* Transfer messages */
2852 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2857 if (G_IS_OBJECT (src_folder))
2858 g_object_unref (src_folder);
2859 if (G_IS_OBJECT(folder))
2860 g_object_unref (G_OBJECT (folder));
2861 if (G_IS_OBJECT(headers))
2862 g_object_unref (headers);
2866 TnyFolderStore *src_folder;
2867 TnyFolderStore *dst_folder;
2868 ModestFolderView *folder_view;
2873 dnd_folder_info_destroyer (DndFolderInfo *info)
2875 if (info->src_folder)
2876 g_object_unref (info->src_folder);
2877 if (info->dst_folder)
2878 g_object_unref (info->dst_folder);
2879 g_slice_free (DndFolderInfo, info);
2883 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2884 GtkWindow *parent_window,
2885 TnyAccount *account)
2888 modest_ui_actions_on_account_connection_error (parent_window, account);
2890 /* Free the helper & info */
2891 dnd_helper_destroyer (info->helper);
2892 dnd_folder_info_destroyer (info);
2896 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2898 GtkWindow *parent_window,
2899 TnyAccount *account,
2902 DndFolderInfo *info = NULL;
2903 ModestMailOperation *mail_op;
2905 info = (DndFolderInfo *) user_data;
2907 if (err || canceled) {
2908 dnd_on_connection_failed_destroyer (info, parent_window, account);
2912 /* Do the mail operation */
2913 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2914 modest_ui_actions_move_folder_error_handler,
2915 info->src_folder, NULL);
2917 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2920 /* Transfer the folder */
2921 modest_mail_operation_xfer_folder (mail_op,
2922 TNY_FOLDER (info->src_folder),
2924 info->helper->delete_source,
2926 info->helper->folder_view);
2929 g_object_unref (G_OBJECT (mail_op));
2930 dnd_helper_destroyer (info->helper);
2931 dnd_folder_info_destroyer (info);
2936 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2938 GtkWindow *parent_window,
2939 TnyAccount *account,
2942 DndFolderInfo *info = NULL;
2944 info = (DndFolderInfo *) user_data;
2946 if (err || canceled) {
2947 dnd_on_connection_failed_destroyer (info, parent_window, account);
2951 /* Connect to source folder and perform the copy/move */
2952 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2954 drag_and_drop_from_folder_view_src_folder_performer,
2959 * This function is used by drag_data_received_cb to manage drag and
2960 * drop of a folder, i.e, and drag from the folder view to the same
2964 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2965 GtkTreeModel *dest_model,
2966 GtkTreePath *dest_row,
2967 GtkSelectionData *selection_data,
2970 GtkTreeIter dest_iter, iter;
2971 TnyFolderStore *dest_folder = NULL;
2972 TnyFolderStore *folder = NULL;
2973 gboolean forbidden = FALSE;
2975 DndFolderInfo *info = NULL;
2977 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2979 g_warning ("%s: BUG: no main window", __FUNCTION__);
2980 dnd_helper_destroyer (helper);
2985 /* check the folder rules for the destination */
2986 folder = tree_path_to_folder (dest_model, dest_row);
2987 if (TNY_IS_FOLDER(folder)) {
2988 ModestTnyFolderRules rules =
2989 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2990 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2991 } else if (TNY_IS_FOLDER_STORE(folder)) {
2992 /* enable local root as destination for folders */
2993 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2994 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2997 g_object_unref (folder);
3000 /* check the folder rules for the source */
3001 folder = tree_path_to_folder (source_model, helper->source_row);
3002 if (TNY_IS_FOLDER(folder)) {
3003 ModestTnyFolderRules rules =
3004 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3005 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3008 g_object_unref (folder);
3012 /* Check if the drag is possible */
3013 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3015 modest_platform_run_information_dialog ((GtkWindow *) win,
3016 _("mail_in_ui_folder_move_target_error"),
3018 /* Restore the previous selection */
3019 folder = tree_path_to_folder (source_model, helper->source_row);
3021 if (TNY_IS_FOLDER (folder))
3022 modest_folder_view_select_folder (helper->folder_view,
3023 TNY_FOLDER (folder), FALSE);
3024 g_object_unref (folder);
3026 dnd_helper_destroyer (helper);
3031 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3032 gtk_tree_model_get (dest_model, &dest_iter,
3035 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3036 gtk_tree_model_get (source_model, &iter,
3040 /* Create the info for the performer */
3041 info = g_slice_new0 (DndFolderInfo);
3042 info->src_folder = g_object_ref (folder);
3043 info->dst_folder = g_object_ref (dest_folder);
3044 info->helper = helper;
3046 /* Connect to the destination folder and perform the copy/move */
3047 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3049 drag_and_drop_from_folder_view_dst_folder_performer,
3053 g_object_unref (dest_folder);
3054 g_object_unref (folder);
3058 * This function receives the data set by the "drag-data-get" signal
3059 * handler. This information comes within the #GtkSelectionData. This
3060 * function will manage both the drags of folders of the treeview and
3061 * drags of headers of the header view widget.
3064 on_drag_data_received (GtkWidget *widget,
3065 GdkDragContext *context,
3068 GtkSelectionData *selection_data,
3073 GtkWidget *source_widget;
3074 GtkTreeModel *dest_model, *source_model;
3075 GtkTreePath *source_row, *dest_row;
3076 GtkTreeViewDropPosition pos;
3077 gboolean delete_source = FALSE;
3078 gboolean success = FALSE;
3080 /* Do not allow further process */
3081 g_signal_stop_emission_by_name (widget, "drag-data-received");
3082 source_widget = gtk_drag_get_source_widget (context);
3084 /* Get the action */
3085 if (context->action == GDK_ACTION_MOVE) {
3086 delete_source = TRUE;
3088 /* Notify that there is no folder selected. We need to
3089 do this in order to update the headers view (and
3090 its monitors, because when moving, the old folder
3091 won't longer exist. We can not wait for the end of
3092 the operation, because the operation won't start if
3093 the folder is in use */
3094 if (source_widget == widget) {
3095 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3096 gtk_tree_selection_unselect_all (sel);
3100 /* Check if the get_data failed */
3101 if (selection_data == NULL || selection_data->length < 0)
3104 /* Select the destination model */
3105 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3107 /* Get the path to the destination row. Can not call
3108 gtk_tree_view_get_drag_dest_row() because the source row
3109 is not selected anymore */
3110 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3113 /* Only allow drops IN other rows */
3115 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3116 pos == GTK_TREE_VIEW_DROP_AFTER)
3120 /* Drags from the header view */
3121 if (source_widget != widget) {
3122 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3124 drag_and_drop_from_header_view (source_model,
3129 DndHelper *helper = NULL;
3131 /* Get the source model and row */
3132 gtk_tree_get_row_drag_data (selection_data,
3136 /* Create the helper */
3137 helper = g_slice_new0 (DndHelper);
3138 helper->delete_source = delete_source;
3139 helper->source_row = gtk_tree_path_copy (source_row);
3140 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3142 drag_and_drop_from_folder_view (source_model,
3148 gtk_tree_path_free (source_row);
3152 gtk_tree_path_free (dest_row);
3155 /* Finish the drag and drop */
3156 gtk_drag_finish (context, success, FALSE, time);
3160 * We define a "drag-drop" signal handler because we do not want to
3161 * use the default one, because the default one always calls
3162 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3163 * signal handler, because there we have all the information available
3164 * to know if the dnd was a success or not.
3167 drag_drop_cb (GtkWidget *widget,
3168 GdkDragContext *context,
3176 if (!context->targets)
3179 /* Check if we're dragging a folder row */
3180 target = gtk_drag_dest_find_target (widget, context, NULL);
3182 /* Request the data from the source. */
3183 gtk_drag_get_data(widget, context, target, time);
3189 * This function expands a node of a tree view if it's not expanded
3190 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3191 * does that, so that's why they're here.
3194 expand_row_timeout (gpointer data)
3196 GtkTreeView *tree_view = data;
3197 GtkTreePath *dest_path = NULL;
3198 GtkTreeViewDropPosition pos;
3199 gboolean result = FALSE;
3201 gdk_threads_enter ();
3203 gtk_tree_view_get_drag_dest_row (tree_view,
3208 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3209 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3210 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3211 gtk_tree_path_free (dest_path);
3215 gtk_tree_path_free (dest_path);
3220 gdk_threads_leave ();
3226 * This function is called whenever the pointer is moved over a widget
3227 * while dragging some data. It installs a timeout that will expand a
3228 * node of the treeview if not expanded yet. This function also calls
3229 * gdk_drag_status in order to set the suggested action that will be
3230 * used by the "drag-data-received" signal handler to know if we
3231 * should do a move or just a copy of the data.
3234 on_drag_motion (GtkWidget *widget,
3235 GdkDragContext *context,
3241 GtkTreeViewDropPosition pos;
3242 GtkTreePath *dest_row;
3243 GtkTreeModel *dest_model;
3244 ModestFolderViewPrivate *priv;
3245 GdkDragAction suggested_action;
3246 gboolean valid_location = FALSE;
3247 TnyFolderStore *folder = NULL;
3249 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3251 if (priv->timer_expander != 0) {
3252 g_source_remove (priv->timer_expander);
3253 priv->timer_expander = 0;
3256 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3261 /* Do not allow drops between folders */
3263 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3264 pos == GTK_TREE_VIEW_DROP_AFTER) {
3265 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3266 gdk_drag_status(context, 0, time);
3267 valid_location = FALSE;
3270 valid_location = TRUE;
3273 /* Check that the destination folder is writable */
3274 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3275 folder = tree_path_to_folder (dest_model, dest_row);
3276 if (folder && TNY_IS_FOLDER (folder)) {
3277 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3279 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3280 valid_location = FALSE;
3285 /* Expand the selected row after 1/2 second */
3286 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3287 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3289 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3291 /* Select the desired action. By default we pick MOVE */
3292 suggested_action = GDK_ACTION_MOVE;
3294 if (context->actions == GDK_ACTION_COPY)
3295 gdk_drag_status(context, GDK_ACTION_COPY, time);
3296 else if (context->actions == GDK_ACTION_MOVE)
3297 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3298 else if (context->actions & suggested_action)
3299 gdk_drag_status(context, suggested_action, time);
3301 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3305 g_object_unref (folder);
3307 gtk_tree_path_free (dest_row);
3309 g_signal_stop_emission_by_name (widget, "drag-motion");
3311 return valid_location;
3315 * This function sets the treeview as a source and a target for dnd
3316 * events. It also connects all the requirede signals.
3319 setup_drag_and_drop (GtkTreeView *self)
3321 /* Set up the folder view as a dnd destination. Set only the
3322 highlight flag, otherwise gtk will have a different
3324 #ifdef MODEST_TOOLKIT_HILDON2
3327 gtk_drag_dest_set (GTK_WIDGET (self),
3328 GTK_DEST_DEFAULT_HIGHLIGHT,
3329 folder_view_drag_types,
3330 G_N_ELEMENTS (folder_view_drag_types),
3331 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3333 g_signal_connect (G_OBJECT (self),
3334 "drag_data_received",
3335 G_CALLBACK (on_drag_data_received),
3339 /* Set up the treeview as a dnd source */
3340 gtk_drag_source_set (GTK_WIDGET (self),
3342 folder_view_drag_types,
3343 G_N_ELEMENTS (folder_view_drag_types),
3344 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3346 g_signal_connect (G_OBJECT (self),
3348 G_CALLBACK (on_drag_motion),
3351 g_signal_connect (G_OBJECT (self),
3353 G_CALLBACK (on_drag_data_get),
3356 g_signal_connect (G_OBJECT (self),
3358 G_CALLBACK (drag_drop_cb),
3363 * This function manages the navigation through the folders using the
3364 * keyboard or the hardware keys in the device
3367 on_key_pressed (GtkWidget *self,
3371 GtkTreeSelection *selection;
3373 GtkTreeModel *model;
3374 gboolean retval = FALSE;
3376 /* Up and Down are automatically managed by the treeview */
3377 if (event->keyval == GDK_Return) {
3378 /* Expand/Collapse the selected row */
3379 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3380 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3383 path = gtk_tree_model_get_path (model, &iter);
3385 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3386 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3388 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3389 gtk_tree_path_free (path);
3391 /* No further processing */
3399 * We listen to the changes in the local folder account name key,
3400 * because we want to show the right name in the view. The local
3401 * folder account name corresponds to the device name in the Maemo
3402 * version. We do this because we do not want to query gconf on each
3403 * tree view refresh. It's better to cache it and change whenever
3407 on_configuration_key_changed (ModestConf* conf,
3409 ModestConfEvent event,
3410 ModestConfNotificationId id,
3411 ModestFolderView *self)
3413 ModestFolderViewPrivate *priv;
3416 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3417 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3419 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3420 g_free (priv->local_account_name);
3422 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3423 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3425 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3426 MODEST_CONF_DEVICE_NAME, NULL);
3428 /* Force a redraw */
3429 #if GTK_CHECK_VERSION(2, 8, 0)
3430 GtkTreeViewColumn * tree_column;
3432 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3434 gtk_tree_view_column_queue_resize (tree_column);
3436 gtk_widget_queue_draw (GTK_WIDGET (self));
3442 modest_folder_view_set_style (ModestFolderView *self,
3443 ModestFolderViewStyle style)
3445 ModestFolderViewPrivate *priv;
3447 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3448 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3449 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3451 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3454 priv->style = style;
3458 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3459 const gchar *account_id)
3461 ModestFolderViewPrivate *priv;
3462 GtkTreeModel *model;
3464 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3466 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3468 /* This will be used by the filter_row callback,
3469 * to decided which rows to show: */
3470 if (priv->visible_account_id) {
3471 g_free (priv->visible_account_id);
3472 priv->visible_account_id = NULL;
3475 priv->visible_account_id = g_strdup (account_id);
3478 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3479 if (GTK_IS_TREE_MODEL_FILTER (model))
3480 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3482 /* Save settings to gconf */
3483 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3484 MODEST_CONF_FOLDER_VIEW_KEY);
3486 /* Notify observers */
3487 g_signal_emit (G_OBJECT(self),
3488 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3493 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3495 ModestFolderViewPrivate *priv;
3497 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3499 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3501 return (const gchar *) priv->visible_account_id;
3505 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3509 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3511 gtk_tree_model_get (model, iter,
3515 gboolean result = FALSE;
3516 if (type == TNY_FOLDER_TYPE_INBOX) {
3520 *inbox_iter = *iter;
3524 if (gtk_tree_model_iter_children (model, &child, iter)) {
3525 if (find_inbox_iter (model, &child, inbox_iter))
3529 } while (gtk_tree_model_iter_next (model, iter));
3538 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3540 #ifndef MODEST_TOOLKIT_HILDON2
3541 GtkTreeModel *model;
3542 GtkTreeIter iter, inbox_iter;
3543 GtkTreeSelection *sel;
3544 GtkTreePath *path = NULL;
3546 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3548 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3552 expand_root_items (self);
3553 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3555 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3556 g_warning ("%s: model is empty", __FUNCTION__);
3560 if (find_inbox_iter (model, &iter, &inbox_iter))
3561 path = gtk_tree_model_get_path (model, &inbox_iter);
3563 path = gtk_tree_path_new_first ();
3565 /* Select the row and free */
3566 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3567 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3568 gtk_tree_path_free (path);
3571 gtk_widget_grab_focus (GTK_WIDGET(self));
3578 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3583 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3584 TnyFolder* a_folder;
3587 gtk_tree_model_get (model, iter,
3588 INSTANCE_COLUMN, &a_folder,
3594 if (folder == a_folder) {
3595 g_object_unref (a_folder);
3596 *folder_iter = *iter;
3599 g_object_unref (a_folder);
3601 if (gtk_tree_model_iter_children (model, &child, iter)) {
3602 if (find_folder_iter (model, &child, folder_iter, folder))
3606 } while (gtk_tree_model_iter_next (model, iter));
3611 #ifndef MODEST_TOOLKIT_HILDON2
3613 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3616 ModestFolderView *self)
3618 ModestFolderViewPrivate *priv = NULL;
3619 GtkTreeSelection *sel;
3620 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3621 GObject *instance = NULL;
3623 if (!MODEST_IS_FOLDER_VIEW(self))
3626 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3628 priv->reexpand = TRUE;
3630 gtk_tree_model_get (tree_model, iter,
3632 INSTANCE_COLUMN, &instance,
3638 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3639 priv->folder_to_select = g_object_ref (instance);
3641 g_object_unref (instance);
3643 if (priv->folder_to_select) {
3645 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3648 path = gtk_tree_model_get_path (tree_model, iter);
3649 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3651 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3653 gtk_tree_selection_select_iter (sel, iter);
3654 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3656 gtk_tree_path_free (path);
3660 modest_folder_view_disable_next_folder_selection (self);
3662 /* Refilter the model */
3663 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3669 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3671 ModestFolderViewPrivate *priv;
3673 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3675 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3677 if (priv->folder_to_select)
3678 g_object_unref(priv->folder_to_select);
3680 priv->folder_to_select = NULL;
3684 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3685 gboolean after_change)
3687 GtkTreeModel *model;
3688 GtkTreeIter iter, folder_iter;
3689 GtkTreeSelection *sel;
3690 ModestFolderViewPrivate *priv = NULL;
3692 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3693 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3695 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3698 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3699 gtk_tree_selection_unselect_all (sel);
3701 if (priv->folder_to_select)
3702 g_object_unref(priv->folder_to_select);
3703 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3707 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3712 /* Refilter the model, before selecting the folder */
3713 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3715 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3716 g_warning ("%s: model is empty", __FUNCTION__);
3720 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3723 path = gtk_tree_model_get_path (model, &folder_iter);
3724 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3726 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3727 gtk_tree_selection_select_iter (sel, &folder_iter);
3728 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3730 gtk_tree_path_free (path);
3738 modest_folder_view_copy_selection (ModestFolderView *self)
3740 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3742 /* Copy selection */
3743 _clipboard_set_selected_data (self, FALSE);
3747 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3749 ModestFolderViewPrivate *priv = NULL;
3750 GtkTreeModel *model = NULL;
3751 const gchar **hidding = NULL;
3752 guint i, n_selected;
3754 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3755 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3757 /* Copy selection */
3758 if (!_clipboard_set_selected_data (folder_view, TRUE))
3761 /* Get hidding ids */
3762 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3764 /* Clear hidding array created by previous cut operation */
3765 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3767 /* Copy hidding array */
3768 priv->n_selected = n_selected;
3769 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3770 for (i=0; i < n_selected; i++)
3771 priv->hidding_ids[i] = g_strdup(hidding[i]);
3773 /* Hide cut folders */
3774 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3775 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3779 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3780 ModestFolderView *folder_view_dst)
3782 GtkTreeModel *filter_model = NULL;
3783 GtkTreeModel *model = NULL;
3784 GtkTreeModel *new_filter_model = NULL;
3785 GtkTreeModel *old_tny_model = NULL;
3786 GtkTreeModel *new_tny_model = NULL;
3787 ModestFolderViewPrivate *dst_priv;
3789 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3790 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3792 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3793 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3794 new_tny_model = NULL;
3797 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3798 g_signal_handler_disconnect (G_OBJECT (old_tny_model), dst_priv->activity_changed_handler);
3799 dst_priv->activity_changed_handler = 0;
3801 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3802 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3804 /* Build new filter model */
3805 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3806 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3813 /* Set copied model */
3814 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3815 #ifndef MODEST_TOOLKIT_HILDON2
3816 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3817 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3819 #ifdef MODEST_TOOLKIT_HILDON2
3821 dst_priv->activity_changed_handler = g_signal_connect (G_OBJECT (new_tny_model), "activity-changed",
3822 G_CALLBACK (on_activity_changed), folder_view_dst);
3826 g_object_unref (new_filter_model);
3830 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3833 GtkTreeModel *model = NULL;
3834 ModestFolderViewPrivate* priv;
3836 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3838 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3839 priv->show_non_move = show;
3840 /* modest_folder_view_update_model(folder_view, */
3841 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3843 /* Hide special folders */
3844 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3845 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3846 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3851 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3854 ModestFolderViewPrivate* priv;
3856 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3858 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3859 priv->show_message_count = show;
3861 g_object_set (G_OBJECT (priv->messages_renderer),
3862 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3866 /* Returns FALSE if it did not selected anything */
3868 _clipboard_set_selected_data (ModestFolderView *folder_view,
3871 ModestFolderViewPrivate *priv = NULL;
3872 TnyFolderStore *folder = NULL;
3873 gboolean retval = FALSE;
3875 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3876 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3878 /* Set selected data on clipboard */
3879 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3880 folder = modest_folder_view_get_selected (folder_view);
3882 /* Do not allow to select an account */
3883 if (TNY_IS_FOLDER (folder)) {
3884 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3889 g_object_unref (folder);
3895 _clear_hidding_filter (ModestFolderView *folder_view)
3897 ModestFolderViewPrivate *priv;
3900 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3901 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3903 if (priv->hidding_ids != NULL) {
3904 for (i=0; i < priv->n_selected; i++)
3905 g_free (priv->hidding_ids[i]);
3906 g_free(priv->hidding_ids);
3912 on_display_name_changed (ModestAccountMgr *mgr,
3913 const gchar *account,
3916 ModestFolderView *self;
3918 self = MODEST_FOLDER_VIEW (user_data);
3920 /* Force a redraw */
3921 #if GTK_CHECK_VERSION(2, 8, 0)
3922 GtkTreeViewColumn * tree_column;
3924 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3926 gtk_tree_view_column_queue_resize (tree_column);
3928 gtk_widget_queue_draw (GTK_WIDGET (self));
3933 modest_folder_view_set_cell_style (ModestFolderView *self,
3934 ModestFolderViewCellStyle cell_style)
3936 ModestFolderViewPrivate *priv = NULL;
3938 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3939 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3941 priv->cell_style = cell_style;
3943 g_object_set (G_OBJECT (priv->messages_renderer),
3944 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3947 gtk_widget_queue_draw (GTK_WIDGET (self));
3951 update_style (ModestFolderView *self)
3953 ModestFolderViewPrivate *priv;
3954 GdkColor style_color;
3955 PangoAttrList *attr_list;
3957 PangoAttribute *attr;
3959 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3960 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3964 attr_list = pango_attr_list_new ();
3965 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3966 gdk_color_parse ("grey", &style_color);
3968 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3969 pango_attr_list_insert (attr_list, attr);
3972 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3974 "SmallSystemFont", NULL,
3977 attr = pango_attr_font_desc_new (pango_font_description_copy
3978 (style->font_desc));
3979 pango_attr_list_insert (attr_list, attr);
3981 g_object_set (G_OBJECT (priv->messages_renderer),
3982 "foreground-gdk", &style_color,
3983 "foreground-set", TRUE,
3984 "attributes", attr_list,
3986 pango_attr_list_unref (attr_list);
3991 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3993 if (strcmp ("style", spec->name) == 0) {
3994 update_style (MODEST_FOLDER_VIEW (obj));
3995 gtk_widget_queue_draw (GTK_WIDGET (obj));
4000 modest_folder_view_set_filter (ModestFolderView *self,
4001 ModestFolderViewFilter filter)
4003 ModestFolderViewPrivate *priv;
4004 GtkTreeModel *filter_model;
4006 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4007 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4009 priv->filter |= filter;
4011 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4012 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4013 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4018 modest_folder_view_unset_filter (ModestFolderView *self,
4019 ModestFolderViewFilter filter)
4021 ModestFolderViewPrivate *priv;
4022 GtkTreeModel *filter_model;
4024 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4025 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4027 priv->filter &= ~filter;
4029 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4030 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4031 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4036 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4037 ModestTnyFolderRules rules)
4039 GtkTreeModel *filter_model;
4041 gboolean fulfil = FALSE;
4043 if (!get_inner_models (self, &filter_model, NULL, NULL))
4046 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4050 TnyFolderStore *folder;
4052 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4054 if (TNY_IS_FOLDER (folder)) {
4055 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4056 /* Folder rules are negative: non_writable, non_deletable... */
4057 if (!(folder_rules & rules))
4060 g_object_unref (folder);
4063 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4069 modest_folder_view_set_list_to_move (ModestFolderView *self,
4072 ModestFolderViewPrivate *priv;
4074 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4075 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4077 if (priv->list_to_move)
4078 g_object_unref (priv->list_to_move);
4081 g_object_ref (list);
4083 priv->list_to_move = list;
4087 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4089 ModestFolderViewPrivate *priv;
4091 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4092 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4095 g_free (priv->mailbox);
4097 priv->mailbox = g_strdup (mailbox);
4099 /* Notify observers */
4100 g_signal_emit (G_OBJECT(self),
4101 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4102 priv->visible_account_id);
4106 modest_folder_view_get_mailbox (ModestFolderView *self)
4108 ModestFolderViewPrivate *priv;
4110 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4111 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4113 return (const gchar *) priv->mailbox;
4117 modest_folder_view_get_activity (ModestFolderView *self)
4119 ModestFolderViewPrivate *priv;
4120 GtkTreeModel *inner_model;
4122 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4123 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4124 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4126 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4127 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4133 #ifdef MODEST_TOOLKIT_HILDON2
4135 on_activity_changed (TnyGtkFolderListStore *store,
4137 ModestFolderView *folder_view)
4139 ModestFolderViewPrivate *priv;
4141 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4142 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4143 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4145 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,