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>
66 #ifdef MODEST_TOOLKIT_HILDON2
67 #include <hildon/hildon.h>
70 /* Folder view drag types */
71 const GtkTargetEntry folder_view_drag_types[] =
73 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
74 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
77 /* Default icon sizes for Fremantle style are different */
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
80 /* Column names depending on we use list store or tree store */
81 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
82 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
83 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
84 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
85 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
87 /* 'private'/'protected' functions */
88 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
89 static void modest_folder_view_init (ModestFolderView *obj);
90 static void modest_folder_view_finalize (GObject *obj);
91 static void modest_folder_view_dispose (GObject *obj);
93 static void tny_account_store_view_init (gpointer g,
96 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
97 TnyAccountStore *account_store);
99 static void on_selection_changed (GtkTreeSelection *sel,
102 static void on_row_activated (GtkTreeView *treeview,
104 GtkTreeViewColumn *column,
107 static void on_account_removed (TnyAccountStore *self,
111 static void on_account_inserted (TnyAccountStore *self,
115 static void on_account_changed (TnyAccountStore *self,
119 static gint cmp_rows (GtkTreeModel *tree_model,
124 static gboolean filter_row (GtkTreeModel *model,
128 static void on_configuration_key_changed (ModestConf* conf,
130 ModestConfEvent event,
131 ModestConfNotificationId notification_id,
132 ModestFolderView *self);
134 static void expand_root_items (ModestFolderView *self);
136 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
139 static void _clear_hidding_filter (ModestFolderView *folder_view);
141 static void on_display_name_changed (ModestAccountMgr *self,
142 const gchar *account,
144 static void update_style (ModestFolderView *self);
145 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
146 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
147 static gboolean inbox_is_special (TnyFolderStore *folder_store);
149 static gboolean get_inner_models (ModestFolderView *self,
150 GtkTreeModel **filter_model,
151 GtkTreeModel **sort_model,
152 GtkTreeModel **tny_model);
153 static void on_activity_changed (TnyGtkFolderListStore *store,
155 ModestFolderView *folder_view);
158 FOLDER_SELECTION_CHANGED_SIGNAL,
159 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
160 FOLDER_ACTIVATED_SIGNAL,
161 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
162 ACTIVITY_CHANGED_SIGNAL,
166 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
167 struct _ModestFolderViewPrivate {
168 TnyAccountStore *account_store;
169 TnyFolderStore *cur_folder_store;
171 TnyFolder *folder_to_select; /* folder to select after the next update */
173 gulong changed_signal;
174 gulong account_inserted_signal;
175 gulong account_removed_signal;
176 gulong account_changed_signal;
177 gulong conf_key_signal;
178 gulong display_name_changed_signal;
180 /* not unref this object, its a singlenton */
181 ModestEmailClipboard *clipboard;
183 /* Filter tree model */
186 ModestFolderViewFilter filter;
187 #ifdef MODEST_TOOLKIT_HILDON2
188 GtkWidget *live_search;
191 TnyFolderStoreQuery *query;
193 guint timer_expander;
195 gchar *local_account_name;
196 gchar *visible_account_id;
198 ModestFolderViewStyle style;
199 ModestFolderViewCellStyle cell_style;
200 gboolean show_message_count;
202 gboolean reselect; /* we use this to force a reselection of the INBOX */
203 gboolean show_non_move;
204 TnyList *list_to_move;
205 gboolean reexpand; /* next time we expose, we'll expand all root folders */
207 GtkCellRenderer *messages_renderer;
209 gulong outbox_deleted_handler;
211 GSList *signal_handlers;
212 GdkColor active_color;
214 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
215 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
216 MODEST_TYPE_FOLDER_VIEW, \
217 ModestFolderViewPrivate))
219 static GObjectClass *parent_class = NULL;
221 static guint signals[LAST_SIGNAL] = {0};
224 modest_folder_view_get_type (void)
226 static GType my_type = 0;
228 static const GTypeInfo my_info = {
229 sizeof(ModestFolderViewClass),
230 NULL, /* base init */
231 NULL, /* base finalize */
232 (GClassInitFunc) modest_folder_view_class_init,
233 NULL, /* class finalize */
234 NULL, /* class data */
235 sizeof(ModestFolderView),
237 (GInstanceInitFunc) modest_folder_view_init,
241 static const GInterfaceInfo tny_account_store_view_info = {
242 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
243 NULL, /* interface_finalize */
244 NULL /* interface_data */
248 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
252 g_type_add_interface_static (my_type,
253 TNY_TYPE_ACCOUNT_STORE_VIEW,
254 &tny_account_store_view_info);
260 modest_folder_view_class_init (ModestFolderViewClass *klass)
262 GObjectClass *gobject_class;
263 GtkTreeViewClass *treeview_class;
264 gobject_class = (GObjectClass*) klass;
265 treeview_class = (GtkTreeViewClass*) klass;
267 parent_class = g_type_class_peek_parent (klass);
268 gobject_class->finalize = modest_folder_view_finalize;
269 gobject_class->dispose = modest_folder_view_dispose;
271 g_type_class_add_private (gobject_class,
272 sizeof(ModestFolderViewPrivate));
274 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
275 g_signal_new ("folder_selection_changed",
276 G_TYPE_FROM_CLASS (gobject_class),
278 G_STRUCT_OFFSET (ModestFolderViewClass,
279 folder_selection_changed),
281 modest_marshal_VOID__POINTER_BOOLEAN,
282 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
285 * This signal is emitted whenever the currently selected
286 * folder display name is computed. Note that the name could
287 * be different to the folder name, because we could append
288 * the unread messages count to the folder name to build the
289 * folder display name
291 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
292 g_signal_new ("folder-display-name-changed",
293 G_TYPE_FROM_CLASS (gobject_class),
295 G_STRUCT_OFFSET (ModestFolderViewClass,
296 folder_display_name_changed),
298 g_cclosure_marshal_VOID__STRING,
299 G_TYPE_NONE, 1, G_TYPE_STRING);
301 signals[FOLDER_ACTIVATED_SIGNAL] =
302 g_signal_new ("folder_activated",
303 G_TYPE_FROM_CLASS (gobject_class),
305 G_STRUCT_OFFSET (ModestFolderViewClass,
308 g_cclosure_marshal_VOID__POINTER,
309 G_TYPE_NONE, 1, G_TYPE_POINTER);
312 * Emitted whenever the visible account changes
314 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
315 g_signal_new ("visible-account-changed",
316 G_TYPE_FROM_CLASS (gobject_class),
318 G_STRUCT_OFFSET (ModestFolderViewClass,
319 visible_account_changed),
321 g_cclosure_marshal_VOID__STRING,
322 G_TYPE_NONE, 1, G_TYPE_STRING);
325 * Emitted when the underlying GtkListStore is updating data
327 signals[ACTIVITY_CHANGED_SIGNAL] =
328 g_signal_new ("activity-changed",
329 G_TYPE_FROM_CLASS (gobject_class),
331 G_STRUCT_OFFSET (ModestFolderViewClass,
334 g_cclosure_marshal_VOID__BOOLEAN,
335 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
337 treeview_class->select_cursor_parent = NULL;
339 #ifdef MODEST_TOOLKIT_HILDON2
340 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
346 /* Retrieves the filter, sort and tny models of the folder view. If
347 any of these does not exist then it returns FALSE */
349 get_inner_models (ModestFolderView *self,
350 GtkTreeModel **filter_model,
351 GtkTreeModel **sort_model,
352 GtkTreeModel **tny_model)
354 GtkTreeModel *s_model, *f_model, *t_model;
356 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
357 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
358 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
362 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
363 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
364 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
368 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
372 *filter_model = f_model;
374 *sort_model = s_model;
376 *tny_model = t_model;
381 /* Simplify checks for NULLs: */
383 strings_are_equal (const gchar *a, const gchar *b)
389 return (strcmp (a, b) == 0);
396 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
398 GObject *instance = NULL;
400 gtk_tree_model_get (model, iter,
401 INSTANCE_COLUMN, &instance,
405 return FALSE; /* keep walking */
407 if (!TNY_IS_ACCOUNT (instance)) {
408 g_object_unref (instance);
409 return FALSE; /* keep walking */
412 /* Check if this is the looked-for account: */
413 TnyAccount *this_account = TNY_ACCOUNT (instance);
414 TnyAccount *account = TNY_ACCOUNT (data);
416 const gchar *this_account_id = tny_account_get_id(this_account);
417 const gchar *account_id = tny_account_get_id(account);
418 g_object_unref (instance);
421 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
422 if (strings_are_equal(this_account_id, account_id)) {
423 /* Tell the model that the data has changed, so that
424 * it calls the cell_data_func callbacks again: */
425 /* TODO: This does not seem to actually cause the new string to be shown: */
426 gtk_tree_model_row_changed (model, path, iter);
428 return TRUE; /* stop walking */
431 return FALSE; /* keep walking */
436 ModestFolderView *self;
437 gchar *previous_name;
438 } GetMmcAccountNameData;
441 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
443 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
445 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
447 if (!strings_are_equal (
448 tny_account_get_name(TNY_ACCOUNT(account)),
449 data->previous_name)) {
451 /* Tell the model that the data has changed, so that
452 * it calls the cell_data_func callbacks again: */
453 ModestFolderView *self = data->self;
454 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
456 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
459 g_free (data->previous_name);
460 g_slice_free (GetMmcAccountNameData, data);
464 convert_parent_folders_to_dots (gchar **item_name)
467 gint n_inbox_parents = 0;
470 gchar *last_separator;
472 if (item_name == NULL)
475 path_start = *item_name;
476 for (c = *item_name; *c != '\0'; c++) {
477 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
479 if (c != path_start) {
480 compare = g_strndup (path_start, c - path_start);
481 compare = g_strstrip (compare);
482 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
492 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
493 if (last_separator != NULL) {
494 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
501 buffer = g_string_new ("");
502 for (i = 0; i < n_parents - n_inbox_parents; i++) {
503 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
505 buffer = g_string_append (buffer, last_separator);
507 *item_name = g_string_free (buffer, FALSE);
513 format_compact_style (gchar **item_name,
515 const gchar *mailbox,
517 gboolean multiaccount,
518 gboolean *use_markup)
522 TnyFolderType folder_type;
523 gboolean l_use_markup;
525 if (!TNY_IS_FOLDER (instance))
528 folder = (TnyFolder *) instance;
530 folder_type = tny_folder_get_folder_type (folder);
531 is_special = (get_cmp_pos (folder_type, folder)!= 4);
534 /* Remove mailbox prefix if any */
535 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
536 if (g_str_has_prefix (*item_name, prefix)) {
537 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
539 *item_name = new_item_name;
543 if (!is_special || multiaccount) {
544 TnyAccount *account = tny_folder_get_account (folder);
545 const gchar *folder_name;
546 gboolean concat_folder_name = FALSE;
549 /* Should not happen */
553 /* convert parent folders to dots */
554 convert_parent_folders_to_dots (item_name);
556 folder_name = tny_folder_get_name (folder);
557 if (g_str_has_suffix (*item_name, folder_name)) {
558 gchar *offset = g_strrstr (*item_name, folder_name);
560 concat_folder_name = TRUE;
563 buffer = g_string_new ("");
565 buffer = g_string_append (buffer, *item_name);
566 if (concat_folder_name) {
567 if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
568 buffer = g_string_append (buffer, folder_name);
569 /* TODO: append a sensitive string to the remote drafts to
570 * be able to know it's the remote one */
571 /* buffer = g_string_append (buffer, " (TODO:remote)"); */
573 buffer = g_string_append (buffer, folder_name);
577 g_object_unref (account);
579 *item_name = g_string_free (buffer, FALSE);
580 l_use_markup = FALSE;
582 l_use_markup = FALSE;
585 *use_markup = l_use_markup;
589 replace_special_folder_prefix (gchar **item_name)
591 const gchar *separator;
594 if (item_name == NULL || *item_name == NULL || **item_name == '\0')
596 separator = g_strstr_len (*item_name, -1, MODEST_FOLDER_PATH_SEPARATOR);
597 if (separator == NULL)
600 prefix = g_strndup (*item_name, separator - *item_name);
603 if (prefix && *prefix != '\0') {
604 TnyFolderType folder_type;
608 downcase = g_utf8_strdown (prefix, -1);
612 if (strcmp (downcase, "inbox") == 0) {
613 folder_type = TNY_FOLDER_TYPE_INBOX;
614 new_name = g_strconcat (_("mcen_me_folder_inbox"), separator, NULL);
616 *item_name = new_name;
618 folder_type = modest_local_folder_info_get_type (prefix);
619 switch (folder_type) {
620 case TNY_FOLDER_TYPE_INBOX:
621 case TNY_FOLDER_TYPE_ARCHIVE:
622 new_name = g_strconcat (modest_local_folder_info_get_type_display_name (folder_type), separator, NULL);
624 *item_name = new_name;
635 text_cell_data (GtkTreeViewColumn *column,
636 GtkCellRenderer *renderer,
637 GtkTreeModel *tree_model,
641 ModestFolderViewPrivate *priv;
642 GObject *rendobj = (GObject *) renderer;
644 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
645 GObject *instance = NULL;
646 gboolean use_markup = FALSE;
648 gtk_tree_model_get (tree_model, iter,
651 INSTANCE_COLUMN, &instance,
653 if (!fname || !instance)
656 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
657 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
659 gchar *item_name = NULL;
660 gint item_weight = 400;
662 if (type != TNY_FOLDER_TYPE_ROOT) {
667 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
668 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
671 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
672 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
674 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
677 /* Sometimes an special folder is reported by the server as
678 NORMAL, like some versions of Dovecot */
679 if (type == TNY_FOLDER_TYPE_NORMAL ||
680 type == TNY_FOLDER_TYPE_UNKNOWN) {
681 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
685 /* note: we cannot reliably get the counts from the
686 * tree model, we need to use explicit calls on
687 * tny_folder for some reason. Select the number to
688 * show: the unread or unsent messages. in case of
689 * outbox/drafts, show all */
690 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
691 (type == TNY_FOLDER_TYPE_OUTBOX) ||
692 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
693 number = tny_folder_get_all_count (TNY_FOLDER(instance));
696 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
700 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
701 item_name = g_strdup (fname);
708 /* Use bold font style if there are unread or unset messages */
710 if (priv->show_message_count) {
711 item_name = g_strdup_printf ("%s (%d)", fname, number);
713 item_name = g_strdup (fname);
717 item_name = g_strdup (fname);
722 } else if (TNY_IS_ACCOUNT (instance)) {
723 /* If it's a server account */
724 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
725 item_name = g_strdup (priv->local_account_name);
727 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
728 /* fname is only correct when the items are first
729 * added to the model, not when the account is
730 * changed later, so get the name from the account
732 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
735 item_name = g_strdup (fname);
741 if (type == TNY_FOLDER_TYPE_INBOX &&
742 g_str_has_suffix (fname, "Inbox")) {
744 item_name = g_strdup (_("mcen_me_folder_inbox"));
748 item_name = g_strdup ("unknown");
750 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
751 gboolean multiaccount;
753 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
754 /* Convert item_name to markup */
755 format_compact_style (&item_name, instance, priv->mailbox,
757 multiaccount, &use_markup);
759 replace_special_folder_prefix (&item_name);
762 if (item_name && item_weight) {
763 /* Set the name in the treeview cell: */
764 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
765 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
766 g_object_set (rendobj,
769 "foreground-set", TRUE,
770 "foreground-gdk", &(priv->active_color),
773 g_object_set (rendobj,
775 "foreground-set", FALSE,
777 "weight", item_weight,
781 /* Notify display name observers */
782 /* TODO: What listens for this signal, and how can it use only the new name? */
783 if (((GObject *) priv->cur_folder_store) == instance) {
784 g_signal_emit (G_OBJECT(self),
785 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
792 /* If it is a Memory card account, make sure that we have the correct name.
793 * This function will be trigerred again when the name has been retrieved: */
794 if (TNY_IS_STORE_ACCOUNT (instance) &&
795 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
797 /* Get the account name asynchronously: */
798 GetMmcAccountNameData *callback_data =
799 g_slice_new0(GetMmcAccountNameData);
800 callback_data->self = self;
802 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
804 callback_data->previous_name = g_strdup (name);
806 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
807 on_get_mmc_account_name, callback_data);
811 g_object_unref (G_OBJECT (instance));
817 messages_cell_data (GtkTreeViewColumn *column,
818 GtkCellRenderer *renderer,
819 GtkTreeModel *tree_model,
823 ModestFolderView *self;
824 ModestFolderViewPrivate *priv;
825 GObject *rendobj = (GObject *) renderer;
826 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
827 GObject *instance = NULL;
828 gchar *item_name = NULL;
830 gtk_tree_model_get (tree_model, iter,
832 INSTANCE_COLUMN, &instance,
837 self = MODEST_FOLDER_VIEW (data);
838 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
841 if (type != TNY_FOLDER_TYPE_ROOT) {
846 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
847 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
850 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
852 /* Sometimes an special folder is reported by the server as
853 NORMAL, like some versions of Dovecot */
854 if (type == TNY_FOLDER_TYPE_NORMAL ||
855 type == TNY_FOLDER_TYPE_UNKNOWN) {
856 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
860 /* note: we cannot reliably get the counts from the tree model, we need
861 * to use explicit calls on tny_folder for some reason.
863 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
864 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
865 (type == TNY_FOLDER_TYPE_OUTBOX) ||
866 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
867 number = tny_folder_get_all_count (TNY_FOLDER(instance));
870 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
874 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
876 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
877 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
883 item_name = g_strdup ("");
886 /* Set the name in the treeview cell: */
887 g_object_set (rendobj,"text", item_name, NULL);
895 g_object_unref (G_OBJECT (instance));
901 GdkPixbuf *pixbuf_open;
902 GdkPixbuf *pixbuf_close;
906 static inline GdkPixbuf *
907 get_composite_pixbuf (const gchar *icon_name,
909 GdkPixbuf *base_pixbuf)
911 GdkPixbuf *emblem, *retval = NULL;
913 emblem = modest_platform_get_icon (icon_name, size);
915 retval = gdk_pixbuf_copy (base_pixbuf);
916 gdk_pixbuf_composite (emblem, retval, 0, 0,
917 MIN (gdk_pixbuf_get_width (emblem),
918 gdk_pixbuf_get_width (retval)),
919 MIN (gdk_pixbuf_get_height (emblem),
920 gdk_pixbuf_get_height (retval)),
921 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
922 g_object_unref (emblem);
927 static inline ThreePixbufs *
928 get_composite_icons (const gchar *icon_code,
930 GdkPixbuf **pixbuf_open,
931 GdkPixbuf **pixbuf_close)
933 ThreePixbufs *retval;
935 if (pixbuf && !*pixbuf) {
937 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
939 *pixbuf = gdk_pixbuf_copy (icon);
945 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
946 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
950 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
951 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
955 retval = g_slice_new0 (ThreePixbufs);
956 if (pixbuf && *pixbuf)
957 retval->pixbuf = g_object_ref (*pixbuf);
959 retval->pixbuf = NULL;
960 if (pixbuf_open && *pixbuf_open)
961 retval->pixbuf_open = g_object_ref (*pixbuf_open);
963 retval->pixbuf_open = NULL;
964 if (pixbuf_close && *pixbuf_close)
965 retval->pixbuf_close = g_object_ref (*pixbuf_close);
967 retval->pixbuf_close = NULL;
972 static inline ThreePixbufs *
973 get_account_protocol_pixbufs (ModestFolderView *folder_view,
974 ModestProtocolType protocol_type,
977 ModestProtocol *protocol;
978 const GdkPixbuf *pixbuf = NULL;
979 ModestFolderViewPrivate *priv;
981 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
983 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
986 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
987 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
988 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
989 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
990 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
991 object, FOLDER_ICON_SIZE);
995 ThreePixbufs *retval;
996 retval = g_slice_new0 (ThreePixbufs);
997 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
998 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
999 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
1006 static inline ThreePixbufs*
1007 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
1009 TnyAccount *account = NULL;
1010 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
1011 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
1012 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
1013 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
1014 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
1016 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1017 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1018 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1019 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1020 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1022 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1023 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1024 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1025 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1026 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1028 ThreePixbufs *retval = NULL;
1030 if (TNY_IS_ACCOUNT (instance)) {
1031 account = g_object_ref (instance);
1032 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1033 account = tny_folder_get_account (TNY_FOLDER (instance));
1037 ModestProtocolType account_store_protocol;
1039 account_store_protocol = modest_tny_account_get_protocol_type (account);
1040 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1041 g_object_unref (account);
1047 /* Sometimes an special folder is reported by the server as
1048 NORMAL, like some versions of Dovecot */
1049 if (type == TNY_FOLDER_TYPE_NORMAL ||
1050 type == TNY_FOLDER_TYPE_UNKNOWN) {
1051 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1054 /* It's not enough with check the folder type. We need to
1055 ensure that we're not giving a special folder icon to a
1056 normal folder with the same name than a special folder */
1057 if (TNY_IS_FOLDER (instance) &&
1058 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1059 type = TNY_FOLDER_TYPE_NORMAL;
1061 /* Remote folders should not be treated as special folders */
1062 if (TNY_IS_FOLDER_STORE (instance) &&
1063 !TNY_IS_ACCOUNT (instance) &&
1064 type != TNY_FOLDER_TYPE_INBOX &&
1065 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1066 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1069 &anorm_pixbuf_close);
1074 case TNY_FOLDER_TYPE_INVALID:
1075 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1078 case TNY_FOLDER_TYPE_ROOT:
1079 if (TNY_IS_ACCOUNT (instance)) {
1081 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1082 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1085 &avirt_pixbuf_close);
1087 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1089 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1090 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1093 &ammc_pixbuf_close);
1095 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1098 &anorm_pixbuf_close);
1103 case TNY_FOLDER_TYPE_INBOX:
1104 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1107 &inbox_pixbuf_close);
1109 case TNY_FOLDER_TYPE_OUTBOX:
1110 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1112 &outbox_pixbuf_open,
1113 &outbox_pixbuf_close);
1115 case TNY_FOLDER_TYPE_JUNK:
1116 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1119 &junk_pixbuf_close);
1121 case TNY_FOLDER_TYPE_SENT:
1122 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1125 &sent_pixbuf_close);
1127 case TNY_FOLDER_TYPE_TRASH:
1128 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1131 &trash_pixbuf_close);
1133 case TNY_FOLDER_TYPE_DRAFTS:
1134 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1137 &draft_pixbuf_close);
1139 case TNY_FOLDER_TYPE_ARCHIVE:
1140 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1145 case TNY_FOLDER_TYPE_NORMAL:
1147 /* Memory card folders could have an special icon */
1148 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1149 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1154 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1156 &normal_pixbuf_open,
1157 &normal_pixbuf_close);
1166 free_pixbufs (ThreePixbufs *pixbufs)
1168 if (pixbufs->pixbuf)
1169 g_object_unref (pixbufs->pixbuf);
1170 if (pixbufs->pixbuf_open)
1171 g_object_unref (pixbufs->pixbuf_open);
1172 if (pixbufs->pixbuf_close)
1173 g_object_unref (pixbufs->pixbuf_close);
1174 g_slice_free (ThreePixbufs, pixbufs);
1178 icon_cell_data (GtkTreeViewColumn *column,
1179 GtkCellRenderer *renderer,
1180 GtkTreeModel *tree_model,
1184 GObject *rendobj = NULL, *instance = NULL;
1185 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1186 gboolean has_children;
1187 ThreePixbufs *pixbufs;
1188 ModestFolderView *folder_view = (ModestFolderView *) data;
1190 rendobj = (GObject *) renderer;
1192 gtk_tree_model_get (tree_model, iter,
1194 INSTANCE_COLUMN, &instance,
1200 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1201 pixbufs = get_folder_icons (folder_view, type, instance);
1202 g_object_unref (instance);
1205 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1208 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1209 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1212 free_pixbufs (pixbufs);
1216 add_columns (GtkWidget *treeview)
1218 GtkTreeViewColumn *column;
1219 GtkCellRenderer *renderer;
1220 GtkTreeSelection *sel;
1221 ModestFolderViewPrivate *priv;
1223 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1226 column = gtk_tree_view_column_new ();
1228 /* Set icon and text render function */
1229 renderer = gtk_cell_renderer_pixbuf_new();
1230 g_object_set (renderer,
1231 "xpad", MODEST_MARGIN_DEFAULT,
1232 "ypad", MODEST_MARGIN_DEFAULT,
1234 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1235 gtk_tree_view_column_set_cell_data_func(column, renderer,
1236 icon_cell_data, treeview, NULL);
1238 renderer = gtk_cell_renderer_text_new();
1239 g_object_set (renderer,
1240 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1241 "ypad", MODEST_MARGIN_DEFAULT,
1242 "xpad", MODEST_MARGIN_DEFAULT,
1243 "ellipsize-set", TRUE, NULL);
1244 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1245 gtk_tree_view_column_set_cell_data_func(column, renderer,
1246 text_cell_data, treeview, NULL);
1248 priv->messages_renderer = gtk_cell_renderer_text_new ();
1249 g_object_set (priv->messages_renderer,
1251 "ypad", MODEST_MARGIN_DEFAULT,
1252 "xpad", MODEST_MARGIN_DOUBLE,
1253 "alignment", PANGO_ALIGN_RIGHT,
1257 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1258 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1259 messages_cell_data, treeview, NULL);
1261 /* Set selection mode */
1262 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1263 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1265 /* Set treeview appearance */
1266 gtk_tree_view_column_set_spacing (column, 2);
1267 gtk_tree_view_column_set_resizable (column, TRUE);
1268 gtk_tree_view_column_set_fixed_width (column, TRUE);
1269 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1270 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1271 gtk_tree_view_set_rules_hint ((GtkTreeView *) treeview, TRUE);
1274 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1278 modest_folder_view_init (ModestFolderView *obj)
1280 ModestFolderViewPrivate *priv;
1283 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1285 priv->timer_expander = 0;
1286 priv->account_store = NULL;
1288 priv->do_refresh = TRUE;
1289 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1290 priv->cur_folder_store = NULL;
1291 priv->visible_account_id = NULL;
1292 priv->mailbox = NULL;
1293 priv->folder_to_select = NULL;
1294 priv->outbox_deleted_handler = 0;
1295 priv->reexpand = TRUE;
1296 priv->signal_handlers = 0;
1297 #ifdef MODEST_TOOLKIT_HILDON2
1298 priv->live_search = NULL;
1301 /* Initialize the local account name */
1302 conf = modest_runtime_get_conf();
1303 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1305 /* Init email clipboard */
1306 priv->clipboard = modest_runtime_get_email_clipboard ();
1307 priv->hidding_ids = NULL;
1308 priv->n_selected = 0;
1309 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1310 priv->reselect = FALSE;
1311 priv->show_non_move = TRUE;
1312 priv->list_to_move = NULL;
1313 priv->show_message_count = TRUE;
1315 /* Build treeview */
1316 add_columns (GTK_WIDGET (obj));
1318 priv->display_name_changed_signal =
1319 g_signal_connect (modest_runtime_get_account_mgr (),
1320 "display_name_changed",
1321 G_CALLBACK (on_display_name_changed),
1325 * Track changes in the local account name (in the device it
1326 * will be the device name)
1328 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1330 G_CALLBACK(on_configuration_key_changed),
1333 gdk_color_parse ("000", &priv->active_color);
1336 g_signal_connect (G_OBJECT (obj), "notify::style",
1337 G_CALLBACK (on_notify_style), (gpointer) obj);
1341 tny_account_store_view_init (gpointer g, gpointer iface_data)
1343 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1345 klass->set_account_store = modest_folder_view_set_account_store;
1349 modest_folder_view_dispose (GObject *obj)
1351 ModestFolderViewPrivate *priv;
1353 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1355 if (priv->signal_handlers) {
1356 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1357 priv->signal_handlers = NULL;
1360 /* Free external references */
1361 if (priv->account_store) {
1362 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1363 priv->account_inserted_signal);
1364 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1365 priv->account_removed_signal);
1366 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1367 priv->account_changed_signal);
1368 g_object_unref (G_OBJECT(priv->account_store));
1369 priv->account_store = NULL;
1373 g_object_unref (G_OBJECT (priv->query));
1377 if (priv->folder_to_select) {
1378 g_object_unref (G_OBJECT(priv->folder_to_select));
1379 priv->folder_to_select = NULL;
1382 if (priv->cur_folder_store) {
1383 g_object_unref (priv->cur_folder_store);
1384 priv->cur_folder_store = NULL;
1387 if (priv->list_to_move) {
1388 g_object_unref (priv->list_to_move);
1389 priv->list_to_move = NULL;
1392 G_OBJECT_CLASS(parent_class)->dispose (obj);
1396 modest_folder_view_finalize (GObject *obj)
1398 ModestFolderViewPrivate *priv;
1399 GtkTreeSelection *sel;
1400 TnyAccount *local_account;
1402 g_return_if_fail (obj);
1404 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1406 if (priv->timer_expander != 0) {
1407 g_source_remove (priv->timer_expander);
1408 priv->timer_expander = 0;
1411 local_account = (TnyAccount *)
1412 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1413 if (local_account) {
1414 if (g_signal_handler_is_connected (local_account,
1415 priv->outbox_deleted_handler))
1416 g_signal_handler_disconnect (local_account,
1417 priv->outbox_deleted_handler);
1418 g_object_unref (local_account);
1421 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1422 priv->display_name_changed_signal)) {
1423 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1424 priv->display_name_changed_signal);
1425 priv->display_name_changed_signal = 0;
1428 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1430 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1432 g_free (priv->local_account_name);
1433 g_free (priv->visible_account_id);
1434 g_free (priv->mailbox);
1436 if (priv->conf_key_signal) {
1437 g_signal_handler_disconnect (modest_runtime_get_conf (),
1438 priv->conf_key_signal);
1439 priv->conf_key_signal = 0;
1442 /* Clear hidding array created by cut operation */
1443 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1445 gdk_color_parse ("000", &priv->active_color);
1447 G_OBJECT_CLASS(parent_class)->finalize (obj);
1452 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1454 ModestFolderViewPrivate *priv;
1457 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1458 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1460 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1461 device = tny_account_store_get_device (account_store);
1463 if (G_UNLIKELY (priv->account_store)) {
1465 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1466 priv->account_inserted_signal))
1467 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1468 priv->account_inserted_signal);
1469 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1470 priv->account_removed_signal))
1471 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1472 priv->account_removed_signal);
1473 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1474 priv->account_changed_signal))
1475 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1476 priv->account_changed_signal);
1477 g_object_unref (G_OBJECT (priv->account_store));
1480 priv->account_store = g_object_ref (G_OBJECT (account_store));
1482 priv->account_removed_signal =
1483 g_signal_connect (G_OBJECT(account_store), "account_removed",
1484 G_CALLBACK (on_account_removed), self);
1486 priv->account_inserted_signal =
1487 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1488 G_CALLBACK (on_account_inserted), self);
1490 priv->account_changed_signal =
1491 g_signal_connect (G_OBJECT(account_store), "account_changed",
1492 G_CALLBACK (on_account_changed), self);
1494 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1495 priv->reselect = FALSE;
1497 g_object_unref (G_OBJECT (device));
1501 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1504 ModestFolderView *self;
1505 GtkTreeModel *model, *filter_model;
1508 self = MODEST_FOLDER_VIEW (user_data);
1510 if (!get_inner_models (self, &filter_model, NULL, &model))
1513 /* Remove outbox from model */
1514 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1515 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1516 g_object_unref (outbox);
1519 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1523 on_account_inserted (TnyAccountStore *account_store,
1524 TnyAccount *account,
1527 ModestFolderViewPrivate *priv;
1528 GtkTreeModel *model, *filter_model;
1530 /* Ignore transport account insertions, we're not showing them
1531 in the folder view */
1532 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1535 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1538 /* If we're adding a new account, and there is no previous
1539 one, we need to select the visible server account */
1540 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1541 !priv->visible_account_id)
1542 modest_widget_memory_restore (modest_runtime_get_conf(),
1543 G_OBJECT (user_data),
1544 MODEST_CONF_FOLDER_VIEW_KEY);
1548 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1549 &filter_model, NULL, &model))
1552 /* Insert the account in the model */
1553 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1555 /* When the model is a list store (plain representation) the
1556 outbox is not a child of any account so we have to manually
1557 delete it because removing the local folders account won't
1558 delete it (because tny_folder_get_account() is not defined
1559 for a merge folder */
1560 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1561 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1563 priv->outbox_deleted_handler =
1564 g_signal_connect (account,
1566 G_CALLBACK (on_outbox_deleted_cb),
1570 /* Refilter the model */
1571 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1576 same_account_selected (ModestFolderView *self,
1577 TnyAccount *account)
1579 ModestFolderViewPrivate *priv;
1580 gboolean same_account = FALSE;
1582 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1584 if (priv->cur_folder_store) {
1585 TnyAccount *selected_folder_account = NULL;
1587 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1588 selected_folder_account =
1589 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1591 selected_folder_account =
1592 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1595 if (selected_folder_account == account)
1596 same_account = TRUE;
1598 g_object_unref (selected_folder_account);
1600 return same_account;
1604 on_account_changed (TnyAccountStore *account_store,
1605 TnyAccount *tny_account,
1608 ModestFolderView *self;
1609 ModestFolderViewPrivate *priv;
1610 GtkTreeModel *model, *filter_model;
1611 GtkTreeSelection *sel;
1612 gboolean same_account;
1614 /* Ignore transport account insertions, we're not showing them
1615 in the folder view */
1616 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1619 self = MODEST_FOLDER_VIEW (user_data);
1620 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1622 /* Get the inner model */
1623 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1624 &filter_model, NULL, &model))
1627 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1629 /* Invalidate the cur_folder_store only if the selected folder
1630 belongs to the account that is being removed */
1631 same_account = same_account_selected (self, tny_account);
1633 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1634 gtk_tree_selection_unselect_all (sel);
1637 /* Remove the account from the model */
1638 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1640 /* Insert the account in the model */
1641 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1643 /* Refilter the model */
1644 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1649 on_account_removed (TnyAccountStore *account_store,
1650 TnyAccount *account,
1653 ModestFolderView *self = NULL;
1654 ModestFolderViewPrivate *priv;
1655 GtkTreeModel *model, *filter_model;
1656 GtkTreeSelection *sel = NULL;
1657 gboolean same_account = FALSE;
1659 /* Ignore transport account removals, we're not showing them
1660 in the folder view */
1661 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1664 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1665 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1669 self = MODEST_FOLDER_VIEW (user_data);
1670 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1672 /* Invalidate the cur_folder_store only if the selected folder
1673 belongs to the account that is being removed */
1674 same_account = same_account_selected (self, account);
1676 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1677 gtk_tree_selection_unselect_all (sel);
1680 /* Invalidate row to select only if the folder to select
1681 belongs to the account that is being removed*/
1682 if (priv->folder_to_select) {
1683 TnyAccount *folder_to_select_account = NULL;
1685 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1686 if (folder_to_select_account == account) {
1687 modest_folder_view_disable_next_folder_selection (self);
1688 g_object_unref (priv->folder_to_select);
1689 priv->folder_to_select = NULL;
1691 g_object_unref (folder_to_select_account);
1694 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1695 &filter_model, NULL, &model))
1698 /* Disconnect the signal handler */
1699 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1700 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1701 if (g_signal_handler_is_connected (account,
1702 priv->outbox_deleted_handler))
1703 g_signal_handler_disconnect (account,
1704 priv->outbox_deleted_handler);
1707 /* Remove the account from the model */
1708 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1710 /* If the removed account is the currently viewed one then
1711 clear the configuration value. The new visible account will be the default account */
1712 if (priv->visible_account_id &&
1713 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1715 /* Clear the current visible account_id */
1716 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1717 modest_folder_view_set_mailbox (self, NULL);
1719 /* Call the restore method, this will set the new visible account */
1720 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1721 MODEST_CONF_FOLDER_VIEW_KEY);
1724 /* Refilter the model */
1725 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1730 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1732 GtkTreeViewColumn *col;
1734 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1736 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1738 g_printerr ("modest: failed get column for title\n");
1742 gtk_tree_view_column_set_title (col, title);
1743 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1748 modest_folder_view_on_map (ModestFolderView *self,
1749 GdkEventExpose *event,
1752 ModestFolderViewPrivate *priv;
1754 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1756 /* This won't happen often */
1757 if (G_UNLIKELY (priv->reselect)) {
1758 /* Select the first inbox or the local account if not found */
1760 /* TODO: this could cause a lock at startup, so we
1761 comment it for the moment. We know that this will
1762 be a bug, because the INBOX is not selected, but we
1763 need to rewrite some parts of Modest to avoid the
1764 deathlock situation */
1765 /* TODO: check if this is still the case */
1766 priv->reselect = FALSE;
1767 /* Notify the display name observers */
1768 g_signal_emit (G_OBJECT(self),
1769 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1773 if (priv->reexpand) {
1774 expand_root_items (self);
1775 priv->reexpand = FALSE;
1782 modest_folder_view_new (TnyFolderStoreQuery *query)
1784 return modest_folder_view_new_full (query, TRUE);
1788 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1791 ModestFolderViewPrivate *priv;
1792 GtkTreeSelection *sel;
1794 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1795 #ifdef MODEST_TOOLKIT_HILDON2
1796 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1799 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1802 priv->query = g_object_ref (query);
1804 priv->do_refresh = do_refresh;
1806 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1807 priv->changed_signal = g_signal_connect (sel, "changed",
1808 G_CALLBACK (on_selection_changed), self);
1810 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1812 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1814 /* Hide headers by default */
1815 gtk_tree_view_set_headers_visible ((GtkTreeView *)self, FALSE);
1817 return GTK_WIDGET(self);
1820 /* this feels dirty; any other way to expand all the root items? */
1822 expand_root_items (ModestFolderView *self)
1825 GtkTreeModel *model;
1828 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1829 path = gtk_tree_path_new_first ();
1831 /* all folders should have child items, so.. */
1833 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1834 gtk_tree_path_next (path);
1835 } while (gtk_tree_model_get_iter (model, &iter, path));
1837 gtk_tree_path_free (path);
1841 is_parent_of (TnyFolder *a, TnyFolder *b)
1844 gboolean retval = FALSE;
1846 a_id = tny_folder_get_id (a);
1848 gchar *string_to_match;
1851 string_to_match = g_strconcat (a_id, "/", NULL);
1852 b_id = tny_folder_get_id (b);
1853 retval = g_str_has_prefix (b_id, string_to_match);
1854 g_free (string_to_match);
1860 typedef struct _ForeachFolderInfo {
1863 } ForeachFolderInfo;
1866 foreach_folder_with_id (GtkTreeModel *model,
1871 ForeachFolderInfo *info;
1874 info = (ForeachFolderInfo *) data;
1875 gtk_tree_model_get (model, iter,
1876 INSTANCE_COLUMN, &instance,
1879 if (TNY_IS_FOLDER (instance)) {
1882 id = tny_folder_get_id (TNY_FOLDER (instance));
1884 collate = g_utf8_collate_key (id, -1);
1885 info->found = !strcmp (info->needle, collate);
1891 g_object_unref (instance);
1899 has_folder_with_id (ModestFolderView *self, const gchar *id)
1901 GtkTreeModel *model;
1902 ForeachFolderInfo info = {NULL, FALSE};
1904 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1905 info.needle = g_utf8_collate_key (id, -1);
1907 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1908 g_free (info.needle);
1914 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1917 gboolean retval = FALSE;
1919 a_id = tny_folder_get_id (a);
1922 b_id = tny_folder_get_id (b);
1925 const gchar *last_bar;
1926 gchar *string_to_match;
1927 last_bar = g_strrstr (b_id, "/");
1932 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1933 retval = has_folder_with_id (self, string_to_match);
1934 g_free (string_to_match);
1942 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1944 ModestFolderViewPrivate *priv;
1945 TnyIterator *iterator;
1946 gboolean retval = TRUE;
1948 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1949 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1951 for (iterator = tny_list_create_iterator (priv->list_to_move);
1952 retval && !tny_iterator_is_done (iterator);
1953 tny_iterator_next (iterator)) {
1955 instance = tny_iterator_get_current (iterator);
1956 if (instance == (GObject *) folder) {
1958 } else if (TNY_IS_FOLDER (instance)) {
1959 retval = !is_parent_of (TNY_FOLDER (instance), folder);
1961 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1964 g_object_unref (instance);
1966 g_object_unref (iterator);
1973 * We use this function to implement the
1974 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1975 * account in this case, and the local folders.
1978 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1980 ModestFolderViewPrivate *priv;
1981 gboolean retval = TRUE;
1982 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1983 GObject *instance = NULL;
1984 const gchar *id = NULL;
1986 gboolean found = FALSE;
1987 gboolean cleared = FALSE;
1988 ModestTnyFolderRules rules = 0;
1991 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1992 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1994 gtk_tree_model_get (model, iter,
1995 NAME_COLUMN, &fname,
1997 INSTANCE_COLUMN, &instance,
2000 /* Do not show if there is no instance, this could indeed
2001 happen when the model is being modified while it's being
2002 drawn. This could occur for example when moving folders
2009 if (TNY_IS_ACCOUNT (instance)) {
2010 TnyAccount *acc = TNY_ACCOUNT (instance);
2011 const gchar *account_id = tny_account_get_id (acc);
2013 /* If it isn't a special folder,
2014 * don't show it unless it is the visible account: */
2015 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2016 !modest_tny_account_is_virtual_local_folders (acc) &&
2017 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2019 /* Show only the visible account id */
2020 if (priv->visible_account_id) {
2021 if (strcmp (account_id, priv->visible_account_id))
2028 /* Never show these to the user. They are merged into one folder
2029 * in the local-folders account instead: */
2030 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2033 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2034 /* Only show special folders for current account if needed */
2035 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2036 TnyAccount *account;
2038 account = tny_folder_get_account (TNY_FOLDER (instance));
2040 if (TNY_IS_ACCOUNT (account)) {
2041 const gchar *account_id = tny_account_get_id (account);
2043 if (!modest_tny_account_is_virtual_local_folders (account) &&
2044 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2045 /* Show only the visible account id */
2046 if (priv->visible_account_id) {
2047 if (strcmp (account_id, priv->visible_account_id)) {
2049 } else if (priv->mailbox) {
2050 /* Filter mailboxes */
2051 if (!g_str_has_prefix (fname, priv->mailbox)) {
2053 } else if (!strcmp (fname, priv->mailbox)) {
2054 /* Hide mailbox parent */
2060 g_object_unref (account);
2067 /* Check hiding (if necessary) */
2068 cleared = modest_email_clipboard_cleared (priv->clipboard);
2069 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2070 id = tny_folder_get_id (TNY_FOLDER(instance));
2071 if (priv->hidding_ids != NULL)
2072 for (i=0; i < priv->n_selected && !found; i++)
2073 if (priv->hidding_ids[i] != NULL && id != NULL)
2074 found = (!strcmp (priv->hidding_ids[i], id));
2079 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2080 folder as no message can be move there according to UI specs */
2081 if (retval && !priv->show_non_move) {
2082 if (priv->list_to_move &&
2083 tny_list_get_length (priv->list_to_move) > 0 &&
2084 TNY_IS_FOLDER (instance)) {
2085 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2087 if (retval && TNY_IS_FOLDER (instance) &&
2088 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2090 case TNY_FOLDER_TYPE_OUTBOX:
2091 case TNY_FOLDER_TYPE_SENT:
2092 case TNY_FOLDER_TYPE_DRAFTS:
2095 case TNY_FOLDER_TYPE_UNKNOWN:
2096 case TNY_FOLDER_TYPE_NORMAL:
2097 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2098 if (type == TNY_FOLDER_TYPE_INVALID)
2099 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2101 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2102 type == TNY_FOLDER_TYPE_SENT
2103 || type == TNY_FOLDER_TYPE_DRAFTS)
2110 if (retval && TNY_IS_ACCOUNT (instance) &&
2111 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2112 ModestProtocolType protocol_type;
2114 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2115 retval = !modest_protocol_registry_protocol_type_has_tag
2116 (modest_runtime_get_protocol_registry (),
2118 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2122 /* apply special filters */
2123 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2124 if (TNY_IS_ACCOUNT (instance))
2128 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2129 if (TNY_IS_FOLDER (instance))
2133 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2134 if (TNY_IS_ACCOUNT (instance)) {
2135 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2137 } else if (TNY_IS_FOLDER (instance)) {
2138 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2143 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2144 if (TNY_IS_ACCOUNT (instance)) {
2145 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2147 } else if (TNY_IS_FOLDER (instance)) {
2148 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2153 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2154 /* A mailbox is a fake folder with an @ in the middle of the name */
2155 if (!TNY_IS_FOLDER (instance) ||
2156 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2159 const gchar *folder_name;
2160 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2161 if (!folder_name || strchr (folder_name, '@') == NULL)
2167 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2168 if (TNY_IS_FOLDER (instance)) {
2169 /* Check folder rules */
2170 ModestTnyFolderRules rules;
2172 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2173 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2174 } else if (TNY_IS_ACCOUNT (instance)) {
2175 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2183 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2184 if (TNY_IS_FOLDER (instance)) {
2185 TnyFolderType guess_type;
2187 if (TNY_FOLDER_TYPE_NORMAL) {
2188 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2194 case TNY_FOLDER_TYPE_OUTBOX:
2195 case TNY_FOLDER_TYPE_SENT:
2196 case TNY_FOLDER_TYPE_DRAFTS:
2197 case TNY_FOLDER_TYPE_ARCHIVE:
2198 case TNY_FOLDER_TYPE_INBOX:
2201 case TNY_FOLDER_TYPE_UNKNOWN:
2202 case TNY_FOLDER_TYPE_NORMAL:
2208 } else if (TNY_IS_ACCOUNT (instance)) {
2213 if (retval && TNY_IS_FOLDER (instance)) {
2214 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2217 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2218 if (TNY_IS_FOLDER (instance)) {
2219 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2220 } else if (TNY_IS_ACCOUNT (instance)) {
2225 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2226 if (TNY_IS_FOLDER (instance)) {
2227 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2228 } else if (TNY_IS_ACCOUNT (instance)) {
2233 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2234 if (TNY_IS_FOLDER (instance)) {
2235 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2236 } else if (TNY_IS_ACCOUNT (instance)) {
2241 #ifdef MODEST_TOOLKIT_HILDON2
2242 if (retval && (priv->live_search)) {
2243 const gchar *needle;
2244 needle = hildon_live_search_get_text (HILDON_LIVE_SEARCH (priv->live_search));
2245 if (needle && needle[0] != '\0') {
2249 haystack = g_strdup (fname);
2250 if (type != TNY_FOLDER_TYPE_ROOT) {
2251 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
2252 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
2254 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2255 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
2256 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
2258 haystack = g_strdup (modest_local_folder_info_get_type_display_name (type));
2263 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
2265 haystack = g_strdup (priv->local_account_name);
2266 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
2268 haystack = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
2272 if (type == TNY_FOLDER_TYPE_INBOX &&
2273 g_str_has_suffix (haystack, "Inbox")) {
2275 haystack = g_strdup (_("mcen_me_folder_inbox"));
2277 format_compact_style (&haystack, instance, priv->mailbox, FALSE,
2278 priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL, NULL);
2279 retval = modest_text_utils_live_search_find (haystack, needle);
2286 g_object_unref (instance);
2294 modest_folder_view_update_model (ModestFolderView *self,
2295 TnyAccountStore *account_store)
2297 ModestFolderViewPrivate *priv;
2298 GtkTreeModel *model;
2299 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2301 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2302 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2305 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2307 /* Notify that there is no folder selected */
2308 g_signal_emit (G_OBJECT(self),
2309 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2311 if (priv->cur_folder_store) {
2312 g_object_unref (priv->cur_folder_store);
2313 priv->cur_folder_store = NULL;
2316 /* FIXME: the local accounts are not shown when the query
2317 selects only the subscribed folders */
2318 TnyGtkFolderListStoreFlags flags;
2319 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2320 if (priv->do_refresh)
2321 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2323 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2324 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2326 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2327 MODEST_FOLDER_PATH_SEPARATOR);
2329 /* When the model is a list store (plain representation) the
2330 outbox is not a child of any account so we have to manually
2331 delete it because removing the local folders account won't
2332 delete it (because tny_folder_get_account() is not defined
2333 for a merge folder */
2334 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2335 TnyAccount *account;
2336 ModestTnyAccountStore *acc_store;
2338 acc_store = modest_runtime_get_account_store ();
2339 account = modest_tny_account_store_get_local_folders_account (acc_store);
2341 if (g_signal_handler_is_connected (account,
2342 priv->outbox_deleted_handler))
2343 g_signal_handler_disconnect (account,
2344 priv->outbox_deleted_handler);
2346 priv->outbox_deleted_handler =
2347 g_signal_connect (account,
2349 G_CALLBACK (on_outbox_deleted_cb),
2351 g_object_unref (account);
2354 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2355 /* Get the accounts */
2356 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2358 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2360 if (priv->visible_account_id) {
2361 TnyAccount *account;
2363 /* Add local folders account */
2364 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2367 tny_list_append (TNY_LIST (model), (GObject *) account);
2368 g_object_unref (account);
2371 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2374 tny_list_append (TNY_LIST (model), (GObject *) account);
2375 g_object_unref (account);
2378 /* Add visible account */
2379 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2380 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2381 priv->visible_account_id);
2383 tny_list_append (TNY_LIST (model), (GObject *) account);
2384 g_object_unref (account);
2386 g_warning ("You need to set an account first");
2387 g_object_unref (model);
2391 g_warning ("You need to set an account first");
2392 g_object_unref (model);
2397 sortable = gtk_tree_model_sort_new_with_model (model);
2398 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2400 GTK_SORT_ASCENDING);
2401 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2403 cmp_rows, NULL, NULL);
2405 /* Create filter model */
2406 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2407 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2412 GtkTreeModel *old_tny_model = NULL;
2413 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2414 if (priv->signal_handlers > 0) {
2415 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2416 G_OBJECT (old_tny_model),
2417 "activity-changed");
2422 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2424 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2427 G_CALLBACK (on_activity_changed),
2430 g_object_unref (model);
2431 g_object_unref (filter_model);
2432 g_object_unref (sortable);
2434 /* Force a reselection of the INBOX next time the widget is shown */
2435 priv->reselect = TRUE;
2442 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2444 GtkTreeModel *model = NULL;
2445 TnyFolderStore *folder = NULL;
2447 ModestFolderView *tree_view = NULL;
2448 ModestFolderViewPrivate *priv = NULL;
2449 gboolean selected = FALSE;
2451 g_return_if_fail (sel);
2452 g_return_if_fail (user_data);
2454 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2456 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2458 tree_view = MODEST_FOLDER_VIEW (user_data);
2461 gtk_tree_model_get (model, &iter,
2462 INSTANCE_COLUMN, &folder,
2465 /* If the folder is the same do not notify */
2466 if (folder && priv->cur_folder_store == folder) {
2467 g_object_unref (folder);
2472 /* Current folder was unselected */
2473 if (priv->cur_folder_store) {
2474 /* We must do this firstly because a libtinymail-camel
2475 implementation detail. If we issue the signal
2476 before doing the sync_async, then that signal could
2477 cause (and it actually does it) a free of the
2478 summary of the folder (because the main window will
2479 clear the headers view */
2481 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2482 priv->cur_folder_store, FALSE);
2484 g_object_unref (priv->cur_folder_store);
2485 priv->cur_folder_store = NULL;
2488 /* New current references */
2489 priv->cur_folder_store = folder;
2491 /* New folder has been selected. Do not notify if there is
2492 nothing new selected */
2494 g_signal_emit (G_OBJECT(tree_view),
2495 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2496 0, priv->cur_folder_store, TRUE);
2501 on_row_activated (GtkTreeView *treeview,
2502 GtkTreePath *treepath,
2503 GtkTreeViewColumn *column,
2506 GtkTreeModel *model = NULL;
2507 TnyFolderStore *folder = NULL;
2509 ModestFolderView *self = NULL;
2510 ModestFolderViewPrivate *priv = NULL;
2512 g_return_if_fail (treeview);
2513 g_return_if_fail (user_data);
2515 self = MODEST_FOLDER_VIEW (user_data);
2516 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2518 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2520 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2523 gtk_tree_model_get (model, &iter,
2524 INSTANCE_COLUMN, &folder,
2527 g_signal_emit (G_OBJECT(self),
2528 signals[FOLDER_ACTIVATED_SIGNAL],
2531 #ifdef MODEST_TOOLKIT_HILDON2
2532 HildonUIMode ui_mode;
2533 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2534 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2535 if (priv->cur_folder_store)
2536 g_object_unref (priv->cur_folder_store);
2537 priv->cur_folder_store = g_object_ref (folder);
2541 g_object_unref (folder);
2545 modest_folder_view_get_selected (ModestFolderView *self)
2547 ModestFolderViewPrivate *priv;
2549 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2551 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2552 if (priv->cur_folder_store)
2553 g_object_ref (priv->cur_folder_store);
2555 return priv->cur_folder_store;
2559 get_cmp_rows_type_pos (GObject *folder)
2561 /* Remote accounts -> Local account -> MMC account .*/
2564 if (TNY_IS_ACCOUNT (folder) &&
2565 modest_tny_account_is_virtual_local_folders (
2566 TNY_ACCOUNT (folder))) {
2568 } else if (TNY_IS_ACCOUNT (folder)) {
2569 TnyAccount *account = TNY_ACCOUNT (folder);
2570 const gchar *account_id = tny_account_get_id (account);
2571 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2577 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2578 return -1; /* Should never happen */
2583 inbox_is_special (TnyFolderStore *folder_store)
2585 gboolean is_special = TRUE;
2587 if (TNY_IS_FOLDER (folder_store)) {
2591 gchar *last_inbox_bar;
2593 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2594 downcase = g_utf8_strdown (id, -1);
2595 last_bar = g_strrstr (downcase, "/");
2597 last_inbox_bar = g_strrstr (downcase, "inbox/");
2598 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2609 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2611 TnyAccount *account;
2612 gboolean is_special;
2613 /* Inbox, Outbox, Drafts, Sent, User */
2616 if (!TNY_IS_FOLDER (folder_store))
2619 case TNY_FOLDER_TYPE_INBOX:
2621 account = tny_folder_get_account (folder_store);
2622 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2624 /* In inbox case we need to know if the inbox is really the top
2625 * inbox of the account, or if it's a submailbox inbox. To do
2626 * this we'll apply an heuristic rule: Find last "/" and check
2627 * if it's preceeded by another Inbox */
2628 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2629 g_object_unref (account);
2630 return is_special?0:4;
2633 case TNY_FOLDER_TYPE_OUTBOX:
2634 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2636 case TNY_FOLDER_TYPE_DRAFTS:
2638 account = tny_folder_get_account (folder_store);
2639 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2640 g_object_unref (account);
2641 return is_special?1:4;
2644 case TNY_FOLDER_TYPE_SENT:
2646 account = tny_folder_get_account (folder_store);
2647 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2648 g_object_unref (account);
2649 return is_special?3:4;
2658 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2660 const gchar *a1_name, *a2_name;
2662 a1_name = tny_account_get_name (a1);
2663 a2_name = tny_account_get_name (a2);
2665 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2669 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2671 TnyAccount *a1 = NULL, *a2 = NULL;
2674 if (TNY_IS_ACCOUNT (s1)) {
2675 a1 = TNY_ACCOUNT (g_object_ref (s1));
2676 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2677 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2680 if (TNY_IS_ACCOUNT (s2)) {
2681 a2 = TNY_ACCOUNT (g_object_ref (s2));
2682 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2683 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2700 /* First we sort with the type of account */
2701 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2705 cmp = compare_account_names (a1, a2);
2709 g_object_unref (a1);
2711 g_object_unref (a2);
2717 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2719 gint is_account1, is_account2;
2721 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2722 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2724 return is_account2 - is_account1;
2728 compare_folders (const gchar *name1, const gchar *name2)
2730 const gchar *separator1, *separator2;
2731 const gchar *next1, *next2;
2735 if (name1 == NULL || name1[0] == '\0')
2737 if (name2 == NULL || name2[0] == '\0')
2740 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2742 top1 = g_strndup (name1, separator1 - name1);
2744 top1 = g_strdup (name1);
2747 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2749 top2 = g_strndup (name2, separator2 - name2);
2751 top2 = g_strdup (name2);
2755 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2762 if (separator1 == NULL && separator2 == NULL)
2765 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2766 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2768 return compare_folders (next1, next2);
2773 * This function orders the mail accounts according to these rules:
2774 * 1st - remote accounts
2775 * 2nd - local account
2779 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2783 gchar *name1 = NULL;
2784 gchar *name2 = NULL;
2785 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2786 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2787 GObject *folder1 = NULL;
2788 GObject *folder2 = NULL;
2790 gtk_tree_model_get (tree_model, iter1,
2791 NAME_COLUMN, &name1,
2793 INSTANCE_COLUMN, &folder1,
2795 gtk_tree_model_get (tree_model, iter2,
2796 NAME_COLUMN, &name2,
2797 TYPE_COLUMN, &type2,
2798 INSTANCE_COLUMN, &folder2,
2801 /* Return if we get no folder. This could happen when folder
2802 operations are happening. The model is updated after the
2803 folder copy/move actually occurs, so there could be
2804 situations where the model to be drawn is not correct */
2805 if (!folder1 || !folder2)
2808 /* Sort by type. First the special folders, then the archives */
2809 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2813 /* Now we sort using the account of each folder */
2814 if (TNY_IS_FOLDER_STORE (folder1) &&
2815 TNY_IS_FOLDER_STORE (folder2)) {
2816 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2820 /* Each group is preceeded by its account */
2821 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2826 /* Pure sort by name */
2827 cmp = compare_folders (name1, name2);
2830 g_object_unref(G_OBJECT(folder1));
2832 g_object_unref(G_OBJECT(folder2));
2842 * We listen to the changes in the local folder account name key,
2843 * because we want to show the right name in the view. The local
2844 * folder account name corresponds to the device name in the Maemo
2845 * version. We do this because we do not want to query gconf on each
2846 * tree view refresh. It's better to cache it and change whenever
2850 on_configuration_key_changed (ModestConf* conf,
2852 ModestConfEvent event,
2853 ModestConfNotificationId id,
2854 ModestFolderView *self)
2856 ModestFolderViewPrivate *priv;
2859 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2860 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2862 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2863 g_free (priv->local_account_name);
2865 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2866 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2868 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2869 MODEST_CONF_DEVICE_NAME, NULL);
2871 /* Force a redraw */
2872 #if GTK_CHECK_VERSION(2, 8, 0)
2873 GtkTreeViewColumn * tree_column;
2875 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2877 gtk_tree_view_column_queue_resize (tree_column);
2879 gtk_widget_queue_draw (GTK_WIDGET (self));
2885 modest_folder_view_set_style (ModestFolderView *self,
2886 ModestFolderViewStyle style)
2888 ModestFolderViewPrivate *priv;
2890 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2891 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2892 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2894 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2897 priv->style = style;
2901 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2902 const gchar *account_id)
2904 ModestFolderViewPrivate *priv;
2905 GtkTreeModel *model;
2907 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2909 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2911 /* This will be used by the filter_row callback,
2912 * to decided which rows to show: */
2913 if (priv->visible_account_id) {
2914 g_free (priv->visible_account_id);
2915 priv->visible_account_id = NULL;
2918 priv->visible_account_id = g_strdup (account_id);
2921 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2922 if (GTK_IS_TREE_MODEL_FILTER (model))
2923 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2925 modest_folder_view_update_model(self,
2926 (TnyAccountStore *) modest_runtime_get_account_store());
2928 /* Save settings to gconf */
2929 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2930 MODEST_CONF_FOLDER_VIEW_KEY);
2932 /* Notify observers */
2933 g_signal_emit (G_OBJECT(self),
2934 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
2939 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2941 ModestFolderViewPrivate *priv;
2943 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2945 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2947 return (const gchar *) priv->visible_account_id;
2952 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2957 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2958 TnyFolder* a_folder;
2961 gtk_tree_model_get (model, iter,
2962 INSTANCE_COLUMN, &a_folder,
2968 if (folder == a_folder) {
2969 g_object_unref (a_folder);
2970 *folder_iter = *iter;
2973 g_object_unref (a_folder);
2975 if (gtk_tree_model_iter_children (model, &child, iter)) {
2976 if (find_folder_iter (model, &child, folder_iter, folder))
2980 } while (gtk_tree_model_iter_next (model, iter));
2987 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2989 ModestFolderViewPrivate *priv;
2991 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2993 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2995 if (priv->folder_to_select)
2996 g_object_unref(priv->folder_to_select);
2998 priv->folder_to_select = NULL;
3002 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3003 gboolean after_change)
3005 GtkTreeModel *model;
3006 GtkTreeIter iter, folder_iter;
3007 GtkTreeSelection *sel;
3008 ModestFolderViewPrivate *priv = NULL;
3010 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3011 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3013 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3016 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3017 gtk_tree_selection_unselect_all (sel);
3019 if (priv->folder_to_select)
3020 g_object_unref(priv->folder_to_select);
3021 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3025 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3030 /* Refilter the model, before selecting the folder */
3031 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3033 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3034 g_warning ("%s: model is empty", __FUNCTION__);
3038 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3041 path = gtk_tree_model_get_path (model, &folder_iter);
3042 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3044 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3045 gtk_tree_selection_select_iter (sel, &folder_iter);
3046 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3048 gtk_tree_path_free (path);
3056 modest_folder_view_copy_selection (ModestFolderView *self)
3058 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3060 /* Copy selection */
3061 _clipboard_set_selected_data (self, FALSE);
3065 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3067 ModestFolderViewPrivate *priv = NULL;
3068 GtkTreeModel *model = NULL;
3069 const gchar **hidding = NULL;
3070 guint i, n_selected;
3072 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3073 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3075 /* Copy selection */
3076 if (!_clipboard_set_selected_data (folder_view, TRUE))
3079 /* Get hidding ids */
3080 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3082 /* Clear hidding array created by previous cut operation */
3083 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3085 /* Copy hidding array */
3086 priv->n_selected = n_selected;
3087 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3088 for (i=0; i < n_selected; i++)
3089 priv->hidding_ids[i] = g_strdup(hidding[i]);
3091 /* Hide cut folders */
3092 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3093 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3097 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3098 ModestFolderView *folder_view_dst)
3100 GtkTreeModel *filter_model = NULL;
3101 GtkTreeModel *model = NULL;
3102 GtkTreeModel *new_filter_model = NULL;
3103 GtkTreeModel *old_tny_model = NULL;
3104 GtkTreeModel *new_tny_model = NULL;
3105 ModestFolderViewPrivate *dst_priv;
3107 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3108 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3110 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3111 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3112 new_tny_model = NULL;
3115 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3116 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3117 G_OBJECT (old_tny_model),
3118 "activity-changed");
3120 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3121 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3123 /* Build new filter model */
3124 new_filter_model = gtk_tree_model_filter_new (model, NULL);
3125 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3132 /* Set copied model */
3133 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3134 if (new_tny_model) {
3135 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3136 G_OBJECT (new_tny_model),
3138 G_CALLBACK (on_activity_changed),
3143 g_object_unref (new_filter_model);
3147 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3150 GtkTreeModel *model = NULL;
3151 ModestFolderViewPrivate* priv;
3153 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3155 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3156 priv->show_non_move = show;
3157 /* modest_folder_view_update_model(folder_view, */
3158 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3160 /* Hide special folders */
3161 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3162 if (GTK_IS_TREE_MODEL_FILTER (model)) {
3163 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3168 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3171 ModestFolderViewPrivate* priv;
3173 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3175 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3176 priv->show_message_count = show;
3178 g_object_set (G_OBJECT (priv->messages_renderer),
3179 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3183 /* Returns FALSE if it did not selected anything */
3185 _clipboard_set_selected_data (ModestFolderView *folder_view,
3188 ModestFolderViewPrivate *priv = NULL;
3189 TnyFolderStore *folder = NULL;
3190 gboolean retval = FALSE;
3192 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3193 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3195 /* Set selected data on clipboard */
3196 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3197 folder = modest_folder_view_get_selected (folder_view);
3199 /* Do not allow to select an account */
3200 if (TNY_IS_FOLDER (folder)) {
3201 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3206 g_object_unref (folder);
3212 _clear_hidding_filter (ModestFolderView *folder_view)
3214 ModestFolderViewPrivate *priv;
3217 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3218 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3220 if (priv->hidding_ids != NULL) {
3221 for (i=0; i < priv->n_selected; i++)
3222 g_free (priv->hidding_ids[i]);
3223 g_free(priv->hidding_ids);
3229 on_display_name_changed (ModestAccountMgr *mgr,
3230 const gchar *account,
3233 ModestFolderView *self;
3235 self = MODEST_FOLDER_VIEW (user_data);
3237 /* Force a redraw */
3238 #if GTK_CHECK_VERSION(2, 8, 0)
3239 GtkTreeViewColumn * tree_column;
3241 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3243 gtk_tree_view_column_queue_resize (tree_column);
3245 gtk_widget_queue_draw (GTK_WIDGET (self));
3250 modest_folder_view_set_cell_style (ModestFolderView *self,
3251 ModestFolderViewCellStyle cell_style)
3253 ModestFolderViewPrivate *priv = NULL;
3255 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3256 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3258 priv->cell_style = cell_style;
3260 g_object_set (G_OBJECT (priv->messages_renderer),
3261 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3264 gtk_widget_queue_draw (GTK_WIDGET (self));
3268 update_style (ModestFolderView *self)
3270 ModestFolderViewPrivate *priv;
3271 GdkColor style_color, style_active_color;
3272 PangoAttrList *attr_list;
3274 PangoAttribute *attr;
3276 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3277 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3281 attr_list = pango_attr_list_new ();
3283 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3284 gdk_color_parse (MODEST_SECONDARY_COLOR, &style_color);
3286 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3287 pango_attr_list_insert (attr_list, attr);
3290 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3292 "SmallSystemFont", NULL,
3295 attr = pango_attr_font_desc_new (pango_font_description_copy
3296 (style->font_desc));
3297 pango_attr_list_insert (attr_list, attr);
3299 g_object_set (G_OBJECT (priv->messages_renderer),
3300 "foreground-gdk", &style_color,
3301 "foreground-set", TRUE,
3302 "attributes", attr_list,
3304 pango_attr_list_unref (attr_list);
3307 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3308 priv->active_color = style_active_color;
3310 gdk_color_parse ("000", &(priv->active_color));
3315 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3317 if (strcmp ("style", spec->name) == 0) {
3318 update_style (MODEST_FOLDER_VIEW (obj));
3319 gtk_widget_queue_draw (GTK_WIDGET (obj));
3324 modest_folder_view_set_filter (ModestFolderView *self,
3325 ModestFolderViewFilter filter)
3327 ModestFolderViewPrivate *priv;
3328 GtkTreeModel *filter_model;
3330 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3331 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3333 priv->filter |= filter;
3335 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3336 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3337 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3342 modest_folder_view_unset_filter (ModestFolderView *self,
3343 ModestFolderViewFilter filter)
3345 ModestFolderViewPrivate *priv;
3346 GtkTreeModel *filter_model;
3348 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3349 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3351 priv->filter &= ~filter;
3353 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3354 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3355 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3360 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3361 ModestTnyFolderRules rules)
3363 GtkTreeModel *filter_model;
3365 gboolean fulfil = FALSE;
3367 if (!get_inner_models (self, &filter_model, NULL, NULL))
3370 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3374 TnyFolderStore *folder;
3376 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3378 if (TNY_IS_FOLDER (folder)) {
3379 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3380 /* Folder rules are negative: non_writable, non_deletable... */
3381 if (!(folder_rules & rules))
3384 g_object_unref (folder);
3387 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3393 modest_folder_view_set_list_to_move (ModestFolderView *self,
3396 ModestFolderViewPrivate *priv;
3398 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3399 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3401 if (priv->list_to_move)
3402 g_object_unref (priv->list_to_move);
3405 g_object_ref (list);
3407 priv->list_to_move = list;
3411 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3413 ModestFolderViewPrivate *priv;
3415 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3416 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3419 g_free (priv->mailbox);
3421 priv->mailbox = g_strdup (mailbox);
3423 /* Notify observers */
3424 g_signal_emit (G_OBJECT(self),
3425 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3426 priv->visible_account_id);
3430 modest_folder_view_get_mailbox (ModestFolderView *self)
3432 ModestFolderViewPrivate *priv;
3434 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3435 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3437 return (const gchar *) priv->mailbox;
3441 modest_folder_view_get_activity (ModestFolderView *self)
3443 ModestFolderViewPrivate *priv;
3444 GtkTreeModel *inner_model;
3446 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3447 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3449 if (!get_inner_models (self, NULL, NULL, &inner_model))
3452 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3453 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3460 on_activity_changed (TnyGtkFolderListStore *store,
3462 ModestFolderView *folder_view)
3464 ModestFolderViewPrivate *priv;
3466 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3467 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3468 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3470 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3475 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3477 GtkTreeModel *model;
3483 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3484 ret_value = TNY_LIST (model);
3485 g_object_ref (ret_value);
3492 #ifdef MODEST_TOOLKIT_HILDON2
3494 on_live_search_refilter (HildonLiveSearch *livesearch,
3495 ModestFolderView *self)
3497 GtkTreeModel *filter_model;
3499 if (get_inner_models (self, &filter_model, NULL, NULL))
3500 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
3506 modest_folder_view_setup_live_search (ModestFolderView *self)
3508 ModestFolderViewPrivate *priv;
3510 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3511 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3512 priv->live_search = hildon_live_search_new ();
3514 g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), self);
3516 return priv->live_search;