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;
260 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
261 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
262 MODEST_TYPE_FOLDER_VIEW, \
263 ModestFolderViewPrivate))
265 static GObjectClass *parent_class = NULL;
267 static guint signals[LAST_SIGNAL] = {0};
270 modest_folder_view_get_type (void)
272 static GType my_type = 0;
274 static const GTypeInfo my_info = {
275 sizeof(ModestFolderViewClass),
276 NULL, /* base init */
277 NULL, /* base finalize */
278 (GClassInitFunc) modest_folder_view_class_init,
279 NULL, /* class finalize */
280 NULL, /* class data */
281 sizeof(ModestFolderView),
283 (GInstanceInitFunc) modest_folder_view_init,
287 static const GInterfaceInfo tny_account_store_view_info = {
288 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
289 NULL, /* interface_finalize */
290 NULL /* interface_data */
294 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
298 g_type_add_interface_static (my_type,
299 TNY_TYPE_ACCOUNT_STORE_VIEW,
300 &tny_account_store_view_info);
306 modest_folder_view_class_init (ModestFolderViewClass *klass)
308 GObjectClass *gobject_class;
309 GtkTreeViewClass *treeview_class;
310 gobject_class = (GObjectClass*) klass;
311 treeview_class = (GtkTreeViewClass*) klass;
313 parent_class = g_type_class_peek_parent (klass);
314 gobject_class->finalize = modest_folder_view_finalize;
316 g_type_class_add_private (gobject_class,
317 sizeof(ModestFolderViewPrivate));
319 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
320 g_signal_new ("folder_selection_changed",
321 G_TYPE_FROM_CLASS (gobject_class),
323 G_STRUCT_OFFSET (ModestFolderViewClass,
324 folder_selection_changed),
326 modest_marshal_VOID__POINTER_BOOLEAN,
327 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
330 * This signal is emitted whenever the currently selected
331 * folder display name is computed. Note that the name could
332 * be different to the folder name, because we could append
333 * the unread messages count to the folder name to build the
334 * folder display name
336 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
337 g_signal_new ("folder-display-name-changed",
338 G_TYPE_FROM_CLASS (gobject_class),
340 G_STRUCT_OFFSET (ModestFolderViewClass,
341 folder_display_name_changed),
343 g_cclosure_marshal_VOID__STRING,
344 G_TYPE_NONE, 1, G_TYPE_STRING);
346 signals[FOLDER_ACTIVATED_SIGNAL] =
347 g_signal_new ("folder_activated",
348 G_TYPE_FROM_CLASS (gobject_class),
350 G_STRUCT_OFFSET (ModestFolderViewClass,
353 g_cclosure_marshal_VOID__POINTER,
354 G_TYPE_NONE, 1, G_TYPE_POINTER);
357 * Emitted whenever the visible account changes
359 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
360 g_signal_new ("visible-account-changed",
361 G_TYPE_FROM_CLASS (gobject_class),
363 G_STRUCT_OFFSET (ModestFolderViewClass,
364 visible_account_changed),
366 g_cclosure_marshal_VOID__STRING,
367 G_TYPE_NONE, 1, G_TYPE_STRING);
370 * Emitted when the underlying GtkListStore is updating data
372 signals[ACTIVITY_CHANGED_SIGNAL] =
373 g_signal_new ("activity-changed",
374 G_TYPE_FROM_CLASS (gobject_class),
376 G_STRUCT_OFFSET (ModestFolderViewClass,
379 g_cclosure_marshal_VOID__BOOLEAN,
380 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
382 treeview_class->select_cursor_parent = NULL;
384 #ifdef MODEST_TOOLKIT_HILDON2
385 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
391 /* Retrieves the filter, sort and tny models of the folder view. If
392 any of these does not exist then it returns FALSE */
394 get_inner_models (ModestFolderView *self,
395 GtkTreeModel **filter_model,
396 GtkTreeModel **sort_model,
397 GtkTreeModel **tny_model)
399 GtkTreeModel *s_model, *f_model, *t_model;
401 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
402 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
403 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
407 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
408 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
409 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
413 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
417 *filter_model = f_model;
419 *sort_model = s_model;
421 *tny_model = t_model;
426 /* Simplify checks for NULLs: */
428 strings_are_equal (const gchar *a, const gchar *b)
434 return (strcmp (a, b) == 0);
441 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
443 GObject *instance = NULL;
445 gtk_tree_model_get (model, iter,
446 INSTANCE_COLUMN, &instance,
450 return FALSE; /* keep walking */
452 if (!TNY_IS_ACCOUNT (instance)) {
453 g_object_unref (instance);
454 return FALSE; /* keep walking */
457 /* Check if this is the looked-for account: */
458 TnyAccount *this_account = TNY_ACCOUNT (instance);
459 TnyAccount *account = TNY_ACCOUNT (data);
461 const gchar *this_account_id = tny_account_get_id(this_account);
462 const gchar *account_id = tny_account_get_id(account);
463 g_object_unref (instance);
466 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
467 if (strings_are_equal(this_account_id, account_id)) {
468 /* Tell the model that the data has changed, so that
469 * it calls the cell_data_func callbacks again: */
470 /* TODO: This does not seem to actually cause the new string to be shown: */
471 gtk_tree_model_row_changed (model, path, iter);
473 return TRUE; /* stop walking */
476 return FALSE; /* keep walking */
481 ModestFolderView *self;
482 gchar *previous_name;
483 } GetMmcAccountNameData;
486 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
488 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
490 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
492 if (!strings_are_equal (
493 tny_account_get_name(TNY_ACCOUNT(account)),
494 data->previous_name)) {
496 /* Tell the model that the data has changed, so that
497 * it calls the cell_data_func callbacks again: */
498 ModestFolderView *self = data->self;
499 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
501 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
504 g_free (data->previous_name);
505 g_slice_free (GetMmcAccountNameData, data);
509 convert_parent_folders_to_dots (gchar **item_name)
513 gchar *last_separator;
515 if (item_name == NULL)
518 for (c = *item_name; *c != '\0'; c++) {
519 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
524 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
525 if (last_separator != NULL) {
526 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
533 buffer = g_string_new ("");
534 for (i = 0; i < n_parents; i++) {
535 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
537 buffer = g_string_append (buffer, last_separator);
539 *item_name = g_string_free (buffer, FALSE);
545 format_compact_style (gchar **item_name,
547 const gchar *mailbox,
549 gboolean multiaccount,
550 gboolean *use_markup)
554 TnyFolderType folder_type;
556 if (!TNY_IS_FOLDER (instance))
559 folder = (TnyFolder *) instance;
561 folder_type = tny_folder_get_folder_type (folder);
562 is_special = (get_cmp_pos (folder_type, folder)!= 4);
565 /* Remove mailbox prefix if any */
566 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
567 if (g_str_has_prefix (*item_name, prefix)) {
568 gchar *new_item_name;
570 new_item_name = g_strdup (*item_name + strlen (prefix));
571 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
572 g_free (new_item_name);
573 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
576 *item_name = new_item_name;
578 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
581 *item_name = g_strdup (_("mcen_me_folder_inbox"));
584 if (!is_special || multiaccount) {
585 TnyAccount *account = tny_folder_get_account (folder);
586 const gchar *folder_name;
587 gboolean concat_folder_name = FALSE;
590 /* Should not happen */
594 /* convert parent folders to dots */
595 convert_parent_folders_to_dots (item_name);
597 folder_name = tny_folder_get_name (folder);
598 if (g_str_has_suffix (*item_name, folder_name)) {
599 gchar *offset = g_strrstr (*item_name, folder_name);
601 concat_folder_name = TRUE;
604 buffer = g_string_new ("");
606 buffer = g_string_append (buffer, *item_name);
607 if (concat_folder_name) {
608 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
609 buffer = g_string_append (buffer, folder_name);
610 if (bold) buffer = g_string_append (buffer, "</span>");
613 g_object_unref (account);
615 *item_name = g_string_free (buffer, FALSE);
623 text_cell_data (GtkTreeViewColumn *column,
624 GtkCellRenderer *renderer,
625 GtkTreeModel *tree_model,
629 ModestFolderViewPrivate *priv;
630 GObject *rendobj = (GObject *) renderer;
632 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
633 GObject *instance = NULL;
634 gboolean use_markup = FALSE;
636 gtk_tree_model_get (tree_model, iter,
639 INSTANCE_COLUMN, &instance,
641 if (!fname || !instance)
644 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
645 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
647 gchar *item_name = NULL;
648 gint item_weight = 400;
650 if (type != TNY_FOLDER_TYPE_ROOT) {
654 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
655 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
656 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
657 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
659 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
662 /* Sometimes an special folder is reported by the server as
663 NORMAL, like some versions of Dovecot */
664 if (type == TNY_FOLDER_TYPE_NORMAL ||
665 type == TNY_FOLDER_TYPE_UNKNOWN) {
666 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
670 /* note: we cannot reliably get the counts from the
671 * tree model, we need to use explicit calls on
672 * tny_folder for some reason. Select the number to
673 * show: the unread or unsent messages. in case of
674 * outbox/drafts, show all */
675 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
676 (type == TNY_FOLDER_TYPE_OUTBOX) ||
677 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
678 number = tny_folder_get_all_count (TNY_FOLDER(instance));
681 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
685 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
686 item_name = g_strdup (fname);
693 /* Use bold font style if there are unread or unset messages */
695 if (priv->show_message_count) {
696 item_name = g_strdup_printf ("%s (%d)", fname, number);
698 item_name = g_strdup (fname);
702 item_name = g_strdup (fname);
707 } else if (TNY_IS_ACCOUNT (instance)) {
708 /* If it's a server account */
709 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
710 item_name = g_strdup (priv->local_account_name);
712 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
713 /* fname is only correct when the items are first
714 * added to the model, not when the account is
715 * changed later, so get the name from the account
717 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
720 item_name = g_strdup (fname);
726 item_name = g_strdup ("unknown");
728 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
729 gboolean multiaccount;
731 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
732 /* Convert item_name to markup */
733 format_compact_style (&item_name, instance, priv->mailbox,
735 multiaccount, &use_markup);
738 if (item_name && item_weight) {
739 /* Set the name in the treeview cell: */
741 g_object_set (rendobj, "markup", item_name, NULL);
743 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
745 /* Notify display name observers */
746 /* TODO: What listens for this signal, and how can it use only the new name? */
747 if (((GObject *) priv->cur_folder_store) == instance) {
748 g_signal_emit (G_OBJECT(self),
749 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
756 /* If it is a Memory card account, make sure that we have the correct name.
757 * This function will be trigerred again when the name has been retrieved: */
758 if (TNY_IS_STORE_ACCOUNT (instance) &&
759 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
761 /* Get the account name asynchronously: */
762 GetMmcAccountNameData *callback_data =
763 g_slice_new0(GetMmcAccountNameData);
764 callback_data->self = self;
766 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
768 callback_data->previous_name = g_strdup (name);
770 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
771 on_get_mmc_account_name, callback_data);
775 g_object_unref (G_OBJECT (instance));
781 messages_cell_data (GtkTreeViewColumn *column,
782 GtkCellRenderer *renderer,
783 GtkTreeModel *tree_model,
787 ModestFolderView *self;
788 ModestFolderViewPrivate *priv;
789 GObject *rendobj = (GObject *) renderer;
790 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
791 GObject *instance = NULL;
792 gchar *item_name = NULL;
794 gtk_tree_model_get (tree_model, iter,
796 INSTANCE_COLUMN, &instance,
801 self = MODEST_FOLDER_VIEW (data);
802 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
805 if (type != TNY_FOLDER_TYPE_ROOT) {
809 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
810 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
811 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
813 /* Sometimes an special folder is reported by the server as
814 NORMAL, like some versions of Dovecot */
815 if (type == TNY_FOLDER_TYPE_NORMAL ||
816 type == TNY_FOLDER_TYPE_UNKNOWN) {
817 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
821 /* note: we cannot reliably get the counts from the tree model, we need
822 * to use explicit calls on tny_folder for some reason.
824 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
825 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
826 (type == TNY_FOLDER_TYPE_OUTBOX) ||
827 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
828 number = tny_folder_get_all_count (TNY_FOLDER(instance));
831 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
835 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
837 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
845 item_name = g_strdup ("");
848 /* Set the name in the treeview cell: */
849 g_object_set (rendobj,"text", item_name, NULL);
857 g_object_unref (G_OBJECT (instance));
863 GdkPixbuf *pixbuf_open;
864 GdkPixbuf *pixbuf_close;
868 static inline GdkPixbuf *
869 get_composite_pixbuf (const gchar *icon_name,
871 GdkPixbuf *base_pixbuf)
873 GdkPixbuf *emblem, *retval = NULL;
875 emblem = modest_platform_get_icon (icon_name, size);
877 retval = gdk_pixbuf_copy (base_pixbuf);
878 gdk_pixbuf_composite (emblem, retval, 0, 0,
879 MIN (gdk_pixbuf_get_width (emblem),
880 gdk_pixbuf_get_width (retval)),
881 MIN (gdk_pixbuf_get_height (emblem),
882 gdk_pixbuf_get_height (retval)),
883 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
884 g_object_unref (emblem);
889 static inline ThreePixbufs *
890 get_composite_icons (const gchar *icon_code,
892 GdkPixbuf **pixbuf_open,
893 GdkPixbuf **pixbuf_close)
895 ThreePixbufs *retval;
898 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
901 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
906 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
910 retval = g_slice_new0 (ThreePixbufs);
912 retval->pixbuf = g_object_ref (*pixbuf);
914 retval->pixbuf_open = g_object_ref (*pixbuf_open);
916 retval->pixbuf_close = g_object_ref (*pixbuf_close);
921 static inline ThreePixbufs *
922 get_account_protocol_pixbufs (ModestFolderView *folder_view,
923 ModestProtocolType protocol_type,
926 ModestProtocol *protocol;
927 const GdkPixbuf *pixbuf = NULL;
928 ModestFolderViewPrivate *priv;
930 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
932 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
935 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
936 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
937 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
938 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
939 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
940 object, FOLDER_ICON_SIZE);
944 ThreePixbufs *retval;
945 retval = g_slice_new0 (ThreePixbufs);
946 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
947 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
948 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
955 static inline ThreePixbufs*
956 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
958 TnyAccount *account = NULL;
959 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
960 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
961 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
962 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
963 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
965 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
966 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
967 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
968 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
969 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
971 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
972 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
973 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
974 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
975 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
977 ThreePixbufs *retval = NULL;
979 if (TNY_IS_ACCOUNT (instance)) {
980 account = g_object_ref (instance);
981 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
982 account = tny_folder_get_account (TNY_FOLDER (instance));
986 ModestProtocolType account_store_protocol;
988 account_store_protocol = modest_tny_account_get_protocol_type (account);
989 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
990 g_object_unref (account);
996 /* Sometimes an special folder is reported by the server as
997 NORMAL, like some versions of Dovecot */
998 if (type == TNY_FOLDER_TYPE_NORMAL ||
999 type == TNY_FOLDER_TYPE_UNKNOWN) {
1000 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1003 /* It's not enough with check the folder type. We need to
1004 ensure that we're not giving a special folder icon to a
1005 normal folder with the same name than a special folder */
1006 if (TNY_IS_FOLDER (instance) &&
1007 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1008 type = TNY_FOLDER_TYPE_NORMAL;
1010 /* Remote folders should not be treated as special folders */
1011 if (TNY_IS_FOLDER_STORE (instance) &&
1012 !TNY_IS_ACCOUNT (instance) &&
1013 type != TNY_FOLDER_TYPE_INBOX &&
1014 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1015 #ifdef MODEST_TOOLKIT_HILDON2
1016 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1019 &anorm_pixbuf_close);
1021 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1023 &normal_pixbuf_open,
1024 &normal_pixbuf_close);
1030 case TNY_FOLDER_TYPE_INVALID:
1031 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1034 case TNY_FOLDER_TYPE_ROOT:
1035 if (TNY_IS_ACCOUNT (instance)) {
1037 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1038 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1041 &avirt_pixbuf_close);
1043 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1045 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1046 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1049 &ammc_pixbuf_close);
1051 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1054 &anorm_pixbuf_close);
1059 case TNY_FOLDER_TYPE_INBOX:
1060 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1063 &inbox_pixbuf_close);
1065 case TNY_FOLDER_TYPE_OUTBOX:
1066 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1068 &outbox_pixbuf_open,
1069 &outbox_pixbuf_close);
1071 case TNY_FOLDER_TYPE_JUNK:
1072 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1075 &junk_pixbuf_close);
1077 case TNY_FOLDER_TYPE_SENT:
1078 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1081 &sent_pixbuf_close);
1083 case TNY_FOLDER_TYPE_TRASH:
1084 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1087 &trash_pixbuf_close);
1089 case TNY_FOLDER_TYPE_DRAFTS:
1090 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1093 &draft_pixbuf_close);
1095 case TNY_FOLDER_TYPE_ARCHIVE:
1096 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1101 case TNY_FOLDER_TYPE_NORMAL:
1103 /* Memory card folders could have an special icon */
1104 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1105 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1110 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1112 &normal_pixbuf_open,
1113 &normal_pixbuf_close);
1122 free_pixbufs (ThreePixbufs *pixbufs)
1124 if (pixbufs->pixbuf)
1125 g_object_unref (pixbufs->pixbuf);
1126 if (pixbufs->pixbuf_open)
1127 g_object_unref (pixbufs->pixbuf_open);
1128 if (pixbufs->pixbuf_close)
1129 g_object_unref (pixbufs->pixbuf_close);
1130 g_slice_free (ThreePixbufs, pixbufs);
1134 icon_cell_data (GtkTreeViewColumn *column,
1135 GtkCellRenderer *renderer,
1136 GtkTreeModel *tree_model,
1140 GObject *rendobj = NULL, *instance = NULL;
1141 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1142 gboolean has_children;
1143 ThreePixbufs *pixbufs;
1144 ModestFolderView *folder_view = (ModestFolderView *) data;
1146 rendobj = (GObject *) renderer;
1148 gtk_tree_model_get (tree_model, iter,
1150 INSTANCE_COLUMN, &instance,
1156 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1157 pixbufs = get_folder_icons (folder_view, type, instance);
1158 g_object_unref (instance);
1161 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1164 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1165 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1168 free_pixbufs (pixbufs);
1172 add_columns (GtkWidget *treeview)
1174 GtkTreeViewColumn *column;
1175 GtkCellRenderer *renderer;
1176 GtkTreeSelection *sel;
1177 ModestFolderViewPrivate *priv;
1179 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1182 column = gtk_tree_view_column_new ();
1184 /* Set icon and text render function */
1185 renderer = gtk_cell_renderer_pixbuf_new();
1186 #ifdef MODEST_TOOLKIT_HILDON2
1187 g_object_set (renderer,
1188 "xpad", MODEST_MARGIN_DEFAULT,
1189 "ypad", MODEST_MARGIN_DEFAULT,
1192 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1193 gtk_tree_view_column_set_cell_data_func(column, renderer,
1194 icon_cell_data, treeview, NULL);
1196 renderer = gtk_cell_renderer_text_new();
1197 g_object_set (renderer,
1198 #ifdef MODEST_TOOLKIT_HILDON2
1199 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1200 "ypad", MODEST_MARGIN_DEFAULT,
1201 "xpad", MODEST_MARGIN_DEFAULT,
1203 "ellipsize", PANGO_ELLIPSIZE_END,
1205 "ellipsize-set", TRUE, NULL);
1206 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1207 gtk_tree_view_column_set_cell_data_func(column, renderer,
1208 text_cell_data, treeview, NULL);
1210 priv->messages_renderer = gtk_cell_renderer_text_new ();
1211 g_object_set (priv->messages_renderer,
1212 #ifdef MODEST_TOOLKIT_HILDON2
1214 "ypad", MODEST_MARGIN_DEFAULT,
1215 "xpad", MODEST_MARGIN_DOUBLE,
1217 "scale", PANGO_SCALE_X_SMALL,
1220 "alignment", PANGO_ALIGN_RIGHT,
1224 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1225 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1226 messages_cell_data, treeview, NULL);
1228 /* Set selection mode */
1229 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1230 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1232 /* Set treeview appearance */
1233 gtk_tree_view_column_set_spacing (column, 2);
1234 gtk_tree_view_column_set_resizable (column, TRUE);
1235 gtk_tree_view_column_set_fixed_width (column, TRUE);
1236 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1237 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1240 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1244 modest_folder_view_init (ModestFolderView *obj)
1246 ModestFolderViewPrivate *priv;
1249 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1251 priv->timer_expander = 0;
1252 priv->account_store = NULL;
1254 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1255 priv->cur_folder_store = NULL;
1256 priv->visible_account_id = NULL;
1257 priv->mailbox = NULL;
1258 priv->folder_to_select = NULL;
1259 priv->outbox_deleted_handler = 0;
1260 priv->reexpand = TRUE;
1261 priv->activity = FALSE;
1262 priv->activity_changed_handler = 0;
1264 /* Initialize the local account name */
1265 conf = modest_runtime_get_conf();
1266 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1268 /* Init email clipboard */
1269 priv->clipboard = modest_runtime_get_email_clipboard ();
1270 priv->hidding_ids = NULL;
1271 priv->n_selected = 0;
1272 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1273 priv->reselect = FALSE;
1274 priv->show_non_move = TRUE;
1275 priv->list_to_move = NULL;
1276 priv->show_message_count = TRUE;
1278 /* Build treeview */
1279 add_columns (GTK_WIDGET (obj));
1281 /* Setup drag and drop */
1282 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1284 /* Connect signals */
1285 g_signal_connect (G_OBJECT (obj),
1287 G_CALLBACK (on_key_pressed), NULL);
1289 priv->display_name_changed_signal =
1290 g_signal_connect (modest_runtime_get_account_mgr (),
1291 "display_name_changed",
1292 G_CALLBACK (on_display_name_changed),
1296 * Track changes in the local account name (in the device it
1297 * will be the device name)
1299 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1301 G_CALLBACK(on_configuration_key_changed),
1305 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1311 tny_account_store_view_init (gpointer g, gpointer iface_data)
1313 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1315 klass->set_account_store = modest_folder_view_set_account_store;
1319 modest_folder_view_finalize (GObject *obj)
1321 ModestFolderViewPrivate *priv;
1322 GtkTreeSelection *sel;
1323 TnyAccount *local_account;
1325 g_return_if_fail (obj);
1327 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1329 if (priv->timer_expander != 0) {
1330 g_source_remove (priv->timer_expander);
1331 priv->timer_expander = 0;
1334 local_account = (TnyAccount *)
1335 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1336 if (local_account) {
1337 if (g_signal_handler_is_connected (local_account,
1338 priv->outbox_deleted_handler))
1339 g_signal_handler_disconnect (local_account,
1340 priv->outbox_deleted_handler);
1341 g_object_unref (local_account);
1344 if (priv->account_store) {
1345 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1346 priv->account_inserted_signal);
1347 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1348 priv->account_removed_signal);
1349 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1350 priv->account_changed_signal);
1351 g_object_unref (G_OBJECT(priv->account_store));
1352 priv->account_store = NULL;
1355 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1356 priv->display_name_changed_signal)) {
1357 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1358 priv->display_name_changed_signal);
1359 priv->display_name_changed_signal = 0;
1363 g_object_unref (G_OBJECT (priv->query));
1367 if (priv->folder_to_select) {
1368 g_object_unref (G_OBJECT(priv->folder_to_select));
1369 priv->folder_to_select = NULL;
1372 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1374 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1376 g_free (priv->local_account_name);
1377 g_free (priv->visible_account_id);
1378 g_free (priv->mailbox);
1380 if (priv->conf_key_signal) {
1381 g_signal_handler_disconnect (modest_runtime_get_conf (),
1382 priv->conf_key_signal);
1383 priv->conf_key_signal = 0;
1386 if (priv->cur_folder_store) {
1387 g_object_unref (priv->cur_folder_store);
1388 priv->cur_folder_store = NULL;
1391 if (priv->list_to_move) {
1392 g_object_unref (priv->list_to_move);
1393 priv->list_to_move = NULL;
1396 /* Clear hidding array created by cut operation */
1397 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1399 G_OBJECT_CLASS(parent_class)->finalize (obj);
1404 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1406 ModestFolderViewPrivate *priv;
1409 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1410 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1412 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1413 device = tny_account_store_get_device (account_store);
1415 if (G_UNLIKELY (priv->account_store)) {
1417 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1418 priv->account_inserted_signal))
1419 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1420 priv->account_inserted_signal);
1421 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1422 priv->account_removed_signal))
1423 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1424 priv->account_removed_signal);
1425 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1426 priv->account_changed_signal))
1427 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1428 priv->account_changed_signal);
1429 g_object_unref (G_OBJECT (priv->account_store));
1432 priv->account_store = g_object_ref (G_OBJECT (account_store));
1434 priv->account_removed_signal =
1435 g_signal_connect (G_OBJECT(account_store), "account_removed",
1436 G_CALLBACK (on_account_removed), self);
1438 priv->account_inserted_signal =
1439 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1440 G_CALLBACK (on_account_inserted), self);
1442 priv->account_changed_signal =
1443 g_signal_connect (G_OBJECT(account_store), "account_changed",
1444 G_CALLBACK (on_account_changed), self);
1446 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1447 priv->reselect = FALSE;
1448 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1450 g_object_unref (G_OBJECT (device));
1454 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1457 ModestFolderView *self;
1458 GtkTreeModel *model, *filter_model;
1461 self = MODEST_FOLDER_VIEW (user_data);
1463 if (!get_inner_models (self, &filter_model, NULL, &model))
1466 /* Remove outbox from model */
1467 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1468 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1469 g_object_unref (outbox);
1472 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1476 on_account_inserted (TnyAccountStore *account_store,
1477 TnyAccount *account,
1480 ModestFolderViewPrivate *priv;
1481 GtkTreeModel *model, *filter_model;
1483 /* Ignore transport account insertions, we're not showing them
1484 in the folder view */
1485 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1488 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1491 /* If we're adding a new account, and there is no previous
1492 one, we need to select the visible server account */
1493 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1494 !priv->visible_account_id)
1495 modest_widget_memory_restore (modest_runtime_get_conf(),
1496 G_OBJECT (user_data),
1497 MODEST_CONF_FOLDER_VIEW_KEY);
1501 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1502 &filter_model, NULL, &model))
1505 /* Insert the account in the model */
1506 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1508 /* When the model is a list store (plain representation) the
1509 outbox is not a child of any account so we have to manually
1510 delete it because removing the local folders account won't
1511 delete it (because tny_folder_get_account() is not defined
1512 for a merge folder */
1513 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1514 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1516 priv->outbox_deleted_handler =
1517 g_signal_connect (account,
1519 G_CALLBACK (on_outbox_deleted_cb),
1523 /* Refilter the model */
1524 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1529 same_account_selected (ModestFolderView *self,
1530 TnyAccount *account)
1532 ModestFolderViewPrivate *priv;
1533 gboolean same_account = FALSE;
1535 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1537 if (priv->cur_folder_store) {
1538 TnyAccount *selected_folder_account = NULL;
1540 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1541 selected_folder_account =
1542 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1544 selected_folder_account =
1545 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1548 if (selected_folder_account == account)
1549 same_account = TRUE;
1551 g_object_unref (selected_folder_account);
1553 return same_account;
1558 * Selects the first inbox or the local account in an idle
1561 on_idle_select_first_inbox_or_local (gpointer user_data)
1563 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1565 gdk_threads_enter ();
1566 modest_folder_view_select_first_inbox_or_local (self);
1567 gdk_threads_leave ();
1573 on_account_changed (TnyAccountStore *account_store,
1574 TnyAccount *tny_account,
1577 ModestFolderView *self;
1578 ModestFolderViewPrivate *priv;
1579 GtkTreeModel *model, *filter_model;
1580 GtkTreeSelection *sel;
1581 gboolean same_account;
1583 /* Ignore transport account insertions, we're not showing them
1584 in the folder view */
1585 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1588 self = MODEST_FOLDER_VIEW (user_data);
1589 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1591 /* Get the inner model */
1592 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1593 &filter_model, NULL, &model))
1596 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1598 /* Invalidate the cur_folder_store only if the selected folder
1599 belongs to the account that is being removed */
1600 same_account = same_account_selected (self, tny_account);
1602 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1603 gtk_tree_selection_unselect_all (sel);
1606 /* Remove the account from the model */
1607 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1609 /* Insert the account in the model */
1610 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1612 /* Refilter the model */
1613 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1615 /* Select the first INBOX if the currently selected folder
1616 belongs to the account that is being deleted */
1617 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1618 g_idle_add (on_idle_select_first_inbox_or_local, self);
1622 on_account_removed (TnyAccountStore *account_store,
1623 TnyAccount *account,
1626 ModestFolderView *self = NULL;
1627 ModestFolderViewPrivate *priv;
1628 GtkTreeModel *model, *filter_model;
1629 GtkTreeSelection *sel = NULL;
1630 gboolean same_account = FALSE;
1632 /* Ignore transport account removals, we're not showing them
1633 in the folder view */
1634 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1637 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1638 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1642 self = MODEST_FOLDER_VIEW (user_data);
1643 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1645 /* Invalidate the cur_folder_store only if the selected folder
1646 belongs to the account that is being removed */
1647 same_account = same_account_selected (self, account);
1649 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1650 gtk_tree_selection_unselect_all (sel);
1653 /* Invalidate row to select only if the folder to select
1654 belongs to the account that is being removed*/
1655 if (priv->folder_to_select) {
1656 TnyAccount *folder_to_select_account = NULL;
1658 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1659 if (folder_to_select_account == account) {
1660 modest_folder_view_disable_next_folder_selection (self);
1661 g_object_unref (priv->folder_to_select);
1662 priv->folder_to_select = NULL;
1664 g_object_unref (folder_to_select_account);
1667 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1668 &filter_model, NULL, &model))
1671 /* Disconnect the signal handler */
1672 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1673 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1674 if (g_signal_handler_is_connected (account,
1675 priv->outbox_deleted_handler))
1676 g_signal_handler_disconnect (account,
1677 priv->outbox_deleted_handler);
1680 /* Remove the account from the model */
1681 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1683 /* If the removed account is the currently viewed one then
1684 clear the configuration value. The new visible account will be the default account */
1685 if (priv->visible_account_id &&
1686 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1688 /* Clear the current visible account_id */
1689 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1690 modest_folder_view_set_mailbox (self, NULL);
1692 /* Call the restore method, this will set the new visible account */
1693 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1694 MODEST_CONF_FOLDER_VIEW_KEY);
1697 /* Refilter the model */
1698 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1700 /* Select the first INBOX if the currently selected folder
1701 belongs to the account that is being deleted */
1703 g_idle_add (on_idle_select_first_inbox_or_local, self);
1707 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1709 GtkTreeViewColumn *col;
1711 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1713 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1715 g_printerr ("modest: failed get column for title\n");
1719 gtk_tree_view_column_set_title (col, title);
1720 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1725 modest_folder_view_on_map (ModestFolderView *self,
1726 GdkEventExpose *event,
1729 ModestFolderViewPrivate *priv;
1731 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1733 /* This won't happen often */
1734 if (G_UNLIKELY (priv->reselect)) {
1735 /* Select the first inbox or the local account if not found */
1737 /* TODO: this could cause a lock at startup, so we
1738 comment it for the moment. We know that this will
1739 be a bug, because the INBOX is not selected, but we
1740 need to rewrite some parts of Modest to avoid the
1741 deathlock situation */
1742 /* TODO: check if this is still the case */
1743 priv->reselect = FALSE;
1744 modest_folder_view_select_first_inbox_or_local (self);
1745 /* Notify the display name observers */
1746 g_signal_emit (G_OBJECT(self),
1747 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1751 if (priv->reexpand) {
1752 expand_root_items (self);
1753 priv->reexpand = FALSE;
1760 modest_folder_view_new (TnyFolderStoreQuery *query)
1763 ModestFolderViewPrivate *priv;
1764 GtkTreeSelection *sel;
1766 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1767 #ifdef MODEST_TOOLKIT_HILDON2
1768 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1771 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1774 priv->query = g_object_ref (query);
1776 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1777 priv->changed_signal = g_signal_connect (sel, "changed",
1778 G_CALLBACK (on_selection_changed), self);
1780 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1782 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1784 return GTK_WIDGET(self);
1787 /* this feels dirty; any other way to expand all the root items? */
1789 expand_root_items (ModestFolderView *self)
1792 GtkTreeModel *model;
1795 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1796 path = gtk_tree_path_new_first ();
1798 /* all folders should have child items, so.. */
1800 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1801 gtk_tree_path_next (path);
1802 } while (gtk_tree_model_get_iter (model, &iter, path));
1804 gtk_tree_path_free (path);
1808 is_parent_of (TnyFolder *a, TnyFolder *b)
1811 gboolean retval = FALSE;
1813 a_id = tny_folder_get_id (a);
1815 gchar *string_to_match;
1818 string_to_match = g_strconcat (a_id, "/", NULL);
1819 b_id = tny_folder_get_id (b);
1820 retval = g_str_has_prefix (b_id, string_to_match);
1821 g_free (string_to_match);
1827 typedef struct _ForeachFolderInfo {
1830 } ForeachFolderInfo;
1833 foreach_folder_with_id (GtkTreeModel *model,
1838 ForeachFolderInfo *info;
1841 info = (ForeachFolderInfo *) data;
1842 gtk_tree_model_get (model, iter,
1843 INSTANCE_COLUMN, &instance,
1846 if (TNY_IS_FOLDER (instance)) {
1849 id = tny_folder_get_id (TNY_FOLDER (instance));
1851 collate = g_utf8_collate_key (id, -1);
1852 info->found = !strcmp (info->needle, collate);
1858 g_object_unref (instance);
1866 has_folder_with_id (ModestFolderView *self, const gchar *id)
1868 GtkTreeModel *model;
1869 ForeachFolderInfo info = {NULL, FALSE};
1871 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1872 info.needle = g_utf8_collate_key (id, -1);
1874 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1875 g_free (info.needle);
1881 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1884 gboolean retval = FALSE;
1886 a_id = tny_folder_get_id (a);
1889 b_id = tny_folder_get_id (b);
1892 const gchar *last_bar;
1893 gchar *string_to_match;
1894 last_bar = g_strrstr (b_id, "/");
1899 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1900 retval = has_folder_with_id (self, string_to_match);
1901 g_free (string_to_match);
1909 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1911 ModestFolderViewPrivate *priv;
1912 TnyIterator *iterator;
1913 gboolean retval = TRUE;
1915 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1916 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1918 for (iterator = tny_list_create_iterator (priv->list_to_move);
1919 retval && !tny_iterator_is_done (iterator);
1920 tny_iterator_next (iterator)) {
1922 instance = tny_iterator_get_current (iterator);
1923 if (instance == (GObject *) folder) {
1925 } else if (TNY_IS_FOLDER (instance)) {
1926 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1928 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1931 g_object_unref (instance);
1933 g_object_unref (iterator);
1940 * We use this function to implement the
1941 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1942 * account in this case, and the local folders.
1945 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1947 ModestFolderViewPrivate *priv;
1948 gboolean retval = TRUE;
1949 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1950 GObject *instance = NULL;
1951 const gchar *id = NULL;
1953 gboolean found = FALSE;
1954 gboolean cleared = FALSE;
1955 ModestTnyFolderRules rules = 0;
1958 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1959 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1961 gtk_tree_model_get (model, iter,
1962 NAME_COLUMN, &fname,
1964 INSTANCE_COLUMN, &instance,
1967 /* Do not show if there is no instance, this could indeed
1968 happen when the model is being modified while it's being
1969 drawn. This could occur for example when moving folders
1976 if (TNY_IS_ACCOUNT (instance)) {
1977 TnyAccount *acc = TNY_ACCOUNT (instance);
1978 const gchar *account_id = tny_account_get_id (acc);
1980 /* If it isn't a special folder,
1981 * don't show it unless it is the visible account: */
1982 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1983 !modest_tny_account_is_virtual_local_folders (acc) &&
1984 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1986 /* Show only the visible account id */
1987 if (priv->visible_account_id) {
1988 if (strcmp (account_id, priv->visible_account_id))
1995 /* Never show these to the user. They are merged into one folder
1996 * in the local-folders account instead: */
1997 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2000 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2001 /* Only show special folders for current account if needed */
2002 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2003 TnyAccount *account;
2005 account = tny_folder_get_account (TNY_FOLDER (instance));
2007 if (TNY_IS_ACCOUNT (account)) {
2008 const gchar *account_id = tny_account_get_id (account);
2010 if (!modest_tny_account_is_virtual_local_folders (account) &&
2011 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2012 /* Show only the visible account id */
2013 if (priv->visible_account_id) {
2014 if (strcmp (account_id, priv->visible_account_id)) {
2016 } else if (priv->mailbox) {
2017 /* Filter mailboxes */
2018 if (!g_str_has_prefix (fname, priv->mailbox)) {
2020 } else if (!strcmp (fname, priv->mailbox)) {
2021 /* Hide mailbox parent */
2027 g_object_unref (account);
2034 /* Check hiding (if necessary) */
2035 cleared = modest_email_clipboard_cleared (priv->clipboard);
2036 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2037 id = tny_folder_get_id (TNY_FOLDER(instance));
2038 if (priv->hidding_ids != NULL)
2039 for (i=0; i < priv->n_selected && !found; i++)
2040 if (priv->hidding_ids[i] != NULL && id != NULL)
2041 found = (!strcmp (priv->hidding_ids[i], id));
2046 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2047 folder as no message can be move there according to UI specs */
2048 if (retval && !priv->show_non_move) {
2049 if (priv->list_to_move &&
2050 tny_list_get_length (priv->list_to_move) > 0 &&
2051 TNY_IS_FOLDER (instance)) {
2052 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2054 if (retval && TNY_IS_FOLDER (instance) &&
2055 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2057 case TNY_FOLDER_TYPE_OUTBOX:
2058 case TNY_FOLDER_TYPE_SENT:
2059 case TNY_FOLDER_TYPE_DRAFTS:
2062 case TNY_FOLDER_TYPE_UNKNOWN:
2063 case TNY_FOLDER_TYPE_NORMAL:
2064 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2065 if (type == TNY_FOLDER_TYPE_INVALID)
2066 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2068 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2069 type == TNY_FOLDER_TYPE_SENT
2070 || type == TNY_FOLDER_TYPE_DRAFTS)
2077 if (retval && TNY_IS_ACCOUNT (instance) &&
2078 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2079 ModestProtocolType protocol_type;
2081 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2082 retval = !modest_protocol_registry_protocol_type_has_tag
2083 (modest_runtime_get_protocol_registry (),
2085 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2089 /* apply special filters */
2090 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2091 if (TNY_IS_ACCOUNT (instance))
2095 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2096 if (TNY_IS_FOLDER (instance))
2100 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2101 if (TNY_IS_ACCOUNT (instance)) {
2102 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2104 } else if (TNY_IS_FOLDER (instance)) {
2105 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2110 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2111 if (TNY_IS_ACCOUNT (instance)) {
2112 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2114 } else if (TNY_IS_FOLDER (instance)) {
2115 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2120 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2121 /* A mailbox is a fake folder with an @ in the middle of the name */
2122 if (!TNY_IS_FOLDER (instance) ||
2123 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2126 const gchar *folder_name;
2127 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2128 if (!folder_name || strchr (folder_name, '@') == NULL)
2134 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2135 if (TNY_IS_FOLDER (instance)) {
2136 /* Check folder rules */
2137 ModestTnyFolderRules rules;
2139 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2140 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2141 } else if (TNY_IS_ACCOUNT (instance)) {
2142 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2150 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2151 if (TNY_IS_FOLDER (instance)) {
2152 TnyFolderType guess_type;
2154 if (TNY_FOLDER_TYPE_NORMAL) {
2155 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2161 case TNY_FOLDER_TYPE_OUTBOX:
2162 case TNY_FOLDER_TYPE_SENT:
2163 case TNY_FOLDER_TYPE_DRAFTS:
2164 case TNY_FOLDER_TYPE_ARCHIVE:
2165 case TNY_FOLDER_TYPE_INBOX:
2168 case TNY_FOLDER_TYPE_UNKNOWN:
2169 case TNY_FOLDER_TYPE_NORMAL:
2175 } else if (TNY_IS_ACCOUNT (instance)) {
2180 if (retval && TNY_IS_FOLDER (instance)) {
2181 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2184 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2185 if (TNY_IS_FOLDER (instance)) {
2186 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2187 } else if (TNY_IS_ACCOUNT (instance)) {
2192 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2193 if (TNY_IS_FOLDER (instance)) {
2194 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2195 } else if (TNY_IS_ACCOUNT (instance)) {
2200 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2201 if (TNY_IS_FOLDER (instance)) {
2202 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2203 } else if (TNY_IS_ACCOUNT (instance)) {
2209 g_object_unref (instance);
2217 modest_folder_view_update_model (ModestFolderView *self,
2218 TnyAccountStore *account_store)
2220 ModestFolderViewPrivate *priv;
2221 GtkTreeModel *model;
2222 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2224 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2225 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2228 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2230 /* Notify that there is no folder selected */
2231 g_signal_emit (G_OBJECT(self),
2232 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2234 if (priv->cur_folder_store) {
2235 g_object_unref (priv->cur_folder_store);
2236 priv->cur_folder_store = NULL;
2239 /* FIXME: the local accounts are not shown when the query
2240 selects only the subscribed folders */
2241 #ifdef MODEST_TOOLKIT_HILDON2
2242 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2243 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2244 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2245 MODEST_FOLDER_PATH_SEPARATOR);
2247 model = tny_gtk_folder_store_tree_model_new (NULL);
2250 /* When the model is a list store (plain representation) the
2251 outbox is not a child of any account so we have to manually
2252 delete it because removing the local folders account won't
2253 delete it (because tny_folder_get_account() is not defined
2254 for a merge folder */
2255 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2256 TnyAccount *account;
2257 ModestTnyAccountStore *acc_store;
2259 acc_store = modest_runtime_get_account_store ();
2260 account = modest_tny_account_store_get_local_folders_account (acc_store);
2262 if (g_signal_handler_is_connected (account,
2263 priv->outbox_deleted_handler))
2264 g_signal_handler_disconnect (account,
2265 priv->outbox_deleted_handler);
2267 priv->outbox_deleted_handler =
2268 g_signal_connect (account,
2270 G_CALLBACK (on_outbox_deleted_cb),
2272 g_object_unref (account);
2275 /* Get the accounts: */
2276 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2278 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2280 sortable = gtk_tree_model_sort_new_with_model (model);
2281 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2283 GTK_SORT_ASCENDING);
2284 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2286 cmp_rows, NULL, NULL);
2288 /* Create filter model */
2289 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2290 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2295 if (priv->activity_changed_handler > 0) {
2296 GtkTreeModel *old_tny_model;
2298 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2299 g_signal_handler_disconnect (G_OBJECT (old_tny_model), priv->activity_changed_handler);
2301 priv->activity_changed_handler = 0;
2305 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2306 #ifndef MODEST_TOOLKIT_HILDON2
2307 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2308 (GCallback) on_row_inserted_maybe_select_folder, self);
2311 #ifdef MODEST_TOOLKIT_HILDON2
2312 priv->activity_changed_handler =
2313 g_signal_connect (G_OBJECT (model), "activity-changed", G_CALLBACK (on_activity_changed), self);
2315 priv->activity = FALSE;
2317 g_object_unref (model);
2318 g_object_unref (filter_model);
2319 g_object_unref (sortable);
2321 /* Force a reselection of the INBOX next time the widget is shown */
2322 priv->reselect = TRUE;
2329 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2331 GtkTreeModel *model = NULL;
2332 TnyFolderStore *folder = NULL;
2334 ModestFolderView *tree_view = NULL;
2335 ModestFolderViewPrivate *priv = NULL;
2336 gboolean selected = FALSE;
2338 g_return_if_fail (sel);
2339 g_return_if_fail (user_data);
2341 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2343 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2345 tree_view = MODEST_FOLDER_VIEW (user_data);
2348 gtk_tree_model_get (model, &iter,
2349 INSTANCE_COLUMN, &folder,
2352 /* If the folder is the same do not notify */
2353 if (folder && priv->cur_folder_store == folder) {
2354 g_object_unref (folder);
2359 /* Current folder was unselected */
2360 if (priv->cur_folder_store) {
2361 /* We must do this firstly because a libtinymail-camel
2362 implementation detail. If we issue the signal
2363 before doing the sync_async, then that signal could
2364 cause (and it actually does it) a free of the
2365 summary of the folder (because the main window will
2366 clear the headers view */
2367 if (TNY_IS_FOLDER(priv->cur_folder_store))
2368 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2369 FALSE, NULL, NULL, NULL);
2371 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2372 priv->cur_folder_store, FALSE);
2374 g_object_unref (priv->cur_folder_store);
2375 priv->cur_folder_store = NULL;
2378 /* New current references */
2379 priv->cur_folder_store = folder;
2381 /* New folder has been selected. Do not notify if there is
2382 nothing new selected */
2384 g_signal_emit (G_OBJECT(tree_view),
2385 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2386 0, priv->cur_folder_store, TRUE);
2391 on_row_activated (GtkTreeView *treeview,
2392 GtkTreePath *treepath,
2393 GtkTreeViewColumn *column,
2396 GtkTreeModel *model = NULL;
2397 TnyFolderStore *folder = NULL;
2399 ModestFolderView *self = NULL;
2400 ModestFolderViewPrivate *priv = NULL;
2402 g_return_if_fail (treeview);
2403 g_return_if_fail (user_data);
2405 self = MODEST_FOLDER_VIEW (user_data);
2406 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2408 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2410 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2413 gtk_tree_model_get (model, &iter,
2414 INSTANCE_COLUMN, &folder,
2417 g_signal_emit (G_OBJECT(self),
2418 signals[FOLDER_ACTIVATED_SIGNAL],
2421 #ifdef MODEST_TOOLKIT_HILDON2
2422 HildonUIMode ui_mode;
2423 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2424 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2425 if (priv->cur_folder_store)
2426 g_object_unref (priv->cur_folder_store);
2427 priv->cur_folder_store = g_object_ref (folder);
2431 g_object_unref (folder);
2435 modest_folder_view_get_selected (ModestFolderView *self)
2437 ModestFolderViewPrivate *priv;
2439 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2441 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2442 if (priv->cur_folder_store)
2443 g_object_ref (priv->cur_folder_store);
2445 return priv->cur_folder_store;
2449 get_cmp_rows_type_pos (GObject *folder)
2451 /* Remote accounts -> Local account -> MMC account .*/
2454 if (TNY_IS_ACCOUNT (folder) &&
2455 modest_tny_account_is_virtual_local_folders (
2456 TNY_ACCOUNT (folder))) {
2458 } else if (TNY_IS_ACCOUNT (folder)) {
2459 TnyAccount *account = TNY_ACCOUNT (folder);
2460 const gchar *account_id = tny_account_get_id (account);
2461 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2467 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2468 return -1; /* Should never happen */
2473 inbox_is_special (TnyFolderStore *folder_store)
2475 gboolean is_special = TRUE;
2477 if (TNY_IS_FOLDER (folder_store)) {
2481 gchar *last_inbox_bar;
2483 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2484 downcase = g_utf8_strdown (id, -1);
2485 last_bar = g_strrstr (downcase, "/");
2487 last_inbox_bar = g_strrstr (downcase, "inbox/");
2488 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2499 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2501 TnyAccount *account;
2502 gboolean is_special;
2503 /* Inbox, Outbox, Drafts, Sent, User */
2506 if (!TNY_IS_FOLDER (folder_store))
2509 case TNY_FOLDER_TYPE_INBOX:
2511 account = tny_folder_get_account (folder_store);
2512 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2514 /* In inbox case we need to know if the inbox is really the top
2515 * inbox of the account, or if it's a submailbox inbox. To do
2516 * this we'll apply an heuristic rule: Find last "/" and check
2517 * if it's preceeded by another Inbox */
2518 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2519 g_object_unref (account);
2520 return is_special?0:4;
2523 case TNY_FOLDER_TYPE_OUTBOX:
2524 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2526 case TNY_FOLDER_TYPE_DRAFTS:
2528 account = tny_folder_get_account (folder_store);
2529 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2530 g_object_unref (account);
2531 return is_special?1:4;
2534 case TNY_FOLDER_TYPE_SENT:
2536 account = tny_folder_get_account (folder_store);
2537 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2538 g_object_unref (account);
2539 return is_special?3:4;
2548 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2550 const gchar *a1_name, *a2_name;
2552 a1_name = tny_account_get_name (a1);
2553 a2_name = tny_account_get_name (a2);
2555 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2559 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2561 TnyAccount *a1 = NULL, *a2 = NULL;
2564 if (TNY_IS_ACCOUNT (s1)) {
2565 a1 = TNY_ACCOUNT (g_object_ref (s1));
2566 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2567 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2570 if (TNY_IS_ACCOUNT (s2)) {
2571 a2 = TNY_ACCOUNT (g_object_ref (s2));
2572 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2573 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2590 /* First we sort with the type of account */
2591 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2595 cmp = compare_account_names (a1, a2);
2599 g_object_unref (a1);
2601 g_object_unref (a2);
2607 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2609 gint is_account1, is_account2;
2611 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2612 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2614 return is_account2 - is_account1;
2618 * This function orders the mail accounts according to these rules:
2619 * 1st - remote accounts
2620 * 2nd - local account
2624 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2628 gchar *name1 = NULL;
2629 gchar *name2 = NULL;
2630 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2631 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2632 GObject *folder1 = NULL;
2633 GObject *folder2 = NULL;
2635 gtk_tree_model_get (tree_model, iter1,
2636 NAME_COLUMN, &name1,
2638 INSTANCE_COLUMN, &folder1,
2640 gtk_tree_model_get (tree_model, iter2,
2641 NAME_COLUMN, &name2,
2642 TYPE_COLUMN, &type2,
2643 INSTANCE_COLUMN, &folder2,
2646 /* Return if we get no folder. This could happen when folder
2647 operations are happening. The model is updated after the
2648 folder copy/move actually occurs, so there could be
2649 situations where the model to be drawn is not correct */
2650 if (!folder1 || !folder2)
2653 /* Sort by type. First the special folders, then the archives */
2654 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2658 /* Now we sort using the account of each folder */
2659 if (TNY_IS_FOLDER_STORE (folder1) &&
2660 TNY_IS_FOLDER_STORE (folder2)) {
2661 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2665 /* Each group is preceeded by its account */
2666 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2671 /* Pure sort by name */
2672 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2675 g_object_unref(G_OBJECT(folder1));
2677 g_object_unref(G_OBJECT(folder2));
2685 /*****************************************************************************/
2686 /* DRAG and DROP stuff */
2687 /*****************************************************************************/
2689 * This function fills the #GtkSelectionData with the row and the
2690 * model that has been dragged. It's called when this widget is a
2691 * source for dnd after the event drop happened
2694 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2695 guint info, guint time, gpointer data)
2697 GtkTreeSelection *selection;
2698 GtkTreeModel *model;
2700 GtkTreePath *source_row;
2702 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2703 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2705 source_row = gtk_tree_model_get_path (model, &iter);
2706 gtk_tree_set_row_drag_data (selection_data,
2710 gtk_tree_path_free (source_row);
2714 typedef struct _DndHelper {
2715 ModestFolderView *folder_view;
2716 gboolean delete_source;
2717 GtkTreePath *source_row;
2721 dnd_helper_destroyer (DndHelper *helper)
2723 /* Free the helper */
2724 gtk_tree_path_free (helper->source_row);
2725 g_slice_free (DndHelper, helper);
2729 xfer_folder_cb (ModestMailOperation *mail_op,
2730 TnyFolder *new_folder,
2734 /* Select the folder */
2735 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2741 /* get the folder for the row the treepath refers to. */
2742 /* folder must be unref'd */
2743 static TnyFolderStore *
2744 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2747 TnyFolderStore *folder = NULL;
2749 if (gtk_tree_model_get_iter (model,&iter, path))
2750 gtk_tree_model_get (model, &iter,
2751 INSTANCE_COLUMN, &folder,
2758 * This function is used by drag_data_received_cb to manage drag and
2759 * drop of a header, i.e, and drag from the header view to the folder
2763 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2764 GtkTreeModel *dest_model,
2765 GtkTreePath *dest_row,
2766 GtkSelectionData *selection_data)
2768 TnyList *headers = NULL;
2769 TnyFolder *folder = NULL, *src_folder = NULL;
2770 TnyFolderType folder_type;
2771 GtkTreeIter source_iter, dest_iter;
2772 ModestWindowMgr *mgr = NULL;
2773 ModestWindow *main_win = NULL;
2774 gchar **uris, **tmp;
2776 /* Build the list of headers */
2777 mgr = modest_runtime_get_window_mgr ();
2778 headers = tny_simple_list_new ();
2779 uris = modest_dnd_selection_data_get_paths (selection_data);
2782 while (*tmp != NULL) {
2785 gboolean first = TRUE;
2788 path = gtk_tree_path_new_from_string (*tmp);
2789 gtk_tree_model_get_iter (source_model, &source_iter, path);
2790 gtk_tree_model_get (source_model, &source_iter,
2791 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2794 /* Do not enable d&d of headers already opened */
2795 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2796 tny_list_append (headers, G_OBJECT (header));
2798 if (G_UNLIKELY (first)) {
2799 src_folder = tny_header_get_folder (header);
2803 /* Free and go on */
2804 gtk_tree_path_free (path);
2805 g_object_unref (header);
2810 /* This could happen ig we perform a d&d very quickly over the
2811 same row that row could dissapear because message is
2813 if (!TNY_IS_FOLDER (src_folder))
2816 /* Get the target folder */
2817 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2818 gtk_tree_model_get (dest_model, &dest_iter,
2822 if (!folder || !TNY_IS_FOLDER(folder)) {
2823 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2827 folder_type = modest_tny_folder_guess_folder_type (folder);
2828 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2829 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2830 goto cleanup; /* cannot move messages there */
2833 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2834 /* g_warning ("folder not writable"); */
2835 goto cleanup; /* verboten! */
2838 /* Ask for confirmation to move */
2839 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2841 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2845 /* Transfer messages */
2846 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2851 if (G_IS_OBJECT (src_folder))
2852 g_object_unref (src_folder);
2853 if (G_IS_OBJECT(folder))
2854 g_object_unref (G_OBJECT (folder));
2855 if (G_IS_OBJECT(headers))
2856 g_object_unref (headers);
2860 TnyFolderStore *src_folder;
2861 TnyFolderStore *dst_folder;
2862 ModestFolderView *folder_view;
2867 dnd_folder_info_destroyer (DndFolderInfo *info)
2869 if (info->src_folder)
2870 g_object_unref (info->src_folder);
2871 if (info->dst_folder)
2872 g_object_unref (info->dst_folder);
2873 g_slice_free (DndFolderInfo, info);
2877 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2878 GtkWindow *parent_window,
2879 TnyAccount *account)
2882 modest_ui_actions_on_account_connection_error (parent_window, account);
2884 /* Free the helper & info */
2885 dnd_helper_destroyer (info->helper);
2886 dnd_folder_info_destroyer (info);
2890 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2892 GtkWindow *parent_window,
2893 TnyAccount *account,
2896 DndFolderInfo *info = NULL;
2897 ModestMailOperation *mail_op;
2899 info = (DndFolderInfo *) user_data;
2901 if (err || canceled) {
2902 dnd_on_connection_failed_destroyer (info, parent_window, account);
2906 /* Do the mail operation */
2907 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2908 modest_ui_actions_move_folder_error_handler,
2909 info->src_folder, NULL);
2911 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2914 /* Transfer the folder */
2915 modest_mail_operation_xfer_folder (mail_op,
2916 TNY_FOLDER (info->src_folder),
2918 info->helper->delete_source,
2920 info->helper->folder_view);
2923 g_object_unref (G_OBJECT (mail_op));
2924 dnd_helper_destroyer (info->helper);
2925 dnd_folder_info_destroyer (info);
2930 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2932 GtkWindow *parent_window,
2933 TnyAccount *account,
2936 DndFolderInfo *info = NULL;
2938 info = (DndFolderInfo *) user_data;
2940 if (err || canceled) {
2941 dnd_on_connection_failed_destroyer (info, parent_window, account);
2945 /* Connect to source folder and perform the copy/move */
2946 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2948 drag_and_drop_from_folder_view_src_folder_performer,
2953 * This function is used by drag_data_received_cb to manage drag and
2954 * drop of a folder, i.e, and drag from the folder view to the same
2958 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2959 GtkTreeModel *dest_model,
2960 GtkTreePath *dest_row,
2961 GtkSelectionData *selection_data,
2964 GtkTreeIter dest_iter, iter;
2965 TnyFolderStore *dest_folder = NULL;
2966 TnyFolderStore *folder = NULL;
2967 gboolean forbidden = FALSE;
2969 DndFolderInfo *info = NULL;
2971 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2973 g_warning ("%s: BUG: no main window", __FUNCTION__);
2974 dnd_helper_destroyer (helper);
2979 /* check the folder rules for the destination */
2980 folder = tree_path_to_folder (dest_model, dest_row);
2981 if (TNY_IS_FOLDER(folder)) {
2982 ModestTnyFolderRules rules =
2983 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2984 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2985 } else if (TNY_IS_FOLDER_STORE(folder)) {
2986 /* enable local root as destination for folders */
2987 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2988 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2991 g_object_unref (folder);
2994 /* check the folder rules for the source */
2995 folder = tree_path_to_folder (source_model, helper->source_row);
2996 if (TNY_IS_FOLDER(folder)) {
2997 ModestTnyFolderRules rules =
2998 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2999 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3002 g_object_unref (folder);
3006 /* Check if the drag is possible */
3007 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3009 modest_platform_run_information_dialog ((GtkWindow *) win,
3010 _("mail_in_ui_folder_move_target_error"),
3012 /* Restore the previous selection */
3013 folder = tree_path_to_folder (source_model, helper->source_row);
3015 if (TNY_IS_FOLDER (folder))
3016 modest_folder_view_select_folder (helper->folder_view,
3017 TNY_FOLDER (folder), FALSE);
3018 g_object_unref (folder);
3020 dnd_helper_destroyer (helper);
3025 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3026 gtk_tree_model_get (dest_model, &dest_iter,
3029 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3030 gtk_tree_model_get (source_model, &iter,
3034 /* Create the info for the performer */
3035 info = g_slice_new0 (DndFolderInfo);
3036 info->src_folder = g_object_ref (folder);
3037 info->dst_folder = g_object_ref (dest_folder);
3038 info->helper = helper;
3040 /* Connect to the destination folder and perform the copy/move */
3041 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3043 drag_and_drop_from_folder_view_dst_folder_performer,
3047 g_object_unref (dest_folder);
3048 g_object_unref (folder);
3052 * This function receives the data set by the "drag-data-get" signal
3053 * handler. This information comes within the #GtkSelectionData. This
3054 * function will manage both the drags of folders of the treeview and
3055 * drags of headers of the header view widget.
3058 on_drag_data_received (GtkWidget *widget,
3059 GdkDragContext *context,
3062 GtkSelectionData *selection_data,
3067 GtkWidget *source_widget;
3068 GtkTreeModel *dest_model, *source_model;
3069 GtkTreePath *source_row, *dest_row;
3070 GtkTreeViewDropPosition pos;
3071 gboolean delete_source = FALSE;
3072 gboolean success = FALSE;
3074 /* Do not allow further process */
3075 g_signal_stop_emission_by_name (widget, "drag-data-received");
3076 source_widget = gtk_drag_get_source_widget (context);
3078 /* Get the action */
3079 if (context->action == GDK_ACTION_MOVE) {
3080 delete_source = TRUE;
3082 /* Notify that there is no folder selected. We need to
3083 do this in order to update the headers view (and
3084 its monitors, because when moving, the old folder
3085 won't longer exist. We can not wait for the end of
3086 the operation, because the operation won't start if
3087 the folder is in use */
3088 if (source_widget == widget) {
3089 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3090 gtk_tree_selection_unselect_all (sel);
3094 /* Check if the get_data failed */
3095 if (selection_data == NULL || selection_data->length < 0)
3098 /* Select the destination model */
3099 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3101 /* Get the path to the destination row. Can not call
3102 gtk_tree_view_get_drag_dest_row() because the source row
3103 is not selected anymore */
3104 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3107 /* Only allow drops IN other rows */
3109 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3110 pos == GTK_TREE_VIEW_DROP_AFTER)
3114 /* Drags from the header view */
3115 if (source_widget != widget) {
3116 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3118 drag_and_drop_from_header_view (source_model,
3123 DndHelper *helper = NULL;
3125 /* Get the source model and row */
3126 gtk_tree_get_row_drag_data (selection_data,
3130 /* Create the helper */
3131 helper = g_slice_new0 (DndHelper);
3132 helper->delete_source = delete_source;
3133 helper->source_row = gtk_tree_path_copy (source_row);
3134 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3136 drag_and_drop_from_folder_view (source_model,
3142 gtk_tree_path_free (source_row);
3146 gtk_tree_path_free (dest_row);
3149 /* Finish the drag and drop */
3150 gtk_drag_finish (context, success, FALSE, time);
3154 * We define a "drag-drop" signal handler because we do not want to
3155 * use the default one, because the default one always calls
3156 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3157 * signal handler, because there we have all the information available
3158 * to know if the dnd was a success or not.
3161 drag_drop_cb (GtkWidget *widget,
3162 GdkDragContext *context,
3170 if (!context->targets)
3173 /* Check if we're dragging a folder row */
3174 target = gtk_drag_dest_find_target (widget, context, NULL);
3176 /* Request the data from the source. */
3177 gtk_drag_get_data(widget, context, target, time);
3183 * This function expands a node of a tree view if it's not expanded
3184 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3185 * does that, so that's why they're here.
3188 expand_row_timeout (gpointer data)
3190 GtkTreeView *tree_view = data;
3191 GtkTreePath *dest_path = NULL;
3192 GtkTreeViewDropPosition pos;
3193 gboolean result = FALSE;
3195 gdk_threads_enter ();
3197 gtk_tree_view_get_drag_dest_row (tree_view,
3202 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3203 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3204 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3205 gtk_tree_path_free (dest_path);
3209 gtk_tree_path_free (dest_path);
3214 gdk_threads_leave ();
3220 * This function is called whenever the pointer is moved over a widget
3221 * while dragging some data. It installs a timeout that will expand a
3222 * node of the treeview if not expanded yet. This function also calls
3223 * gdk_drag_status in order to set the suggested action that will be
3224 * used by the "drag-data-received" signal handler to know if we
3225 * should do a move or just a copy of the data.
3228 on_drag_motion (GtkWidget *widget,
3229 GdkDragContext *context,
3235 GtkTreeViewDropPosition pos;
3236 GtkTreePath *dest_row;
3237 GtkTreeModel *dest_model;
3238 ModestFolderViewPrivate *priv;
3239 GdkDragAction suggested_action;
3240 gboolean valid_location = FALSE;
3241 TnyFolderStore *folder = NULL;
3243 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3245 if (priv->timer_expander != 0) {
3246 g_source_remove (priv->timer_expander);
3247 priv->timer_expander = 0;
3250 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3255 /* Do not allow drops between folders */
3257 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3258 pos == GTK_TREE_VIEW_DROP_AFTER) {
3259 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3260 gdk_drag_status(context, 0, time);
3261 valid_location = FALSE;
3264 valid_location = TRUE;
3267 /* Check that the destination folder is writable */
3268 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3269 folder = tree_path_to_folder (dest_model, dest_row);
3270 if (folder && TNY_IS_FOLDER (folder)) {
3271 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3273 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3274 valid_location = FALSE;
3279 /* Expand the selected row after 1/2 second */
3280 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3281 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3283 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3285 /* Select the desired action. By default we pick MOVE */
3286 suggested_action = GDK_ACTION_MOVE;
3288 if (context->actions == GDK_ACTION_COPY)
3289 gdk_drag_status(context, GDK_ACTION_COPY, time);
3290 else if (context->actions == GDK_ACTION_MOVE)
3291 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3292 else if (context->actions & suggested_action)
3293 gdk_drag_status(context, suggested_action, time);
3295 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3299 g_object_unref (folder);
3301 gtk_tree_path_free (dest_row);
3303 g_signal_stop_emission_by_name (widget, "drag-motion");
3305 return valid_location;
3309 * This function sets the treeview as a source and a target for dnd
3310 * events. It also connects all the requirede signals.
3313 setup_drag_and_drop (GtkTreeView *self)
3315 /* Set up the folder view as a dnd destination. Set only the
3316 highlight flag, otherwise gtk will have a different
3318 #ifdef MODEST_TOOLKIT_HILDON2
3321 gtk_drag_dest_set (GTK_WIDGET (self),
3322 GTK_DEST_DEFAULT_HIGHLIGHT,
3323 folder_view_drag_types,
3324 G_N_ELEMENTS (folder_view_drag_types),
3325 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3327 g_signal_connect (G_OBJECT (self),
3328 "drag_data_received",
3329 G_CALLBACK (on_drag_data_received),
3333 /* Set up the treeview as a dnd source */
3334 gtk_drag_source_set (GTK_WIDGET (self),
3336 folder_view_drag_types,
3337 G_N_ELEMENTS (folder_view_drag_types),
3338 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3340 g_signal_connect (G_OBJECT (self),
3342 G_CALLBACK (on_drag_motion),
3345 g_signal_connect (G_OBJECT (self),
3347 G_CALLBACK (on_drag_data_get),
3350 g_signal_connect (G_OBJECT (self),
3352 G_CALLBACK (drag_drop_cb),
3357 * This function manages the navigation through the folders using the
3358 * keyboard or the hardware keys in the device
3361 on_key_pressed (GtkWidget *self,
3365 GtkTreeSelection *selection;
3367 GtkTreeModel *model;
3368 gboolean retval = FALSE;
3370 /* Up and Down are automatically managed by the treeview */
3371 if (event->keyval == GDK_Return) {
3372 /* Expand/Collapse the selected row */
3373 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3374 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3377 path = gtk_tree_model_get_path (model, &iter);
3379 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3380 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3382 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3383 gtk_tree_path_free (path);
3385 /* No further processing */
3393 * We listen to the changes in the local folder account name key,
3394 * because we want to show the right name in the view. The local
3395 * folder account name corresponds to the device name in the Maemo
3396 * version. We do this because we do not want to query gconf on each
3397 * tree view refresh. It's better to cache it and change whenever
3401 on_configuration_key_changed (ModestConf* conf,
3403 ModestConfEvent event,
3404 ModestConfNotificationId id,
3405 ModestFolderView *self)
3407 ModestFolderViewPrivate *priv;
3410 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3411 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3413 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3414 g_free (priv->local_account_name);
3416 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3417 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3419 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3420 MODEST_CONF_DEVICE_NAME, NULL);
3422 /* Force a redraw */
3423 #if GTK_CHECK_VERSION(2, 8, 0)
3424 GtkTreeViewColumn * tree_column;
3426 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3428 gtk_tree_view_column_queue_resize (tree_column);
3430 gtk_widget_queue_draw (GTK_WIDGET (self));
3436 modest_folder_view_set_style (ModestFolderView *self,
3437 ModestFolderViewStyle style)
3439 ModestFolderViewPrivate *priv;
3441 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3442 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3443 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3445 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3448 priv->style = style;
3452 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3453 const gchar *account_id)
3455 ModestFolderViewPrivate *priv;
3456 GtkTreeModel *model;
3458 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3460 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3462 /* This will be used by the filter_row callback,
3463 * to decided which rows to show: */
3464 if (priv->visible_account_id) {
3465 g_free (priv->visible_account_id);
3466 priv->visible_account_id = NULL;
3469 priv->visible_account_id = g_strdup (account_id);
3472 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3473 if (GTK_IS_TREE_MODEL_FILTER (model))
3474 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3476 /* Save settings to gconf */
3477 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3478 MODEST_CONF_FOLDER_VIEW_KEY);
3480 /* Notify observers */
3481 g_signal_emit (G_OBJECT(self),
3482 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3487 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3489 ModestFolderViewPrivate *priv;
3491 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3493 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3495 return (const gchar *) priv->visible_account_id;
3499 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3503 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3505 gtk_tree_model_get (model, iter,
3509 gboolean result = FALSE;
3510 if (type == TNY_FOLDER_TYPE_INBOX) {
3514 *inbox_iter = *iter;
3518 if (gtk_tree_model_iter_children (model, &child, iter)) {
3519 if (find_inbox_iter (model, &child, inbox_iter))
3523 } while (gtk_tree_model_iter_next (model, iter));
3532 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3534 #ifndef MODEST_TOOLKIT_HILDON2
3535 GtkTreeModel *model;
3536 GtkTreeIter iter, inbox_iter;
3537 GtkTreeSelection *sel;
3538 GtkTreePath *path = NULL;
3540 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3542 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3546 expand_root_items (self);
3547 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3549 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3550 g_warning ("%s: model is empty", __FUNCTION__);
3554 if (find_inbox_iter (model, &iter, &inbox_iter))
3555 path = gtk_tree_model_get_path (model, &inbox_iter);
3557 path = gtk_tree_path_new_first ();
3559 /* Select the row and free */
3560 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3561 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3562 gtk_tree_path_free (path);
3565 gtk_widget_grab_focus (GTK_WIDGET(self));
3572 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3577 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3578 TnyFolder* a_folder;
3581 gtk_tree_model_get (model, iter,
3582 INSTANCE_COLUMN, &a_folder,
3588 if (folder == a_folder) {
3589 g_object_unref (a_folder);
3590 *folder_iter = *iter;
3593 g_object_unref (a_folder);
3595 if (gtk_tree_model_iter_children (model, &child, iter)) {
3596 if (find_folder_iter (model, &child, folder_iter, folder))
3600 } while (gtk_tree_model_iter_next (model, iter));
3605 #ifndef MODEST_TOOLKIT_HILDON2
3607 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3610 ModestFolderView *self)
3612 ModestFolderViewPrivate *priv = NULL;
3613 GtkTreeSelection *sel;
3614 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3615 GObject *instance = NULL;
3617 if (!MODEST_IS_FOLDER_VIEW(self))
3620 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3622 priv->reexpand = TRUE;
3624 gtk_tree_model_get (tree_model, iter,
3626 INSTANCE_COLUMN, &instance,
3632 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3633 priv->folder_to_select = g_object_ref (instance);
3635 g_object_unref (instance);
3637 if (priv->folder_to_select) {
3639 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3642 path = gtk_tree_model_get_path (tree_model, iter);
3643 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3645 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3647 gtk_tree_selection_select_iter (sel, iter);
3648 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3650 gtk_tree_path_free (path);
3654 modest_folder_view_disable_next_folder_selection (self);
3656 /* Refilter the model */
3657 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3663 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3665 ModestFolderViewPrivate *priv;
3667 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3669 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3671 if (priv->folder_to_select)
3672 g_object_unref(priv->folder_to_select);
3674 priv->folder_to_select = NULL;
3678 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3679 gboolean after_change)
3681 GtkTreeModel *model;
3682 GtkTreeIter iter, folder_iter;
3683 GtkTreeSelection *sel;
3684 ModestFolderViewPrivate *priv = NULL;
3686 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3687 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3689 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3692 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3693 gtk_tree_selection_unselect_all (sel);
3695 if (priv->folder_to_select)
3696 g_object_unref(priv->folder_to_select);
3697 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3701 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3706 /* Refilter the model, before selecting the folder */
3707 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3709 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3710 g_warning ("%s: model is empty", __FUNCTION__);
3714 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3717 path = gtk_tree_model_get_path (model, &folder_iter);
3718 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3720 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3721 gtk_tree_selection_select_iter (sel, &folder_iter);
3722 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3724 gtk_tree_path_free (path);
3732 modest_folder_view_copy_selection (ModestFolderView *self)
3734 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3736 /* Copy selection */
3737 _clipboard_set_selected_data (self, FALSE);
3741 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3743 ModestFolderViewPrivate *priv = NULL;
3744 GtkTreeModel *model = NULL;
3745 const gchar **hidding = NULL;
3746 guint i, n_selected;
3748 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3749 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3751 /* Copy selection */
3752 if (!_clipboard_set_selected_data (folder_view, TRUE))
3755 /* Get hidding ids */
3756 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3758 /* Clear hidding array created by previous cut operation */
3759 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3761 /* Copy hidding array */
3762 priv->n_selected = n_selected;
3763 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3764 for (i=0; i < n_selected; i++)
3765 priv->hidding_ids[i] = g_strdup(hidding[i]);
3767 /* Hide cut folders */
3768 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3769 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3773 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3774 ModestFolderView *folder_view_dst)
3776 GtkTreeModel *filter_model = NULL;
3777 GtkTreeModel *model = NULL;
3778 GtkTreeModel *new_filter_model = NULL;
3779 GtkTreeModel *old_tny_model = NULL;
3780 GtkTreeModel *new_tny_model = NULL;
3781 ModestFolderViewPrivate *dst_priv;
3783 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3784 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3786 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3787 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3788 new_tny_model = NULL;
3791 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3792 g_signal_handler_disconnect (G_OBJECT (old_tny_model), dst_priv->activity_changed_handler);
3793 dst_priv->activity_changed_handler = 0;
3795 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3796 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3798 /* Build new filter model */
3799 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3800 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3807 /* Set copied model */
3808 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3809 #ifndef MODEST_TOOLKIT_HILDON2
3810 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3811 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3813 #ifdef MODEST_TOOLKIT_HILDON2
3815 dst_priv->activity_changed_handler = g_signal_connect (G_OBJECT (new_tny_model), "activity-changed",
3816 G_CALLBACK (on_activity_changed), folder_view_dst);
3818 dst_priv->activity = FALSE;
3821 g_object_unref (new_filter_model);
3825 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3828 GtkTreeModel *model = NULL;
3829 ModestFolderViewPrivate* priv;
3831 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3833 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3834 priv->show_non_move = show;
3835 /* modest_folder_view_update_model(folder_view, */
3836 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3838 /* Hide special folders */
3839 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3840 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3841 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3846 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3849 ModestFolderViewPrivate* priv;
3851 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3853 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3854 priv->show_message_count = show;
3856 g_object_set (G_OBJECT (priv->messages_renderer),
3857 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3861 /* Returns FALSE if it did not selected anything */
3863 _clipboard_set_selected_data (ModestFolderView *folder_view,
3866 ModestFolderViewPrivate *priv = NULL;
3867 TnyFolderStore *folder = NULL;
3868 gboolean retval = FALSE;
3870 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3871 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3873 /* Set selected data on clipboard */
3874 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3875 folder = modest_folder_view_get_selected (folder_view);
3877 /* Do not allow to select an account */
3878 if (TNY_IS_FOLDER (folder)) {
3879 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3884 g_object_unref (folder);
3890 _clear_hidding_filter (ModestFolderView *folder_view)
3892 ModestFolderViewPrivate *priv;
3895 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3896 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3898 if (priv->hidding_ids != NULL) {
3899 for (i=0; i < priv->n_selected; i++)
3900 g_free (priv->hidding_ids[i]);
3901 g_free(priv->hidding_ids);
3907 on_display_name_changed (ModestAccountMgr *mgr,
3908 const gchar *account,
3911 ModestFolderView *self;
3913 self = MODEST_FOLDER_VIEW (user_data);
3915 /* Force a redraw */
3916 #if GTK_CHECK_VERSION(2, 8, 0)
3917 GtkTreeViewColumn * tree_column;
3919 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3921 gtk_tree_view_column_queue_resize (tree_column);
3923 gtk_widget_queue_draw (GTK_WIDGET (self));
3928 modest_folder_view_set_cell_style (ModestFolderView *self,
3929 ModestFolderViewCellStyle cell_style)
3931 ModestFolderViewPrivate *priv = NULL;
3933 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3934 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3936 priv->cell_style = cell_style;
3938 g_object_set (G_OBJECT (priv->messages_renderer),
3939 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3942 gtk_widget_queue_draw (GTK_WIDGET (self));
3946 update_style (ModestFolderView *self)
3948 ModestFolderViewPrivate *priv;
3949 GdkColor style_color;
3950 PangoAttrList *attr_list;
3952 PangoAttribute *attr;
3954 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3955 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3959 attr_list = pango_attr_list_new ();
3960 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3961 gdk_color_parse ("grey", &style_color);
3963 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3964 pango_attr_list_insert (attr_list, attr);
3967 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3969 "SmallSystemFont", NULL,
3972 attr = pango_attr_font_desc_new (pango_font_description_copy
3973 (style->font_desc));
3974 pango_attr_list_insert (attr_list, attr);
3976 g_object_set (G_OBJECT (priv->messages_renderer),
3977 "foreground-gdk", &style_color,
3978 "foreground-set", TRUE,
3979 "attributes", attr_list,
3981 pango_attr_list_unref (attr_list);
3986 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3988 if (strcmp ("style", spec->name) == 0) {
3989 update_style (MODEST_FOLDER_VIEW (obj));
3990 gtk_widget_queue_draw (GTK_WIDGET (obj));
3995 modest_folder_view_set_filter (ModestFolderView *self,
3996 ModestFolderViewFilter filter)
3998 ModestFolderViewPrivate *priv;
3999 GtkTreeModel *filter_model;
4001 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4002 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4004 priv->filter |= filter;
4006 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4007 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4008 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4013 modest_folder_view_unset_filter (ModestFolderView *self,
4014 ModestFolderViewFilter filter)
4016 ModestFolderViewPrivate *priv;
4017 GtkTreeModel *filter_model;
4019 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4020 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4022 priv->filter &= ~filter;
4024 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4025 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4026 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4031 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4032 ModestTnyFolderRules rules)
4034 GtkTreeModel *filter_model;
4036 gboolean fulfil = FALSE;
4038 if (!get_inner_models (self, &filter_model, NULL, NULL))
4041 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4045 TnyFolderStore *folder;
4047 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4049 if (TNY_IS_FOLDER (folder)) {
4050 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4051 /* Folder rules are negative: non_writable, non_deletable... */
4052 if (!(folder_rules & rules))
4055 g_object_unref (folder);
4058 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4064 modest_folder_view_set_list_to_move (ModestFolderView *self,
4067 ModestFolderViewPrivate *priv;
4069 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4070 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4072 if (priv->list_to_move)
4073 g_object_unref (priv->list_to_move);
4076 g_object_ref (list);
4078 priv->list_to_move = list;
4082 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4084 ModestFolderViewPrivate *priv;
4086 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4087 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4090 g_free (priv->mailbox);
4092 priv->mailbox = g_strdup (mailbox);
4094 /* Notify observers */
4095 g_signal_emit (G_OBJECT(self),
4096 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4097 priv->visible_account_id);
4101 modest_folder_view_get_mailbox (ModestFolderView *self)
4103 ModestFolderViewPrivate *priv;
4105 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4106 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4108 return (const gchar *) priv->mailbox;
4112 modest_folder_view_get_activity (ModestFolderView *self)
4114 ModestFolderViewPrivate *priv;
4116 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4117 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4119 return priv->activity;
4122 #ifdef MODEST_TOOLKIT_HILDON2
4124 on_activity_changed (TnyGtkFolderListStore *store,
4126 ModestFolderView *folder_view)
4128 ModestFolderViewPrivate *priv;
4130 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4131 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4132 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4134 priv->activity = activity;
4136 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,