1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
65 #include <modest-account-protocol.h>
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
98 static void modest_folder_view_init (ModestFolderView *obj);
99 static void modest_folder_view_finalize (GObject *obj);
100 static void modest_folder_view_dispose (GObject *obj);
102 static void tny_account_store_view_init (gpointer g,
103 gpointer iface_data);
105 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
106 TnyAccountStore *account_store);
108 static void on_selection_changed (GtkTreeSelection *sel,
111 static void on_row_activated (GtkTreeView *treeview,
113 GtkTreeViewColumn *column,
116 static void on_account_removed (TnyAccountStore *self,
120 static void on_account_inserted (TnyAccountStore *self,
124 static void on_account_changed (TnyAccountStore *self,
128 static gint cmp_rows (GtkTreeModel *tree_model,
133 static gboolean filter_row (GtkTreeModel *model,
137 static gboolean on_key_pressed (GtkWidget *self,
141 static void on_configuration_key_changed (ModestConf* conf,
143 ModestConfEvent event,
144 ModestConfNotificationId notification_id,
145 ModestFolderView *self);
148 static void on_drag_data_get (GtkWidget *widget,
149 GdkDragContext *context,
150 GtkSelectionData *selection_data,
155 static void on_drag_data_received (GtkWidget *widget,
156 GdkDragContext *context,
159 GtkSelectionData *selection_data,
164 static gboolean on_drag_motion (GtkWidget *widget,
165 GdkDragContext *context,
171 static void expand_root_items (ModestFolderView *self);
173 static gint expand_row_timeout (gpointer data);
175 static void setup_drag_and_drop (GtkTreeView *self);
177 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
180 static void _clear_hidding_filter (ModestFolderView *folder_view);
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
186 ModestFolderView *self);
189 static void on_display_name_changed (ModestAccountMgr *self,
190 const gchar *account,
192 static void update_style (ModestFolderView *self);
193 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
195 static gboolean inbox_is_special (TnyFolderStore *folder_store);
197 static gboolean get_inner_models (ModestFolderView *self,
198 GtkTreeModel **filter_model,
199 GtkTreeModel **sort_model,
200 GtkTreeModel **tny_model);
201 #ifdef MODEST_TOOLKIT_HILDON2
202 static void on_activity_changed (TnyGtkFolderListStore *store,
204 ModestFolderView *folder_view);
208 FOLDER_SELECTION_CHANGED_SIGNAL,
209 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
210 FOLDER_ACTIVATED_SIGNAL,
211 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
212 ACTIVITY_CHANGED_SIGNAL,
216 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
217 struct _ModestFolderViewPrivate {
218 TnyAccountStore *account_store;
219 TnyFolderStore *cur_folder_store;
221 TnyFolder *folder_to_select; /* folder to select after the next update */
223 /* not unref this object, its a singlenton */
224 ModestEmailClipboard *clipboard;
226 /* Filter tree model */
229 ModestFolderViewFilter filter;
231 TnyFolderStoreQuery *query;
233 guint timer_expander;
235 gchar *local_account_name;
236 gchar *visible_account_id;
238 ModestFolderViewStyle style;
239 ModestFolderViewCellStyle cell_style;
240 gboolean show_message_count;
242 gboolean reselect; /* we use this to force a reselection of the INBOX */
243 gboolean show_non_move;
244 TnyList *list_to_move;
245 gboolean reexpand; /* next time we expose, we'll expand all root folders */
247 GtkCellRenderer *messages_renderer;
249 GSList *signal_handlers;
250 GdkColor *active_color;
252 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
253 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
254 MODEST_TYPE_FOLDER_VIEW, \
255 ModestFolderViewPrivate))
257 static GObjectClass *parent_class = NULL;
259 static guint signals[LAST_SIGNAL] = {0};
262 modest_folder_view_get_type (void)
264 static GType my_type = 0;
266 static const GTypeInfo my_info = {
267 sizeof(ModestFolderViewClass),
268 NULL, /* base init */
269 NULL, /* base finalize */
270 (GClassInitFunc) modest_folder_view_class_init,
271 NULL, /* class finalize */
272 NULL, /* class data */
273 sizeof(ModestFolderView),
275 (GInstanceInitFunc) modest_folder_view_init,
279 static const GInterfaceInfo tny_account_store_view_info = {
280 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
281 NULL, /* interface_finalize */
282 NULL /* interface_data */
286 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
290 g_type_add_interface_static (my_type,
291 TNY_TYPE_ACCOUNT_STORE_VIEW,
292 &tny_account_store_view_info);
298 modest_folder_view_class_init (ModestFolderViewClass *klass)
300 GObjectClass *gobject_class;
301 GtkTreeViewClass *treeview_class;
302 gobject_class = (GObjectClass*) klass;
303 treeview_class = (GtkTreeViewClass*) klass;
305 parent_class = g_type_class_peek_parent (klass);
306 gobject_class->finalize = modest_folder_view_finalize;
307 gobject_class->finalize = modest_folder_view_dispose;
309 g_type_class_add_private (gobject_class,
310 sizeof(ModestFolderViewPrivate));
312 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
313 g_signal_new ("folder_selection_changed",
314 G_TYPE_FROM_CLASS (gobject_class),
316 G_STRUCT_OFFSET (ModestFolderViewClass,
317 folder_selection_changed),
319 modest_marshal_VOID__POINTER_BOOLEAN,
320 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
323 * This signal is emitted whenever the currently selected
324 * folder display name is computed. Note that the name could
325 * be different to the folder name, because we could append
326 * the unread messages count to the folder name to build the
327 * folder display name
329 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
330 g_signal_new ("folder-display-name-changed",
331 G_TYPE_FROM_CLASS (gobject_class),
333 G_STRUCT_OFFSET (ModestFolderViewClass,
334 folder_display_name_changed),
336 g_cclosure_marshal_VOID__STRING,
337 G_TYPE_NONE, 1, G_TYPE_STRING);
339 signals[FOLDER_ACTIVATED_SIGNAL] =
340 g_signal_new ("folder_activated",
341 G_TYPE_FROM_CLASS (gobject_class),
343 G_STRUCT_OFFSET (ModestFolderViewClass,
346 g_cclosure_marshal_VOID__POINTER,
347 G_TYPE_NONE, 1, G_TYPE_POINTER);
350 * Emitted whenever the visible account changes
352 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
353 g_signal_new ("visible-account-changed",
354 G_TYPE_FROM_CLASS (gobject_class),
356 G_STRUCT_OFFSET (ModestFolderViewClass,
357 visible_account_changed),
359 g_cclosure_marshal_VOID__STRING,
360 G_TYPE_NONE, 1, G_TYPE_STRING);
363 * Emitted when the underlying GtkListStore is updating data
365 signals[ACTIVITY_CHANGED_SIGNAL] =
366 g_signal_new ("activity-changed",
367 G_TYPE_FROM_CLASS (gobject_class),
369 G_STRUCT_OFFSET (ModestFolderViewClass,
372 g_cclosure_marshal_VOID__BOOLEAN,
373 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
375 treeview_class->select_cursor_parent = NULL;
377 #ifdef MODEST_TOOLKIT_HILDON2
378 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
384 /* Retrieves the filter, sort and tny models of the folder view. If
385 any of these does not exist then it returns FALSE */
387 get_inner_models (ModestFolderView *self,
388 GtkTreeModel **filter_model,
389 GtkTreeModel **sort_model,
390 GtkTreeModel **tny_model)
392 GtkTreeModel *s_model, *f_model, *t_model;
394 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
395 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
396 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
400 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
401 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
402 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
406 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
410 *filter_model = f_model;
412 *sort_model = s_model;
414 *tny_model = t_model;
419 /* Simplify checks for NULLs: */
421 strings_are_equal (const gchar *a, const gchar *b)
427 return (strcmp (a, b) == 0);
434 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
436 GObject *instance = NULL;
438 gtk_tree_model_get (model, iter,
439 INSTANCE_COLUMN, &instance,
443 return FALSE; /* keep walking */
445 if (!TNY_IS_ACCOUNT (instance)) {
446 g_object_unref (instance);
447 return FALSE; /* keep walking */
450 /* Check if this is the looked-for account: */
451 TnyAccount *this_account = TNY_ACCOUNT (instance);
452 TnyAccount *account = TNY_ACCOUNT (data);
454 const gchar *this_account_id = tny_account_get_id(this_account);
455 const gchar *account_id = tny_account_get_id(account);
456 g_object_unref (instance);
459 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
460 if (strings_are_equal(this_account_id, account_id)) {
461 /* Tell the model that the data has changed, so that
462 * it calls the cell_data_func callbacks again: */
463 /* TODO: This does not seem to actually cause the new string to be shown: */
464 gtk_tree_model_row_changed (model, path, iter);
466 return TRUE; /* stop walking */
469 return FALSE; /* keep walking */
474 ModestFolderView *self;
475 gchar *previous_name;
476 } GetMmcAccountNameData;
479 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
481 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
483 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
485 if (!strings_are_equal (
486 tny_account_get_name(TNY_ACCOUNT(account)),
487 data->previous_name)) {
489 /* Tell the model that the data has changed, so that
490 * it calls the cell_data_func callbacks again: */
491 ModestFolderView *self = data->self;
492 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
494 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
497 g_free (data->previous_name);
498 g_slice_free (GetMmcAccountNameData, data);
502 convert_parent_folders_to_dots (gchar **item_name)
506 gchar *last_separator;
508 if (item_name == NULL)
511 for (c = *item_name; *c != '\0'; c++) {
512 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
517 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
518 if (last_separator != NULL) {
519 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
526 buffer = g_string_new ("");
527 for (i = 0; i < n_parents; i++) {
528 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
530 buffer = g_string_append (buffer, last_separator);
532 *item_name = g_string_free (buffer, FALSE);
538 format_compact_style (gchar **item_name,
540 const gchar *mailbox,
542 gboolean multiaccount,
543 gboolean *use_markup)
547 TnyFolderType folder_type;
549 if (!TNY_IS_FOLDER (instance))
552 folder = (TnyFolder *) instance;
554 folder_type = tny_folder_get_folder_type (folder);
555 is_special = (get_cmp_pos (folder_type, folder)!= 4);
558 /* Remove mailbox prefix if any */
559 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
560 if (g_str_has_prefix (*item_name, prefix)) {
561 gchar *new_item_name;
563 new_item_name = g_strdup (*item_name + strlen (prefix));
564 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
565 g_free (new_item_name);
566 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
569 *item_name = new_item_name;
571 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
574 *item_name = g_strdup (_("mcen_me_folder_inbox"));
577 if (!is_special || multiaccount) {
578 TnyAccount *account = tny_folder_get_account (folder);
579 const gchar *folder_name;
580 gboolean concat_folder_name = FALSE;
583 /* Should not happen */
587 /* convert parent folders to dots */
588 convert_parent_folders_to_dots (item_name);
590 folder_name = tny_folder_get_name (folder);
591 if (g_str_has_suffix (*item_name, folder_name)) {
592 gchar *offset = g_strrstr (*item_name, folder_name);
594 concat_folder_name = TRUE;
597 buffer = g_string_new ("");
599 buffer = g_string_append (buffer, *item_name);
600 if (concat_folder_name) {
601 buffer = g_string_append (buffer, folder_name);
604 g_object_unref (account);
606 *item_name = g_string_free (buffer, FALSE);
614 text_cell_data (GtkTreeViewColumn *column,
615 GtkCellRenderer *renderer,
616 GtkTreeModel *tree_model,
620 ModestFolderViewPrivate *priv;
621 GObject *rendobj = (GObject *) renderer;
623 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
624 GObject *instance = NULL;
625 gboolean use_markup = FALSE;
627 gtk_tree_model_get (tree_model, iter,
630 INSTANCE_COLUMN, &instance,
632 if (!fname || !instance)
635 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
636 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
638 gchar *item_name = NULL;
639 gint item_weight = 400;
641 if (type != TNY_FOLDER_TYPE_ROOT) {
646 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
647 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
650 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
651 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
653 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
656 /* Sometimes an special folder is reported by the server as
657 NORMAL, like some versions of Dovecot */
658 if (type == TNY_FOLDER_TYPE_NORMAL ||
659 type == TNY_FOLDER_TYPE_UNKNOWN) {
660 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
664 /* note: we cannot reliably get the counts from the
665 * tree model, we need to use explicit calls on
666 * tny_folder for some reason. Select the number to
667 * show: the unread or unsent messages. in case of
668 * outbox/drafts, show all */
669 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
670 (type == TNY_FOLDER_TYPE_OUTBOX) ||
671 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
672 number = tny_folder_get_all_count (TNY_FOLDER(instance));
675 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
679 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
680 item_name = g_strdup (fname);
687 /* Use bold font style if there are unread or unset messages */
689 if (priv->show_message_count) {
690 item_name = g_strdup_printf ("%s (%d)", fname, number);
692 item_name = g_strdup (fname);
696 item_name = g_strdup (fname);
701 } else if (TNY_IS_ACCOUNT (instance)) {
702 /* If it's a server account */
703 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
704 item_name = g_strdup (priv->local_account_name);
706 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
707 /* fname is only correct when the items are first
708 * added to the model, not when the account is
709 * changed later, so get the name from the account
711 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
714 item_name = g_strdup (fname);
720 item_name = g_strdup ("unknown");
722 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
723 gboolean multiaccount;
725 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
726 /* Convert item_name to markup */
727 format_compact_style (&item_name, instance, priv->mailbox,
729 multiaccount, &use_markup);
732 if (item_name && item_weight) {
733 /* Set the name in the treeview cell: */
734 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 && priv->active_color) {
735 g_object_set (rendobj,
738 "foreground-set", TRUE,
739 "foreground-gdk", priv->active_color,
742 g_object_set (rendobj,
744 "foreground-set", FALSE,
746 "weight", item_weight,
750 /* Notify display name observers */
751 /* TODO: What listens for this signal, and how can it use only the new name? */
752 if (((GObject *) priv->cur_folder_store) == instance) {
753 g_signal_emit (G_OBJECT(self),
754 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
761 /* If it is a Memory card account, make sure that we have the correct name.
762 * This function will be trigerred again when the name has been retrieved: */
763 if (TNY_IS_STORE_ACCOUNT (instance) &&
764 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
766 /* Get the account name asynchronously: */
767 GetMmcAccountNameData *callback_data =
768 g_slice_new0(GetMmcAccountNameData);
769 callback_data->self = self;
771 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
773 callback_data->previous_name = g_strdup (name);
775 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
776 on_get_mmc_account_name, callback_data);
780 g_object_unref (G_OBJECT (instance));
786 messages_cell_data (GtkTreeViewColumn *column,
787 GtkCellRenderer *renderer,
788 GtkTreeModel *tree_model,
792 ModestFolderView *self;
793 ModestFolderViewPrivate *priv;
794 GObject *rendobj = (GObject *) renderer;
795 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
796 GObject *instance = NULL;
797 gchar *item_name = NULL;
799 gtk_tree_model_get (tree_model, iter,
801 INSTANCE_COLUMN, &instance,
806 self = MODEST_FOLDER_VIEW (data);
807 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
810 if (type != TNY_FOLDER_TYPE_ROOT) {
815 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
816 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
819 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
821 /* Sometimes an special folder is reported by the server as
822 NORMAL, like some versions of Dovecot */
823 if (type == TNY_FOLDER_TYPE_NORMAL ||
824 type == TNY_FOLDER_TYPE_UNKNOWN) {
825 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
829 /* note: we cannot reliably get the counts from the tree model, we need
830 * to use explicit calls on tny_folder for some reason.
832 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
833 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
834 (type == TNY_FOLDER_TYPE_OUTBOX) ||
835 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
836 number = tny_folder_get_all_count (TNY_FOLDER(instance));
839 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
843 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
845 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_va_new_messages"),
847 } else if (number == 1) {
848 item_name = g_strdup_printf (drafts?_("mcen_ti_message"):_("mcen_va_new_message"),
856 item_name = g_strdup ("");
859 /* Set the name in the treeview cell: */
860 g_object_set (rendobj,"text", item_name, NULL);
868 g_object_unref (G_OBJECT (instance));
874 GdkPixbuf *pixbuf_open;
875 GdkPixbuf *pixbuf_close;
879 static inline GdkPixbuf *
880 get_composite_pixbuf (const gchar *icon_name,
882 GdkPixbuf *base_pixbuf)
884 GdkPixbuf *emblem, *retval = NULL;
886 emblem = modest_platform_get_icon (icon_name, size);
888 retval = gdk_pixbuf_copy (base_pixbuf);
889 gdk_pixbuf_composite (emblem, retval, 0, 0,
890 MIN (gdk_pixbuf_get_width (emblem),
891 gdk_pixbuf_get_width (retval)),
892 MIN (gdk_pixbuf_get_height (emblem),
893 gdk_pixbuf_get_height (retval)),
894 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
895 g_object_unref (emblem);
900 static inline ThreePixbufs *
901 get_composite_icons (const gchar *icon_code,
903 GdkPixbuf **pixbuf_open,
904 GdkPixbuf **pixbuf_close)
906 ThreePixbufs *retval;
909 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
912 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
917 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
921 retval = g_slice_new0 (ThreePixbufs);
923 retval->pixbuf = g_object_ref (*pixbuf);
925 retval->pixbuf_open = g_object_ref (*pixbuf_open);
927 retval->pixbuf_close = g_object_ref (*pixbuf_close);
932 static inline ThreePixbufs *
933 get_account_protocol_pixbufs (ModestFolderView *folder_view,
934 ModestProtocolType protocol_type,
937 ModestProtocol *protocol;
938 const GdkPixbuf *pixbuf = NULL;
939 ModestFolderViewPrivate *priv;
941 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
943 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
946 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
947 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
948 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
949 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
950 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
951 object, FOLDER_ICON_SIZE);
955 ThreePixbufs *retval;
956 retval = g_slice_new0 (ThreePixbufs);
957 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
958 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
959 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
966 static inline ThreePixbufs*
967 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
969 TnyAccount *account = NULL;
970 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
971 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
972 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
973 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
974 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
976 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
977 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
978 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
979 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
980 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
982 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
983 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
984 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
985 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
986 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
988 ThreePixbufs *retval = NULL;
990 if (TNY_IS_ACCOUNT (instance)) {
991 account = g_object_ref (instance);
992 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
993 account = tny_folder_get_account (TNY_FOLDER (instance));
997 ModestProtocolType account_store_protocol;
999 account_store_protocol = modest_tny_account_get_protocol_type (account);
1000 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1001 g_object_unref (account);
1007 /* Sometimes an special folder is reported by the server as
1008 NORMAL, like some versions of Dovecot */
1009 if (type == TNY_FOLDER_TYPE_NORMAL ||
1010 type == TNY_FOLDER_TYPE_UNKNOWN) {
1011 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1014 /* It's not enough with check the folder type. We need to
1015 ensure that we're not giving a special folder icon to a
1016 normal folder with the same name than a special folder */
1017 if (TNY_IS_FOLDER (instance) &&
1018 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1019 type = TNY_FOLDER_TYPE_NORMAL;
1021 /* Remote folders should not be treated as special folders */
1022 if (TNY_IS_FOLDER_STORE (instance) &&
1023 !TNY_IS_ACCOUNT (instance) &&
1024 type != TNY_FOLDER_TYPE_INBOX &&
1025 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1026 #ifdef MODEST_TOOLKIT_HILDON2
1027 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1030 &anorm_pixbuf_close);
1032 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1034 &normal_pixbuf_open,
1035 &normal_pixbuf_close);
1041 case TNY_FOLDER_TYPE_INVALID:
1042 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1045 case TNY_FOLDER_TYPE_ROOT:
1046 if (TNY_IS_ACCOUNT (instance)) {
1048 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1049 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1052 &avirt_pixbuf_close);
1054 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1056 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1057 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1060 &ammc_pixbuf_close);
1062 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1065 &anorm_pixbuf_close);
1070 case TNY_FOLDER_TYPE_INBOX:
1071 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1074 &inbox_pixbuf_close);
1076 case TNY_FOLDER_TYPE_OUTBOX:
1077 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1079 &outbox_pixbuf_open,
1080 &outbox_pixbuf_close);
1082 case TNY_FOLDER_TYPE_JUNK:
1083 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1086 &junk_pixbuf_close);
1088 case TNY_FOLDER_TYPE_SENT:
1089 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1092 &sent_pixbuf_close);
1094 case TNY_FOLDER_TYPE_TRASH:
1095 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1098 &trash_pixbuf_close);
1100 case TNY_FOLDER_TYPE_DRAFTS:
1101 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1104 &draft_pixbuf_close);
1106 case TNY_FOLDER_TYPE_ARCHIVE:
1107 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1112 case TNY_FOLDER_TYPE_NORMAL:
1114 /* Memory card folders could have an special icon */
1115 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1116 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1121 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1123 &normal_pixbuf_open,
1124 &normal_pixbuf_close);
1133 free_pixbufs (ThreePixbufs *pixbufs)
1135 if (pixbufs->pixbuf)
1136 g_object_unref (pixbufs->pixbuf);
1137 if (pixbufs->pixbuf_open)
1138 g_object_unref (pixbufs->pixbuf_open);
1139 if (pixbufs->pixbuf_close)
1140 g_object_unref (pixbufs->pixbuf_close);
1141 g_slice_free (ThreePixbufs, pixbufs);
1145 icon_cell_data (GtkTreeViewColumn *column,
1146 GtkCellRenderer *renderer,
1147 GtkTreeModel *tree_model,
1151 GObject *rendobj = NULL, *instance = NULL;
1152 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1153 gboolean has_children;
1154 ThreePixbufs *pixbufs;
1155 ModestFolderView *folder_view = (ModestFolderView *) data;
1157 rendobj = (GObject *) renderer;
1159 gtk_tree_model_get (tree_model, iter,
1161 INSTANCE_COLUMN, &instance,
1167 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1168 pixbufs = get_folder_icons (folder_view, type, instance);
1169 g_object_unref (instance);
1172 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1175 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1176 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1179 free_pixbufs (pixbufs);
1183 add_columns (GtkWidget *treeview)
1185 GtkTreeViewColumn *column;
1186 GtkCellRenderer *renderer;
1187 GtkTreeSelection *sel;
1188 ModestFolderViewPrivate *priv;
1190 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1193 column = gtk_tree_view_column_new ();
1195 /* Set icon and text render function */
1196 renderer = gtk_cell_renderer_pixbuf_new();
1197 #ifdef MODEST_TOOLKIT_HILDON2
1198 g_object_set (renderer,
1199 "xpad", MODEST_MARGIN_DEFAULT,
1200 "ypad", MODEST_MARGIN_DEFAULT,
1203 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1204 gtk_tree_view_column_set_cell_data_func(column, renderer,
1205 icon_cell_data, treeview, NULL);
1207 renderer = gtk_cell_renderer_text_new();
1208 g_object_set (renderer,
1209 #ifdef MODEST_TOOLKIT_HILDON2
1210 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1211 "ypad", MODEST_MARGIN_DEFAULT,
1212 "xpad", MODEST_MARGIN_DEFAULT,
1214 "ellipsize", PANGO_ELLIPSIZE_END,
1216 "ellipsize-set", TRUE, NULL);
1217 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1218 gtk_tree_view_column_set_cell_data_func(column, renderer,
1219 text_cell_data, treeview, NULL);
1221 priv->messages_renderer = gtk_cell_renderer_text_new ();
1222 g_object_set (priv->messages_renderer,
1223 #ifdef MODEST_TOOLKIT_HILDON2
1225 "ypad", MODEST_MARGIN_DEFAULT,
1226 "xpad", MODEST_MARGIN_DOUBLE,
1228 "scale", PANGO_SCALE_X_SMALL,
1231 "alignment", PANGO_ALIGN_RIGHT,
1235 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1236 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1237 messages_cell_data, treeview, NULL);
1239 /* Set selection mode */
1240 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1241 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1243 /* Set treeview appearance */
1244 gtk_tree_view_column_set_spacing (column, 2);
1245 gtk_tree_view_column_set_resizable (column, TRUE);
1246 gtk_tree_view_column_set_fixed_width (column, TRUE);
1247 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1248 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1251 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1255 modest_folder_view_init (ModestFolderView *obj)
1257 ModestFolderViewPrivate *priv;
1260 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1262 priv->timer_expander = 0;
1263 priv->account_store = NULL;
1265 priv->do_refresh = TRUE;
1266 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1267 priv->cur_folder_store = NULL;
1268 priv->visible_account_id = NULL;
1269 priv->mailbox = NULL;
1270 priv->folder_to_select = NULL;
1271 priv->reexpand = TRUE;
1272 priv->signal_handlers = 0;
1274 /* Initialize the local account name */
1275 conf = modest_runtime_get_conf();
1276 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1278 /* Init email clipboard */
1279 priv->clipboard = modest_runtime_get_email_clipboard ();
1280 priv->hidding_ids = NULL;
1281 priv->n_selected = 0;
1282 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1283 priv->reselect = FALSE;
1284 priv->show_non_move = TRUE;
1285 priv->list_to_move = NULL;
1286 priv->show_message_count = TRUE;
1288 /* Build treeview */
1289 add_columns (GTK_WIDGET (obj));
1291 /* Setup drag and drop */
1292 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1294 /* Connect signals */
1295 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1296 G_OBJECT (obj), "key-press-event",
1297 G_CALLBACK (on_key_pressed), NULL);
1299 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1300 (GObject*) modest_runtime_get_account_mgr (),
1301 "display_name_changed",
1302 G_CALLBACK (on_display_name_changed),
1306 * Track changes in the local account name (in the device it
1307 * will be the device name)
1309 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1312 G_CALLBACK(on_configuration_key_changed),
1315 priv->active_color = NULL;
1318 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1319 G_OBJECT (obj), "notify::style",
1320 G_CALLBACK (on_notify_style), (gpointer) obj);
1324 tny_account_store_view_init (gpointer g, gpointer iface_data)
1326 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1328 klass->set_account_store = modest_folder_view_set_account_store;
1332 modest_folder_view_dispose (GObject *obj)
1334 static gboolean disposed = FALSE;
1335 ModestFolderViewPrivate *priv;
1340 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1342 #ifdef MODEST_TOOLKIT_HILDON2
1343 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1346 /* Free external references */
1347 if (priv->account_store) {
1348 g_object_unref (G_OBJECT(priv->account_store));
1349 priv->account_store = NULL;
1353 g_object_unref (G_OBJECT (priv->query));
1357 if (priv->folder_to_select) {
1358 g_object_unref (G_OBJECT(priv->folder_to_select));
1359 priv->folder_to_select = NULL;
1362 if (priv->cur_folder_store) {
1363 g_object_unref (priv->cur_folder_store);
1364 priv->cur_folder_store = NULL;
1367 if (priv->list_to_move) {
1368 g_object_unref (priv->list_to_move);
1369 priv->list_to_move = NULL;
1374 modest_folder_view_finalize (GObject *obj)
1376 ModestFolderViewPrivate *priv;
1378 g_return_if_fail (obj);
1380 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1382 if (priv->active_color) {
1383 gdk_color_free (priv->active_color);
1384 priv->active_color = NULL;
1387 if (priv->timer_expander != 0) {
1388 g_source_remove (priv->timer_expander);
1389 priv->timer_expander = 0;
1392 g_free (priv->local_account_name);
1393 g_free (priv->visible_account_id);
1394 g_free (priv->mailbox);
1396 /* Clear hidding array created by cut operation */
1397 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1399 G_OBJECT_CLASS(parent_class)->finalize (obj);
1404 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1406 ModestFolderViewPrivate *priv;
1409 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1410 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1412 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1413 device = tny_account_store_get_device (account_store);
1415 if (G_UNLIKELY (priv->account_store)) {
1417 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1418 G_OBJECT (priv->account_store),
1419 "account_inserted"))
1420 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1421 G_OBJECT (priv->account_store),
1422 "account_inserted");
1423 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1424 G_OBJECT (priv->account_store),
1426 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1427 G_OBJECT (priv->account_store),
1429 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1430 G_OBJECT (priv->account_store),
1432 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1433 G_OBJECT (priv->account_store),
1435 g_object_unref (G_OBJECT (priv->account_store));
1438 priv->account_store = g_object_ref (G_OBJECT (account_store));
1440 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1441 G_OBJECT(account_store), "account_removed",
1442 G_CALLBACK (on_account_removed), self);
1444 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1445 G_OBJECT(account_store), "account_inserted",
1446 G_CALLBACK (on_account_inserted), self);
1448 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1449 G_OBJECT(account_store), "account_changed",
1450 G_CALLBACK (on_account_changed), self);
1452 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1453 priv->reselect = FALSE;
1454 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1456 g_object_unref (G_OBJECT (device));
1460 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1463 ModestFolderView *self;
1464 GtkTreeModel *model, *filter_model;
1467 self = MODEST_FOLDER_VIEW (user_data);
1469 if (!get_inner_models (self, &filter_model, NULL, &model))
1472 /* Remove outbox from model */
1473 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1474 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1475 g_object_unref (outbox);
1478 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1482 on_account_inserted (TnyAccountStore *account_store,
1483 TnyAccount *account,
1486 ModestFolderViewPrivate *priv;
1487 GtkTreeModel *model, *filter_model;
1489 /* Ignore transport account insertions, we're not showing them
1490 in the folder view */
1491 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1494 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1497 /* If we're adding a new account, and there is no previous
1498 one, we need to select the visible server account */
1499 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1500 !priv->visible_account_id)
1501 modest_widget_memory_restore (modest_runtime_get_conf(),
1502 G_OBJECT (user_data),
1503 MODEST_CONF_FOLDER_VIEW_KEY);
1507 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1508 &filter_model, NULL, &model))
1511 /* Insert the account in the model */
1512 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1514 /* When the model is a list store (plain representation) the
1515 outbox is not a child of any account so we have to manually
1516 delete it because removing the local folders account won't
1517 delete it (because tny_folder_get_account() is not defined
1518 for a merge folder */
1519 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1520 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1521 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1522 (GObject*) account, "outbox-deleted",
1523 G_CALLBACK (on_outbox_deleted_cb),
1527 /* Refilter the model */
1528 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1533 same_account_selected (ModestFolderView *self,
1534 TnyAccount *account)
1536 ModestFolderViewPrivate *priv;
1537 gboolean same_account = FALSE;
1539 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1541 if (priv->cur_folder_store) {
1542 TnyAccount *selected_folder_account = NULL;
1544 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1545 selected_folder_account =
1546 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1548 selected_folder_account =
1549 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1552 if (selected_folder_account == account)
1553 same_account = TRUE;
1555 g_object_unref (selected_folder_account);
1557 return same_account;
1562 * Selects the first inbox or the local account in an idle
1565 on_idle_select_first_inbox_or_local (gpointer user_data)
1567 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1569 gdk_threads_enter ();
1570 modest_folder_view_select_first_inbox_or_local (self);
1571 gdk_threads_leave ();
1577 on_account_changed (TnyAccountStore *account_store,
1578 TnyAccount *tny_account,
1581 ModestFolderView *self;
1582 ModestFolderViewPrivate *priv;
1583 GtkTreeModel *model, *filter_model;
1584 GtkTreeSelection *sel;
1585 gboolean same_account;
1587 /* Ignore transport account insertions, we're not showing them
1588 in the folder view */
1589 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1592 self = MODEST_FOLDER_VIEW (user_data);
1593 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1595 /* Get the inner model */
1596 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1597 &filter_model, NULL, &model))
1600 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1602 /* Invalidate the cur_folder_store only if the selected folder
1603 belongs to the account that is being removed */
1604 same_account = same_account_selected (self, tny_account);
1606 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1607 gtk_tree_selection_unselect_all (sel);
1610 /* Remove the account from the model */
1611 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1613 /* Insert the account in the model */
1614 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1616 /* Refilter the model */
1617 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1619 /* Select the first INBOX if the currently selected folder
1620 belongs to the account that is being deleted */
1621 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1622 g_idle_add (on_idle_select_first_inbox_or_local, self);
1626 on_account_removed (TnyAccountStore *account_store,
1627 TnyAccount *account,
1630 ModestFolderView *self = NULL;
1631 ModestFolderViewPrivate *priv;
1632 GtkTreeModel *model, *filter_model;
1633 GtkTreeSelection *sel = NULL;
1634 gboolean same_account = FALSE;
1636 /* Ignore transport account removals, we're not showing them
1637 in the folder view */
1638 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1641 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1642 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1646 self = MODEST_FOLDER_VIEW (user_data);
1647 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1649 /* Invalidate the cur_folder_store only if the selected folder
1650 belongs to the account that is being removed */
1651 same_account = same_account_selected (self, account);
1653 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1654 gtk_tree_selection_unselect_all (sel);
1657 /* Invalidate row to select only if the folder to select
1658 belongs to the account that is being removed*/
1659 if (priv->folder_to_select) {
1660 TnyAccount *folder_to_select_account = NULL;
1662 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1663 if (folder_to_select_account == account) {
1664 modest_folder_view_disable_next_folder_selection (self);
1665 g_object_unref (priv->folder_to_select);
1666 priv->folder_to_select = NULL;
1668 g_object_unref (folder_to_select_account);
1671 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1672 &filter_model, NULL, &model))
1675 /* Disconnect the signal handler */
1676 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1677 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1678 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject*) account, "outbox-deleted"))
1679 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1680 (GObject *) account,
1684 /* Remove the account from the model */
1685 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1687 /* If the removed account is the currently viewed one then
1688 clear the configuration value. The new visible account will be the default account */
1689 if (priv->visible_account_id &&
1690 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1692 /* Clear the current visible account_id */
1693 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1694 modest_folder_view_set_mailbox (self, NULL);
1696 /* Call the restore method, this will set the new visible account */
1697 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1698 MODEST_CONF_FOLDER_VIEW_KEY);
1701 /* Refilter the model */
1702 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1704 /* Select the first INBOX if the currently selected folder
1705 belongs to the account that is being deleted */
1707 g_idle_add (on_idle_select_first_inbox_or_local, self);
1711 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1713 GtkTreeViewColumn *col;
1715 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1717 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1719 g_printerr ("modest: failed get column for title\n");
1723 gtk_tree_view_column_set_title (col, title);
1724 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1729 modest_folder_view_on_map (ModestFolderView *self,
1730 GdkEventExpose *event,
1733 ModestFolderViewPrivate *priv;
1735 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1737 /* This won't happen often */
1738 if (G_UNLIKELY (priv->reselect)) {
1739 /* Select the first inbox or the local account if not found */
1741 /* TODO: this could cause a lock at startup, so we
1742 comment it for the moment. We know that this will
1743 be a bug, because the INBOX is not selected, but we
1744 need to rewrite some parts of Modest to avoid the
1745 deathlock situation */
1746 /* TODO: check if this is still the case */
1747 priv->reselect = FALSE;
1748 modest_folder_view_select_first_inbox_or_local (self);
1749 /* Notify the display name observers */
1750 g_signal_emit (G_OBJECT(self),
1751 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1755 if (priv->reexpand) {
1756 expand_root_items (self);
1757 priv->reexpand = FALSE;
1764 modest_folder_view_new (TnyFolderStoreQuery *query)
1766 return modest_folder_view_new_full (query, TRUE);
1770 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1773 ModestFolderViewPrivate *priv;
1774 GtkTreeSelection *sel;
1776 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1777 #ifdef MODEST_TOOLKIT_HILDON2
1778 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1781 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1784 priv->query = g_object_ref (query);
1786 priv->do_refresh = do_refresh;
1788 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1789 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1790 (GObject*) sel, "changed",
1791 G_CALLBACK (on_selection_changed), self);
1793 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1794 self, "row-activated",
1795 G_CALLBACK (on_row_activated), self);
1797 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1798 self, "expose-event",
1799 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, *old_tny_model;
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 (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) account,
2285 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2286 (GObject *) account,
2289 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2290 (GObject*) account, "outbox-deleted",
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 (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2317 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) old_tny_model,
2318 "activity-changed"))
2319 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2320 G_OBJECT (old_tny_model),
2321 "activity-changed");
2325 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2326 #ifndef MODEST_TOOLKIT_HILDON2
2327 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2328 G_OBJECT(filter_model), "row-inserted",
2329 (GCallback) on_row_inserted_maybe_select_folder, self);
2332 #ifdef MODEST_TOOLKIT_HILDON2
2333 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2336 G_CALLBACK (on_activity_changed),
2340 g_object_unref (model);
2341 g_object_unref (filter_model);
2342 g_object_unref (sortable);
2344 /* Force a reselection of the INBOX next time the widget is shown */
2345 priv->reselect = TRUE;
2352 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2354 GtkTreeModel *model = NULL;
2355 TnyFolderStore *folder = NULL;
2357 ModestFolderView *tree_view = NULL;
2358 ModestFolderViewPrivate *priv = NULL;
2359 gboolean selected = FALSE;
2361 g_return_if_fail (sel);
2362 g_return_if_fail (user_data);
2364 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2366 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2368 tree_view = MODEST_FOLDER_VIEW (user_data);
2371 gtk_tree_model_get (model, &iter,
2372 INSTANCE_COLUMN, &folder,
2375 /* If the folder is the same do not notify */
2376 if (folder && priv->cur_folder_store == folder) {
2377 g_object_unref (folder);
2382 /* Current folder was unselected */
2383 if (priv->cur_folder_store) {
2384 /* We must do this firstly because a libtinymail-camel
2385 implementation detail. If we issue the signal
2386 before doing the sync_async, then that signal could
2387 cause (and it actually does it) a free of the
2388 summary of the folder (because the main window will
2389 clear the headers view */
2390 #ifndef MODEST_TOOLKIT_HILDON2
2391 if (TNY_IS_FOLDER(priv->cur_folder_store))
2392 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2393 FALSE, NULL, NULL, NULL);
2396 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2397 priv->cur_folder_store, FALSE);
2399 g_object_unref (priv->cur_folder_store);
2400 priv->cur_folder_store = NULL;
2403 /* New current references */
2404 priv->cur_folder_store = folder;
2406 /* New folder has been selected. Do not notify if there is
2407 nothing new selected */
2409 g_signal_emit (G_OBJECT(tree_view),
2410 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2411 0, priv->cur_folder_store, TRUE);
2416 on_row_activated (GtkTreeView *treeview,
2417 GtkTreePath *treepath,
2418 GtkTreeViewColumn *column,
2421 GtkTreeModel *model = NULL;
2422 TnyFolderStore *folder = NULL;
2424 ModestFolderView *self = NULL;
2425 ModestFolderViewPrivate *priv = NULL;
2427 g_return_if_fail (treeview);
2428 g_return_if_fail (user_data);
2430 self = MODEST_FOLDER_VIEW (user_data);
2431 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2433 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2435 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2438 gtk_tree_model_get (model, &iter,
2439 INSTANCE_COLUMN, &folder,
2442 g_signal_emit (G_OBJECT(self),
2443 signals[FOLDER_ACTIVATED_SIGNAL],
2446 #ifdef MODEST_TOOLKIT_HILDON2
2447 HildonUIMode ui_mode;
2448 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2449 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2450 if (priv->cur_folder_store)
2451 g_object_unref (priv->cur_folder_store);
2452 priv->cur_folder_store = g_object_ref (folder);
2456 g_object_unref (folder);
2460 modest_folder_view_get_selected (ModestFolderView *self)
2462 ModestFolderViewPrivate *priv;
2464 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2466 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2467 if (priv->cur_folder_store)
2468 g_object_ref (priv->cur_folder_store);
2470 return priv->cur_folder_store;
2474 get_cmp_rows_type_pos (GObject *folder)
2476 /* Remote accounts -> Local account -> MMC account .*/
2479 if (TNY_IS_ACCOUNT (folder) &&
2480 modest_tny_account_is_virtual_local_folders (
2481 TNY_ACCOUNT (folder))) {
2483 } else if (TNY_IS_ACCOUNT (folder)) {
2484 TnyAccount *account = TNY_ACCOUNT (folder);
2485 const gchar *account_id = tny_account_get_id (account);
2486 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2492 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2493 return -1; /* Should never happen */
2498 inbox_is_special (TnyFolderStore *folder_store)
2500 gboolean is_special = TRUE;
2502 if (TNY_IS_FOLDER (folder_store)) {
2506 gchar *last_inbox_bar;
2508 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2509 downcase = g_utf8_strdown (id, -1);
2510 last_bar = g_strrstr (downcase, "/");
2512 last_inbox_bar = g_strrstr (downcase, "inbox/");
2513 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2524 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2526 TnyAccount *account;
2527 gboolean is_special;
2528 /* Inbox, Outbox, Drafts, Sent, User */
2531 if (!TNY_IS_FOLDER (folder_store))
2534 case TNY_FOLDER_TYPE_INBOX:
2536 account = tny_folder_get_account (folder_store);
2537 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2539 /* In inbox case we need to know if the inbox is really the top
2540 * inbox of the account, or if it's a submailbox inbox. To do
2541 * this we'll apply an heuristic rule: Find last "/" and check
2542 * if it's preceeded by another Inbox */
2543 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2544 g_object_unref (account);
2545 return is_special?0:4;
2548 case TNY_FOLDER_TYPE_OUTBOX:
2549 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2551 case TNY_FOLDER_TYPE_DRAFTS:
2553 account = tny_folder_get_account (folder_store);
2554 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2555 g_object_unref (account);
2556 return is_special?1:4;
2559 case TNY_FOLDER_TYPE_SENT:
2561 account = tny_folder_get_account (folder_store);
2562 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2563 g_object_unref (account);
2564 return is_special?3:4;
2573 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2575 const gchar *a1_name, *a2_name;
2577 a1_name = tny_account_get_name (a1);
2578 a2_name = tny_account_get_name (a2);
2580 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2584 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2586 TnyAccount *a1 = NULL, *a2 = NULL;
2589 if (TNY_IS_ACCOUNT (s1)) {
2590 a1 = TNY_ACCOUNT (g_object_ref (s1));
2591 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2592 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2595 if (TNY_IS_ACCOUNT (s2)) {
2596 a2 = TNY_ACCOUNT (g_object_ref (s2));
2597 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2598 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2615 /* First we sort with the type of account */
2616 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2620 cmp = compare_account_names (a1, a2);
2624 g_object_unref (a1);
2626 g_object_unref (a2);
2632 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2634 gint is_account1, is_account2;
2636 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2637 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2639 return is_account2 - is_account1;
2643 * This function orders the mail accounts according to these rules:
2644 * 1st - remote accounts
2645 * 2nd - local account
2649 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2653 gchar *name1 = NULL;
2654 gchar *name2 = NULL;
2655 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2656 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2657 GObject *folder1 = NULL;
2658 GObject *folder2 = NULL;
2660 gtk_tree_model_get (tree_model, iter1,
2661 NAME_COLUMN, &name1,
2663 INSTANCE_COLUMN, &folder1,
2665 gtk_tree_model_get (tree_model, iter2,
2666 NAME_COLUMN, &name2,
2667 TYPE_COLUMN, &type2,
2668 INSTANCE_COLUMN, &folder2,
2671 /* Return if we get no folder. This could happen when folder
2672 operations are happening. The model is updated after the
2673 folder copy/move actually occurs, so there could be
2674 situations where the model to be drawn is not correct */
2675 if (!folder1 || !folder2)
2678 /* Sort by type. First the special folders, then the archives */
2679 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2683 /* Now we sort using the account of each folder */
2684 if (TNY_IS_FOLDER_STORE (folder1) &&
2685 TNY_IS_FOLDER_STORE (folder2)) {
2686 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2690 /* Each group is preceeded by its account */
2691 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2696 /* Pure sort by name */
2697 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2700 g_object_unref(G_OBJECT(folder1));
2702 g_object_unref(G_OBJECT(folder2));
2710 /*****************************************************************************/
2711 /* DRAG and DROP stuff */
2712 /*****************************************************************************/
2714 * This function fills the #GtkSelectionData with the row and the
2715 * model that has been dragged. It's called when this widget is a
2716 * source for dnd after the event drop happened
2719 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2720 guint info, guint time, gpointer data)
2722 GtkTreeSelection *selection;
2723 GtkTreeModel *model;
2725 GtkTreePath *source_row;
2727 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2728 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2730 source_row = gtk_tree_model_get_path (model, &iter);
2731 gtk_tree_set_row_drag_data (selection_data,
2735 gtk_tree_path_free (source_row);
2739 typedef struct _DndHelper {
2740 ModestFolderView *folder_view;
2741 gboolean delete_source;
2742 GtkTreePath *source_row;
2746 dnd_helper_destroyer (DndHelper *helper)
2748 /* Free the helper */
2749 gtk_tree_path_free (helper->source_row);
2750 g_slice_free (DndHelper, helper);
2754 xfer_folder_cb (ModestMailOperation *mail_op,
2755 TnyFolder *new_folder,
2759 /* Select the folder */
2760 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2766 /* get the folder for the row the treepath refers to. */
2767 /* folder must be unref'd */
2768 static TnyFolderStore *
2769 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2772 TnyFolderStore *folder = NULL;
2774 if (gtk_tree_model_get_iter (model,&iter, path))
2775 gtk_tree_model_get (model, &iter,
2776 INSTANCE_COLUMN, &folder,
2783 * This function is used by drag_data_received_cb to manage drag and
2784 * drop of a header, i.e, and drag from the header view to the folder
2788 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2789 GtkTreeModel *dest_model,
2790 GtkTreePath *dest_row,
2791 GtkSelectionData *selection_data)
2793 TnyList *headers = NULL;
2794 TnyFolder *folder = NULL, *src_folder = NULL;
2795 TnyFolderType folder_type;
2796 GtkTreeIter source_iter, dest_iter;
2797 ModestWindowMgr *mgr = NULL;
2798 ModestWindow *main_win = NULL;
2799 gchar **uris, **tmp;
2801 /* Build the list of headers */
2802 mgr = modest_runtime_get_window_mgr ();
2803 headers = tny_simple_list_new ();
2804 uris = modest_dnd_selection_data_get_paths (selection_data);
2807 while (*tmp != NULL) {
2810 gboolean first = TRUE;
2813 path = gtk_tree_path_new_from_string (*tmp);
2814 gtk_tree_model_get_iter (source_model, &source_iter, path);
2815 gtk_tree_model_get (source_model, &source_iter,
2816 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2819 /* Do not enable d&d of headers already opened */
2820 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2821 tny_list_append (headers, G_OBJECT (header));
2823 if (G_UNLIKELY (first)) {
2824 src_folder = tny_header_get_folder (header);
2828 /* Free and go on */
2829 gtk_tree_path_free (path);
2830 g_object_unref (header);
2835 /* This could happen ig we perform a d&d very quickly over the
2836 same row that row could dissapear because message is
2838 if (!TNY_IS_FOLDER (src_folder))
2841 /* Get the target folder */
2842 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2843 gtk_tree_model_get (dest_model, &dest_iter,
2847 if (!folder || !TNY_IS_FOLDER(folder)) {
2848 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2852 folder_type = modest_tny_folder_guess_folder_type (folder);
2853 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2854 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2855 goto cleanup; /* cannot move messages there */
2858 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2859 /* g_warning ("folder not writable"); */
2860 goto cleanup; /* verboten! */
2863 /* Ask for confirmation to move */
2864 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2866 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2870 /* Transfer messages */
2871 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2876 if (G_IS_OBJECT (src_folder))
2877 g_object_unref (src_folder);
2878 if (G_IS_OBJECT(folder))
2879 g_object_unref (G_OBJECT (folder));
2880 if (G_IS_OBJECT(headers))
2881 g_object_unref (headers);
2885 TnyFolderStore *src_folder;
2886 TnyFolderStore *dst_folder;
2887 ModestFolderView *folder_view;
2892 dnd_folder_info_destroyer (DndFolderInfo *info)
2894 if (info->src_folder)
2895 g_object_unref (info->src_folder);
2896 if (info->dst_folder)
2897 g_object_unref (info->dst_folder);
2898 g_slice_free (DndFolderInfo, info);
2902 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2903 GtkWindow *parent_window,
2904 TnyAccount *account)
2907 modest_ui_actions_on_account_connection_error (parent_window, account);
2909 /* Free the helper & info */
2910 dnd_helper_destroyer (info->helper);
2911 dnd_folder_info_destroyer (info);
2915 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2917 GtkWindow *parent_window,
2918 TnyAccount *account,
2921 DndFolderInfo *info = NULL;
2922 ModestMailOperation *mail_op;
2924 info = (DndFolderInfo *) user_data;
2926 if (err || canceled) {
2927 dnd_on_connection_failed_destroyer (info, parent_window, account);
2931 /* Do the mail operation */
2932 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2933 modest_ui_actions_move_folder_error_handler,
2934 info->src_folder, NULL);
2936 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2939 /* Transfer the folder */
2940 modest_mail_operation_xfer_folder (mail_op,
2941 TNY_FOLDER (info->src_folder),
2943 info->helper->delete_source,
2945 info->helper->folder_view);
2948 g_object_unref (G_OBJECT (mail_op));
2949 dnd_helper_destroyer (info->helper);
2950 dnd_folder_info_destroyer (info);
2955 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2957 GtkWindow *parent_window,
2958 TnyAccount *account,
2961 DndFolderInfo *info = NULL;
2963 info = (DndFolderInfo *) user_data;
2965 if (err || canceled) {
2966 dnd_on_connection_failed_destroyer (info, parent_window, account);
2970 /* Connect to source folder and perform the copy/move */
2971 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2973 drag_and_drop_from_folder_view_src_folder_performer,
2978 * This function is used by drag_data_received_cb to manage drag and
2979 * drop of a folder, i.e, and drag from the folder view to the same
2983 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2984 GtkTreeModel *dest_model,
2985 GtkTreePath *dest_row,
2986 GtkSelectionData *selection_data,
2989 GtkTreeIter dest_iter, iter;
2990 TnyFolderStore *dest_folder = NULL;
2991 TnyFolderStore *folder = NULL;
2992 gboolean forbidden = FALSE;
2994 DndFolderInfo *info = NULL;
2996 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2998 g_warning ("%s: BUG: no main window", __FUNCTION__);
2999 dnd_helper_destroyer (helper);
3004 /* check the folder rules for the destination */
3005 folder = tree_path_to_folder (dest_model, dest_row);
3006 if (TNY_IS_FOLDER(folder)) {
3007 ModestTnyFolderRules rules =
3008 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3009 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3010 } else if (TNY_IS_FOLDER_STORE(folder)) {
3011 /* enable local root as destination for folders */
3012 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3013 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3016 g_object_unref (folder);
3019 /* check the folder rules for the source */
3020 folder = tree_path_to_folder (source_model, helper->source_row);
3021 if (TNY_IS_FOLDER(folder)) {
3022 ModestTnyFolderRules rules =
3023 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3024 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3027 g_object_unref (folder);
3031 /* Check if the drag is possible */
3032 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3034 modest_platform_run_information_dialog ((GtkWindow *) win,
3035 _("mail_in_ui_folder_move_target_error"),
3037 /* Restore the previous selection */
3038 folder = tree_path_to_folder (source_model, helper->source_row);
3040 if (TNY_IS_FOLDER (folder))
3041 modest_folder_view_select_folder (helper->folder_view,
3042 TNY_FOLDER (folder), FALSE);
3043 g_object_unref (folder);
3045 dnd_helper_destroyer (helper);
3050 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3051 gtk_tree_model_get (dest_model, &dest_iter,
3054 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3055 gtk_tree_model_get (source_model, &iter,
3059 /* Create the info for the performer */
3060 info = g_slice_new0 (DndFolderInfo);
3061 info->src_folder = g_object_ref (folder);
3062 info->dst_folder = g_object_ref (dest_folder);
3063 info->helper = helper;
3065 /* Connect to the destination folder and perform the copy/move */
3066 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3068 drag_and_drop_from_folder_view_dst_folder_performer,
3072 g_object_unref (dest_folder);
3073 g_object_unref (folder);
3077 * This function receives the data set by the "drag-data-get" signal
3078 * handler. This information comes within the #GtkSelectionData. This
3079 * function will manage both the drags of folders of the treeview and
3080 * drags of headers of the header view widget.
3083 on_drag_data_received (GtkWidget *widget,
3084 GdkDragContext *context,
3087 GtkSelectionData *selection_data,
3092 GtkWidget *source_widget;
3093 GtkTreeModel *dest_model, *source_model;
3094 GtkTreePath *source_row, *dest_row;
3095 GtkTreeViewDropPosition pos;
3096 gboolean delete_source = FALSE;
3097 gboolean success = FALSE;
3099 /* Do not allow further process */
3100 g_signal_stop_emission_by_name (widget, "drag-data-received");
3101 source_widget = gtk_drag_get_source_widget (context);
3103 /* Get the action */
3104 if (context->action == GDK_ACTION_MOVE) {
3105 delete_source = TRUE;
3107 /* Notify that there is no folder selected. We need to
3108 do this in order to update the headers view (and
3109 its monitors, because when moving, the old folder
3110 won't longer exist. We can not wait for the end of
3111 the operation, because the operation won't start if
3112 the folder is in use */
3113 if (source_widget == widget) {
3114 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3115 gtk_tree_selection_unselect_all (sel);
3119 /* Check if the get_data failed */
3120 if (selection_data == NULL || selection_data->length < 0)
3123 /* Select the destination model */
3124 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3126 /* Get the path to the destination row. Can not call
3127 gtk_tree_view_get_drag_dest_row() because the source row
3128 is not selected anymore */
3129 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3132 /* Only allow drops IN other rows */
3134 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3135 pos == GTK_TREE_VIEW_DROP_AFTER)
3139 /* Drags from the header view */
3140 if (source_widget != widget) {
3141 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3143 drag_and_drop_from_header_view (source_model,
3148 DndHelper *helper = NULL;
3150 /* Get the source model and row */
3151 gtk_tree_get_row_drag_data (selection_data,
3155 /* Create the helper */
3156 helper = g_slice_new0 (DndHelper);
3157 helper->delete_source = delete_source;
3158 helper->source_row = gtk_tree_path_copy (source_row);
3159 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3161 drag_and_drop_from_folder_view (source_model,
3167 gtk_tree_path_free (source_row);
3171 gtk_tree_path_free (dest_row);
3174 /* Finish the drag and drop */
3175 gtk_drag_finish (context, success, FALSE, time);
3179 * We define a "drag-drop" signal handler because we do not want to
3180 * use the default one, because the default one always calls
3181 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3182 * signal handler, because there we have all the information available
3183 * to know if the dnd was a success or not.
3186 drag_drop_cb (GtkWidget *widget,
3187 GdkDragContext *context,
3195 if (!context->targets)
3198 /* Check if we're dragging a folder row */
3199 target = gtk_drag_dest_find_target (widget, context, NULL);
3201 /* Request the data from the source. */
3202 gtk_drag_get_data(widget, context, target, time);
3208 * This function expands a node of a tree view if it's not expanded
3209 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3210 * does that, so that's why they're here.
3213 expand_row_timeout (gpointer data)
3215 GtkTreeView *tree_view = data;
3216 GtkTreePath *dest_path = NULL;
3217 GtkTreeViewDropPosition pos;
3218 gboolean result = FALSE;
3220 gdk_threads_enter ();
3222 gtk_tree_view_get_drag_dest_row (tree_view,
3227 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3228 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3229 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3230 gtk_tree_path_free (dest_path);
3234 gtk_tree_path_free (dest_path);
3239 gdk_threads_leave ();
3245 * This function is called whenever the pointer is moved over a widget
3246 * while dragging some data. It installs a timeout that will expand a
3247 * node of the treeview if not expanded yet. This function also calls
3248 * gdk_drag_status in order to set the suggested action that will be
3249 * used by the "drag-data-received" signal handler to know if we
3250 * should do a move or just a copy of the data.
3253 on_drag_motion (GtkWidget *widget,
3254 GdkDragContext *context,
3260 GtkTreeViewDropPosition pos;
3261 GtkTreePath *dest_row;
3262 GtkTreeModel *dest_model;
3263 ModestFolderViewPrivate *priv;
3264 GdkDragAction suggested_action;
3265 gboolean valid_location = FALSE;
3266 TnyFolderStore *folder = NULL;
3268 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3270 if (priv->timer_expander != 0) {
3271 g_source_remove (priv->timer_expander);
3272 priv->timer_expander = 0;
3275 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3280 /* Do not allow drops between folders */
3282 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3283 pos == GTK_TREE_VIEW_DROP_AFTER) {
3284 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3285 gdk_drag_status(context, 0, time);
3286 valid_location = FALSE;
3289 valid_location = TRUE;
3292 /* Check that the destination folder is writable */
3293 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3294 folder = tree_path_to_folder (dest_model, dest_row);
3295 if (folder && TNY_IS_FOLDER (folder)) {
3296 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3298 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3299 valid_location = FALSE;
3304 /* Expand the selected row after 1/2 second */
3305 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3306 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3308 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3310 /* Select the desired action. By default we pick MOVE */
3311 suggested_action = GDK_ACTION_MOVE;
3313 if (context->actions == GDK_ACTION_COPY)
3314 gdk_drag_status(context, GDK_ACTION_COPY, time);
3315 else if (context->actions == GDK_ACTION_MOVE)
3316 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3317 else if (context->actions & suggested_action)
3318 gdk_drag_status(context, suggested_action, time);
3320 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3324 g_object_unref (folder);
3326 gtk_tree_path_free (dest_row);
3328 g_signal_stop_emission_by_name (widget, "drag-motion");
3330 return valid_location;
3334 * This function sets the treeview as a source and a target for dnd
3335 * events. It also connects all the requirede signals.
3338 setup_drag_and_drop (GtkTreeView *self)
3340 /* Set up the folder view as a dnd destination. Set only the
3341 highlight flag, otherwise gtk will have a different
3343 #ifdef MODEST_TOOLKIT_HILDON2
3346 ModestFolderViewPrivate *priv;
3348 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3350 gtk_drag_dest_set (GTK_WIDGET (self),
3351 GTK_DEST_DEFAULT_HIGHLIGHT,
3352 folder_view_drag_types,
3353 G_N_ELEMENTS (folder_view_drag_types),
3354 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3356 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3357 G_OBJECT (self), "drag_data_received",
3358 G_CALLBACK (on_drag_data_received), NULL);
3361 /* Set up the treeview as a dnd source */
3362 gtk_drag_source_set (GTK_WIDGET (self),
3364 folder_view_drag_types,
3365 G_N_ELEMENTS (folder_view_drag_types),
3366 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3368 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3369 G_OBJECT (self), "drag_motion",
3370 G_CALLBACK (on_drag_motion), NULL);
3372 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3373 G_OBJECT (self), "drag_data_get",
3374 G_CALLBACK (on_drag_data_get), NULL);
3376 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3377 G_OBJECT (self), "drag_drop",
3378 G_CALLBACK (drag_drop_cb), NULL);
3382 * This function manages the navigation through the folders using the
3383 * keyboard or the hardware keys in the device
3386 on_key_pressed (GtkWidget *self,
3390 GtkTreeSelection *selection;
3392 GtkTreeModel *model;
3393 gboolean retval = FALSE;
3395 /* Up and Down are automatically managed by the treeview */
3396 if (event->keyval == GDK_Return) {
3397 /* Expand/Collapse the selected row */
3398 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3399 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3402 path = gtk_tree_model_get_path (model, &iter);
3404 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3405 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3407 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3408 gtk_tree_path_free (path);
3410 /* No further processing */
3418 * We listen to the changes in the local folder account name key,
3419 * because we want to show the right name in the view. The local
3420 * folder account name corresponds to the device name in the Maemo
3421 * version. We do this because we do not want to query gconf on each
3422 * tree view refresh. It's better to cache it and change whenever
3426 on_configuration_key_changed (ModestConf* conf,
3428 ModestConfEvent event,
3429 ModestConfNotificationId id,
3430 ModestFolderView *self)
3432 ModestFolderViewPrivate *priv;
3435 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3436 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3438 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3439 g_free (priv->local_account_name);
3441 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3442 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3444 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3445 MODEST_CONF_DEVICE_NAME, NULL);
3447 /* Force a redraw */
3448 #if GTK_CHECK_VERSION(2, 8, 0)
3449 GtkTreeViewColumn * tree_column;
3451 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3453 gtk_tree_view_column_queue_resize (tree_column);
3455 gtk_widget_queue_draw (GTK_WIDGET (self));
3461 modest_folder_view_set_style (ModestFolderView *self,
3462 ModestFolderViewStyle style)
3464 ModestFolderViewPrivate *priv;
3466 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3467 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3468 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3470 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3473 priv->style = style;
3477 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3478 const gchar *account_id)
3480 ModestFolderViewPrivate *priv;
3481 GtkTreeModel *model;
3483 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3485 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3487 /* This will be used by the filter_row callback,
3488 * to decided which rows to show: */
3489 if (priv->visible_account_id) {
3490 g_free (priv->visible_account_id);
3491 priv->visible_account_id = NULL;
3494 priv->visible_account_id = g_strdup (account_id);
3497 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3498 if (GTK_IS_TREE_MODEL_FILTER (model))
3499 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3501 /* Save settings to gconf */
3502 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3503 MODEST_CONF_FOLDER_VIEW_KEY);
3505 /* Notify observers */
3506 g_signal_emit (G_OBJECT(self),
3507 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3512 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3514 ModestFolderViewPrivate *priv;
3516 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3518 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3520 return (const gchar *) priv->visible_account_id;
3524 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3528 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3530 gtk_tree_model_get (model, iter,
3534 gboolean result = FALSE;
3535 if (type == TNY_FOLDER_TYPE_INBOX) {
3539 *inbox_iter = *iter;
3543 if (gtk_tree_model_iter_children (model, &child, iter)) {
3544 if (find_inbox_iter (model, &child, inbox_iter))
3548 } while (gtk_tree_model_iter_next (model, iter));
3557 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3559 #ifndef MODEST_TOOLKIT_HILDON2
3560 GtkTreeModel *model;
3561 GtkTreeIter iter, inbox_iter;
3562 GtkTreeSelection *sel;
3563 GtkTreePath *path = NULL;
3565 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3567 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3571 expand_root_items (self);
3572 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3574 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3575 g_warning ("%s: model is empty", __FUNCTION__);
3579 if (find_inbox_iter (model, &iter, &inbox_iter))
3580 path = gtk_tree_model_get_path (model, &inbox_iter);
3582 path = gtk_tree_path_new_first ();
3584 /* Select the row and free */
3585 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3586 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3587 gtk_tree_path_free (path);
3590 gtk_widget_grab_focus (GTK_WIDGET(self));
3597 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3602 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3603 TnyFolder* a_folder;
3606 gtk_tree_model_get (model, iter,
3607 INSTANCE_COLUMN, &a_folder,
3613 if (folder == a_folder) {
3614 g_object_unref (a_folder);
3615 *folder_iter = *iter;
3618 g_object_unref (a_folder);
3620 if (gtk_tree_model_iter_children (model, &child, iter)) {
3621 if (find_folder_iter (model, &child, folder_iter, folder))
3625 } while (gtk_tree_model_iter_next (model, iter));
3630 #ifndef MODEST_TOOLKIT_HILDON2
3632 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3635 ModestFolderView *self)
3637 ModestFolderViewPrivate *priv = NULL;
3638 GtkTreeSelection *sel;
3639 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3640 GObject *instance = NULL;
3642 if (!MODEST_IS_FOLDER_VIEW(self))
3645 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3647 priv->reexpand = TRUE;
3649 gtk_tree_model_get (tree_model, iter,
3651 INSTANCE_COLUMN, &instance,
3657 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3658 priv->folder_to_select = g_object_ref (instance);
3660 g_object_unref (instance);
3662 if (priv->folder_to_select) {
3664 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3667 path = gtk_tree_model_get_path (tree_model, iter);
3668 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3670 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3672 gtk_tree_selection_select_iter (sel, iter);
3673 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3675 gtk_tree_path_free (path);
3679 modest_folder_view_disable_next_folder_selection (self);
3681 /* Refilter the model */
3682 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3688 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3690 ModestFolderViewPrivate *priv;
3692 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3694 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3696 if (priv->folder_to_select)
3697 g_object_unref(priv->folder_to_select);
3699 priv->folder_to_select = NULL;
3703 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3704 gboolean after_change)
3706 GtkTreeModel *model;
3707 GtkTreeIter iter, folder_iter;
3708 GtkTreeSelection *sel;
3709 ModestFolderViewPrivate *priv = NULL;
3711 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3712 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3714 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3717 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3718 gtk_tree_selection_unselect_all (sel);
3720 if (priv->folder_to_select)
3721 g_object_unref(priv->folder_to_select);
3722 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3726 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3731 /* Refilter the model, before selecting the folder */
3732 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3734 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3735 g_warning ("%s: model is empty", __FUNCTION__);
3739 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3742 path = gtk_tree_model_get_path (model, &folder_iter);
3743 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3745 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3746 gtk_tree_selection_select_iter (sel, &folder_iter);
3747 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3749 gtk_tree_path_free (path);
3757 modest_folder_view_copy_selection (ModestFolderView *self)
3759 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3761 /* Copy selection */
3762 _clipboard_set_selected_data (self, FALSE);
3766 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3768 ModestFolderViewPrivate *priv = NULL;
3769 GtkTreeModel *model = NULL;
3770 const gchar **hidding = NULL;
3771 guint i, n_selected;
3773 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3774 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3776 /* Copy selection */
3777 if (!_clipboard_set_selected_data (folder_view, TRUE))
3780 /* Get hidding ids */
3781 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3783 /* Clear hidding array created by previous cut operation */
3784 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3786 /* Copy hidding array */
3787 priv->n_selected = n_selected;
3788 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3789 for (i=0; i < n_selected; i++)
3790 priv->hidding_ids[i] = g_strdup(hidding[i]);
3792 /* Hide cut folders */
3793 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3794 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3798 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3799 ModestFolderView *folder_view_dst)
3801 GtkTreeModel *filter_model = NULL;
3802 GtkTreeModel *model = NULL;
3803 GtkTreeModel *new_filter_model = NULL;
3804 GtkTreeModel *old_tny_model = NULL;
3805 GtkTreeModel *new_tny_model = NULL;
3806 ModestFolderViewPrivate *dst_priv;
3808 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3809 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3811 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3812 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3813 new_tny_model = NULL;
3816 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3817 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3818 G_OBJECT (old_tny_model),
3819 "activity-changed");
3821 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3822 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3824 /* Build new filter model */
3825 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3826 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3833 /* Set copied model */
3834 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3835 #ifndef MODEST_TOOLKIT_HILDON2
3836 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3837 G_OBJECT(new_filter_model), "row-inserted",
3838 (GCallback) on_row_inserted_maybe_select_folder,
3841 #ifdef MODEST_TOOLKIT_HILDON2
3842 if (new_tny_model) {
3843 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3844 G_OBJECT (new_tny_model),
3846 G_CALLBACK (on_activity_changed),
3852 g_object_unref (new_filter_model);
3856 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3859 GtkTreeModel *model = NULL;
3860 ModestFolderViewPrivate* priv;
3862 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3864 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3865 priv->show_non_move = show;
3866 /* modest_folder_view_update_model(folder_view, */
3867 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3869 /* Hide special folders */
3870 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3871 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3872 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3877 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3880 ModestFolderViewPrivate* priv;
3882 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3884 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3885 priv->show_message_count = show;
3887 g_object_set (G_OBJECT (priv->messages_renderer),
3888 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3892 /* Returns FALSE if it did not selected anything */
3894 _clipboard_set_selected_data (ModestFolderView *folder_view,
3897 ModestFolderViewPrivate *priv = NULL;
3898 TnyFolderStore *folder = NULL;
3899 gboolean retval = FALSE;
3901 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3902 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3904 /* Set selected data on clipboard */
3905 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3906 folder = modest_folder_view_get_selected (folder_view);
3908 /* Do not allow to select an account */
3909 if (TNY_IS_FOLDER (folder)) {
3910 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3915 g_object_unref (folder);
3921 _clear_hidding_filter (ModestFolderView *folder_view)
3923 ModestFolderViewPrivate *priv;
3926 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3927 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3929 if (priv->hidding_ids != NULL) {
3930 for (i=0; i < priv->n_selected; i++)
3931 g_free (priv->hidding_ids[i]);
3932 g_free(priv->hidding_ids);
3938 on_display_name_changed (ModestAccountMgr *mgr,
3939 const gchar *account,
3942 ModestFolderView *self;
3944 self = MODEST_FOLDER_VIEW (user_data);
3946 /* Force a redraw */
3947 #if GTK_CHECK_VERSION(2, 8, 0)
3948 GtkTreeViewColumn * tree_column;
3950 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3952 gtk_tree_view_column_queue_resize (tree_column);
3954 gtk_widget_queue_draw (GTK_WIDGET (self));
3959 modest_folder_view_set_cell_style (ModestFolderView *self,
3960 ModestFolderViewCellStyle cell_style)
3962 ModestFolderViewPrivate *priv = NULL;
3964 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3965 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3967 priv->cell_style = cell_style;
3969 g_object_set (G_OBJECT (priv->messages_renderer),
3970 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3973 gtk_widget_queue_draw (GTK_WIDGET (self));
3977 update_style (ModestFolderView *self)
3979 ModestFolderViewPrivate *priv;
3980 GdkColor style_color, style_active_color;
3981 PangoAttrList *attr_list;
3983 PangoAttribute *attr;
3985 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3986 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3990 attr_list = pango_attr_list_new ();
3991 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3992 gdk_color_parse ("grey", &style_color);
3994 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3995 pango_attr_list_insert (attr_list, attr);
3998 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4000 "SmallSystemFont", NULL,
4003 attr = pango_attr_font_desc_new (pango_font_description_copy
4004 (style->font_desc));
4005 pango_attr_list_insert (attr_list, attr);
4007 g_object_set (G_OBJECT (priv->messages_renderer),
4008 "foreground-gdk", &style_color,
4009 "foreground-set", TRUE,
4010 "attributes", attr_list,
4012 pango_attr_list_unref (attr_list);
4014 if (priv->active_color)
4015 gdk_color_free (priv->active_color);
4017 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4018 priv->active_color = gdk_color_copy (&style_active_color);
4023 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4025 if (strcmp ("style", spec->name) == 0) {
4026 update_style (MODEST_FOLDER_VIEW (obj));
4027 gtk_widget_queue_draw (GTK_WIDGET (obj));
4032 modest_folder_view_set_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_unset_filter (ModestFolderView *self,
4051 ModestFolderViewFilter filter)
4053 ModestFolderViewPrivate *priv;
4054 GtkTreeModel *filter_model;
4056 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4057 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4059 priv->filter &= ~filter;
4061 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4062 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4063 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4068 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4069 ModestTnyFolderRules rules)
4071 GtkTreeModel *filter_model;
4073 gboolean fulfil = FALSE;
4075 if (!get_inner_models (self, &filter_model, NULL, NULL))
4078 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4082 TnyFolderStore *folder;
4084 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4086 if (TNY_IS_FOLDER (folder)) {
4087 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4088 /* Folder rules are negative: non_writable, non_deletable... */
4089 if (!(folder_rules & rules))
4092 g_object_unref (folder);
4095 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4101 modest_folder_view_set_list_to_move (ModestFolderView *self,
4104 ModestFolderViewPrivate *priv;
4106 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4107 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4109 if (priv->list_to_move)
4110 g_object_unref (priv->list_to_move);
4113 g_object_ref (list);
4115 priv->list_to_move = list;
4119 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4121 ModestFolderViewPrivate *priv;
4123 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4124 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4127 g_free (priv->mailbox);
4129 priv->mailbox = g_strdup (mailbox);
4131 /* Notify observers */
4132 g_signal_emit (G_OBJECT(self),
4133 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4134 priv->visible_account_id);
4138 modest_folder_view_get_mailbox (ModestFolderView *self)
4140 ModestFolderViewPrivate *priv;
4142 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4143 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4145 return (const gchar *) priv->mailbox;
4149 modest_folder_view_get_activity (ModestFolderView *self)
4151 ModestFolderViewPrivate *priv;
4152 GtkTreeModel *inner_model;
4154 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4155 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4156 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4158 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4159 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4165 #ifdef MODEST_TOOLKIT_HILDON2
4167 on_activity_changed (TnyGtkFolderListStore *store,
4169 ModestFolderView *folder_view)
4171 ModestFolderViewPrivate *priv;
4173 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4174 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4175 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4177 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,