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;
240 guint timer_expander;
242 gchar *local_account_name;
243 gchar *visible_account_id;
245 ModestFolderViewStyle style;
246 ModestFolderViewCellStyle cell_style;
247 gboolean show_message_count;
249 gboolean reselect; /* we use this to force a reselection of the INBOX */
250 gboolean show_non_move;
251 TnyList *list_to_move;
252 gboolean reexpand; /* next time we expose, we'll expand all root folders */
254 GtkCellRenderer *messages_renderer;
256 gulong outbox_deleted_handler;
258 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) {
655 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
656 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
659 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
660 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
662 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
665 /* Sometimes an special folder is reported by the server as
666 NORMAL, like some versions of Dovecot */
667 if (type == TNY_FOLDER_TYPE_NORMAL ||
668 type == TNY_FOLDER_TYPE_UNKNOWN) {
669 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
673 /* note: we cannot reliably get the counts from the
674 * tree model, we need to use explicit calls on
675 * tny_folder for some reason. Select the number to
676 * show: the unread or unsent messages. in case of
677 * outbox/drafts, show all */
678 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
679 (type == TNY_FOLDER_TYPE_OUTBOX) ||
680 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
681 number = tny_folder_get_all_count (TNY_FOLDER(instance));
684 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
688 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
689 item_name = g_strdup (fname);
696 /* Use bold font style if there are unread or unset messages */
698 if (priv->show_message_count) {
699 item_name = g_strdup_printf ("%s (%d)", fname, number);
701 item_name = g_strdup (fname);
705 item_name = g_strdup (fname);
710 } else if (TNY_IS_ACCOUNT (instance)) {
711 /* If it's a server account */
712 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
713 item_name = g_strdup (priv->local_account_name);
715 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
716 /* fname is only correct when the items are first
717 * added to the model, not when the account is
718 * changed later, so get the name from the account
720 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
723 item_name = g_strdup (fname);
729 item_name = g_strdup ("unknown");
731 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
732 gboolean multiaccount;
734 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
735 /* Convert item_name to markup */
736 format_compact_style (&item_name, instance, priv->mailbox,
738 multiaccount, &use_markup);
741 if (item_name && item_weight) {
742 /* Set the name in the treeview cell: */
744 g_object_set (rendobj, "markup", item_name, "weight-set", FALSE, NULL);
746 g_object_set (rendobj, "text", item_name, "weight-set", TRUE, "weight", item_weight, NULL);
748 /* Notify display name observers */
749 /* TODO: What listens for this signal, and how can it use only the new name? */
750 if (((GObject *) priv->cur_folder_store) == instance) {
751 g_signal_emit (G_OBJECT(self),
752 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
759 /* If it is a Memory card account, make sure that we have the correct name.
760 * This function will be trigerred again when the name has been retrieved: */
761 if (TNY_IS_STORE_ACCOUNT (instance) &&
762 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
764 /* Get the account name asynchronously: */
765 GetMmcAccountNameData *callback_data =
766 g_slice_new0(GetMmcAccountNameData);
767 callback_data->self = self;
769 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
771 callback_data->previous_name = g_strdup (name);
773 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
774 on_get_mmc_account_name, callback_data);
778 g_object_unref (G_OBJECT (instance));
784 messages_cell_data (GtkTreeViewColumn *column,
785 GtkCellRenderer *renderer,
786 GtkTreeModel *tree_model,
790 ModestFolderView *self;
791 ModestFolderViewPrivate *priv;
792 GObject *rendobj = (GObject *) renderer;
793 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
794 GObject *instance = NULL;
795 gchar *item_name = NULL;
797 gtk_tree_model_get (tree_model, iter,
799 INSTANCE_COLUMN, &instance,
804 self = MODEST_FOLDER_VIEW (data);
805 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
808 if (type != TNY_FOLDER_TYPE_ROOT) {
813 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
814 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
817 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
819 /* Sometimes an special folder is reported by the server as
820 NORMAL, like some versions of Dovecot */
821 if (type == TNY_FOLDER_TYPE_NORMAL ||
822 type == TNY_FOLDER_TYPE_UNKNOWN) {
823 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
827 /* note: we cannot reliably get the counts from the tree model, we need
828 * to use explicit calls on tny_folder for some reason.
830 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
831 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
832 (type == TNY_FOLDER_TYPE_OUTBOX) ||
833 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
834 number = tny_folder_get_all_count (TNY_FOLDER(instance));
837 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
841 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
843 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
845 } else if (number == 1) {
846 item_name = g_strdup_printf (drafts?_("mcen_ti_message"):_("mcen_ti_new_message"),
854 item_name = g_strdup ("");
857 /* Set the name in the treeview cell: */
858 g_object_set (rendobj,"text", item_name, NULL);
866 g_object_unref (G_OBJECT (instance));
872 GdkPixbuf *pixbuf_open;
873 GdkPixbuf *pixbuf_close;
877 static inline GdkPixbuf *
878 get_composite_pixbuf (const gchar *icon_name,
880 GdkPixbuf *base_pixbuf)
882 GdkPixbuf *emblem, *retval = NULL;
884 emblem = modest_platform_get_icon (icon_name, size);
886 retval = gdk_pixbuf_copy (base_pixbuf);
887 gdk_pixbuf_composite (emblem, retval, 0, 0,
888 MIN (gdk_pixbuf_get_width (emblem),
889 gdk_pixbuf_get_width (retval)),
890 MIN (gdk_pixbuf_get_height (emblem),
891 gdk_pixbuf_get_height (retval)),
892 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
893 g_object_unref (emblem);
898 static inline ThreePixbufs *
899 get_composite_icons (const gchar *icon_code,
901 GdkPixbuf **pixbuf_open,
902 GdkPixbuf **pixbuf_close)
904 ThreePixbufs *retval;
907 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
910 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
915 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
919 retval = g_slice_new0 (ThreePixbufs);
921 retval->pixbuf = g_object_ref (*pixbuf);
923 retval->pixbuf_open = g_object_ref (*pixbuf_open);
925 retval->pixbuf_close = g_object_ref (*pixbuf_close);
930 static inline ThreePixbufs *
931 get_account_protocol_pixbufs (ModestFolderView *folder_view,
932 ModestProtocolType protocol_type,
935 ModestProtocol *protocol;
936 const GdkPixbuf *pixbuf = NULL;
937 ModestFolderViewPrivate *priv;
939 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
941 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
944 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
945 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
946 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
947 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
948 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
949 object, FOLDER_ICON_SIZE);
953 ThreePixbufs *retval;
954 retval = g_slice_new0 (ThreePixbufs);
955 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
956 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
957 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
964 static inline ThreePixbufs*
965 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
967 TnyAccount *account = NULL;
968 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
969 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
970 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
971 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
972 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
974 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
975 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
976 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
977 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
978 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
980 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
981 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
982 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
983 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
984 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
986 ThreePixbufs *retval = NULL;
988 if (TNY_IS_ACCOUNT (instance)) {
989 account = g_object_ref (instance);
990 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
991 account = tny_folder_get_account (TNY_FOLDER (instance));
995 ModestProtocolType account_store_protocol;
997 account_store_protocol = modest_tny_account_get_protocol_type (account);
998 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
999 g_object_unref (account);
1005 /* Sometimes an special folder is reported by the server as
1006 NORMAL, like some versions of Dovecot */
1007 if (type == TNY_FOLDER_TYPE_NORMAL ||
1008 type == TNY_FOLDER_TYPE_UNKNOWN) {
1009 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1012 /* It's not enough with check the folder type. We need to
1013 ensure that we're not giving a special folder icon to a
1014 normal folder with the same name than a special folder */
1015 if (TNY_IS_FOLDER (instance) &&
1016 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1017 type = TNY_FOLDER_TYPE_NORMAL;
1019 /* Remote folders should not be treated as special folders */
1020 if (TNY_IS_FOLDER_STORE (instance) &&
1021 !TNY_IS_ACCOUNT (instance) &&
1022 type != TNY_FOLDER_TYPE_INBOX &&
1023 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1024 #ifdef MODEST_TOOLKIT_HILDON2
1025 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1028 &anorm_pixbuf_close);
1030 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1032 &normal_pixbuf_open,
1033 &normal_pixbuf_close);
1039 case TNY_FOLDER_TYPE_INVALID:
1040 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1043 case TNY_FOLDER_TYPE_ROOT:
1044 if (TNY_IS_ACCOUNT (instance)) {
1046 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1047 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1050 &avirt_pixbuf_close);
1052 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1054 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1055 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1058 &ammc_pixbuf_close);
1060 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1063 &anorm_pixbuf_close);
1068 case TNY_FOLDER_TYPE_INBOX:
1069 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1072 &inbox_pixbuf_close);
1074 case TNY_FOLDER_TYPE_OUTBOX:
1075 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1077 &outbox_pixbuf_open,
1078 &outbox_pixbuf_close);
1080 case TNY_FOLDER_TYPE_JUNK:
1081 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1084 &junk_pixbuf_close);
1086 case TNY_FOLDER_TYPE_SENT:
1087 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1090 &sent_pixbuf_close);
1092 case TNY_FOLDER_TYPE_TRASH:
1093 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1096 &trash_pixbuf_close);
1098 case TNY_FOLDER_TYPE_DRAFTS:
1099 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1102 &draft_pixbuf_close);
1104 case TNY_FOLDER_TYPE_ARCHIVE:
1105 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1110 case TNY_FOLDER_TYPE_NORMAL:
1112 /* Memory card folders could have an special icon */
1113 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1114 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1119 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1121 &normal_pixbuf_open,
1122 &normal_pixbuf_close);
1131 free_pixbufs (ThreePixbufs *pixbufs)
1133 if (pixbufs->pixbuf)
1134 g_object_unref (pixbufs->pixbuf);
1135 if (pixbufs->pixbuf_open)
1136 g_object_unref (pixbufs->pixbuf_open);
1137 if (pixbufs->pixbuf_close)
1138 g_object_unref (pixbufs->pixbuf_close);
1139 g_slice_free (ThreePixbufs, pixbufs);
1143 icon_cell_data (GtkTreeViewColumn *column,
1144 GtkCellRenderer *renderer,
1145 GtkTreeModel *tree_model,
1149 GObject *rendobj = NULL, *instance = NULL;
1150 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1151 gboolean has_children;
1152 ThreePixbufs *pixbufs;
1153 ModestFolderView *folder_view = (ModestFolderView *) data;
1155 rendobj = (GObject *) renderer;
1157 gtk_tree_model_get (tree_model, iter,
1159 INSTANCE_COLUMN, &instance,
1165 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1166 pixbufs = get_folder_icons (folder_view, type, instance);
1167 g_object_unref (instance);
1170 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1173 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1174 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1177 free_pixbufs (pixbufs);
1181 add_columns (GtkWidget *treeview)
1183 GtkTreeViewColumn *column;
1184 GtkCellRenderer *renderer;
1185 GtkTreeSelection *sel;
1186 ModestFolderViewPrivate *priv;
1188 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1191 column = gtk_tree_view_column_new ();
1193 /* Set icon and text render function */
1194 renderer = gtk_cell_renderer_pixbuf_new();
1195 #ifdef MODEST_TOOLKIT_HILDON2
1196 g_object_set (renderer,
1197 "xpad", MODEST_MARGIN_DEFAULT,
1198 "ypad", MODEST_MARGIN_DEFAULT,
1201 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1202 gtk_tree_view_column_set_cell_data_func(column, renderer,
1203 icon_cell_data, treeview, NULL);
1205 renderer = gtk_cell_renderer_text_new();
1206 g_object_set (renderer,
1207 #ifdef MODEST_TOOLKIT_HILDON2
1208 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1209 "ypad", MODEST_MARGIN_DEFAULT,
1210 "xpad", MODEST_MARGIN_DEFAULT,
1212 "ellipsize", PANGO_ELLIPSIZE_END,
1214 "ellipsize-set", TRUE, NULL);
1215 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1216 gtk_tree_view_column_set_cell_data_func(column, renderer,
1217 text_cell_data, treeview, NULL);
1219 priv->messages_renderer = gtk_cell_renderer_text_new ();
1220 g_object_set (priv->messages_renderer,
1221 #ifdef MODEST_TOOLKIT_HILDON2
1223 "ypad", MODEST_MARGIN_DEFAULT,
1224 "xpad", MODEST_MARGIN_DOUBLE,
1226 "scale", PANGO_SCALE_X_SMALL,
1229 "alignment", PANGO_ALIGN_RIGHT,
1233 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1234 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1235 messages_cell_data, treeview, NULL);
1237 /* Set selection mode */
1238 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1239 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1241 /* Set treeview appearance */
1242 gtk_tree_view_column_set_spacing (column, 2);
1243 gtk_tree_view_column_set_resizable (column, TRUE);
1244 gtk_tree_view_column_set_fixed_width (column, TRUE);
1245 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1246 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1249 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1253 modest_folder_view_init (ModestFolderView *obj)
1255 ModestFolderViewPrivate *priv;
1258 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1260 priv->timer_expander = 0;
1261 priv->account_store = NULL;
1263 priv->do_refresh = TRUE;
1264 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1265 priv->cur_folder_store = NULL;
1266 priv->visible_account_id = NULL;
1267 priv->mailbox = NULL;
1268 priv->folder_to_select = NULL;
1269 priv->outbox_deleted_handler = 0;
1270 priv->reexpand = TRUE;
1271 priv->activity_changed_handler = 0;
1273 /* Initialize the local account name */
1274 conf = modest_runtime_get_conf();
1275 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1277 /* Init email clipboard */
1278 priv->clipboard = modest_runtime_get_email_clipboard ();
1279 priv->hidding_ids = NULL;
1280 priv->n_selected = 0;
1281 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1282 priv->reselect = FALSE;
1283 priv->show_non_move = TRUE;
1284 priv->list_to_move = NULL;
1285 priv->show_message_count = TRUE;
1287 /* Build treeview */
1288 add_columns (GTK_WIDGET (obj));
1290 /* Setup drag and drop */
1291 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1293 /* Connect signals */
1294 g_signal_connect (G_OBJECT (obj),
1296 G_CALLBACK (on_key_pressed), NULL);
1298 priv->display_name_changed_signal =
1299 g_signal_connect (modest_runtime_get_account_mgr (),
1300 "display_name_changed",
1301 G_CALLBACK (on_display_name_changed),
1305 * Track changes in the local account name (in the device it
1306 * will be the device name)
1308 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1310 G_CALLBACK(on_configuration_key_changed),
1314 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1320 tny_account_store_view_init (gpointer g, gpointer iface_data)
1322 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1324 klass->set_account_store = modest_folder_view_set_account_store;
1328 modest_folder_view_finalize (GObject *obj)
1330 ModestFolderViewPrivate *priv;
1331 GtkTreeSelection *sel;
1332 TnyAccount *local_account;
1334 g_return_if_fail (obj);
1336 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1338 if (priv->timer_expander != 0) {
1339 g_source_remove (priv->timer_expander);
1340 priv->timer_expander = 0;
1343 local_account = (TnyAccount *)
1344 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1345 if (local_account) {
1346 if (g_signal_handler_is_connected (local_account,
1347 priv->outbox_deleted_handler))
1348 g_signal_handler_disconnect (local_account,
1349 priv->outbox_deleted_handler);
1350 g_object_unref (local_account);
1353 if (priv->account_store) {
1354 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1355 priv->account_inserted_signal);
1356 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1357 priv->account_removed_signal);
1358 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1359 priv->account_changed_signal);
1360 g_object_unref (G_OBJECT(priv->account_store));
1361 priv->account_store = NULL;
1364 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1365 priv->display_name_changed_signal)) {
1366 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1367 priv->display_name_changed_signal);
1368 priv->display_name_changed_signal = 0;
1372 g_object_unref (G_OBJECT (priv->query));
1376 if (priv->folder_to_select) {
1377 g_object_unref (G_OBJECT(priv->folder_to_select));
1378 priv->folder_to_select = NULL;
1381 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1383 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1385 g_free (priv->local_account_name);
1386 g_free (priv->visible_account_id);
1387 g_free (priv->mailbox);
1389 if (priv->conf_key_signal) {
1390 g_signal_handler_disconnect (modest_runtime_get_conf (),
1391 priv->conf_key_signal);
1392 priv->conf_key_signal = 0;
1395 if (priv->cur_folder_store) {
1396 g_object_unref (priv->cur_folder_store);
1397 priv->cur_folder_store = NULL;
1400 if (priv->list_to_move) {
1401 g_object_unref (priv->list_to_move);
1402 priv->list_to_move = NULL;
1405 /* Clear hidding array created by cut operation */
1406 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1408 G_OBJECT_CLASS(parent_class)->finalize (obj);
1413 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1415 ModestFolderViewPrivate *priv;
1418 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1419 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1421 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1422 device = tny_account_store_get_device (account_store);
1424 if (G_UNLIKELY (priv->account_store)) {
1426 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1427 priv->account_inserted_signal))
1428 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1429 priv->account_inserted_signal);
1430 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1431 priv->account_removed_signal))
1432 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1433 priv->account_removed_signal);
1434 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1435 priv->account_changed_signal))
1436 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1437 priv->account_changed_signal);
1438 g_object_unref (G_OBJECT (priv->account_store));
1441 priv->account_store = g_object_ref (G_OBJECT (account_store));
1443 priv->account_removed_signal =
1444 g_signal_connect (G_OBJECT(account_store), "account_removed",
1445 G_CALLBACK (on_account_removed), self);
1447 priv->account_inserted_signal =
1448 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1449 G_CALLBACK (on_account_inserted), self);
1451 priv->account_changed_signal =
1452 g_signal_connect (G_OBJECT(account_store), "account_changed",
1453 G_CALLBACK (on_account_changed), self);
1455 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1456 priv->reselect = FALSE;
1457 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1459 g_object_unref (G_OBJECT (device));
1463 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1466 ModestFolderView *self;
1467 GtkTreeModel *model, *filter_model;
1470 self = MODEST_FOLDER_VIEW (user_data);
1472 if (!get_inner_models (self, &filter_model, NULL, &model))
1475 /* Remove outbox from model */
1476 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1477 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1478 g_object_unref (outbox);
1481 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1485 on_account_inserted (TnyAccountStore *account_store,
1486 TnyAccount *account,
1489 ModestFolderViewPrivate *priv;
1490 GtkTreeModel *model, *filter_model;
1492 /* Ignore transport account insertions, we're not showing them
1493 in the folder view */
1494 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1497 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1500 /* If we're adding a new account, and there is no previous
1501 one, we need to select the visible server account */
1502 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1503 !priv->visible_account_id)
1504 modest_widget_memory_restore (modest_runtime_get_conf(),
1505 G_OBJECT (user_data),
1506 MODEST_CONF_FOLDER_VIEW_KEY);
1510 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1511 &filter_model, NULL, &model))
1514 /* Insert the account in the model */
1515 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1517 /* When the model is a list store (plain representation) the
1518 outbox is not a child of any account so we have to manually
1519 delete it because removing the local folders account won't
1520 delete it (because tny_folder_get_account() is not defined
1521 for a merge folder */
1522 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1523 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1525 priv->outbox_deleted_handler =
1526 g_signal_connect (account,
1528 G_CALLBACK (on_outbox_deleted_cb),
1532 /* Refilter the model */
1533 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1538 same_account_selected (ModestFolderView *self,
1539 TnyAccount *account)
1541 ModestFolderViewPrivate *priv;
1542 gboolean same_account = FALSE;
1544 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1546 if (priv->cur_folder_store) {
1547 TnyAccount *selected_folder_account = NULL;
1549 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1550 selected_folder_account =
1551 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1553 selected_folder_account =
1554 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1557 if (selected_folder_account == account)
1558 same_account = TRUE;
1560 g_object_unref (selected_folder_account);
1562 return same_account;
1567 * Selects the first inbox or the local account in an idle
1570 on_idle_select_first_inbox_or_local (gpointer user_data)
1572 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1574 gdk_threads_enter ();
1575 modest_folder_view_select_first_inbox_or_local (self);
1576 gdk_threads_leave ();
1582 on_account_changed (TnyAccountStore *account_store,
1583 TnyAccount *tny_account,
1586 ModestFolderView *self;
1587 ModestFolderViewPrivate *priv;
1588 GtkTreeModel *model, *filter_model;
1589 GtkTreeSelection *sel;
1590 gboolean same_account;
1592 /* Ignore transport account insertions, we're not showing them
1593 in the folder view */
1594 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1597 self = MODEST_FOLDER_VIEW (user_data);
1598 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1600 /* Get the inner model */
1601 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1602 &filter_model, NULL, &model))
1605 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1607 /* Invalidate the cur_folder_store only if the selected folder
1608 belongs to the account that is being removed */
1609 same_account = same_account_selected (self, tny_account);
1611 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1612 gtk_tree_selection_unselect_all (sel);
1615 /* Remove the account from the model */
1616 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1618 /* Insert the account in the model */
1619 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1621 /* Refilter the model */
1622 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1624 /* Select the first INBOX if the currently selected folder
1625 belongs to the account that is being deleted */
1626 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1627 g_idle_add (on_idle_select_first_inbox_or_local, self);
1631 on_account_removed (TnyAccountStore *account_store,
1632 TnyAccount *account,
1635 ModestFolderView *self = NULL;
1636 ModestFolderViewPrivate *priv;
1637 GtkTreeModel *model, *filter_model;
1638 GtkTreeSelection *sel = NULL;
1639 gboolean same_account = FALSE;
1641 /* Ignore transport account removals, we're not showing them
1642 in the folder view */
1643 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1646 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1647 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1651 self = MODEST_FOLDER_VIEW (user_data);
1652 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1654 /* Invalidate the cur_folder_store only if the selected folder
1655 belongs to the account that is being removed */
1656 same_account = same_account_selected (self, account);
1658 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1659 gtk_tree_selection_unselect_all (sel);
1662 /* Invalidate row to select only if the folder to select
1663 belongs to the account that is being removed*/
1664 if (priv->folder_to_select) {
1665 TnyAccount *folder_to_select_account = NULL;
1667 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1668 if (folder_to_select_account == account) {
1669 modest_folder_view_disable_next_folder_selection (self);
1670 g_object_unref (priv->folder_to_select);
1671 priv->folder_to_select = NULL;
1673 g_object_unref (folder_to_select_account);
1676 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1677 &filter_model, NULL, &model))
1680 /* Disconnect the signal handler */
1681 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1682 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1683 if (g_signal_handler_is_connected (account,
1684 priv->outbox_deleted_handler))
1685 g_signal_handler_disconnect (account,
1686 priv->outbox_deleted_handler);
1689 /* Remove the account from the model */
1690 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1692 /* If the removed account is the currently viewed one then
1693 clear the configuration value. The new visible account will be the default account */
1694 if (priv->visible_account_id &&
1695 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1697 /* Clear the current visible account_id */
1698 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1699 modest_folder_view_set_mailbox (self, NULL);
1701 /* Call the restore method, this will set the new visible account */
1702 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1703 MODEST_CONF_FOLDER_VIEW_KEY);
1706 /* Refilter the model */
1707 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1709 /* Select the first INBOX if the currently selected folder
1710 belongs to the account that is being deleted */
1712 g_idle_add (on_idle_select_first_inbox_or_local, self);
1716 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1718 GtkTreeViewColumn *col;
1720 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1722 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1724 g_printerr ("modest: failed get column for title\n");
1728 gtk_tree_view_column_set_title (col, title);
1729 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1734 modest_folder_view_on_map (ModestFolderView *self,
1735 GdkEventExpose *event,
1738 ModestFolderViewPrivate *priv;
1740 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1742 /* This won't happen often */
1743 if (G_UNLIKELY (priv->reselect)) {
1744 /* Select the first inbox or the local account if not found */
1746 /* TODO: this could cause a lock at startup, so we
1747 comment it for the moment. We know that this will
1748 be a bug, because the INBOX is not selected, but we
1749 need to rewrite some parts of Modest to avoid the
1750 deathlock situation */
1751 /* TODO: check if this is still the case */
1752 priv->reselect = FALSE;
1753 modest_folder_view_select_first_inbox_or_local (self);
1754 /* Notify the display name observers */
1755 g_signal_emit (G_OBJECT(self),
1756 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1760 if (priv->reexpand) {
1761 expand_root_items (self);
1762 priv->reexpand = FALSE;
1769 modest_folder_view_new (TnyFolderStoreQuery *query)
1771 return modest_folder_view_new_full (query, TRUE);
1775 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1778 ModestFolderViewPrivate *priv;
1779 GtkTreeSelection *sel;
1781 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1782 #ifdef MODEST_TOOLKIT_HILDON2
1783 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1786 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1789 priv->query = g_object_ref (query);
1791 priv->do_refresh = do_refresh;
1793 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1794 priv->changed_signal = g_signal_connect (sel, "changed",
1795 G_CALLBACK (on_selection_changed), self);
1797 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1799 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1801 return GTK_WIDGET(self);
1804 /* this feels dirty; any other way to expand all the root items? */
1806 expand_root_items (ModestFolderView *self)
1809 GtkTreeModel *model;
1812 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1813 path = gtk_tree_path_new_first ();
1815 /* all folders should have child items, so.. */
1817 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1818 gtk_tree_path_next (path);
1819 } while (gtk_tree_model_get_iter (model, &iter, path));
1821 gtk_tree_path_free (path);
1825 is_parent_of (TnyFolder *a, TnyFolder *b)
1828 gboolean retval = FALSE;
1830 a_id = tny_folder_get_id (a);
1832 gchar *string_to_match;
1835 string_to_match = g_strconcat (a_id, "/", NULL);
1836 b_id = tny_folder_get_id (b);
1837 retval = g_str_has_prefix (b_id, string_to_match);
1838 g_free (string_to_match);
1844 typedef struct _ForeachFolderInfo {
1847 } ForeachFolderInfo;
1850 foreach_folder_with_id (GtkTreeModel *model,
1855 ForeachFolderInfo *info;
1858 info = (ForeachFolderInfo *) data;
1859 gtk_tree_model_get (model, iter,
1860 INSTANCE_COLUMN, &instance,
1863 if (TNY_IS_FOLDER (instance)) {
1866 id = tny_folder_get_id (TNY_FOLDER (instance));
1868 collate = g_utf8_collate_key (id, -1);
1869 info->found = !strcmp (info->needle, collate);
1875 g_object_unref (instance);
1883 has_folder_with_id (ModestFolderView *self, const gchar *id)
1885 GtkTreeModel *model;
1886 ForeachFolderInfo info = {NULL, FALSE};
1888 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1889 info.needle = g_utf8_collate_key (id, -1);
1891 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1892 g_free (info.needle);
1898 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1901 gboolean retval = FALSE;
1903 a_id = tny_folder_get_id (a);
1906 b_id = tny_folder_get_id (b);
1909 const gchar *last_bar;
1910 gchar *string_to_match;
1911 last_bar = g_strrstr (b_id, "/");
1916 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1917 retval = has_folder_with_id (self, string_to_match);
1918 g_free (string_to_match);
1926 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1928 ModestFolderViewPrivate *priv;
1929 TnyIterator *iterator;
1930 gboolean retval = TRUE;
1932 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1933 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1935 for (iterator = tny_list_create_iterator (priv->list_to_move);
1936 retval && !tny_iterator_is_done (iterator);
1937 tny_iterator_next (iterator)) {
1939 instance = tny_iterator_get_current (iterator);
1940 if (instance == (GObject *) folder) {
1942 } else if (TNY_IS_FOLDER (instance)) {
1943 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1945 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1948 g_object_unref (instance);
1950 g_object_unref (iterator);
1957 * We use this function to implement the
1958 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1959 * account in this case, and the local folders.
1962 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1964 ModestFolderViewPrivate *priv;
1965 gboolean retval = TRUE;
1966 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1967 GObject *instance = NULL;
1968 const gchar *id = NULL;
1970 gboolean found = FALSE;
1971 gboolean cleared = FALSE;
1972 ModestTnyFolderRules rules = 0;
1975 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1976 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1978 gtk_tree_model_get (model, iter,
1979 NAME_COLUMN, &fname,
1981 INSTANCE_COLUMN, &instance,
1984 /* Do not show if there is no instance, this could indeed
1985 happen when the model is being modified while it's being
1986 drawn. This could occur for example when moving folders
1993 if (TNY_IS_ACCOUNT (instance)) {
1994 TnyAccount *acc = TNY_ACCOUNT (instance);
1995 const gchar *account_id = tny_account_get_id (acc);
1997 /* If it isn't a special folder,
1998 * don't show it unless it is the visible account: */
1999 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2000 !modest_tny_account_is_virtual_local_folders (acc) &&
2001 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2003 /* Show only the visible account id */
2004 if (priv->visible_account_id) {
2005 if (strcmp (account_id, priv->visible_account_id))
2012 /* Never show these to the user. They are merged into one folder
2013 * in the local-folders account instead: */
2014 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2017 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2018 /* Only show special folders for current account if needed */
2019 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2020 TnyAccount *account;
2022 account = tny_folder_get_account (TNY_FOLDER (instance));
2024 if (TNY_IS_ACCOUNT (account)) {
2025 const gchar *account_id = tny_account_get_id (account);
2027 if (!modest_tny_account_is_virtual_local_folders (account) &&
2028 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2029 /* Show only the visible account id */
2030 if (priv->visible_account_id) {
2031 if (strcmp (account_id, priv->visible_account_id)) {
2033 } else if (priv->mailbox) {
2034 /* Filter mailboxes */
2035 if (!g_str_has_prefix (fname, priv->mailbox)) {
2037 } else if (!strcmp (fname, priv->mailbox)) {
2038 /* Hide mailbox parent */
2044 g_object_unref (account);
2051 /* Check hiding (if necessary) */
2052 cleared = modest_email_clipboard_cleared (priv->clipboard);
2053 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2054 id = tny_folder_get_id (TNY_FOLDER(instance));
2055 if (priv->hidding_ids != NULL)
2056 for (i=0; i < priv->n_selected && !found; i++)
2057 if (priv->hidding_ids[i] != NULL && id != NULL)
2058 found = (!strcmp (priv->hidding_ids[i], id));
2063 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2064 folder as no message can be move there according to UI specs */
2065 if (retval && !priv->show_non_move) {
2066 if (priv->list_to_move &&
2067 tny_list_get_length (priv->list_to_move) > 0 &&
2068 TNY_IS_FOLDER (instance)) {
2069 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2071 if (retval && TNY_IS_FOLDER (instance) &&
2072 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2074 case TNY_FOLDER_TYPE_OUTBOX:
2075 case TNY_FOLDER_TYPE_SENT:
2076 case TNY_FOLDER_TYPE_DRAFTS:
2079 case TNY_FOLDER_TYPE_UNKNOWN:
2080 case TNY_FOLDER_TYPE_NORMAL:
2081 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2082 if (type == TNY_FOLDER_TYPE_INVALID)
2083 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2085 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2086 type == TNY_FOLDER_TYPE_SENT
2087 || type == TNY_FOLDER_TYPE_DRAFTS)
2094 if (retval && TNY_IS_ACCOUNT (instance) &&
2095 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2096 ModestProtocolType protocol_type;
2098 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2099 retval = !modest_protocol_registry_protocol_type_has_tag
2100 (modest_runtime_get_protocol_registry (),
2102 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2106 /* apply special filters */
2107 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2108 if (TNY_IS_ACCOUNT (instance))
2112 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2113 if (TNY_IS_FOLDER (instance))
2117 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2118 if (TNY_IS_ACCOUNT (instance)) {
2119 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2121 } else if (TNY_IS_FOLDER (instance)) {
2122 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2127 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2128 if (TNY_IS_ACCOUNT (instance)) {
2129 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2131 } else if (TNY_IS_FOLDER (instance)) {
2132 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2137 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2138 /* A mailbox is a fake folder with an @ in the middle of the name */
2139 if (!TNY_IS_FOLDER (instance) ||
2140 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2143 const gchar *folder_name;
2144 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2145 if (!folder_name || strchr (folder_name, '@') == NULL)
2151 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2152 if (TNY_IS_FOLDER (instance)) {
2153 /* Check folder rules */
2154 ModestTnyFolderRules rules;
2156 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2157 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2158 } else if (TNY_IS_ACCOUNT (instance)) {
2159 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2167 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2168 if (TNY_IS_FOLDER (instance)) {
2169 TnyFolderType guess_type;
2171 if (TNY_FOLDER_TYPE_NORMAL) {
2172 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2178 case TNY_FOLDER_TYPE_OUTBOX:
2179 case TNY_FOLDER_TYPE_SENT:
2180 case TNY_FOLDER_TYPE_DRAFTS:
2181 case TNY_FOLDER_TYPE_ARCHIVE:
2182 case TNY_FOLDER_TYPE_INBOX:
2185 case TNY_FOLDER_TYPE_UNKNOWN:
2186 case TNY_FOLDER_TYPE_NORMAL:
2192 } else if (TNY_IS_ACCOUNT (instance)) {
2197 if (retval && TNY_IS_FOLDER (instance)) {
2198 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2201 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2202 if (TNY_IS_FOLDER (instance)) {
2203 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2204 } else if (TNY_IS_ACCOUNT (instance)) {
2209 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2210 if (TNY_IS_FOLDER (instance)) {
2211 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2212 } else if (TNY_IS_ACCOUNT (instance)) {
2217 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2218 if (TNY_IS_FOLDER (instance)) {
2219 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2220 } else if (TNY_IS_ACCOUNT (instance)) {
2226 g_object_unref (instance);
2234 modest_folder_view_update_model (ModestFolderView *self,
2235 TnyAccountStore *account_store)
2237 ModestFolderViewPrivate *priv;
2238 GtkTreeModel *model;
2239 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2241 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2242 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2245 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2247 /* Notify that there is no folder selected */
2248 g_signal_emit (G_OBJECT(self),
2249 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2251 if (priv->cur_folder_store) {
2252 g_object_unref (priv->cur_folder_store);
2253 priv->cur_folder_store = NULL;
2256 /* FIXME: the local accounts are not shown when the query
2257 selects only the subscribed folders */
2258 #ifdef MODEST_TOOLKIT_HILDON2
2259 TnyGtkFolderListStoreFlags flags;
2260 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2261 if (!priv->do_refresh)
2262 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2263 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2265 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2266 MODEST_FOLDER_PATH_SEPARATOR);
2268 model = tny_gtk_folder_store_tree_model_new (NULL);
2271 /* When the model is a list store (plain representation) the
2272 outbox is not a child of any account so we have to manually
2273 delete it because removing the local folders account won't
2274 delete it (because tny_folder_get_account() is not defined
2275 for a merge folder */
2276 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2277 TnyAccount *account;
2278 ModestTnyAccountStore *acc_store;
2280 acc_store = modest_runtime_get_account_store ();
2281 account = modest_tny_account_store_get_local_folders_account (acc_store);
2283 if (g_signal_handler_is_connected (account,
2284 priv->outbox_deleted_handler))
2285 g_signal_handler_disconnect (account,
2286 priv->outbox_deleted_handler);
2288 priv->outbox_deleted_handler =
2289 g_signal_connect (account,
2291 G_CALLBACK (on_outbox_deleted_cb),
2293 g_object_unref (account);
2296 /* Get the accounts: */
2297 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2299 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2301 sortable = gtk_tree_model_sort_new_with_model (model);
2302 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2304 GTK_SORT_ASCENDING);
2305 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2307 cmp_rows, NULL, NULL);
2309 /* Create filter model */
2310 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2311 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2316 if (priv->activity_changed_handler > 0) {
2317 GtkTreeModel *old_tny_model;
2319 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2320 g_signal_handler_disconnect (G_OBJECT (old_tny_model), priv->activity_changed_handler);
2322 priv->activity_changed_handler = 0;
2326 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2327 #ifndef MODEST_TOOLKIT_HILDON2
2328 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2329 (GCallback) on_row_inserted_maybe_select_folder, self);
2332 #ifdef MODEST_TOOLKIT_HILDON2
2333 priv->activity_changed_handler =
2334 g_signal_connect (G_OBJECT (model), "activity-changed", G_CALLBACK (on_activity_changed), self);
2337 g_object_unref (model);
2338 g_object_unref (filter_model);
2339 g_object_unref (sortable);
2341 /* Force a reselection of the INBOX next time the widget is shown */
2342 priv->reselect = TRUE;
2349 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2351 GtkTreeModel *model = NULL;
2352 TnyFolderStore *folder = NULL;
2354 ModestFolderView *tree_view = NULL;
2355 ModestFolderViewPrivate *priv = NULL;
2356 gboolean selected = FALSE;
2358 g_return_if_fail (sel);
2359 g_return_if_fail (user_data);
2361 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2363 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2365 tree_view = MODEST_FOLDER_VIEW (user_data);
2368 gtk_tree_model_get (model, &iter,
2369 INSTANCE_COLUMN, &folder,
2372 /* If the folder is the same do not notify */
2373 if (folder && priv->cur_folder_store == folder) {
2374 g_object_unref (folder);
2379 /* Current folder was unselected */
2380 if (priv->cur_folder_store) {
2381 /* We must do this firstly because a libtinymail-camel
2382 implementation detail. If we issue the signal
2383 before doing the sync_async, then that signal could
2384 cause (and it actually does it) a free of the
2385 summary of the folder (because the main window will
2386 clear the headers view */
2387 if (TNY_IS_FOLDER(priv->cur_folder_store))
2388 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2389 FALSE, NULL, NULL, NULL);
2391 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2392 priv->cur_folder_store, FALSE);
2394 g_object_unref (priv->cur_folder_store);
2395 priv->cur_folder_store = NULL;
2398 /* New current references */
2399 priv->cur_folder_store = folder;
2401 /* New folder has been selected. Do not notify if there is
2402 nothing new selected */
2404 g_signal_emit (G_OBJECT(tree_view),
2405 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2406 0, priv->cur_folder_store, TRUE);
2411 on_row_activated (GtkTreeView *treeview,
2412 GtkTreePath *treepath,
2413 GtkTreeViewColumn *column,
2416 GtkTreeModel *model = NULL;
2417 TnyFolderStore *folder = NULL;
2419 ModestFolderView *self = NULL;
2420 ModestFolderViewPrivate *priv = NULL;
2422 g_return_if_fail (treeview);
2423 g_return_if_fail (user_data);
2425 self = MODEST_FOLDER_VIEW (user_data);
2426 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2428 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2430 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2433 gtk_tree_model_get (model, &iter,
2434 INSTANCE_COLUMN, &folder,
2437 g_signal_emit (G_OBJECT(self),
2438 signals[FOLDER_ACTIVATED_SIGNAL],
2441 #ifdef MODEST_TOOLKIT_HILDON2
2442 HildonUIMode ui_mode;
2443 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2444 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2445 if (priv->cur_folder_store)
2446 g_object_unref (priv->cur_folder_store);
2447 priv->cur_folder_store = g_object_ref (folder);
2451 g_object_unref (folder);
2455 modest_folder_view_get_selected (ModestFolderView *self)
2457 ModestFolderViewPrivate *priv;
2459 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2461 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2462 if (priv->cur_folder_store)
2463 g_object_ref (priv->cur_folder_store);
2465 return priv->cur_folder_store;
2469 get_cmp_rows_type_pos (GObject *folder)
2471 /* Remote accounts -> Local account -> MMC account .*/
2474 if (TNY_IS_ACCOUNT (folder) &&
2475 modest_tny_account_is_virtual_local_folders (
2476 TNY_ACCOUNT (folder))) {
2478 } else if (TNY_IS_ACCOUNT (folder)) {
2479 TnyAccount *account = TNY_ACCOUNT (folder);
2480 const gchar *account_id = tny_account_get_id (account);
2481 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2487 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2488 return -1; /* Should never happen */
2493 inbox_is_special (TnyFolderStore *folder_store)
2495 gboolean is_special = TRUE;
2497 if (TNY_IS_FOLDER (folder_store)) {
2501 gchar *last_inbox_bar;
2503 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2504 downcase = g_utf8_strdown (id, -1);
2505 last_bar = g_strrstr (downcase, "/");
2507 last_inbox_bar = g_strrstr (downcase, "inbox/");
2508 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2519 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2521 TnyAccount *account;
2522 gboolean is_special;
2523 /* Inbox, Outbox, Drafts, Sent, User */
2526 if (!TNY_IS_FOLDER (folder_store))
2529 case TNY_FOLDER_TYPE_INBOX:
2531 account = tny_folder_get_account (folder_store);
2532 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2534 /* In inbox case we need to know if the inbox is really the top
2535 * inbox of the account, or if it's a submailbox inbox. To do
2536 * this we'll apply an heuristic rule: Find last "/" and check
2537 * if it's preceeded by another Inbox */
2538 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2539 g_object_unref (account);
2540 return is_special?0:4;
2543 case TNY_FOLDER_TYPE_OUTBOX:
2544 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2546 case TNY_FOLDER_TYPE_DRAFTS:
2548 account = tny_folder_get_account (folder_store);
2549 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2550 g_object_unref (account);
2551 return is_special?1:4;
2554 case TNY_FOLDER_TYPE_SENT:
2556 account = tny_folder_get_account (folder_store);
2557 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2558 g_object_unref (account);
2559 return is_special?3:4;
2568 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2570 const gchar *a1_name, *a2_name;
2572 a1_name = tny_account_get_name (a1);
2573 a2_name = tny_account_get_name (a2);
2575 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2579 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2581 TnyAccount *a1 = NULL, *a2 = NULL;
2584 if (TNY_IS_ACCOUNT (s1)) {
2585 a1 = TNY_ACCOUNT (g_object_ref (s1));
2586 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2587 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2590 if (TNY_IS_ACCOUNT (s2)) {
2591 a2 = TNY_ACCOUNT (g_object_ref (s2));
2592 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2593 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2610 /* First we sort with the type of account */
2611 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2615 cmp = compare_account_names (a1, a2);
2619 g_object_unref (a1);
2621 g_object_unref (a2);
2627 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2629 gint is_account1, is_account2;
2631 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2632 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2634 return is_account2 - is_account1;
2638 * This function orders the mail accounts according to these rules:
2639 * 1st - remote accounts
2640 * 2nd - local account
2644 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2648 gchar *name1 = NULL;
2649 gchar *name2 = NULL;
2650 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2651 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2652 GObject *folder1 = NULL;
2653 GObject *folder2 = NULL;
2655 gtk_tree_model_get (tree_model, iter1,
2656 NAME_COLUMN, &name1,
2658 INSTANCE_COLUMN, &folder1,
2660 gtk_tree_model_get (tree_model, iter2,
2661 NAME_COLUMN, &name2,
2662 TYPE_COLUMN, &type2,
2663 INSTANCE_COLUMN, &folder2,
2666 /* Return if we get no folder. This could happen when folder
2667 operations are happening. The model is updated after the
2668 folder copy/move actually occurs, so there could be
2669 situations where the model to be drawn is not correct */
2670 if (!folder1 || !folder2)
2673 /* Sort by type. First the special folders, then the archives */
2674 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2678 /* Now we sort using the account of each folder */
2679 if (TNY_IS_FOLDER_STORE (folder1) &&
2680 TNY_IS_FOLDER_STORE (folder2)) {
2681 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2685 /* Each group is preceeded by its account */
2686 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2691 /* Pure sort by name */
2692 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2695 g_object_unref(G_OBJECT(folder1));
2697 g_object_unref(G_OBJECT(folder2));
2705 /*****************************************************************************/
2706 /* DRAG and DROP stuff */
2707 /*****************************************************************************/
2709 * This function fills the #GtkSelectionData with the row and the
2710 * model that has been dragged. It's called when this widget is a
2711 * source for dnd after the event drop happened
2714 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2715 guint info, guint time, gpointer data)
2717 GtkTreeSelection *selection;
2718 GtkTreeModel *model;
2720 GtkTreePath *source_row;
2722 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2723 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2725 source_row = gtk_tree_model_get_path (model, &iter);
2726 gtk_tree_set_row_drag_data (selection_data,
2730 gtk_tree_path_free (source_row);
2734 typedef struct _DndHelper {
2735 ModestFolderView *folder_view;
2736 gboolean delete_source;
2737 GtkTreePath *source_row;
2741 dnd_helper_destroyer (DndHelper *helper)
2743 /* Free the helper */
2744 gtk_tree_path_free (helper->source_row);
2745 g_slice_free (DndHelper, helper);
2749 xfer_folder_cb (ModestMailOperation *mail_op,
2750 TnyFolder *new_folder,
2754 /* Select the folder */
2755 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2761 /* get the folder for the row the treepath refers to. */
2762 /* folder must be unref'd */
2763 static TnyFolderStore *
2764 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2767 TnyFolderStore *folder = NULL;
2769 if (gtk_tree_model_get_iter (model,&iter, path))
2770 gtk_tree_model_get (model, &iter,
2771 INSTANCE_COLUMN, &folder,
2778 * This function is used by drag_data_received_cb to manage drag and
2779 * drop of a header, i.e, and drag from the header view to the folder
2783 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2784 GtkTreeModel *dest_model,
2785 GtkTreePath *dest_row,
2786 GtkSelectionData *selection_data)
2788 TnyList *headers = NULL;
2789 TnyFolder *folder = NULL, *src_folder = NULL;
2790 TnyFolderType folder_type;
2791 GtkTreeIter source_iter, dest_iter;
2792 ModestWindowMgr *mgr = NULL;
2793 ModestWindow *main_win = NULL;
2794 gchar **uris, **tmp;
2796 /* Build the list of headers */
2797 mgr = modest_runtime_get_window_mgr ();
2798 headers = tny_simple_list_new ();
2799 uris = modest_dnd_selection_data_get_paths (selection_data);
2802 while (*tmp != NULL) {
2805 gboolean first = TRUE;
2808 path = gtk_tree_path_new_from_string (*tmp);
2809 gtk_tree_model_get_iter (source_model, &source_iter, path);
2810 gtk_tree_model_get (source_model, &source_iter,
2811 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2814 /* Do not enable d&d of headers already opened */
2815 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2816 tny_list_append (headers, G_OBJECT (header));
2818 if (G_UNLIKELY (first)) {
2819 src_folder = tny_header_get_folder (header);
2823 /* Free and go on */
2824 gtk_tree_path_free (path);
2825 g_object_unref (header);
2830 /* This could happen ig we perform a d&d very quickly over the
2831 same row that row could dissapear because message is
2833 if (!TNY_IS_FOLDER (src_folder))
2836 /* Get the target folder */
2837 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2838 gtk_tree_model_get (dest_model, &dest_iter,
2842 if (!folder || !TNY_IS_FOLDER(folder)) {
2843 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2847 folder_type = modest_tny_folder_guess_folder_type (folder);
2848 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2849 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2850 goto cleanup; /* cannot move messages there */
2853 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2854 /* g_warning ("folder not writable"); */
2855 goto cleanup; /* verboten! */
2858 /* Ask for confirmation to move */
2859 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2861 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2865 /* Transfer messages */
2866 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2871 if (G_IS_OBJECT (src_folder))
2872 g_object_unref (src_folder);
2873 if (G_IS_OBJECT(folder))
2874 g_object_unref (G_OBJECT (folder));
2875 if (G_IS_OBJECT(headers))
2876 g_object_unref (headers);
2880 TnyFolderStore *src_folder;
2881 TnyFolderStore *dst_folder;
2882 ModestFolderView *folder_view;
2887 dnd_folder_info_destroyer (DndFolderInfo *info)
2889 if (info->src_folder)
2890 g_object_unref (info->src_folder);
2891 if (info->dst_folder)
2892 g_object_unref (info->dst_folder);
2893 g_slice_free (DndFolderInfo, info);
2897 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2898 GtkWindow *parent_window,
2899 TnyAccount *account)
2902 modest_ui_actions_on_account_connection_error (parent_window, account);
2904 /* Free the helper & info */
2905 dnd_helper_destroyer (info->helper);
2906 dnd_folder_info_destroyer (info);
2910 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2912 GtkWindow *parent_window,
2913 TnyAccount *account,
2916 DndFolderInfo *info = NULL;
2917 ModestMailOperation *mail_op;
2919 info = (DndFolderInfo *) user_data;
2921 if (err || canceled) {
2922 dnd_on_connection_failed_destroyer (info, parent_window, account);
2926 /* Do the mail operation */
2927 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2928 modest_ui_actions_move_folder_error_handler,
2929 info->src_folder, NULL);
2931 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2934 /* Transfer the folder */
2935 modest_mail_operation_xfer_folder (mail_op,
2936 TNY_FOLDER (info->src_folder),
2938 info->helper->delete_source,
2940 info->helper->folder_view);
2943 g_object_unref (G_OBJECT (mail_op));
2944 dnd_helper_destroyer (info->helper);
2945 dnd_folder_info_destroyer (info);
2950 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2952 GtkWindow *parent_window,
2953 TnyAccount *account,
2956 DndFolderInfo *info = NULL;
2958 info = (DndFolderInfo *) user_data;
2960 if (err || canceled) {
2961 dnd_on_connection_failed_destroyer (info, parent_window, account);
2965 /* Connect to source folder and perform the copy/move */
2966 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2968 drag_and_drop_from_folder_view_src_folder_performer,
2973 * This function is used by drag_data_received_cb to manage drag and
2974 * drop of a folder, i.e, and drag from the folder view to the same
2978 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2979 GtkTreeModel *dest_model,
2980 GtkTreePath *dest_row,
2981 GtkSelectionData *selection_data,
2984 GtkTreeIter dest_iter, iter;
2985 TnyFolderStore *dest_folder = NULL;
2986 TnyFolderStore *folder = NULL;
2987 gboolean forbidden = FALSE;
2989 DndFolderInfo *info = NULL;
2991 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2993 g_warning ("%s: BUG: no main window", __FUNCTION__);
2994 dnd_helper_destroyer (helper);
2999 /* check the folder rules for the destination */
3000 folder = tree_path_to_folder (dest_model, dest_row);
3001 if (TNY_IS_FOLDER(folder)) {
3002 ModestTnyFolderRules rules =
3003 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3004 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3005 } else if (TNY_IS_FOLDER_STORE(folder)) {
3006 /* enable local root as destination for folders */
3007 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3008 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3011 g_object_unref (folder);
3014 /* check the folder rules for the source */
3015 folder = tree_path_to_folder (source_model, helper->source_row);
3016 if (TNY_IS_FOLDER(folder)) {
3017 ModestTnyFolderRules rules =
3018 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3019 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3022 g_object_unref (folder);
3026 /* Check if the drag is possible */
3027 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3029 modest_platform_run_information_dialog ((GtkWindow *) win,
3030 _("mail_in_ui_folder_move_target_error"),
3032 /* Restore the previous selection */
3033 folder = tree_path_to_folder (source_model, helper->source_row);
3035 if (TNY_IS_FOLDER (folder))
3036 modest_folder_view_select_folder (helper->folder_view,
3037 TNY_FOLDER (folder), FALSE);
3038 g_object_unref (folder);
3040 dnd_helper_destroyer (helper);
3045 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3046 gtk_tree_model_get (dest_model, &dest_iter,
3049 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3050 gtk_tree_model_get (source_model, &iter,
3054 /* Create the info for the performer */
3055 info = g_slice_new0 (DndFolderInfo);
3056 info->src_folder = g_object_ref (folder);
3057 info->dst_folder = g_object_ref (dest_folder);
3058 info->helper = helper;
3060 /* Connect to the destination folder and perform the copy/move */
3061 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3063 drag_and_drop_from_folder_view_dst_folder_performer,
3067 g_object_unref (dest_folder);
3068 g_object_unref (folder);
3072 * This function receives the data set by the "drag-data-get" signal
3073 * handler. This information comes within the #GtkSelectionData. This
3074 * function will manage both the drags of folders of the treeview and
3075 * drags of headers of the header view widget.
3078 on_drag_data_received (GtkWidget *widget,
3079 GdkDragContext *context,
3082 GtkSelectionData *selection_data,
3087 GtkWidget *source_widget;
3088 GtkTreeModel *dest_model, *source_model;
3089 GtkTreePath *source_row, *dest_row;
3090 GtkTreeViewDropPosition pos;
3091 gboolean delete_source = FALSE;
3092 gboolean success = FALSE;
3094 /* Do not allow further process */
3095 g_signal_stop_emission_by_name (widget, "drag-data-received");
3096 source_widget = gtk_drag_get_source_widget (context);
3098 /* Get the action */
3099 if (context->action == GDK_ACTION_MOVE) {
3100 delete_source = TRUE;
3102 /* Notify that there is no folder selected. We need to
3103 do this in order to update the headers view (and
3104 its monitors, because when moving, the old folder
3105 won't longer exist. We can not wait for the end of
3106 the operation, because the operation won't start if
3107 the folder is in use */
3108 if (source_widget == widget) {
3109 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3110 gtk_tree_selection_unselect_all (sel);
3114 /* Check if the get_data failed */
3115 if (selection_data == NULL || selection_data->length < 0)
3118 /* Select the destination model */
3119 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3121 /* Get the path to the destination row. Can not call
3122 gtk_tree_view_get_drag_dest_row() because the source row
3123 is not selected anymore */
3124 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3127 /* Only allow drops IN other rows */
3129 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3130 pos == GTK_TREE_VIEW_DROP_AFTER)
3134 /* Drags from the header view */
3135 if (source_widget != widget) {
3136 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3138 drag_and_drop_from_header_view (source_model,
3143 DndHelper *helper = NULL;
3145 /* Get the source model and row */
3146 gtk_tree_get_row_drag_data (selection_data,
3150 /* Create the helper */
3151 helper = g_slice_new0 (DndHelper);
3152 helper->delete_source = delete_source;
3153 helper->source_row = gtk_tree_path_copy (source_row);
3154 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3156 drag_and_drop_from_folder_view (source_model,
3162 gtk_tree_path_free (source_row);
3166 gtk_tree_path_free (dest_row);
3169 /* Finish the drag and drop */
3170 gtk_drag_finish (context, success, FALSE, time);
3174 * We define a "drag-drop" signal handler because we do not want to
3175 * use the default one, because the default one always calls
3176 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3177 * signal handler, because there we have all the information available
3178 * to know if the dnd was a success or not.
3181 drag_drop_cb (GtkWidget *widget,
3182 GdkDragContext *context,
3190 if (!context->targets)
3193 /* Check if we're dragging a folder row */
3194 target = gtk_drag_dest_find_target (widget, context, NULL);
3196 /* Request the data from the source. */
3197 gtk_drag_get_data(widget, context, target, time);
3203 * This function expands a node of a tree view if it's not expanded
3204 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3205 * does that, so that's why they're here.
3208 expand_row_timeout (gpointer data)
3210 GtkTreeView *tree_view = data;
3211 GtkTreePath *dest_path = NULL;
3212 GtkTreeViewDropPosition pos;
3213 gboolean result = FALSE;
3215 gdk_threads_enter ();
3217 gtk_tree_view_get_drag_dest_row (tree_view,
3222 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3223 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3224 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3225 gtk_tree_path_free (dest_path);
3229 gtk_tree_path_free (dest_path);
3234 gdk_threads_leave ();
3240 * This function is called whenever the pointer is moved over a widget
3241 * while dragging some data. It installs a timeout that will expand a
3242 * node of the treeview if not expanded yet. This function also calls
3243 * gdk_drag_status in order to set the suggested action that will be
3244 * used by the "drag-data-received" signal handler to know if we
3245 * should do a move or just a copy of the data.
3248 on_drag_motion (GtkWidget *widget,
3249 GdkDragContext *context,
3255 GtkTreeViewDropPosition pos;
3256 GtkTreePath *dest_row;
3257 GtkTreeModel *dest_model;
3258 ModestFolderViewPrivate *priv;
3259 GdkDragAction suggested_action;
3260 gboolean valid_location = FALSE;
3261 TnyFolderStore *folder = NULL;
3263 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3265 if (priv->timer_expander != 0) {
3266 g_source_remove (priv->timer_expander);
3267 priv->timer_expander = 0;
3270 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3275 /* Do not allow drops between folders */
3277 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3278 pos == GTK_TREE_VIEW_DROP_AFTER) {
3279 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3280 gdk_drag_status(context, 0, time);
3281 valid_location = FALSE;
3284 valid_location = TRUE;
3287 /* Check that the destination folder is writable */
3288 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3289 folder = tree_path_to_folder (dest_model, dest_row);
3290 if (folder && TNY_IS_FOLDER (folder)) {
3291 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3293 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3294 valid_location = FALSE;
3299 /* Expand the selected row after 1/2 second */
3300 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3301 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3303 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3305 /* Select the desired action. By default we pick MOVE */
3306 suggested_action = GDK_ACTION_MOVE;
3308 if (context->actions == GDK_ACTION_COPY)
3309 gdk_drag_status(context, GDK_ACTION_COPY, time);
3310 else if (context->actions == GDK_ACTION_MOVE)
3311 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3312 else if (context->actions & suggested_action)
3313 gdk_drag_status(context, suggested_action, time);
3315 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3319 g_object_unref (folder);
3321 gtk_tree_path_free (dest_row);
3323 g_signal_stop_emission_by_name (widget, "drag-motion");
3325 return valid_location;
3329 * This function sets the treeview as a source and a target for dnd
3330 * events. It also connects all the requirede signals.
3333 setup_drag_and_drop (GtkTreeView *self)
3335 /* Set up the folder view as a dnd destination. Set only the
3336 highlight flag, otherwise gtk will have a different
3338 #ifdef MODEST_TOOLKIT_HILDON2
3341 gtk_drag_dest_set (GTK_WIDGET (self),
3342 GTK_DEST_DEFAULT_HIGHLIGHT,
3343 folder_view_drag_types,
3344 G_N_ELEMENTS (folder_view_drag_types),
3345 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3347 g_signal_connect (G_OBJECT (self),
3348 "drag_data_received",
3349 G_CALLBACK (on_drag_data_received),
3353 /* Set up the treeview as a dnd source */
3354 gtk_drag_source_set (GTK_WIDGET (self),
3356 folder_view_drag_types,
3357 G_N_ELEMENTS (folder_view_drag_types),
3358 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3360 g_signal_connect (G_OBJECT (self),
3362 G_CALLBACK (on_drag_motion),
3365 g_signal_connect (G_OBJECT (self),
3367 G_CALLBACK (on_drag_data_get),
3370 g_signal_connect (G_OBJECT (self),
3372 G_CALLBACK (drag_drop_cb),
3377 * This function manages the navigation through the folders using the
3378 * keyboard or the hardware keys in the device
3381 on_key_pressed (GtkWidget *self,
3385 GtkTreeSelection *selection;
3387 GtkTreeModel *model;
3388 gboolean retval = FALSE;
3390 /* Up and Down are automatically managed by the treeview */
3391 if (event->keyval == GDK_Return) {
3392 /* Expand/Collapse the selected row */
3393 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3394 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3397 path = gtk_tree_model_get_path (model, &iter);
3399 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3400 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3402 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3403 gtk_tree_path_free (path);
3405 /* No further processing */
3413 * We listen to the changes in the local folder account name key,
3414 * because we want to show the right name in the view. The local
3415 * folder account name corresponds to the device name in the Maemo
3416 * version. We do this because we do not want to query gconf on each
3417 * tree view refresh. It's better to cache it and change whenever
3421 on_configuration_key_changed (ModestConf* conf,
3423 ModestConfEvent event,
3424 ModestConfNotificationId id,
3425 ModestFolderView *self)
3427 ModestFolderViewPrivate *priv;
3430 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3431 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3433 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3434 g_free (priv->local_account_name);
3436 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3437 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3439 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3440 MODEST_CONF_DEVICE_NAME, NULL);
3442 /* Force a redraw */
3443 #if GTK_CHECK_VERSION(2, 8, 0)
3444 GtkTreeViewColumn * tree_column;
3446 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3448 gtk_tree_view_column_queue_resize (tree_column);
3450 gtk_widget_queue_draw (GTK_WIDGET (self));
3456 modest_folder_view_set_style (ModestFolderView *self,
3457 ModestFolderViewStyle style)
3459 ModestFolderViewPrivate *priv;
3461 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3462 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3463 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3465 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3468 priv->style = style;
3472 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3473 const gchar *account_id)
3475 ModestFolderViewPrivate *priv;
3476 GtkTreeModel *model;
3478 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3480 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3482 /* This will be used by the filter_row callback,
3483 * to decided which rows to show: */
3484 if (priv->visible_account_id) {
3485 g_free (priv->visible_account_id);
3486 priv->visible_account_id = NULL;
3489 priv->visible_account_id = g_strdup (account_id);
3492 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3493 if (GTK_IS_TREE_MODEL_FILTER (model))
3494 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3496 /* Save settings to gconf */
3497 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3498 MODEST_CONF_FOLDER_VIEW_KEY);
3500 /* Notify observers */
3501 g_signal_emit (G_OBJECT(self),
3502 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3507 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3509 ModestFolderViewPrivate *priv;
3511 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3513 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3515 return (const gchar *) priv->visible_account_id;
3519 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3523 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3525 gtk_tree_model_get (model, iter,
3529 gboolean result = FALSE;
3530 if (type == TNY_FOLDER_TYPE_INBOX) {
3534 *inbox_iter = *iter;
3538 if (gtk_tree_model_iter_children (model, &child, iter)) {
3539 if (find_inbox_iter (model, &child, inbox_iter))
3543 } while (gtk_tree_model_iter_next (model, iter));
3552 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3554 #ifndef MODEST_TOOLKIT_HILDON2
3555 GtkTreeModel *model;
3556 GtkTreeIter iter, inbox_iter;
3557 GtkTreeSelection *sel;
3558 GtkTreePath *path = NULL;
3560 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3562 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3566 expand_root_items (self);
3567 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3569 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3570 g_warning ("%s: model is empty", __FUNCTION__);
3574 if (find_inbox_iter (model, &iter, &inbox_iter))
3575 path = gtk_tree_model_get_path (model, &inbox_iter);
3577 path = gtk_tree_path_new_first ();
3579 /* Select the row and free */
3580 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3581 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3582 gtk_tree_path_free (path);
3585 gtk_widget_grab_focus (GTK_WIDGET(self));
3592 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3597 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3598 TnyFolder* a_folder;
3601 gtk_tree_model_get (model, iter,
3602 INSTANCE_COLUMN, &a_folder,
3608 if (folder == a_folder) {
3609 g_object_unref (a_folder);
3610 *folder_iter = *iter;
3613 g_object_unref (a_folder);
3615 if (gtk_tree_model_iter_children (model, &child, iter)) {
3616 if (find_folder_iter (model, &child, folder_iter, folder))
3620 } while (gtk_tree_model_iter_next (model, iter));
3625 #ifndef MODEST_TOOLKIT_HILDON2
3627 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3630 ModestFolderView *self)
3632 ModestFolderViewPrivate *priv = NULL;
3633 GtkTreeSelection *sel;
3634 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3635 GObject *instance = NULL;
3637 if (!MODEST_IS_FOLDER_VIEW(self))
3640 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3642 priv->reexpand = TRUE;
3644 gtk_tree_model_get (tree_model, iter,
3646 INSTANCE_COLUMN, &instance,
3652 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3653 priv->folder_to_select = g_object_ref (instance);
3655 g_object_unref (instance);
3657 if (priv->folder_to_select) {
3659 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3662 path = gtk_tree_model_get_path (tree_model, iter);
3663 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3665 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3667 gtk_tree_selection_select_iter (sel, iter);
3668 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3670 gtk_tree_path_free (path);
3674 modest_folder_view_disable_next_folder_selection (self);
3676 /* Refilter the model */
3677 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3683 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3685 ModestFolderViewPrivate *priv;
3687 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3689 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3691 if (priv->folder_to_select)
3692 g_object_unref(priv->folder_to_select);
3694 priv->folder_to_select = NULL;
3698 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3699 gboolean after_change)
3701 GtkTreeModel *model;
3702 GtkTreeIter iter, folder_iter;
3703 GtkTreeSelection *sel;
3704 ModestFolderViewPrivate *priv = NULL;
3706 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3707 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3709 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3712 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3713 gtk_tree_selection_unselect_all (sel);
3715 if (priv->folder_to_select)
3716 g_object_unref(priv->folder_to_select);
3717 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3721 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3726 /* Refilter the model, before selecting the folder */
3727 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3729 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3730 g_warning ("%s: model is empty", __FUNCTION__);
3734 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3737 path = gtk_tree_model_get_path (model, &folder_iter);
3738 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3740 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3741 gtk_tree_selection_select_iter (sel, &folder_iter);
3742 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3744 gtk_tree_path_free (path);
3752 modest_folder_view_copy_selection (ModestFolderView *self)
3754 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3756 /* Copy selection */
3757 _clipboard_set_selected_data (self, FALSE);
3761 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3763 ModestFolderViewPrivate *priv = NULL;
3764 GtkTreeModel *model = NULL;
3765 const gchar **hidding = NULL;
3766 guint i, n_selected;
3768 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3769 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3771 /* Copy selection */
3772 if (!_clipboard_set_selected_data (folder_view, TRUE))
3775 /* Get hidding ids */
3776 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3778 /* Clear hidding array created by previous cut operation */
3779 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3781 /* Copy hidding array */
3782 priv->n_selected = n_selected;
3783 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3784 for (i=0; i < n_selected; i++)
3785 priv->hidding_ids[i] = g_strdup(hidding[i]);
3787 /* Hide cut folders */
3788 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3789 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3793 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3794 ModestFolderView *folder_view_dst)
3796 GtkTreeModel *filter_model = NULL;
3797 GtkTreeModel *model = NULL;
3798 GtkTreeModel *new_filter_model = NULL;
3799 GtkTreeModel *old_tny_model = NULL;
3800 GtkTreeModel *new_tny_model = NULL;
3801 ModestFolderViewPrivate *dst_priv;
3803 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3804 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3806 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3807 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3808 new_tny_model = NULL;
3811 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3812 g_signal_handler_disconnect (G_OBJECT (old_tny_model), dst_priv->activity_changed_handler);
3813 dst_priv->activity_changed_handler = 0;
3815 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3816 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3818 /* Build new filter model */
3819 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3820 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3827 /* Set copied model */
3828 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3829 #ifndef MODEST_TOOLKIT_HILDON2
3830 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3831 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3833 #ifdef MODEST_TOOLKIT_HILDON2
3835 dst_priv->activity_changed_handler = g_signal_connect (G_OBJECT (new_tny_model), "activity-changed",
3836 G_CALLBACK (on_activity_changed), folder_view_dst);
3840 g_object_unref (new_filter_model);
3844 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3847 GtkTreeModel *model = NULL;
3848 ModestFolderViewPrivate* priv;
3850 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3852 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3853 priv->show_non_move = show;
3854 /* modest_folder_view_update_model(folder_view, */
3855 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3857 /* Hide special folders */
3858 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3859 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3860 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3865 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3868 ModestFolderViewPrivate* priv;
3870 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3872 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3873 priv->show_message_count = show;
3875 g_object_set (G_OBJECT (priv->messages_renderer),
3876 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3880 /* Returns FALSE if it did not selected anything */
3882 _clipboard_set_selected_data (ModestFolderView *folder_view,
3885 ModestFolderViewPrivate *priv = NULL;
3886 TnyFolderStore *folder = NULL;
3887 gboolean retval = FALSE;
3889 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3890 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3892 /* Set selected data on clipboard */
3893 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3894 folder = modest_folder_view_get_selected (folder_view);
3896 /* Do not allow to select an account */
3897 if (TNY_IS_FOLDER (folder)) {
3898 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3903 g_object_unref (folder);
3909 _clear_hidding_filter (ModestFolderView *folder_view)
3911 ModestFolderViewPrivate *priv;
3914 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3915 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3917 if (priv->hidding_ids != NULL) {
3918 for (i=0; i < priv->n_selected; i++)
3919 g_free (priv->hidding_ids[i]);
3920 g_free(priv->hidding_ids);
3926 on_display_name_changed (ModestAccountMgr *mgr,
3927 const gchar *account,
3930 ModestFolderView *self;
3932 self = MODEST_FOLDER_VIEW (user_data);
3934 /* Force a redraw */
3935 #if GTK_CHECK_VERSION(2, 8, 0)
3936 GtkTreeViewColumn * tree_column;
3938 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3940 gtk_tree_view_column_queue_resize (tree_column);
3942 gtk_widget_queue_draw (GTK_WIDGET (self));
3947 modest_folder_view_set_cell_style (ModestFolderView *self,
3948 ModestFolderViewCellStyle cell_style)
3950 ModestFolderViewPrivate *priv = NULL;
3952 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3953 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3955 priv->cell_style = cell_style;
3957 g_object_set (G_OBJECT (priv->messages_renderer),
3958 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3961 gtk_widget_queue_draw (GTK_WIDGET (self));
3965 update_style (ModestFolderView *self)
3967 ModestFolderViewPrivate *priv;
3968 GdkColor style_color;
3969 PangoAttrList *attr_list;
3971 PangoAttribute *attr;
3973 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3974 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3978 attr_list = pango_attr_list_new ();
3979 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3980 gdk_color_parse ("grey", &style_color);
3982 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3983 pango_attr_list_insert (attr_list, attr);
3986 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3988 "SmallSystemFont", NULL,
3991 attr = pango_attr_font_desc_new (pango_font_description_copy
3992 (style->font_desc));
3993 pango_attr_list_insert (attr_list, attr);
3995 g_object_set (G_OBJECT (priv->messages_renderer),
3996 "foreground-gdk", &style_color,
3997 "foreground-set", TRUE,
3998 "attributes", attr_list,
4000 pango_attr_list_unref (attr_list);
4005 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4007 if (strcmp ("style", spec->name) == 0) {
4008 update_style (MODEST_FOLDER_VIEW (obj));
4009 gtk_widget_queue_draw (GTK_WIDGET (obj));
4014 modest_folder_view_set_filter (ModestFolderView *self,
4015 ModestFolderViewFilter filter)
4017 ModestFolderViewPrivate *priv;
4018 GtkTreeModel *filter_model;
4020 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4021 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4023 priv->filter |= filter;
4025 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4026 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4027 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4032 modest_folder_view_unset_filter (ModestFolderView *self,
4033 ModestFolderViewFilter filter)
4035 ModestFolderViewPrivate *priv;
4036 GtkTreeModel *filter_model;
4038 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4039 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4041 priv->filter &= ~filter;
4043 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4044 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4045 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4050 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4051 ModestTnyFolderRules rules)
4053 GtkTreeModel *filter_model;
4055 gboolean fulfil = FALSE;
4057 if (!get_inner_models (self, &filter_model, NULL, NULL))
4060 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4064 TnyFolderStore *folder;
4066 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4068 if (TNY_IS_FOLDER (folder)) {
4069 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4070 /* Folder rules are negative: non_writable, non_deletable... */
4071 if (!(folder_rules & rules))
4074 g_object_unref (folder);
4077 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4083 modest_folder_view_set_list_to_move (ModestFolderView *self,
4086 ModestFolderViewPrivate *priv;
4088 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4089 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4091 if (priv->list_to_move)
4092 g_object_unref (priv->list_to_move);
4095 g_object_ref (list);
4097 priv->list_to_move = list;
4101 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4103 ModestFolderViewPrivate *priv;
4105 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4106 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4109 g_free (priv->mailbox);
4111 priv->mailbox = g_strdup (mailbox);
4113 /* Notify observers */
4114 g_signal_emit (G_OBJECT(self),
4115 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4116 priv->visible_account_id);
4120 modest_folder_view_get_mailbox (ModestFolderView *self)
4122 ModestFolderViewPrivate *priv;
4124 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4125 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4127 return (const gchar *) priv->mailbox;
4131 modest_folder_view_get_activity (ModestFolderView *self)
4133 ModestFolderViewPrivate *priv;
4134 GtkTreeModel *inner_model;
4136 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4137 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4138 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4140 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4141 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4147 #ifdef MODEST_TOOLKIT_HILDON2
4149 on_activity_changed (TnyGtkFolderListStore *store,
4151 ModestFolderView *folder_view)
4153 ModestFolderViewPrivate *priv;
4155 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4156 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4157 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4159 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,