1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <tny-account-store-view.h>
34 #include <tny-gtk-account-list-model.h>
35 #include <tny-gtk-folder-list-store.h>
36 #include <tny-gtk-folder-store-tree-model.h>
37 #include <tny-gtk-header-list-model.h>
38 #include <tny-merge-folder.h>
39 #include <tny-folder.h>
40 #include <tny-folder-store-observer.h>
41 #include <tny-account-store.h>
42 #include <tny-account.h>
43 #include <tny-folder.h>
44 #include <tny-camel-folder.h>
45 #include <tny-simple-list.h>
46 #include <tny-camel-account.h>
47 #include <modest-defs.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-folder.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-tny-outbox-account.h>
52 #include <modest-marshal.h>
53 #include <modest-icon-names.h>
54 #include <modest-tny-account-store.h>
55 #include <modest-tny-local-folders-account.h>
56 #include <modest-text-utils.h>
57 #include <modest-runtime.h>
58 #include "modest-folder-view.h"
59 #include <modest-platform.h>
60 #include <modest-widget-memory.h>
61 #include <modest-ui-actions.h>
62 #include "modest-dnd.h"
63 #include "modest-ui-constants.h"
64 #include "widgets/modest-window.h"
65 #include <modest-account-protocol.h>
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
70 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
74 /* Default icon sizes for Fremantle style are different */
75 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
98 static void modest_folder_view_init (ModestFolderView *obj);
99 static void modest_folder_view_finalize (GObject *obj);
101 static void tny_account_store_view_init (gpointer g,
102 gpointer iface_data);
104 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
105 TnyAccountStore *account_store);
107 static void on_selection_changed (GtkTreeSelection *sel,
110 static void on_row_activated (GtkTreeView *treeview,
112 GtkTreeViewColumn *column,
115 static void on_account_removed (TnyAccountStore *self,
119 static void on_account_inserted (TnyAccountStore *self,
123 static void on_account_changed (TnyAccountStore *self,
127 static gint cmp_rows (GtkTreeModel *tree_model,
132 static gboolean filter_row (GtkTreeModel *model,
136 static gboolean on_key_pressed (GtkWidget *self,
140 static void on_configuration_key_changed (ModestConf* conf,
142 ModestConfEvent event,
143 ModestConfNotificationId notification_id,
144 ModestFolderView *self);
147 static void on_drag_data_get (GtkWidget *widget,
148 GdkDragContext *context,
149 GtkSelectionData *selection_data,
154 static void on_drag_data_received (GtkWidget *widget,
155 GdkDragContext *context,
158 GtkSelectionData *selection_data,
163 static gboolean on_drag_motion (GtkWidget *widget,
164 GdkDragContext *context,
170 static void expand_root_items (ModestFolderView *self);
172 static gint expand_row_timeout (gpointer data);
174 static void setup_drag_and_drop (GtkTreeView *self);
176 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
179 static void _clear_hidding_filter (ModestFolderView *folder_view);
181 #ifndef MODEST_TOOLKIT_HILDON2
182 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
185 ModestFolderView *self);
188 static void on_display_name_changed (ModestAccountMgr *self,
189 const gchar *account,
191 static void update_style (ModestFolderView *self);
192 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
193 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
194 static gboolean inbox_is_special (TnyFolderStore *folder_store);
196 static gboolean get_inner_models (ModestFolderView *self,
197 GtkTreeModel **filter_model,
198 GtkTreeModel **sort_model,
199 GtkTreeModel **tny_model);
200 #ifdef MODEST_TOOLKIT_HILDON2
202 on_activity_changed (TnyGtkFolderListStore *store,
204 ModestFolderView *folder_view);
208 FOLDER_SELECTION_CHANGED_SIGNAL,
209 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
210 FOLDER_ACTIVATED_SIGNAL,
211 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
212 ACTIVITY_CHANGED_SIGNAL,
216 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
217 struct _ModestFolderViewPrivate {
218 TnyAccountStore *account_store;
219 TnyFolderStore *cur_folder_store;
221 TnyFolder *folder_to_select; /* folder to select after the next update */
223 gulong changed_signal;
224 gulong account_inserted_signal;
225 gulong account_removed_signal;
226 gulong account_changed_signal;
227 gulong conf_key_signal;
228 gulong display_name_changed_signal;
230 /* not unref this object, its a singlenton */
231 ModestEmailClipboard *clipboard;
233 /* Filter tree model */
236 ModestFolderViewFilter filter;
238 TnyFolderStoreQuery *query;
239 guint timer_expander;
241 gchar *local_account_name;
242 gchar *visible_account_id;
244 ModestFolderViewStyle style;
245 ModestFolderViewCellStyle cell_style;
246 gboolean show_message_count;
248 gboolean reselect; /* we use this to force a reselection of the INBOX */
249 gboolean show_non_move;
250 TnyList *list_to_move;
251 gboolean reexpand; /* next time we expose, we'll expand all root folders */
253 GtkCellRenderer *messages_renderer;
255 gulong outbox_deleted_handler;
257 guint activity_changed_handler;
259 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
260 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
261 MODEST_TYPE_FOLDER_VIEW, \
262 ModestFolderViewPrivate))
264 static GObjectClass *parent_class = NULL;
266 static guint signals[LAST_SIGNAL] = {0};
269 modest_folder_view_get_type (void)
271 static GType my_type = 0;
273 static const GTypeInfo my_info = {
274 sizeof(ModestFolderViewClass),
275 NULL, /* base init */
276 NULL, /* base finalize */
277 (GClassInitFunc) modest_folder_view_class_init,
278 NULL, /* class finalize */
279 NULL, /* class data */
280 sizeof(ModestFolderView),
282 (GInstanceInitFunc) modest_folder_view_init,
286 static const GInterfaceInfo tny_account_store_view_info = {
287 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
288 NULL, /* interface_finalize */
289 NULL /* interface_data */
293 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
297 g_type_add_interface_static (my_type,
298 TNY_TYPE_ACCOUNT_STORE_VIEW,
299 &tny_account_store_view_info);
305 modest_folder_view_class_init (ModestFolderViewClass *klass)
307 GObjectClass *gobject_class;
308 GtkTreeViewClass *treeview_class;
309 gobject_class = (GObjectClass*) klass;
310 treeview_class = (GtkTreeViewClass*) klass;
312 parent_class = g_type_class_peek_parent (klass);
313 gobject_class->finalize = modest_folder_view_finalize;
315 g_type_class_add_private (gobject_class,
316 sizeof(ModestFolderViewPrivate));
318 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
319 g_signal_new ("folder_selection_changed",
320 G_TYPE_FROM_CLASS (gobject_class),
322 G_STRUCT_OFFSET (ModestFolderViewClass,
323 folder_selection_changed),
325 modest_marshal_VOID__POINTER_BOOLEAN,
326 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
329 * This signal is emitted whenever the currently selected
330 * folder display name is computed. Note that the name could
331 * be different to the folder name, because we could append
332 * the unread messages count to the folder name to build the
333 * folder display name
335 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
336 g_signal_new ("folder-display-name-changed",
337 G_TYPE_FROM_CLASS (gobject_class),
339 G_STRUCT_OFFSET (ModestFolderViewClass,
340 folder_display_name_changed),
342 g_cclosure_marshal_VOID__STRING,
343 G_TYPE_NONE, 1, G_TYPE_STRING);
345 signals[FOLDER_ACTIVATED_SIGNAL] =
346 g_signal_new ("folder_activated",
347 G_TYPE_FROM_CLASS (gobject_class),
349 G_STRUCT_OFFSET (ModestFolderViewClass,
352 g_cclosure_marshal_VOID__POINTER,
353 G_TYPE_NONE, 1, G_TYPE_POINTER);
356 * Emitted whenever the visible account changes
358 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
359 g_signal_new ("visible-account-changed",
360 G_TYPE_FROM_CLASS (gobject_class),
362 G_STRUCT_OFFSET (ModestFolderViewClass,
363 visible_account_changed),
365 g_cclosure_marshal_VOID__STRING,
366 G_TYPE_NONE, 1, G_TYPE_STRING);
369 * Emitted when the underlying GtkListStore is updating data
371 signals[ACTIVITY_CHANGED_SIGNAL] =
372 g_signal_new ("activity-changed",
373 G_TYPE_FROM_CLASS (gobject_class),
375 G_STRUCT_OFFSET (ModestFolderViewClass,
378 g_cclosure_marshal_VOID__BOOLEAN,
379 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
381 treeview_class->select_cursor_parent = NULL;
383 #ifdef MODEST_TOOLKIT_HILDON2
384 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
390 /* Retrieves the filter, sort and tny models of the folder view. If
391 any of these does not exist then it returns FALSE */
393 get_inner_models (ModestFolderView *self,
394 GtkTreeModel **filter_model,
395 GtkTreeModel **sort_model,
396 GtkTreeModel **tny_model)
398 GtkTreeModel *s_model, *f_model, *t_model;
400 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
401 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
402 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
406 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
407 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
408 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
412 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
416 *filter_model = f_model;
418 *sort_model = s_model;
420 *tny_model = t_model;
425 /* Simplify checks for NULLs: */
427 strings_are_equal (const gchar *a, const gchar *b)
433 return (strcmp (a, b) == 0);
440 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
442 GObject *instance = NULL;
444 gtk_tree_model_get (model, iter,
445 INSTANCE_COLUMN, &instance,
449 return FALSE; /* keep walking */
451 if (!TNY_IS_ACCOUNT (instance)) {
452 g_object_unref (instance);
453 return FALSE; /* keep walking */
456 /* Check if this is the looked-for account: */
457 TnyAccount *this_account = TNY_ACCOUNT (instance);
458 TnyAccount *account = TNY_ACCOUNT (data);
460 const gchar *this_account_id = tny_account_get_id(this_account);
461 const gchar *account_id = tny_account_get_id(account);
462 g_object_unref (instance);
465 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
466 if (strings_are_equal(this_account_id, account_id)) {
467 /* Tell the model that the data has changed, so that
468 * it calls the cell_data_func callbacks again: */
469 /* TODO: This does not seem to actually cause the new string to be shown: */
470 gtk_tree_model_row_changed (model, path, iter);
472 return TRUE; /* stop walking */
475 return FALSE; /* keep walking */
480 ModestFolderView *self;
481 gchar *previous_name;
482 } GetMmcAccountNameData;
485 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
487 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
489 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
491 if (!strings_are_equal (
492 tny_account_get_name(TNY_ACCOUNT(account)),
493 data->previous_name)) {
495 /* Tell the model that the data has changed, so that
496 * it calls the cell_data_func callbacks again: */
497 ModestFolderView *self = data->self;
498 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
500 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
503 g_free (data->previous_name);
504 g_slice_free (GetMmcAccountNameData, data);
508 convert_parent_folders_to_dots (gchar **item_name)
512 gchar *last_separator;
514 if (item_name == NULL)
517 for (c = *item_name; *c != '\0'; c++) {
518 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
523 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
524 if (last_separator != NULL) {
525 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
532 buffer = g_string_new ("");
533 for (i = 0; i < n_parents; i++) {
534 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
536 buffer = g_string_append (buffer, last_separator);
538 *item_name = g_string_free (buffer, FALSE);
544 format_compact_style (gchar **item_name,
546 const gchar *mailbox,
548 gboolean multiaccount,
549 gboolean *use_markup)
553 TnyFolderType folder_type;
555 if (!TNY_IS_FOLDER (instance))
558 folder = (TnyFolder *) instance;
560 folder_type = tny_folder_get_folder_type (folder);
561 is_special = (get_cmp_pos (folder_type, folder)!= 4);
564 /* Remove mailbox prefix if any */
565 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
566 if (g_str_has_prefix (*item_name, prefix)) {
567 gchar *new_item_name;
569 new_item_name = g_strdup (*item_name + strlen (prefix));
570 if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
571 g_free (new_item_name);
572 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
575 *item_name = new_item_name;
577 } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
580 *item_name = g_strdup (_("mcen_me_folder_inbox"));
583 if (!is_special || multiaccount) {
584 TnyAccount *account = tny_folder_get_account (folder);
585 const gchar *folder_name;
586 gboolean concat_folder_name = FALSE;
589 /* Should not happen */
593 /* convert parent folders to dots */
594 convert_parent_folders_to_dots (item_name);
596 folder_name = tny_folder_get_name (folder);
597 if (g_str_has_suffix (*item_name, folder_name)) {
598 gchar *offset = g_strrstr (*item_name, folder_name);
600 concat_folder_name = TRUE;
603 buffer = g_string_new ("");
605 buffer = g_string_append (buffer, *item_name);
606 if (concat_folder_name) {
607 if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
608 buffer = g_string_append (buffer, folder_name);
609 if (bold) buffer = g_string_append (buffer, "</span>");
612 g_object_unref (account);
614 *item_name = g_string_free (buffer, FALSE);
622 text_cell_data (GtkTreeViewColumn *column,
623 GtkCellRenderer *renderer,
624 GtkTreeModel *tree_model,
628 ModestFolderViewPrivate *priv;
629 GObject *rendobj = (GObject *) renderer;
631 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
632 GObject *instance = NULL;
633 gboolean use_markup = FALSE;
635 gtk_tree_model_get (tree_model, iter,
638 INSTANCE_COLUMN, &instance,
640 if (!fname || !instance)
643 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
644 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
646 gchar *item_name = NULL;
647 gint item_weight = 400;
649 if (type != TNY_FOLDER_TYPE_ROOT) {
653 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
654 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
655 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
656 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
658 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
661 /* Sometimes an special folder is reported by the server as
662 NORMAL, like some versions of Dovecot */
663 if (type == TNY_FOLDER_TYPE_NORMAL ||
664 type == TNY_FOLDER_TYPE_UNKNOWN) {
665 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
669 /* note: we cannot reliably get the counts from the
670 * tree model, we need to use explicit calls on
671 * tny_folder for some reason. Select the number to
672 * show: the unread or unsent messages. in case of
673 * outbox/drafts, show all */
674 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
675 (type == TNY_FOLDER_TYPE_OUTBOX) ||
676 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
677 number = tny_folder_get_all_count (TNY_FOLDER(instance));
680 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
684 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
685 item_name = g_strdup (fname);
692 /* Use bold font style if there are unread or unset messages */
694 if (priv->show_message_count) {
695 item_name = g_strdup_printf ("%s (%d)", fname, number);
697 item_name = g_strdup (fname);
701 item_name = g_strdup (fname);
706 } else if (TNY_IS_ACCOUNT (instance)) {
707 /* If it's a server account */
708 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
709 item_name = g_strdup (priv->local_account_name);
711 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
712 /* fname is only correct when the items are first
713 * added to the model, not when the account is
714 * changed later, so get the name from the account
716 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
719 item_name = g_strdup (fname);
725 item_name = g_strdup ("unknown");
727 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
728 gboolean multiaccount;
730 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
731 /* Convert item_name to markup */
732 format_compact_style (&item_name, instance, priv->mailbox,
734 multiaccount, &use_markup);
737 if (item_name && item_weight) {
738 /* Set the name in the treeview cell: */
740 g_object_set (rendobj, "markup", item_name, NULL);
742 g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
744 /* Notify display name observers */
745 /* TODO: What listens for this signal, and how can it use only the new name? */
746 if (((GObject *) priv->cur_folder_store) == instance) {
747 g_signal_emit (G_OBJECT(self),
748 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
755 /* If it is a Memory card account, make sure that we have the correct name.
756 * This function will be trigerred again when the name has been retrieved: */
757 if (TNY_IS_STORE_ACCOUNT (instance) &&
758 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
760 /* Get the account name asynchronously: */
761 GetMmcAccountNameData *callback_data =
762 g_slice_new0(GetMmcAccountNameData);
763 callback_data->self = self;
765 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
767 callback_data->previous_name = g_strdup (name);
769 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
770 on_get_mmc_account_name, callback_data);
774 g_object_unref (G_OBJECT (instance));
780 messages_cell_data (GtkTreeViewColumn *column,
781 GtkCellRenderer *renderer,
782 GtkTreeModel *tree_model,
786 ModestFolderView *self;
787 ModestFolderViewPrivate *priv;
788 GObject *rendobj = (GObject *) renderer;
789 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
790 GObject *instance = NULL;
791 gchar *item_name = NULL;
793 gtk_tree_model_get (tree_model, iter,
795 INSTANCE_COLUMN, &instance,
800 self = MODEST_FOLDER_VIEW (data);
801 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
804 if (type != TNY_FOLDER_TYPE_ROOT) {
808 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
809 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
810 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
812 /* Sometimes an special folder is reported by the server as
813 NORMAL, like some versions of Dovecot */
814 if (type == TNY_FOLDER_TYPE_NORMAL ||
815 type == TNY_FOLDER_TYPE_UNKNOWN) {
816 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
820 /* note: we cannot reliably get the counts from the tree model, we need
821 * to use explicit calls on tny_folder for some reason.
823 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
824 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
825 (type == TNY_FOLDER_TYPE_OUTBOX) ||
826 (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
827 number = tny_folder_get_all_count (TNY_FOLDER(instance));
830 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
834 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
836 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"),
844 item_name = g_strdup ("");
847 /* Set the name in the treeview cell: */
848 g_object_set (rendobj,"text", item_name, NULL);
856 g_object_unref (G_OBJECT (instance));
862 GdkPixbuf *pixbuf_open;
863 GdkPixbuf *pixbuf_close;
867 static inline GdkPixbuf *
868 get_composite_pixbuf (const gchar *icon_name,
870 GdkPixbuf *base_pixbuf)
872 GdkPixbuf *emblem, *retval = NULL;
874 emblem = modest_platform_get_icon (icon_name, size);
876 retval = gdk_pixbuf_copy (base_pixbuf);
877 gdk_pixbuf_composite (emblem, retval, 0, 0,
878 MIN (gdk_pixbuf_get_width (emblem),
879 gdk_pixbuf_get_width (retval)),
880 MIN (gdk_pixbuf_get_height (emblem),
881 gdk_pixbuf_get_height (retval)),
882 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
883 g_object_unref (emblem);
888 static inline ThreePixbufs *
889 get_composite_icons (const gchar *icon_code,
891 GdkPixbuf **pixbuf_open,
892 GdkPixbuf **pixbuf_close)
894 ThreePixbufs *retval;
897 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
900 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
905 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
909 retval = g_slice_new0 (ThreePixbufs);
911 retval->pixbuf = g_object_ref (*pixbuf);
913 retval->pixbuf_open = g_object_ref (*pixbuf_open);
915 retval->pixbuf_close = g_object_ref (*pixbuf_close);
920 static inline ThreePixbufs *
921 get_account_protocol_pixbufs (ModestFolderView *folder_view,
922 ModestProtocolType protocol_type,
925 ModestProtocol *protocol;
926 const GdkPixbuf *pixbuf = NULL;
927 ModestFolderViewPrivate *priv;
929 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
931 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
934 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
935 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
936 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
937 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
938 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
939 object, FOLDER_ICON_SIZE);
943 ThreePixbufs *retval;
944 retval = g_slice_new0 (ThreePixbufs);
945 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
946 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
947 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
954 static inline ThreePixbufs*
955 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
957 TnyAccount *account = NULL;
958 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
959 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
960 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
961 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
962 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
964 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
965 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
966 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
967 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
968 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
970 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
971 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
972 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
973 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
974 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
976 ThreePixbufs *retval = NULL;
978 if (TNY_IS_ACCOUNT (instance)) {
979 account = g_object_ref (instance);
980 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
981 account = tny_folder_get_account (TNY_FOLDER (instance));
985 ModestProtocolType account_store_protocol;
987 account_store_protocol = modest_tny_account_get_protocol_type (account);
988 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
989 g_object_unref (account);
995 /* Sometimes an special folder is reported by the server as
996 NORMAL, like some versions of Dovecot */
997 if (type == TNY_FOLDER_TYPE_NORMAL ||
998 type == TNY_FOLDER_TYPE_UNKNOWN) {
999 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1002 /* It's not enough with check the folder type. We need to
1003 ensure that we're not giving a special folder icon to a
1004 normal folder with the same name than a special folder */
1005 if (TNY_IS_FOLDER (instance) &&
1006 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1007 type = TNY_FOLDER_TYPE_NORMAL;
1009 /* Remote folders should not be treated as special folders */
1010 if (TNY_IS_FOLDER_STORE (instance) &&
1011 !TNY_IS_ACCOUNT (instance) &&
1012 type != TNY_FOLDER_TYPE_INBOX &&
1013 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1014 #ifdef MODEST_TOOLKIT_HILDON2
1015 return get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1018 &anorm_pixbuf_close);
1020 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1022 &normal_pixbuf_open,
1023 &normal_pixbuf_close);
1029 case TNY_FOLDER_TYPE_INVALID:
1030 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1033 case TNY_FOLDER_TYPE_ROOT:
1034 if (TNY_IS_ACCOUNT (instance)) {
1036 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1037 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1040 &avirt_pixbuf_close);
1042 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1044 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1045 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1048 &ammc_pixbuf_close);
1050 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1053 &anorm_pixbuf_close);
1058 case TNY_FOLDER_TYPE_INBOX:
1059 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1062 &inbox_pixbuf_close);
1064 case TNY_FOLDER_TYPE_OUTBOX:
1065 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1067 &outbox_pixbuf_open,
1068 &outbox_pixbuf_close);
1070 case TNY_FOLDER_TYPE_JUNK:
1071 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1074 &junk_pixbuf_close);
1076 case TNY_FOLDER_TYPE_SENT:
1077 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1080 &sent_pixbuf_close);
1082 case TNY_FOLDER_TYPE_TRASH:
1083 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1086 &trash_pixbuf_close);
1088 case TNY_FOLDER_TYPE_DRAFTS:
1089 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1092 &draft_pixbuf_close);
1094 case TNY_FOLDER_TYPE_ARCHIVE:
1095 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1100 case TNY_FOLDER_TYPE_NORMAL:
1102 /* Memory card folders could have an special icon */
1103 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1104 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1109 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1111 &normal_pixbuf_open,
1112 &normal_pixbuf_close);
1121 free_pixbufs (ThreePixbufs *pixbufs)
1123 if (pixbufs->pixbuf)
1124 g_object_unref (pixbufs->pixbuf);
1125 if (pixbufs->pixbuf_open)
1126 g_object_unref (pixbufs->pixbuf_open);
1127 if (pixbufs->pixbuf_close)
1128 g_object_unref (pixbufs->pixbuf_close);
1129 g_slice_free (ThreePixbufs, pixbufs);
1133 icon_cell_data (GtkTreeViewColumn *column,
1134 GtkCellRenderer *renderer,
1135 GtkTreeModel *tree_model,
1139 GObject *rendobj = NULL, *instance = NULL;
1140 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1141 gboolean has_children;
1142 ThreePixbufs *pixbufs;
1143 ModestFolderView *folder_view = (ModestFolderView *) data;
1145 rendobj = (GObject *) renderer;
1147 gtk_tree_model_get (tree_model, iter,
1149 INSTANCE_COLUMN, &instance,
1155 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1156 pixbufs = get_folder_icons (folder_view, type, instance);
1157 g_object_unref (instance);
1160 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1163 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1164 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1167 free_pixbufs (pixbufs);
1171 add_columns (GtkWidget *treeview)
1173 GtkTreeViewColumn *column;
1174 GtkCellRenderer *renderer;
1175 GtkTreeSelection *sel;
1176 ModestFolderViewPrivate *priv;
1178 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1181 column = gtk_tree_view_column_new ();
1183 /* Set icon and text render function */
1184 renderer = gtk_cell_renderer_pixbuf_new();
1185 #ifdef MODEST_TOOLKIT_HILDON2
1186 g_object_set (renderer,
1187 "xpad", MODEST_MARGIN_DEFAULT,
1188 "ypad", MODEST_MARGIN_DEFAULT,
1191 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1192 gtk_tree_view_column_set_cell_data_func(column, renderer,
1193 icon_cell_data, treeview, NULL);
1195 renderer = gtk_cell_renderer_text_new();
1196 g_object_set (renderer,
1197 #ifdef MODEST_TOOLKIT_HILDON2
1198 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1199 "ypad", MODEST_MARGIN_DEFAULT,
1200 "xpad", MODEST_MARGIN_DEFAULT,
1202 "ellipsize", PANGO_ELLIPSIZE_END,
1204 "ellipsize-set", TRUE, NULL);
1205 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1206 gtk_tree_view_column_set_cell_data_func(column, renderer,
1207 text_cell_data, treeview, NULL);
1209 priv->messages_renderer = gtk_cell_renderer_text_new ();
1210 g_object_set (priv->messages_renderer,
1211 #ifdef MODEST_TOOLKIT_HILDON2
1213 "ypad", MODEST_MARGIN_DEFAULT,
1214 "xpad", MODEST_MARGIN_DOUBLE,
1216 "scale", PANGO_SCALE_X_SMALL,
1219 "alignment", PANGO_ALIGN_RIGHT,
1223 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1224 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1225 messages_cell_data, treeview, NULL);
1227 /* Set selection mode */
1228 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1229 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1231 /* Set treeview appearance */
1232 gtk_tree_view_column_set_spacing (column, 2);
1233 gtk_tree_view_column_set_resizable (column, TRUE);
1234 gtk_tree_view_column_set_fixed_width (column, TRUE);
1235 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1236 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1239 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1243 modest_folder_view_init (ModestFolderView *obj)
1245 ModestFolderViewPrivate *priv;
1248 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1250 priv->timer_expander = 0;
1251 priv->account_store = NULL;
1253 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1254 priv->cur_folder_store = NULL;
1255 priv->visible_account_id = NULL;
1256 priv->mailbox = NULL;
1257 priv->folder_to_select = NULL;
1258 priv->outbox_deleted_handler = 0;
1259 priv->reexpand = TRUE;
1260 priv->activity_changed_handler = 0;
1262 /* Initialize the local account name */
1263 conf = modest_runtime_get_conf();
1264 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1266 /* Init email clipboard */
1267 priv->clipboard = modest_runtime_get_email_clipboard ();
1268 priv->hidding_ids = NULL;
1269 priv->n_selected = 0;
1270 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1271 priv->reselect = FALSE;
1272 priv->show_non_move = TRUE;
1273 priv->list_to_move = NULL;
1274 priv->show_message_count = TRUE;
1276 /* Build treeview */
1277 add_columns (GTK_WIDGET (obj));
1279 /* Setup drag and drop */
1280 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1282 /* Connect signals */
1283 g_signal_connect (G_OBJECT (obj),
1285 G_CALLBACK (on_key_pressed), NULL);
1287 priv->display_name_changed_signal =
1288 g_signal_connect (modest_runtime_get_account_mgr (),
1289 "display_name_changed",
1290 G_CALLBACK (on_display_name_changed),
1294 * Track changes in the local account name (in the device it
1295 * will be the device name)
1297 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1299 G_CALLBACK(on_configuration_key_changed),
1303 g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1309 tny_account_store_view_init (gpointer g, gpointer iface_data)
1311 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1313 klass->set_account_store = modest_folder_view_set_account_store;
1317 modest_folder_view_finalize (GObject *obj)
1319 ModestFolderViewPrivate *priv;
1320 GtkTreeSelection *sel;
1321 TnyAccount *local_account;
1323 g_return_if_fail (obj);
1325 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1327 if (priv->timer_expander != 0) {
1328 g_source_remove (priv->timer_expander);
1329 priv->timer_expander = 0;
1332 local_account = (TnyAccount *)
1333 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1334 if (local_account) {
1335 if (g_signal_handler_is_connected (local_account,
1336 priv->outbox_deleted_handler))
1337 g_signal_handler_disconnect (local_account,
1338 priv->outbox_deleted_handler);
1339 g_object_unref (local_account);
1342 if (priv->account_store) {
1343 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1344 priv->account_inserted_signal);
1345 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1346 priv->account_removed_signal);
1347 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1348 priv->account_changed_signal);
1349 g_object_unref (G_OBJECT(priv->account_store));
1350 priv->account_store = NULL;
1353 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1354 priv->display_name_changed_signal)) {
1355 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1356 priv->display_name_changed_signal);
1357 priv->display_name_changed_signal = 0;
1361 g_object_unref (G_OBJECT (priv->query));
1365 if (priv->folder_to_select) {
1366 g_object_unref (G_OBJECT(priv->folder_to_select));
1367 priv->folder_to_select = NULL;
1370 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1372 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1374 g_free (priv->local_account_name);
1375 g_free (priv->visible_account_id);
1376 g_free (priv->mailbox);
1378 if (priv->conf_key_signal) {
1379 g_signal_handler_disconnect (modest_runtime_get_conf (),
1380 priv->conf_key_signal);
1381 priv->conf_key_signal = 0;
1384 if (priv->cur_folder_store) {
1385 g_object_unref (priv->cur_folder_store);
1386 priv->cur_folder_store = NULL;
1389 if (priv->list_to_move) {
1390 g_object_unref (priv->list_to_move);
1391 priv->list_to_move = NULL;
1394 /* Clear hidding array created by cut operation */
1395 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1397 G_OBJECT_CLASS(parent_class)->finalize (obj);
1402 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1404 ModestFolderViewPrivate *priv;
1407 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1408 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1410 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1411 device = tny_account_store_get_device (account_store);
1413 if (G_UNLIKELY (priv->account_store)) {
1415 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1416 priv->account_inserted_signal))
1417 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1418 priv->account_inserted_signal);
1419 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1420 priv->account_removed_signal))
1421 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1422 priv->account_removed_signal);
1423 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1424 priv->account_changed_signal))
1425 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1426 priv->account_changed_signal);
1427 g_object_unref (G_OBJECT (priv->account_store));
1430 priv->account_store = g_object_ref (G_OBJECT (account_store));
1432 priv->account_removed_signal =
1433 g_signal_connect (G_OBJECT(account_store), "account_removed",
1434 G_CALLBACK (on_account_removed), self);
1436 priv->account_inserted_signal =
1437 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1438 G_CALLBACK (on_account_inserted), self);
1440 priv->account_changed_signal =
1441 g_signal_connect (G_OBJECT(account_store), "account_changed",
1442 G_CALLBACK (on_account_changed), self);
1444 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1445 priv->reselect = FALSE;
1446 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1448 g_object_unref (G_OBJECT (device));
1452 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1455 ModestFolderView *self;
1456 GtkTreeModel *model, *filter_model;
1459 self = MODEST_FOLDER_VIEW (user_data);
1461 if (!get_inner_models (self, &filter_model, NULL, &model))
1464 /* Remove outbox from model */
1465 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1466 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1467 g_object_unref (outbox);
1470 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1474 on_account_inserted (TnyAccountStore *account_store,
1475 TnyAccount *account,
1478 ModestFolderViewPrivate *priv;
1479 GtkTreeModel *model, *filter_model;
1481 /* Ignore transport account insertions, we're not showing them
1482 in the folder view */
1483 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1486 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1489 /* If we're adding a new account, and there is no previous
1490 one, we need to select the visible server account */
1491 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1492 !priv->visible_account_id)
1493 modest_widget_memory_restore (modest_runtime_get_conf(),
1494 G_OBJECT (user_data),
1495 MODEST_CONF_FOLDER_VIEW_KEY);
1499 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1500 &filter_model, NULL, &model))
1503 /* Insert the account in the model */
1504 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1506 /* When the model is a list store (plain representation) the
1507 outbox is not a child of any account so we have to manually
1508 delete it because removing the local folders account won't
1509 delete it (because tny_folder_get_account() is not defined
1510 for a merge folder */
1511 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1512 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1514 priv->outbox_deleted_handler =
1515 g_signal_connect (account,
1517 G_CALLBACK (on_outbox_deleted_cb),
1521 /* Refilter the model */
1522 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1527 same_account_selected (ModestFolderView *self,
1528 TnyAccount *account)
1530 ModestFolderViewPrivate *priv;
1531 gboolean same_account = FALSE;
1533 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1535 if (priv->cur_folder_store) {
1536 TnyAccount *selected_folder_account = NULL;
1538 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1539 selected_folder_account =
1540 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1542 selected_folder_account =
1543 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1546 if (selected_folder_account == account)
1547 same_account = TRUE;
1549 g_object_unref (selected_folder_account);
1551 return same_account;
1556 * Selects the first inbox or the local account in an idle
1559 on_idle_select_first_inbox_or_local (gpointer user_data)
1561 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1563 gdk_threads_enter ();
1564 modest_folder_view_select_first_inbox_or_local (self);
1565 gdk_threads_leave ();
1571 on_account_changed (TnyAccountStore *account_store,
1572 TnyAccount *tny_account,
1575 ModestFolderView *self;
1576 ModestFolderViewPrivate *priv;
1577 GtkTreeModel *model, *filter_model;
1578 GtkTreeSelection *sel;
1579 gboolean same_account;
1581 /* Ignore transport account insertions, we're not showing them
1582 in the folder view */
1583 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1586 self = MODEST_FOLDER_VIEW (user_data);
1587 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1589 /* Get the inner model */
1590 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1591 &filter_model, NULL, &model))
1594 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1596 /* Invalidate the cur_folder_store only if the selected folder
1597 belongs to the account that is being removed */
1598 same_account = same_account_selected (self, tny_account);
1600 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1601 gtk_tree_selection_unselect_all (sel);
1604 /* Remove the account from the model */
1605 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1607 /* Insert the account in the model */
1608 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1610 /* Refilter the model */
1611 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1613 /* Select the first INBOX if the currently selected folder
1614 belongs to the account that is being deleted */
1615 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1616 g_idle_add (on_idle_select_first_inbox_or_local, self);
1620 on_account_removed (TnyAccountStore *account_store,
1621 TnyAccount *account,
1624 ModestFolderView *self = NULL;
1625 ModestFolderViewPrivate *priv;
1626 GtkTreeModel *model, *filter_model;
1627 GtkTreeSelection *sel = NULL;
1628 gboolean same_account = FALSE;
1630 /* Ignore transport account removals, we're not showing them
1631 in the folder view */
1632 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1635 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1636 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1640 self = MODEST_FOLDER_VIEW (user_data);
1641 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1643 /* Invalidate the cur_folder_store only if the selected folder
1644 belongs to the account that is being removed */
1645 same_account = same_account_selected (self, account);
1647 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1648 gtk_tree_selection_unselect_all (sel);
1651 /* Invalidate row to select only if the folder to select
1652 belongs to the account that is being removed*/
1653 if (priv->folder_to_select) {
1654 TnyAccount *folder_to_select_account = NULL;
1656 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1657 if (folder_to_select_account == account) {
1658 modest_folder_view_disable_next_folder_selection (self);
1659 g_object_unref (priv->folder_to_select);
1660 priv->folder_to_select = NULL;
1662 g_object_unref (folder_to_select_account);
1665 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1666 &filter_model, NULL, &model))
1669 /* Disconnect the signal handler */
1670 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1671 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1672 if (g_signal_handler_is_connected (account,
1673 priv->outbox_deleted_handler))
1674 g_signal_handler_disconnect (account,
1675 priv->outbox_deleted_handler);
1678 /* Remove the account from the model */
1679 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1681 /* If the removed account is the currently viewed one then
1682 clear the configuration value. The new visible account will be the default account */
1683 if (priv->visible_account_id &&
1684 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1686 /* Clear the current visible account_id */
1687 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1688 modest_folder_view_set_mailbox (self, NULL);
1690 /* Call the restore method, this will set the new visible account */
1691 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1692 MODEST_CONF_FOLDER_VIEW_KEY);
1695 /* Refilter the model */
1696 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1698 /* Select the first INBOX if the currently selected folder
1699 belongs to the account that is being deleted */
1701 g_idle_add (on_idle_select_first_inbox_or_local, self);
1705 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1707 GtkTreeViewColumn *col;
1709 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1711 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1713 g_printerr ("modest: failed get column for title\n");
1717 gtk_tree_view_column_set_title (col, title);
1718 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1723 modest_folder_view_on_map (ModestFolderView *self,
1724 GdkEventExpose *event,
1727 ModestFolderViewPrivate *priv;
1729 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1731 /* This won't happen often */
1732 if (G_UNLIKELY (priv->reselect)) {
1733 /* Select the first inbox or the local account if not found */
1735 /* TODO: this could cause a lock at startup, so we
1736 comment it for the moment. We know that this will
1737 be a bug, because the INBOX is not selected, but we
1738 need to rewrite some parts of Modest to avoid the
1739 deathlock situation */
1740 /* TODO: check if this is still the case */
1741 priv->reselect = FALSE;
1742 modest_folder_view_select_first_inbox_or_local (self);
1743 /* Notify the display name observers */
1744 g_signal_emit (G_OBJECT(self),
1745 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1749 if (priv->reexpand) {
1750 expand_root_items (self);
1751 priv->reexpand = FALSE;
1758 modest_folder_view_new (TnyFolderStoreQuery *query)
1761 ModestFolderViewPrivate *priv;
1762 GtkTreeSelection *sel;
1764 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1765 #ifdef MODEST_TOOLKIT_HILDON2
1766 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1769 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1772 priv->query = g_object_ref (query);
1774 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1775 priv->changed_signal = g_signal_connect (sel, "changed",
1776 G_CALLBACK (on_selection_changed), self);
1778 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1780 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1782 return GTK_WIDGET(self);
1785 /* this feels dirty; any other way to expand all the root items? */
1787 expand_root_items (ModestFolderView *self)
1790 GtkTreeModel *model;
1793 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1794 path = gtk_tree_path_new_first ();
1796 /* all folders should have child items, so.. */
1798 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1799 gtk_tree_path_next (path);
1800 } while (gtk_tree_model_get_iter (model, &iter, path));
1802 gtk_tree_path_free (path);
1806 is_parent_of (TnyFolder *a, TnyFolder *b)
1809 gboolean retval = FALSE;
1811 a_id = tny_folder_get_id (a);
1813 gchar *string_to_match;
1816 string_to_match = g_strconcat (a_id, "/", NULL);
1817 b_id = tny_folder_get_id (b);
1818 retval = g_str_has_prefix (b_id, string_to_match);
1819 g_free (string_to_match);
1825 typedef struct _ForeachFolderInfo {
1828 } ForeachFolderInfo;
1831 foreach_folder_with_id (GtkTreeModel *model,
1836 ForeachFolderInfo *info;
1839 info = (ForeachFolderInfo *) data;
1840 gtk_tree_model_get (model, iter,
1841 INSTANCE_COLUMN, &instance,
1844 if (TNY_IS_FOLDER (instance)) {
1847 id = tny_folder_get_id (TNY_FOLDER (instance));
1849 collate = g_utf8_collate_key (id, -1);
1850 info->found = !strcmp (info->needle, collate);
1856 g_object_unref (instance);
1864 has_folder_with_id (ModestFolderView *self, const gchar *id)
1866 GtkTreeModel *model;
1867 ForeachFolderInfo info = {NULL, FALSE};
1869 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1870 info.needle = g_utf8_collate_key (id, -1);
1872 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1873 g_free (info.needle);
1879 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1882 gboolean retval = FALSE;
1884 a_id = tny_folder_get_id (a);
1887 b_id = tny_folder_get_id (b);
1890 const gchar *last_bar;
1891 gchar *string_to_match;
1892 last_bar = g_strrstr (b_id, "/");
1897 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1898 retval = has_folder_with_id (self, string_to_match);
1899 g_free (string_to_match);
1907 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1909 ModestFolderViewPrivate *priv;
1910 TnyIterator *iterator;
1911 gboolean retval = TRUE;
1913 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1914 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1916 for (iterator = tny_list_create_iterator (priv->list_to_move);
1917 retval && !tny_iterator_is_done (iterator);
1918 tny_iterator_next (iterator)) {
1920 instance = tny_iterator_get_current (iterator);
1921 if (instance == (GObject *) folder) {
1923 } else if (TNY_IS_FOLDER (instance)) {
1924 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1926 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1929 g_object_unref (instance);
1931 g_object_unref (iterator);
1938 * We use this function to implement the
1939 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1940 * account in this case, and the local folders.
1943 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1945 ModestFolderViewPrivate *priv;
1946 gboolean retval = TRUE;
1947 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1948 GObject *instance = NULL;
1949 const gchar *id = NULL;
1951 gboolean found = FALSE;
1952 gboolean cleared = FALSE;
1953 ModestTnyFolderRules rules = 0;
1956 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1957 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1959 gtk_tree_model_get (model, iter,
1960 NAME_COLUMN, &fname,
1962 INSTANCE_COLUMN, &instance,
1965 /* Do not show if there is no instance, this could indeed
1966 happen when the model is being modified while it's being
1967 drawn. This could occur for example when moving folders
1974 if (TNY_IS_ACCOUNT (instance)) {
1975 TnyAccount *acc = TNY_ACCOUNT (instance);
1976 const gchar *account_id = tny_account_get_id (acc);
1978 /* If it isn't a special folder,
1979 * don't show it unless it is the visible account: */
1980 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1981 !modest_tny_account_is_virtual_local_folders (acc) &&
1982 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1984 /* Show only the visible account id */
1985 if (priv->visible_account_id) {
1986 if (strcmp (account_id, priv->visible_account_id))
1993 /* Never show these to the user. They are merged into one folder
1994 * in the local-folders account instead: */
1995 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1998 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1999 /* Only show special folders for current account if needed */
2000 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2001 TnyAccount *account;
2003 account = tny_folder_get_account (TNY_FOLDER (instance));
2005 if (TNY_IS_ACCOUNT (account)) {
2006 const gchar *account_id = tny_account_get_id (account);
2008 if (!modest_tny_account_is_virtual_local_folders (account) &&
2009 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2010 /* Show only the visible account id */
2011 if (priv->visible_account_id) {
2012 if (strcmp (account_id, priv->visible_account_id)) {
2014 } else if (priv->mailbox) {
2015 /* Filter mailboxes */
2016 if (!g_str_has_prefix (fname, priv->mailbox)) {
2018 } else if (!strcmp (fname, priv->mailbox)) {
2019 /* Hide mailbox parent */
2025 g_object_unref (account);
2032 /* Check hiding (if necessary) */
2033 cleared = modest_email_clipboard_cleared (priv->clipboard);
2034 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2035 id = tny_folder_get_id (TNY_FOLDER(instance));
2036 if (priv->hidding_ids != NULL)
2037 for (i=0; i < priv->n_selected && !found; i++)
2038 if (priv->hidding_ids[i] != NULL && id != NULL)
2039 found = (!strcmp (priv->hidding_ids[i], id));
2044 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2045 folder as no message can be move there according to UI specs */
2046 if (retval && !priv->show_non_move) {
2047 if (priv->list_to_move &&
2048 tny_list_get_length (priv->list_to_move) > 0 &&
2049 TNY_IS_FOLDER (instance)) {
2050 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2052 if (retval && TNY_IS_FOLDER (instance) &&
2053 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2055 case TNY_FOLDER_TYPE_OUTBOX:
2056 case TNY_FOLDER_TYPE_SENT:
2057 case TNY_FOLDER_TYPE_DRAFTS:
2060 case TNY_FOLDER_TYPE_UNKNOWN:
2061 case TNY_FOLDER_TYPE_NORMAL:
2062 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2063 if (type == TNY_FOLDER_TYPE_INVALID)
2064 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2066 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2067 type == TNY_FOLDER_TYPE_SENT
2068 || type == TNY_FOLDER_TYPE_DRAFTS)
2075 if (retval && TNY_IS_ACCOUNT (instance) &&
2076 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2077 ModestProtocolType protocol_type;
2079 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2080 retval = !modest_protocol_registry_protocol_type_has_tag
2081 (modest_runtime_get_protocol_registry (),
2083 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2087 /* apply special filters */
2088 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2089 if (TNY_IS_ACCOUNT (instance))
2093 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2094 if (TNY_IS_FOLDER (instance))
2098 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2099 if (TNY_IS_ACCOUNT (instance)) {
2100 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2102 } else if (TNY_IS_FOLDER (instance)) {
2103 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2108 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2109 if (TNY_IS_ACCOUNT (instance)) {
2110 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2112 } else if (TNY_IS_FOLDER (instance)) {
2113 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2118 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2119 /* A mailbox is a fake folder with an @ in the middle of the name */
2120 if (!TNY_IS_FOLDER (instance) ||
2121 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2124 const gchar *folder_name;
2125 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2126 if (!folder_name || strchr (folder_name, '@') == NULL)
2132 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2133 if (TNY_IS_FOLDER (instance)) {
2134 /* Check folder rules */
2135 ModestTnyFolderRules rules;
2137 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2138 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2139 } else if (TNY_IS_ACCOUNT (instance)) {
2140 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2148 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2149 if (TNY_IS_FOLDER (instance)) {
2150 TnyFolderType guess_type;
2152 if (TNY_FOLDER_TYPE_NORMAL) {
2153 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2159 case TNY_FOLDER_TYPE_OUTBOX:
2160 case TNY_FOLDER_TYPE_SENT:
2161 case TNY_FOLDER_TYPE_DRAFTS:
2162 case TNY_FOLDER_TYPE_ARCHIVE:
2163 case TNY_FOLDER_TYPE_INBOX:
2166 case TNY_FOLDER_TYPE_UNKNOWN:
2167 case TNY_FOLDER_TYPE_NORMAL:
2173 } else if (TNY_IS_ACCOUNT (instance)) {
2178 if (retval && TNY_IS_FOLDER (instance)) {
2179 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2182 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2183 if (TNY_IS_FOLDER (instance)) {
2184 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2185 } else if (TNY_IS_ACCOUNT (instance)) {
2190 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2191 if (TNY_IS_FOLDER (instance)) {
2192 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2193 } else if (TNY_IS_ACCOUNT (instance)) {
2198 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2199 if (TNY_IS_FOLDER (instance)) {
2200 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2201 } else if (TNY_IS_ACCOUNT (instance)) {
2207 g_object_unref (instance);
2215 modest_folder_view_update_model (ModestFolderView *self,
2216 TnyAccountStore *account_store)
2218 ModestFolderViewPrivate *priv;
2219 GtkTreeModel *model;
2220 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2222 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2223 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2226 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2228 /* Notify that there is no folder selected */
2229 g_signal_emit (G_OBJECT(self),
2230 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2232 if (priv->cur_folder_store) {
2233 g_object_unref (priv->cur_folder_store);
2234 priv->cur_folder_store = NULL;
2237 /* FIXME: the local accounts are not shown when the query
2238 selects only the subscribed folders */
2239 #ifdef MODEST_TOOLKIT_HILDON2
2240 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2241 TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2242 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2243 MODEST_FOLDER_PATH_SEPARATOR);
2245 model = tny_gtk_folder_store_tree_model_new (NULL);
2248 /* When the model is a list store (plain representation) the
2249 outbox is not a child of any account so we have to manually
2250 delete it because removing the local folders account won't
2251 delete it (because tny_folder_get_account() is not defined
2252 for a merge folder */
2253 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2254 TnyAccount *account;
2255 ModestTnyAccountStore *acc_store;
2257 acc_store = modest_runtime_get_account_store ();
2258 account = modest_tny_account_store_get_local_folders_account (acc_store);
2260 if (g_signal_handler_is_connected (account,
2261 priv->outbox_deleted_handler))
2262 g_signal_handler_disconnect (account,
2263 priv->outbox_deleted_handler);
2265 priv->outbox_deleted_handler =
2266 g_signal_connect (account,
2268 G_CALLBACK (on_outbox_deleted_cb),
2270 g_object_unref (account);
2273 /* Get the accounts: */
2274 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2276 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2278 sortable = gtk_tree_model_sort_new_with_model (model);
2279 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2281 GTK_SORT_ASCENDING);
2282 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2284 cmp_rows, NULL, NULL);
2286 /* Create filter model */
2287 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2288 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2293 if (priv->activity_changed_handler > 0) {
2294 GtkTreeModel *old_tny_model;
2296 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2297 g_signal_handler_disconnect (G_OBJECT (old_tny_model), priv->activity_changed_handler);
2299 priv->activity_changed_handler = 0;
2303 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2304 #ifndef MODEST_TOOLKIT_HILDON2
2305 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2306 (GCallback) on_row_inserted_maybe_select_folder, self);
2309 #ifdef MODEST_TOOLKIT_HILDON2
2310 priv->activity_changed_handler =
2311 g_signal_connect (G_OBJECT (model), "activity-changed", G_CALLBACK (on_activity_changed), self);
2314 g_object_unref (model);
2315 g_object_unref (filter_model);
2316 g_object_unref (sortable);
2318 /* Force a reselection of the INBOX next time the widget is shown */
2319 priv->reselect = TRUE;
2326 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2328 GtkTreeModel *model = NULL;
2329 TnyFolderStore *folder = NULL;
2331 ModestFolderView *tree_view = NULL;
2332 ModestFolderViewPrivate *priv = NULL;
2333 gboolean selected = FALSE;
2335 g_return_if_fail (sel);
2336 g_return_if_fail (user_data);
2338 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2340 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2342 tree_view = MODEST_FOLDER_VIEW (user_data);
2345 gtk_tree_model_get (model, &iter,
2346 INSTANCE_COLUMN, &folder,
2349 /* If the folder is the same do not notify */
2350 if (folder && priv->cur_folder_store == folder) {
2351 g_object_unref (folder);
2356 /* Current folder was unselected */
2357 if (priv->cur_folder_store) {
2358 /* We must do this firstly because a libtinymail-camel
2359 implementation detail. If we issue the signal
2360 before doing the sync_async, then that signal could
2361 cause (and it actually does it) a free of the
2362 summary of the folder (because the main window will
2363 clear the headers view */
2364 if (TNY_IS_FOLDER(priv->cur_folder_store))
2365 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2366 FALSE, NULL, NULL, NULL);
2368 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2369 priv->cur_folder_store, FALSE);
2371 g_object_unref (priv->cur_folder_store);
2372 priv->cur_folder_store = NULL;
2375 /* New current references */
2376 priv->cur_folder_store = folder;
2378 /* New folder has been selected. Do not notify if there is
2379 nothing new selected */
2381 g_signal_emit (G_OBJECT(tree_view),
2382 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2383 0, priv->cur_folder_store, TRUE);
2388 on_row_activated (GtkTreeView *treeview,
2389 GtkTreePath *treepath,
2390 GtkTreeViewColumn *column,
2393 GtkTreeModel *model = NULL;
2394 TnyFolderStore *folder = NULL;
2396 ModestFolderView *self = NULL;
2397 ModestFolderViewPrivate *priv = NULL;
2399 g_return_if_fail (treeview);
2400 g_return_if_fail (user_data);
2402 self = MODEST_FOLDER_VIEW (user_data);
2403 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2405 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2407 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2410 gtk_tree_model_get (model, &iter,
2411 INSTANCE_COLUMN, &folder,
2414 g_signal_emit (G_OBJECT(self),
2415 signals[FOLDER_ACTIVATED_SIGNAL],
2418 #ifdef MODEST_TOOLKIT_HILDON2
2419 HildonUIMode ui_mode;
2420 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2421 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2422 if (priv->cur_folder_store)
2423 g_object_unref (priv->cur_folder_store);
2424 priv->cur_folder_store = g_object_ref (folder);
2428 g_object_unref (folder);
2432 modest_folder_view_get_selected (ModestFolderView *self)
2434 ModestFolderViewPrivate *priv;
2436 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2438 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2439 if (priv->cur_folder_store)
2440 g_object_ref (priv->cur_folder_store);
2442 return priv->cur_folder_store;
2446 get_cmp_rows_type_pos (GObject *folder)
2448 /* Remote accounts -> Local account -> MMC account .*/
2451 if (TNY_IS_ACCOUNT (folder) &&
2452 modest_tny_account_is_virtual_local_folders (
2453 TNY_ACCOUNT (folder))) {
2455 } else if (TNY_IS_ACCOUNT (folder)) {
2456 TnyAccount *account = TNY_ACCOUNT (folder);
2457 const gchar *account_id = tny_account_get_id (account);
2458 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2464 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2465 return -1; /* Should never happen */
2470 inbox_is_special (TnyFolderStore *folder_store)
2472 gboolean is_special = TRUE;
2474 if (TNY_IS_FOLDER (folder_store)) {
2478 gchar *last_inbox_bar;
2480 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2481 downcase = g_utf8_strdown (id, -1);
2482 last_bar = g_strrstr (downcase, "/");
2484 last_inbox_bar = g_strrstr (downcase, "inbox/");
2485 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2496 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2498 TnyAccount *account;
2499 gboolean is_special;
2500 /* Inbox, Outbox, Drafts, Sent, User */
2503 if (!TNY_IS_FOLDER (folder_store))
2506 case TNY_FOLDER_TYPE_INBOX:
2508 account = tny_folder_get_account (folder_store);
2509 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2511 /* In inbox case we need to know if the inbox is really the top
2512 * inbox of the account, or if it's a submailbox inbox. To do
2513 * this we'll apply an heuristic rule: Find last "/" and check
2514 * if it's preceeded by another Inbox */
2515 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2516 g_object_unref (account);
2517 return is_special?0:4;
2520 case TNY_FOLDER_TYPE_OUTBOX:
2521 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2523 case TNY_FOLDER_TYPE_DRAFTS:
2525 account = tny_folder_get_account (folder_store);
2526 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2527 g_object_unref (account);
2528 return is_special?1:4;
2531 case TNY_FOLDER_TYPE_SENT:
2533 account = tny_folder_get_account (folder_store);
2534 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2535 g_object_unref (account);
2536 return is_special?3:4;
2545 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2547 const gchar *a1_name, *a2_name;
2549 a1_name = tny_account_get_name (a1);
2550 a2_name = tny_account_get_name (a2);
2552 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2556 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2558 TnyAccount *a1 = NULL, *a2 = NULL;
2561 if (TNY_IS_ACCOUNT (s1)) {
2562 a1 = TNY_ACCOUNT (g_object_ref (s1));
2563 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2564 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2567 if (TNY_IS_ACCOUNT (s2)) {
2568 a2 = TNY_ACCOUNT (g_object_ref (s2));
2569 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2570 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2587 /* First we sort with the type of account */
2588 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2592 cmp = compare_account_names (a1, a2);
2596 g_object_unref (a1);
2598 g_object_unref (a2);
2604 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2606 gint is_account1, is_account2;
2608 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2609 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2611 return is_account2 - is_account1;
2615 * This function orders the mail accounts according to these rules:
2616 * 1st - remote accounts
2617 * 2nd - local account
2621 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2625 gchar *name1 = NULL;
2626 gchar *name2 = NULL;
2627 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2628 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2629 GObject *folder1 = NULL;
2630 GObject *folder2 = NULL;
2632 gtk_tree_model_get (tree_model, iter1,
2633 NAME_COLUMN, &name1,
2635 INSTANCE_COLUMN, &folder1,
2637 gtk_tree_model_get (tree_model, iter2,
2638 NAME_COLUMN, &name2,
2639 TYPE_COLUMN, &type2,
2640 INSTANCE_COLUMN, &folder2,
2643 /* Return if we get no folder. This could happen when folder
2644 operations are happening. The model is updated after the
2645 folder copy/move actually occurs, so there could be
2646 situations where the model to be drawn is not correct */
2647 if (!folder1 || !folder2)
2650 /* Sort by type. First the special folders, then the archives */
2651 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2655 /* Now we sort using the account of each folder */
2656 if (TNY_IS_FOLDER_STORE (folder1) &&
2657 TNY_IS_FOLDER_STORE (folder2)) {
2658 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2662 /* Each group is preceeded by its account */
2663 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2668 /* Pure sort by name */
2669 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2672 g_object_unref(G_OBJECT(folder1));
2674 g_object_unref(G_OBJECT(folder2));
2682 /*****************************************************************************/
2683 /* DRAG and DROP stuff */
2684 /*****************************************************************************/
2686 * This function fills the #GtkSelectionData with the row and the
2687 * model that has been dragged. It's called when this widget is a
2688 * source for dnd after the event drop happened
2691 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2692 guint info, guint time, gpointer data)
2694 GtkTreeSelection *selection;
2695 GtkTreeModel *model;
2697 GtkTreePath *source_row;
2699 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2700 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2702 source_row = gtk_tree_model_get_path (model, &iter);
2703 gtk_tree_set_row_drag_data (selection_data,
2707 gtk_tree_path_free (source_row);
2711 typedef struct _DndHelper {
2712 ModestFolderView *folder_view;
2713 gboolean delete_source;
2714 GtkTreePath *source_row;
2718 dnd_helper_destroyer (DndHelper *helper)
2720 /* Free the helper */
2721 gtk_tree_path_free (helper->source_row);
2722 g_slice_free (DndHelper, helper);
2726 xfer_folder_cb (ModestMailOperation *mail_op,
2727 TnyFolder *new_folder,
2731 /* Select the folder */
2732 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2738 /* get the folder for the row the treepath refers to. */
2739 /* folder must be unref'd */
2740 static TnyFolderStore *
2741 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2744 TnyFolderStore *folder = NULL;
2746 if (gtk_tree_model_get_iter (model,&iter, path))
2747 gtk_tree_model_get (model, &iter,
2748 INSTANCE_COLUMN, &folder,
2755 * This function is used by drag_data_received_cb to manage drag and
2756 * drop of a header, i.e, and drag from the header view to the folder
2760 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2761 GtkTreeModel *dest_model,
2762 GtkTreePath *dest_row,
2763 GtkSelectionData *selection_data)
2765 TnyList *headers = NULL;
2766 TnyFolder *folder = NULL, *src_folder = NULL;
2767 TnyFolderType folder_type;
2768 GtkTreeIter source_iter, dest_iter;
2769 ModestWindowMgr *mgr = NULL;
2770 ModestWindow *main_win = NULL;
2771 gchar **uris, **tmp;
2773 /* Build the list of headers */
2774 mgr = modest_runtime_get_window_mgr ();
2775 headers = tny_simple_list_new ();
2776 uris = modest_dnd_selection_data_get_paths (selection_data);
2779 while (*tmp != NULL) {
2782 gboolean first = TRUE;
2785 path = gtk_tree_path_new_from_string (*tmp);
2786 gtk_tree_model_get_iter (source_model, &source_iter, path);
2787 gtk_tree_model_get (source_model, &source_iter,
2788 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2791 /* Do not enable d&d of headers already opened */
2792 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2793 tny_list_append (headers, G_OBJECT (header));
2795 if (G_UNLIKELY (first)) {
2796 src_folder = tny_header_get_folder (header);
2800 /* Free and go on */
2801 gtk_tree_path_free (path);
2802 g_object_unref (header);
2807 /* This could happen ig we perform a d&d very quickly over the
2808 same row that row could dissapear because message is
2810 if (!TNY_IS_FOLDER (src_folder))
2813 /* Get the target folder */
2814 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2815 gtk_tree_model_get (dest_model, &dest_iter,
2819 if (!folder || !TNY_IS_FOLDER(folder)) {
2820 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2824 folder_type = modest_tny_folder_guess_folder_type (folder);
2825 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2826 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
2827 goto cleanup; /* cannot move messages there */
2830 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2831 /* g_warning ("folder not writable"); */
2832 goto cleanup; /* verboten! */
2835 /* Ask for confirmation to move */
2836 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2838 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2842 /* Transfer messages */
2843 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2848 if (G_IS_OBJECT (src_folder))
2849 g_object_unref (src_folder);
2850 if (G_IS_OBJECT(folder))
2851 g_object_unref (G_OBJECT (folder));
2852 if (G_IS_OBJECT(headers))
2853 g_object_unref (headers);
2857 TnyFolderStore *src_folder;
2858 TnyFolderStore *dst_folder;
2859 ModestFolderView *folder_view;
2864 dnd_folder_info_destroyer (DndFolderInfo *info)
2866 if (info->src_folder)
2867 g_object_unref (info->src_folder);
2868 if (info->dst_folder)
2869 g_object_unref (info->dst_folder);
2870 g_slice_free (DndFolderInfo, info);
2874 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2875 GtkWindow *parent_window,
2876 TnyAccount *account)
2879 modest_ui_actions_on_account_connection_error (parent_window, account);
2881 /* Free the helper & info */
2882 dnd_helper_destroyer (info->helper);
2883 dnd_folder_info_destroyer (info);
2887 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2889 GtkWindow *parent_window,
2890 TnyAccount *account,
2893 DndFolderInfo *info = NULL;
2894 ModestMailOperation *mail_op;
2896 info = (DndFolderInfo *) user_data;
2898 if (err || canceled) {
2899 dnd_on_connection_failed_destroyer (info, parent_window, account);
2903 /* Do the mail operation */
2904 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2905 modest_ui_actions_move_folder_error_handler,
2906 info->src_folder, NULL);
2908 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2911 /* Transfer the folder */
2912 modest_mail_operation_xfer_folder (mail_op,
2913 TNY_FOLDER (info->src_folder),
2915 info->helper->delete_source,
2917 info->helper->folder_view);
2920 g_object_unref (G_OBJECT (mail_op));
2921 dnd_helper_destroyer (info->helper);
2922 dnd_folder_info_destroyer (info);
2927 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2929 GtkWindow *parent_window,
2930 TnyAccount *account,
2933 DndFolderInfo *info = NULL;
2935 info = (DndFolderInfo *) user_data;
2937 if (err || canceled) {
2938 dnd_on_connection_failed_destroyer (info, parent_window, account);
2942 /* Connect to source folder and perform the copy/move */
2943 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2945 drag_and_drop_from_folder_view_src_folder_performer,
2950 * This function is used by drag_data_received_cb to manage drag and
2951 * drop of a folder, i.e, and drag from the folder view to the same
2955 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
2956 GtkTreeModel *dest_model,
2957 GtkTreePath *dest_row,
2958 GtkSelectionData *selection_data,
2961 GtkTreeIter dest_iter, iter;
2962 TnyFolderStore *dest_folder = NULL;
2963 TnyFolderStore *folder = NULL;
2964 gboolean forbidden = FALSE;
2966 DndFolderInfo *info = NULL;
2968 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2970 g_warning ("%s: BUG: no main window", __FUNCTION__);
2971 dnd_helper_destroyer (helper);
2976 /* check the folder rules for the destination */
2977 folder = tree_path_to_folder (dest_model, dest_row);
2978 if (TNY_IS_FOLDER(folder)) {
2979 ModestTnyFolderRules rules =
2980 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2981 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2982 } else if (TNY_IS_FOLDER_STORE(folder)) {
2983 /* enable local root as destination for folders */
2984 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2985 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2988 g_object_unref (folder);
2991 /* check the folder rules for the source */
2992 folder = tree_path_to_folder (source_model, helper->source_row);
2993 if (TNY_IS_FOLDER(folder)) {
2994 ModestTnyFolderRules rules =
2995 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2996 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2999 g_object_unref (folder);
3003 /* Check if the drag is possible */
3004 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3006 modest_platform_run_information_dialog ((GtkWindow *) win,
3007 _("mail_in_ui_folder_move_target_error"),
3009 /* Restore the previous selection */
3010 folder = tree_path_to_folder (source_model, helper->source_row);
3012 if (TNY_IS_FOLDER (folder))
3013 modest_folder_view_select_folder (helper->folder_view,
3014 TNY_FOLDER (folder), FALSE);
3015 g_object_unref (folder);
3017 dnd_helper_destroyer (helper);
3022 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3023 gtk_tree_model_get (dest_model, &dest_iter,
3026 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3027 gtk_tree_model_get (source_model, &iter,
3031 /* Create the info for the performer */
3032 info = g_slice_new0 (DndFolderInfo);
3033 info->src_folder = g_object_ref (folder);
3034 info->dst_folder = g_object_ref (dest_folder);
3035 info->helper = helper;
3037 /* Connect to the destination folder and perform the copy/move */
3038 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3040 drag_and_drop_from_folder_view_dst_folder_performer,
3044 g_object_unref (dest_folder);
3045 g_object_unref (folder);
3049 * This function receives the data set by the "drag-data-get" signal
3050 * handler. This information comes within the #GtkSelectionData. This
3051 * function will manage both the drags of folders of the treeview and
3052 * drags of headers of the header view widget.
3055 on_drag_data_received (GtkWidget *widget,
3056 GdkDragContext *context,
3059 GtkSelectionData *selection_data,
3064 GtkWidget *source_widget;
3065 GtkTreeModel *dest_model, *source_model;
3066 GtkTreePath *source_row, *dest_row;
3067 GtkTreeViewDropPosition pos;
3068 gboolean delete_source = FALSE;
3069 gboolean success = FALSE;
3071 /* Do not allow further process */
3072 g_signal_stop_emission_by_name (widget, "drag-data-received");
3073 source_widget = gtk_drag_get_source_widget (context);
3075 /* Get the action */
3076 if (context->action == GDK_ACTION_MOVE) {
3077 delete_source = TRUE;
3079 /* Notify that there is no folder selected. We need to
3080 do this in order to update the headers view (and
3081 its monitors, because when moving, the old folder
3082 won't longer exist. We can not wait for the end of
3083 the operation, because the operation won't start if
3084 the folder is in use */
3085 if (source_widget == widget) {
3086 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3087 gtk_tree_selection_unselect_all (sel);
3091 /* Check if the get_data failed */
3092 if (selection_data == NULL || selection_data->length < 0)
3095 /* Select the destination model */
3096 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3098 /* Get the path to the destination row. Can not call
3099 gtk_tree_view_get_drag_dest_row() because the source row
3100 is not selected anymore */
3101 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3104 /* Only allow drops IN other rows */
3106 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3107 pos == GTK_TREE_VIEW_DROP_AFTER)
3111 /* Drags from the header view */
3112 if (source_widget != widget) {
3113 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3115 drag_and_drop_from_header_view (source_model,
3120 DndHelper *helper = NULL;
3122 /* Get the source model and row */
3123 gtk_tree_get_row_drag_data (selection_data,
3127 /* Create the helper */
3128 helper = g_slice_new0 (DndHelper);
3129 helper->delete_source = delete_source;
3130 helper->source_row = gtk_tree_path_copy (source_row);
3131 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3133 drag_and_drop_from_folder_view (source_model,
3139 gtk_tree_path_free (source_row);
3143 gtk_tree_path_free (dest_row);
3146 /* Finish the drag and drop */
3147 gtk_drag_finish (context, success, FALSE, time);
3151 * We define a "drag-drop" signal handler because we do not want to
3152 * use the default one, because the default one always calls
3153 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3154 * signal handler, because there we have all the information available
3155 * to know if the dnd was a success or not.
3158 drag_drop_cb (GtkWidget *widget,
3159 GdkDragContext *context,
3167 if (!context->targets)
3170 /* Check if we're dragging a folder row */
3171 target = gtk_drag_dest_find_target (widget, context, NULL);
3173 /* Request the data from the source. */
3174 gtk_drag_get_data(widget, context, target, time);
3180 * This function expands a node of a tree view if it's not expanded
3181 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3182 * does that, so that's why they're here.
3185 expand_row_timeout (gpointer data)
3187 GtkTreeView *tree_view = data;
3188 GtkTreePath *dest_path = NULL;
3189 GtkTreeViewDropPosition pos;
3190 gboolean result = FALSE;
3192 gdk_threads_enter ();
3194 gtk_tree_view_get_drag_dest_row (tree_view,
3199 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3200 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3201 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3202 gtk_tree_path_free (dest_path);
3206 gtk_tree_path_free (dest_path);
3211 gdk_threads_leave ();
3217 * This function is called whenever the pointer is moved over a widget
3218 * while dragging some data. It installs a timeout that will expand a
3219 * node of the treeview if not expanded yet. This function also calls
3220 * gdk_drag_status in order to set the suggested action that will be
3221 * used by the "drag-data-received" signal handler to know if we
3222 * should do a move or just a copy of the data.
3225 on_drag_motion (GtkWidget *widget,
3226 GdkDragContext *context,
3232 GtkTreeViewDropPosition pos;
3233 GtkTreePath *dest_row;
3234 GtkTreeModel *dest_model;
3235 ModestFolderViewPrivate *priv;
3236 GdkDragAction suggested_action;
3237 gboolean valid_location = FALSE;
3238 TnyFolderStore *folder = NULL;
3240 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3242 if (priv->timer_expander != 0) {
3243 g_source_remove (priv->timer_expander);
3244 priv->timer_expander = 0;
3247 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3252 /* Do not allow drops between folders */
3254 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3255 pos == GTK_TREE_VIEW_DROP_AFTER) {
3256 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3257 gdk_drag_status(context, 0, time);
3258 valid_location = FALSE;
3261 valid_location = TRUE;
3264 /* Check that the destination folder is writable */
3265 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3266 folder = tree_path_to_folder (dest_model, dest_row);
3267 if (folder && TNY_IS_FOLDER (folder)) {
3268 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3270 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3271 valid_location = FALSE;
3276 /* Expand the selected row after 1/2 second */
3277 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3278 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3280 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3282 /* Select the desired action. By default we pick MOVE */
3283 suggested_action = GDK_ACTION_MOVE;
3285 if (context->actions == GDK_ACTION_COPY)
3286 gdk_drag_status(context, GDK_ACTION_COPY, time);
3287 else if (context->actions == GDK_ACTION_MOVE)
3288 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3289 else if (context->actions & suggested_action)
3290 gdk_drag_status(context, suggested_action, time);
3292 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3296 g_object_unref (folder);
3298 gtk_tree_path_free (dest_row);
3300 g_signal_stop_emission_by_name (widget, "drag-motion");
3302 return valid_location;
3306 * This function sets the treeview as a source and a target for dnd
3307 * events. It also connects all the requirede signals.
3310 setup_drag_and_drop (GtkTreeView *self)
3312 /* Set up the folder view as a dnd destination. Set only the
3313 highlight flag, otherwise gtk will have a different
3315 #ifdef MODEST_TOOLKIT_HILDON2
3318 gtk_drag_dest_set (GTK_WIDGET (self),
3319 GTK_DEST_DEFAULT_HIGHLIGHT,
3320 folder_view_drag_types,
3321 G_N_ELEMENTS (folder_view_drag_types),
3322 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3324 g_signal_connect (G_OBJECT (self),
3325 "drag_data_received",
3326 G_CALLBACK (on_drag_data_received),
3330 /* Set up the treeview as a dnd source */
3331 gtk_drag_source_set (GTK_WIDGET (self),
3333 folder_view_drag_types,
3334 G_N_ELEMENTS (folder_view_drag_types),
3335 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3337 g_signal_connect (G_OBJECT (self),
3339 G_CALLBACK (on_drag_motion),
3342 g_signal_connect (G_OBJECT (self),
3344 G_CALLBACK (on_drag_data_get),
3347 g_signal_connect (G_OBJECT (self),
3349 G_CALLBACK (drag_drop_cb),
3354 * This function manages the navigation through the folders using the
3355 * keyboard or the hardware keys in the device
3358 on_key_pressed (GtkWidget *self,
3362 GtkTreeSelection *selection;
3364 GtkTreeModel *model;
3365 gboolean retval = FALSE;
3367 /* Up and Down are automatically managed by the treeview */
3368 if (event->keyval == GDK_Return) {
3369 /* Expand/Collapse the selected row */
3370 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3371 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3374 path = gtk_tree_model_get_path (model, &iter);
3376 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3377 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3379 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3380 gtk_tree_path_free (path);
3382 /* No further processing */
3390 * We listen to the changes in the local folder account name key,
3391 * because we want to show the right name in the view. The local
3392 * folder account name corresponds to the device name in the Maemo
3393 * version. We do this because we do not want to query gconf on each
3394 * tree view refresh. It's better to cache it and change whenever
3398 on_configuration_key_changed (ModestConf* conf,
3400 ModestConfEvent event,
3401 ModestConfNotificationId id,
3402 ModestFolderView *self)
3404 ModestFolderViewPrivate *priv;
3407 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3408 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3410 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3411 g_free (priv->local_account_name);
3413 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3414 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3416 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3417 MODEST_CONF_DEVICE_NAME, NULL);
3419 /* Force a redraw */
3420 #if GTK_CHECK_VERSION(2, 8, 0)
3421 GtkTreeViewColumn * tree_column;
3423 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3425 gtk_tree_view_column_queue_resize (tree_column);
3427 gtk_widget_queue_draw (GTK_WIDGET (self));
3433 modest_folder_view_set_style (ModestFolderView *self,
3434 ModestFolderViewStyle style)
3436 ModestFolderViewPrivate *priv;
3438 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3439 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3440 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3442 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3445 priv->style = style;
3449 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3450 const gchar *account_id)
3452 ModestFolderViewPrivate *priv;
3453 GtkTreeModel *model;
3455 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3457 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3459 /* This will be used by the filter_row callback,
3460 * to decided which rows to show: */
3461 if (priv->visible_account_id) {
3462 g_free (priv->visible_account_id);
3463 priv->visible_account_id = NULL;
3466 priv->visible_account_id = g_strdup (account_id);
3469 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3470 if (GTK_IS_TREE_MODEL_FILTER (model))
3471 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3473 /* Save settings to gconf */
3474 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3475 MODEST_CONF_FOLDER_VIEW_KEY);
3477 /* Notify observers */
3478 g_signal_emit (G_OBJECT(self),
3479 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3484 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3486 ModestFolderViewPrivate *priv;
3488 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3490 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3492 return (const gchar *) priv->visible_account_id;
3496 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3500 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3502 gtk_tree_model_get (model, iter,
3506 gboolean result = FALSE;
3507 if (type == TNY_FOLDER_TYPE_INBOX) {
3511 *inbox_iter = *iter;
3515 if (gtk_tree_model_iter_children (model, &child, iter)) {
3516 if (find_inbox_iter (model, &child, inbox_iter))
3520 } while (gtk_tree_model_iter_next (model, iter));
3529 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3531 #ifndef MODEST_TOOLKIT_HILDON2
3532 GtkTreeModel *model;
3533 GtkTreeIter iter, inbox_iter;
3534 GtkTreeSelection *sel;
3535 GtkTreePath *path = NULL;
3537 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3539 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3543 expand_root_items (self);
3544 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3546 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3547 g_warning ("%s: model is empty", __FUNCTION__);
3551 if (find_inbox_iter (model, &iter, &inbox_iter))
3552 path = gtk_tree_model_get_path (model, &inbox_iter);
3554 path = gtk_tree_path_new_first ();
3556 /* Select the row and free */
3557 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3558 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3559 gtk_tree_path_free (path);
3562 gtk_widget_grab_focus (GTK_WIDGET(self));
3569 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3574 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3575 TnyFolder* a_folder;
3578 gtk_tree_model_get (model, iter,
3579 INSTANCE_COLUMN, &a_folder,
3585 if (folder == a_folder) {
3586 g_object_unref (a_folder);
3587 *folder_iter = *iter;
3590 g_object_unref (a_folder);
3592 if (gtk_tree_model_iter_children (model, &child, iter)) {
3593 if (find_folder_iter (model, &child, folder_iter, folder))
3597 } while (gtk_tree_model_iter_next (model, iter));
3602 #ifndef MODEST_TOOLKIT_HILDON2
3604 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3607 ModestFolderView *self)
3609 ModestFolderViewPrivate *priv = NULL;
3610 GtkTreeSelection *sel;
3611 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3612 GObject *instance = NULL;
3614 if (!MODEST_IS_FOLDER_VIEW(self))
3617 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3619 priv->reexpand = TRUE;
3621 gtk_tree_model_get (tree_model, iter,
3623 INSTANCE_COLUMN, &instance,
3629 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3630 priv->folder_to_select = g_object_ref (instance);
3632 g_object_unref (instance);
3634 if (priv->folder_to_select) {
3636 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3639 path = gtk_tree_model_get_path (tree_model, iter);
3640 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3642 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3644 gtk_tree_selection_select_iter (sel, iter);
3645 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3647 gtk_tree_path_free (path);
3651 modest_folder_view_disable_next_folder_selection (self);
3653 /* Refilter the model */
3654 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3660 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3662 ModestFolderViewPrivate *priv;
3664 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3666 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3668 if (priv->folder_to_select)
3669 g_object_unref(priv->folder_to_select);
3671 priv->folder_to_select = NULL;
3675 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3676 gboolean after_change)
3678 GtkTreeModel *model;
3679 GtkTreeIter iter, folder_iter;
3680 GtkTreeSelection *sel;
3681 ModestFolderViewPrivate *priv = NULL;
3683 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3684 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3686 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3689 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3690 gtk_tree_selection_unselect_all (sel);
3692 if (priv->folder_to_select)
3693 g_object_unref(priv->folder_to_select);
3694 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3698 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3703 /* Refilter the model, before selecting the folder */
3704 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3706 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3707 g_warning ("%s: model is empty", __FUNCTION__);
3711 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3714 path = gtk_tree_model_get_path (model, &folder_iter);
3715 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3717 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3718 gtk_tree_selection_select_iter (sel, &folder_iter);
3719 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3721 gtk_tree_path_free (path);
3729 modest_folder_view_copy_selection (ModestFolderView *self)
3731 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3733 /* Copy selection */
3734 _clipboard_set_selected_data (self, FALSE);
3738 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3740 ModestFolderViewPrivate *priv = NULL;
3741 GtkTreeModel *model = NULL;
3742 const gchar **hidding = NULL;
3743 guint i, n_selected;
3745 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3746 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3748 /* Copy selection */
3749 if (!_clipboard_set_selected_data (folder_view, TRUE))
3752 /* Get hidding ids */
3753 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3755 /* Clear hidding array created by previous cut operation */
3756 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3758 /* Copy hidding array */
3759 priv->n_selected = n_selected;
3760 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3761 for (i=0; i < n_selected; i++)
3762 priv->hidding_ids[i] = g_strdup(hidding[i]);
3764 /* Hide cut folders */
3765 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3766 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3770 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3771 ModestFolderView *folder_view_dst)
3773 GtkTreeModel *filter_model = NULL;
3774 GtkTreeModel *model = NULL;
3775 GtkTreeModel *new_filter_model = NULL;
3776 GtkTreeModel *old_tny_model = NULL;
3777 GtkTreeModel *new_tny_model = NULL;
3778 ModestFolderViewPrivate *dst_priv;
3780 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3781 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3783 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3784 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3785 new_tny_model = NULL;
3788 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3789 g_signal_handler_disconnect (G_OBJECT (old_tny_model), dst_priv->activity_changed_handler);
3790 dst_priv->activity_changed_handler = 0;
3792 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3793 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3795 /* Build new filter model */
3796 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3797 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3804 /* Set copied model */
3805 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3806 #ifndef MODEST_TOOLKIT_HILDON2
3807 g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3808 (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3810 #ifdef MODEST_TOOLKIT_HILDON2
3812 dst_priv->activity_changed_handler = g_signal_connect (G_OBJECT (new_tny_model), "activity-changed",
3813 G_CALLBACK (on_activity_changed), folder_view_dst);
3817 g_object_unref (new_filter_model);
3821 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3824 GtkTreeModel *model = NULL;
3825 ModestFolderViewPrivate* priv;
3827 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3829 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3830 priv->show_non_move = show;
3831 /* modest_folder_view_update_model(folder_view, */
3832 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3834 /* Hide special folders */
3835 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3836 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3837 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3842 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3845 ModestFolderViewPrivate* priv;
3847 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3849 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3850 priv->show_message_count = show;
3852 g_object_set (G_OBJECT (priv->messages_renderer),
3853 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3857 /* Returns FALSE if it did not selected anything */
3859 _clipboard_set_selected_data (ModestFolderView *folder_view,
3862 ModestFolderViewPrivate *priv = NULL;
3863 TnyFolderStore *folder = NULL;
3864 gboolean retval = FALSE;
3866 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3867 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3869 /* Set selected data on clipboard */
3870 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3871 folder = modest_folder_view_get_selected (folder_view);
3873 /* Do not allow to select an account */
3874 if (TNY_IS_FOLDER (folder)) {
3875 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3880 g_object_unref (folder);
3886 _clear_hidding_filter (ModestFolderView *folder_view)
3888 ModestFolderViewPrivate *priv;
3891 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3892 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3894 if (priv->hidding_ids != NULL) {
3895 for (i=0; i < priv->n_selected; i++)
3896 g_free (priv->hidding_ids[i]);
3897 g_free(priv->hidding_ids);
3903 on_display_name_changed (ModestAccountMgr *mgr,
3904 const gchar *account,
3907 ModestFolderView *self;
3909 self = MODEST_FOLDER_VIEW (user_data);
3911 /* Force a redraw */
3912 #if GTK_CHECK_VERSION(2, 8, 0)
3913 GtkTreeViewColumn * tree_column;
3915 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3917 gtk_tree_view_column_queue_resize (tree_column);
3919 gtk_widget_queue_draw (GTK_WIDGET (self));
3924 modest_folder_view_set_cell_style (ModestFolderView *self,
3925 ModestFolderViewCellStyle cell_style)
3927 ModestFolderViewPrivate *priv = NULL;
3929 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3930 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3932 priv->cell_style = cell_style;
3934 g_object_set (G_OBJECT (priv->messages_renderer),
3935 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3938 gtk_widget_queue_draw (GTK_WIDGET (self));
3942 update_style (ModestFolderView *self)
3944 ModestFolderViewPrivate *priv;
3945 GdkColor style_color;
3946 PangoAttrList *attr_list;
3948 PangoAttribute *attr;
3950 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3951 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3955 attr_list = pango_attr_list_new ();
3956 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3957 gdk_color_parse ("grey", &style_color);
3959 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3960 pango_attr_list_insert (attr_list, attr);
3963 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3965 "SmallSystemFont", NULL,
3968 attr = pango_attr_font_desc_new (pango_font_description_copy
3969 (style->font_desc));
3970 pango_attr_list_insert (attr_list, attr);
3972 g_object_set (G_OBJECT (priv->messages_renderer),
3973 "foreground-gdk", &style_color,
3974 "foreground-set", TRUE,
3975 "attributes", attr_list,
3977 pango_attr_list_unref (attr_list);
3982 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3984 if (strcmp ("style", spec->name) == 0) {
3985 update_style (MODEST_FOLDER_VIEW (obj));
3986 gtk_widget_queue_draw (GTK_WIDGET (obj));
3991 modest_folder_view_set_filter (ModestFolderView *self,
3992 ModestFolderViewFilter filter)
3994 ModestFolderViewPrivate *priv;
3995 GtkTreeModel *filter_model;
3997 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3998 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4000 priv->filter |= filter;
4002 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4003 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4004 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4009 modest_folder_view_unset_filter (ModestFolderView *self,
4010 ModestFolderViewFilter filter)
4012 ModestFolderViewPrivate *priv;
4013 GtkTreeModel *filter_model;
4015 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4016 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4018 priv->filter &= ~filter;
4020 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4021 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4022 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4027 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4028 ModestTnyFolderRules rules)
4030 GtkTreeModel *filter_model;
4032 gboolean fulfil = FALSE;
4034 if (!get_inner_models (self, &filter_model, NULL, NULL))
4037 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4041 TnyFolderStore *folder;
4043 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4045 if (TNY_IS_FOLDER (folder)) {
4046 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4047 /* Folder rules are negative: non_writable, non_deletable... */
4048 if (!(folder_rules & rules))
4051 g_object_unref (folder);
4054 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4060 modest_folder_view_set_list_to_move (ModestFolderView *self,
4063 ModestFolderViewPrivate *priv;
4065 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4066 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4068 if (priv->list_to_move)
4069 g_object_unref (priv->list_to_move);
4072 g_object_ref (list);
4074 priv->list_to_move = list;
4078 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4080 ModestFolderViewPrivate *priv;
4082 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4083 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4086 g_free (priv->mailbox);
4088 priv->mailbox = g_strdup (mailbox);
4090 /* Notify observers */
4091 g_signal_emit (G_OBJECT(self),
4092 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4093 priv->visible_account_id);
4097 modest_folder_view_get_mailbox (ModestFolderView *self)
4099 ModestFolderViewPrivate *priv;
4101 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4102 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4104 return (const gchar *) priv->mailbox;
4108 modest_folder_view_get_activity (ModestFolderView *self)
4110 ModestFolderViewPrivate *priv;
4111 GtkTreeModel *inner_model;
4113 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4114 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4115 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4117 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4118 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4124 #ifdef MODEST_TOOLKIT_HILDON2
4126 on_activity_changed (TnyGtkFolderListStore *store,
4128 ModestFolderView *folder_view)
4130 ModestFolderViewPrivate *priv;
4132 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4133 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4134 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4136 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,