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 #ifdef MODEST_TOOLKIT_HILDON2
79 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
81 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
84 /* Column names depending on we use list store or tree store */
85 #ifdef MODEST_TOOLKIT_HILDON2
86 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
87 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
88 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
89 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
90 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
92 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
93 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
94 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
95 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
96 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
99 /* 'private'/'protected' functions */
100 static void modest_folder_view_class_init (ModestFolderViewClass *klass);
101 static void modest_folder_view_init (ModestFolderView *obj);
102 static void modest_folder_view_finalize (GObject *obj);
103 static void modest_folder_view_dispose (GObject *obj);
105 static void tny_account_store_view_init (gpointer g,
106 gpointer iface_data);
108 static void modest_folder_view_set_account_store (TnyAccountStoreView *self,
109 TnyAccountStore *account_store);
111 static void on_selection_changed (GtkTreeSelection *sel,
114 static void on_row_activated (GtkTreeView *treeview,
116 GtkTreeViewColumn *column,
119 static void on_account_removed (TnyAccountStore *self,
123 static void on_account_inserted (TnyAccountStore *self,
127 static void on_account_changed (TnyAccountStore *self,
131 static gint cmp_rows (GtkTreeModel *tree_model,
136 static gboolean filter_row (GtkTreeModel *model,
140 static gboolean on_key_pressed (GtkWidget *self,
144 static void on_configuration_key_changed (ModestConf* conf,
146 ModestConfEvent event,
147 ModestConfNotificationId notification_id,
148 ModestFolderView *self);
151 static void on_drag_data_get (GtkWidget *widget,
152 GdkDragContext *context,
153 GtkSelectionData *selection_data,
158 static void on_drag_data_received (GtkWidget *widget,
159 GdkDragContext *context,
162 GtkSelectionData *selection_data,
167 static gboolean on_drag_motion (GtkWidget *widget,
168 GdkDragContext *context,
174 static void expand_root_items (ModestFolderView *self);
176 static gint expand_row_timeout (gpointer data);
178 static void setup_drag_and_drop (GtkTreeView *self);
180 static gboolean _clipboard_set_selected_data (ModestFolderView *folder_view,
183 static void _clear_hidding_filter (ModestFolderView *folder_view);
185 #ifndef MODEST_TOOLKIT_HILDON2
186 static void on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
189 ModestFolderView *self);
192 static void on_display_name_changed (ModestAccountMgr *self,
193 const gchar *account,
195 static void update_style (ModestFolderView *self);
196 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
197 static gint get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
198 static gboolean inbox_is_special (TnyFolderStore *folder_store);
200 static gboolean get_inner_models (ModestFolderView *self,
201 GtkTreeModel **filter_model,
202 GtkTreeModel **sort_model,
203 GtkTreeModel **tny_model);
204 #ifdef MODEST_TOOLKIT_HILDON2
205 static void on_activity_changed (TnyGtkFolderListStore *store,
207 ModestFolderView *folder_view);
211 FOLDER_SELECTION_CHANGED_SIGNAL,
212 FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
213 FOLDER_ACTIVATED_SIGNAL,
214 VISIBLE_ACCOUNT_CHANGED_SIGNAL,
215 ACTIVITY_CHANGED_SIGNAL,
219 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
220 struct _ModestFolderViewPrivate {
221 TnyAccountStore *account_store;
222 TnyFolderStore *cur_folder_store;
224 TnyFolder *folder_to_select; /* folder to select after the next update */
226 gulong changed_signal;
227 gulong account_inserted_signal;
228 gulong account_removed_signal;
229 gulong account_changed_signal;
230 gulong conf_key_signal;
231 gulong display_name_changed_signal;
233 /* not unref this object, its a singlenton */
234 ModestEmailClipboard *clipboard;
236 /* Filter tree model */
239 ModestFolderViewFilter filter;
240 #ifdef MODEST_TOOLKIT_HILDON2
241 GtkWidget *live_search;
244 TnyFolderStoreQuery *query;
246 guint timer_expander;
248 gchar *local_account_name;
249 gchar *visible_account_id;
251 ModestFolderViewStyle style;
252 ModestFolderViewCellStyle cell_style;
253 gboolean show_message_count;
255 gboolean reselect; /* we use this to force a reselection of the INBOX */
256 gboolean show_non_move;
257 TnyList *list_to_move;
258 gboolean reexpand; /* next time we expose, we'll expand all root folders */
260 GtkCellRenderer *messages_renderer;
262 gulong outbox_deleted_handler;
264 GSList *signal_handlers;
265 GdkColor active_color;
267 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o) \
268 (G_TYPE_INSTANCE_GET_PRIVATE((o), \
269 MODEST_TYPE_FOLDER_VIEW, \
270 ModestFolderViewPrivate))
272 static GObjectClass *parent_class = NULL;
274 static guint signals[LAST_SIGNAL] = {0};
277 modest_folder_view_get_type (void)
279 static GType my_type = 0;
281 static const GTypeInfo my_info = {
282 sizeof(ModestFolderViewClass),
283 NULL, /* base init */
284 NULL, /* base finalize */
285 (GClassInitFunc) modest_folder_view_class_init,
286 NULL, /* class finalize */
287 NULL, /* class data */
288 sizeof(ModestFolderView),
290 (GInstanceInitFunc) modest_folder_view_init,
294 static const GInterfaceInfo tny_account_store_view_info = {
295 (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
296 NULL, /* interface_finalize */
297 NULL /* interface_data */
301 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
305 g_type_add_interface_static (my_type,
306 TNY_TYPE_ACCOUNT_STORE_VIEW,
307 &tny_account_store_view_info);
313 modest_folder_view_class_init (ModestFolderViewClass *klass)
315 GObjectClass *gobject_class;
316 GtkTreeViewClass *treeview_class;
317 gobject_class = (GObjectClass*) klass;
318 treeview_class = (GtkTreeViewClass*) klass;
320 parent_class = g_type_class_peek_parent (klass);
321 gobject_class->finalize = modest_folder_view_finalize;
322 gobject_class->dispose = modest_folder_view_dispose;
324 g_type_class_add_private (gobject_class,
325 sizeof(ModestFolderViewPrivate));
327 signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
328 g_signal_new ("folder_selection_changed",
329 G_TYPE_FROM_CLASS (gobject_class),
331 G_STRUCT_OFFSET (ModestFolderViewClass,
332 folder_selection_changed),
334 modest_marshal_VOID__POINTER_BOOLEAN,
335 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
338 * This signal is emitted whenever the currently selected
339 * folder display name is computed. Note that the name could
340 * be different to the folder name, because we could append
341 * the unread messages count to the folder name to build the
342 * folder display name
344 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
345 g_signal_new ("folder-display-name-changed",
346 G_TYPE_FROM_CLASS (gobject_class),
348 G_STRUCT_OFFSET (ModestFolderViewClass,
349 folder_display_name_changed),
351 g_cclosure_marshal_VOID__STRING,
352 G_TYPE_NONE, 1, G_TYPE_STRING);
354 signals[FOLDER_ACTIVATED_SIGNAL] =
355 g_signal_new ("folder_activated",
356 G_TYPE_FROM_CLASS (gobject_class),
358 G_STRUCT_OFFSET (ModestFolderViewClass,
361 g_cclosure_marshal_VOID__POINTER,
362 G_TYPE_NONE, 1, G_TYPE_POINTER);
365 * Emitted whenever the visible account changes
367 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
368 g_signal_new ("visible-account-changed",
369 G_TYPE_FROM_CLASS (gobject_class),
371 G_STRUCT_OFFSET (ModestFolderViewClass,
372 visible_account_changed),
374 g_cclosure_marshal_VOID__STRING,
375 G_TYPE_NONE, 1, G_TYPE_STRING);
378 * Emitted when the underlying GtkListStore is updating data
380 signals[ACTIVITY_CHANGED_SIGNAL] =
381 g_signal_new ("activity-changed",
382 G_TYPE_FROM_CLASS (gobject_class),
384 G_STRUCT_OFFSET (ModestFolderViewClass,
387 g_cclosure_marshal_VOID__BOOLEAN,
388 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
390 treeview_class->select_cursor_parent = NULL;
392 #ifdef MODEST_TOOLKIT_HILDON2
393 gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
399 /* Retrieves the filter, sort and tny models of the folder view. If
400 any of these does not exist then it returns FALSE */
402 get_inner_models (ModestFolderView *self,
403 GtkTreeModel **filter_model,
404 GtkTreeModel **sort_model,
405 GtkTreeModel **tny_model)
407 GtkTreeModel *s_model, *f_model, *t_model;
409 f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
410 if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
411 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
415 s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
416 if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
417 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
421 t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
425 *filter_model = f_model;
427 *sort_model = s_model;
429 *tny_model = t_model;
434 /* Simplify checks for NULLs: */
436 strings_are_equal (const gchar *a, const gchar *b)
442 return (strcmp (a, b) == 0);
449 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
451 GObject *instance = NULL;
453 gtk_tree_model_get (model, iter,
454 INSTANCE_COLUMN, &instance,
458 return FALSE; /* keep walking */
460 if (!TNY_IS_ACCOUNT (instance)) {
461 g_object_unref (instance);
462 return FALSE; /* keep walking */
465 /* Check if this is the looked-for account: */
466 TnyAccount *this_account = TNY_ACCOUNT (instance);
467 TnyAccount *account = TNY_ACCOUNT (data);
469 const gchar *this_account_id = tny_account_get_id(this_account);
470 const gchar *account_id = tny_account_get_id(account);
471 g_object_unref (instance);
474 /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
475 if (strings_are_equal(this_account_id, account_id)) {
476 /* Tell the model that the data has changed, so that
477 * it calls the cell_data_func callbacks again: */
478 /* TODO: This does not seem to actually cause the new string to be shown: */
479 gtk_tree_model_row_changed (model, path, iter);
481 return TRUE; /* stop walking */
484 return FALSE; /* keep walking */
489 ModestFolderView *self;
490 gchar *previous_name;
491 } GetMmcAccountNameData;
494 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
496 /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
498 GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
500 if (!strings_are_equal (
501 tny_account_get_name(TNY_ACCOUNT(account)),
502 data->previous_name)) {
504 /* Tell the model that the data has changed, so that
505 * it calls the cell_data_func callbacks again: */
506 ModestFolderView *self = data->self;
507 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
509 gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
512 g_free (data->previous_name);
513 g_slice_free (GetMmcAccountNameData, data);
517 convert_parent_folders_to_dots (gchar **item_name)
520 gint n_inbox_parents = 0;
523 gchar *last_separator;
525 if (item_name == NULL)
528 path_start = *item_name;
529 for (c = *item_name; *c != '\0'; c++) {
530 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
532 if (c != path_start) {
533 compare = g_strndup (path_start, c - path_start);
534 compare = g_strstrip (compare);
535 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
545 last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
546 if (last_separator != NULL) {
547 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
554 buffer = g_string_new ("");
555 for (i = 0; i < n_parents - n_inbox_parents; i++) {
556 buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
558 buffer = g_string_append (buffer, last_separator);
560 *item_name = g_string_free (buffer, FALSE);
566 format_compact_style (gchar **item_name,
568 const gchar *mailbox,
570 gboolean multiaccount,
571 gboolean *use_markup)
575 TnyFolderType folder_type;
576 gboolean l_use_markup;
578 if (!TNY_IS_FOLDER (instance))
581 folder = (TnyFolder *) instance;
583 folder_type = tny_folder_get_folder_type (folder);
584 is_special = (get_cmp_pos (folder_type, folder)!= 4);
587 /* Remove mailbox prefix if any */
588 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
589 if (g_str_has_prefix (*item_name, prefix)) {
590 gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
592 *item_name = new_item_name;
596 if (!is_special || multiaccount) {
597 TnyAccount *account = tny_folder_get_account (folder);
598 const gchar *folder_name;
599 gboolean concat_folder_name = FALSE;
602 /* Should not happen */
606 /* convert parent folders to dots */
607 convert_parent_folders_to_dots (item_name);
609 folder_name = tny_folder_get_name (folder);
610 if (g_str_has_suffix (*item_name, folder_name)) {
611 gchar *offset = g_strrstr (*item_name, folder_name);
613 concat_folder_name = TRUE;
616 buffer = g_string_new ("");
618 buffer = g_string_append (buffer, *item_name);
619 if (concat_folder_name) {
620 if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
621 buffer = g_string_append (buffer, folder_name);
622 /* TODO: append a sensitive string to the remote drafts to
623 * be able to know it's the remote one */
624 /* buffer = g_string_append (buffer, " (TODO:remote)"); */
626 buffer = g_string_append (buffer, folder_name);
630 g_object_unref (account);
632 *item_name = g_string_free (buffer, FALSE);
633 l_use_markup = FALSE;
635 l_use_markup = FALSE;
638 *use_markup = l_use_markup;
642 replace_special_folder_prefix (gchar **item_name)
644 const gchar *separator;
647 if (item_name == NULL || *item_name == NULL || **item_name == '\0')
649 separator = g_strstr_len (*item_name, -1, MODEST_FOLDER_PATH_SEPARATOR);
650 if (separator == NULL)
653 prefix = g_strndup (*item_name, separator - *item_name);
656 if (prefix && *prefix != '\0') {
657 TnyFolderType folder_type;
661 downcase = g_utf8_strdown (prefix, -1);
665 if (strcmp (downcase, "inbox") == 0) {
666 folder_type = TNY_FOLDER_TYPE_INBOX;
667 new_name = g_strconcat (_("mcen_me_folder_inbox"), separator, NULL);
669 *item_name = new_name;
671 folder_type = modest_local_folder_info_get_type (prefix);
672 switch (folder_type) {
673 case TNY_FOLDER_TYPE_INBOX:
674 case TNY_FOLDER_TYPE_ARCHIVE:
675 new_name = g_strconcat (modest_local_folder_info_get_type_display_name (folder_type), separator, NULL);
677 *item_name = new_name;
688 text_cell_data (GtkTreeViewColumn *column,
689 GtkCellRenderer *renderer,
690 GtkTreeModel *tree_model,
694 ModestFolderViewPrivate *priv;
695 GObject *rendobj = (GObject *) renderer;
697 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
698 GObject *instance = NULL;
699 gboolean use_markup = FALSE;
701 gtk_tree_model_get (tree_model, iter,
704 INSTANCE_COLUMN, &instance,
706 if (!fname || !instance)
709 ModestFolderView *self = MODEST_FOLDER_VIEW (data);
710 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
712 gchar *item_name = NULL;
713 gint item_weight = 400;
715 if (type != TNY_FOLDER_TYPE_ROOT) {
720 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
721 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
724 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
725 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
727 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
730 /* Sometimes an special folder is reported by the server as
731 NORMAL, like some versions of Dovecot */
732 if (type == TNY_FOLDER_TYPE_NORMAL ||
733 type == TNY_FOLDER_TYPE_UNKNOWN) {
734 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
738 /* note: we cannot reliably get the counts from the
739 * tree model, we need to use explicit calls on
740 * tny_folder for some reason. Select the number to
741 * show: the unread or unsent messages. in case of
742 * outbox/drafts, show all */
743 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
744 (type == TNY_FOLDER_TYPE_OUTBOX) ||
745 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
746 number = tny_folder_get_all_count (TNY_FOLDER(instance));
749 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
753 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
754 item_name = g_strdup (fname);
761 /* Use bold font style if there are unread or unset messages */
763 if (priv->show_message_count) {
764 item_name = g_strdup_printf ("%s (%d)", fname, number);
766 item_name = g_strdup (fname);
770 item_name = g_strdup (fname);
775 } else if (TNY_IS_ACCOUNT (instance)) {
776 /* If it's a server account */
777 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
778 item_name = g_strdup (priv->local_account_name);
780 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
781 /* fname is only correct when the items are first
782 * added to the model, not when the account is
783 * changed later, so get the name from the account
785 item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
788 item_name = g_strdup (fname);
794 if (type == TNY_FOLDER_TYPE_INBOX &&
795 g_str_has_suffix (fname, "Inbox")) {
797 item_name = g_strdup (_("mcen_me_folder_inbox"));
801 item_name = g_strdup ("unknown");
803 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
804 gboolean multiaccount;
806 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
807 /* Convert item_name to markup */
808 format_compact_style (&item_name, instance, priv->mailbox,
810 multiaccount, &use_markup);
812 replace_special_folder_prefix (&item_name);
815 if (item_name && item_weight) {
816 /* Set the name in the treeview cell: */
817 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 &&
818 (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
819 g_object_set (rendobj,
822 "foreground-set", TRUE,
823 "foreground-gdk", &(priv->active_color),
826 g_object_set (rendobj,
828 "foreground-set", FALSE,
830 "weight", item_weight,
834 /* Notify display name observers */
835 /* TODO: What listens for this signal, and how can it use only the new name? */
836 if (((GObject *) priv->cur_folder_store) == instance) {
837 g_signal_emit (G_OBJECT(self),
838 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
845 /* If it is a Memory card account, make sure that we have the correct name.
846 * This function will be trigerred again when the name has been retrieved: */
847 if (TNY_IS_STORE_ACCOUNT (instance) &&
848 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
850 /* Get the account name asynchronously: */
851 GetMmcAccountNameData *callback_data =
852 g_slice_new0(GetMmcAccountNameData);
853 callback_data->self = self;
855 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
857 callback_data->previous_name = g_strdup (name);
859 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
860 on_get_mmc_account_name, callback_data);
864 g_object_unref (G_OBJECT (instance));
870 messages_cell_data (GtkTreeViewColumn *column,
871 GtkCellRenderer *renderer,
872 GtkTreeModel *tree_model,
876 ModestFolderView *self;
877 ModestFolderViewPrivate *priv;
878 GObject *rendobj = (GObject *) renderer;
879 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
880 GObject *instance = NULL;
881 gchar *item_name = NULL;
883 gtk_tree_model_get (tree_model, iter,
885 INSTANCE_COLUMN, &instance,
890 self = MODEST_FOLDER_VIEW (data);
891 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
894 if (type != TNY_FOLDER_TYPE_ROOT) {
899 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
900 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
903 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
905 /* Sometimes an special folder is reported by the server as
906 NORMAL, like some versions of Dovecot */
907 if (type == TNY_FOLDER_TYPE_NORMAL ||
908 type == TNY_FOLDER_TYPE_UNKNOWN) {
909 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
913 /* note: we cannot reliably get the counts from the tree model, we need
914 * to use explicit calls on tny_folder for some reason.
916 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
917 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
918 (type == TNY_FOLDER_TYPE_OUTBOX) ||
919 (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
920 number = tny_folder_get_all_count (TNY_FOLDER(instance));
923 number = tny_folder_get_unread_count (TNY_FOLDER(instance));
927 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
929 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
930 (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
936 item_name = g_strdup ("");
939 /* Set the name in the treeview cell: */
940 g_object_set (rendobj,"text", item_name, NULL);
948 g_object_unref (G_OBJECT (instance));
954 GdkPixbuf *pixbuf_open;
955 GdkPixbuf *pixbuf_close;
959 static inline GdkPixbuf *
960 get_composite_pixbuf (const gchar *icon_name,
962 GdkPixbuf *base_pixbuf)
964 GdkPixbuf *emblem, *retval = NULL;
966 emblem = modest_platform_get_icon (icon_name, size);
968 retval = gdk_pixbuf_copy (base_pixbuf);
969 gdk_pixbuf_composite (emblem, retval, 0, 0,
970 MIN (gdk_pixbuf_get_width (emblem),
971 gdk_pixbuf_get_width (retval)),
972 MIN (gdk_pixbuf_get_height (emblem),
973 gdk_pixbuf_get_height (retval)),
974 0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
975 g_object_unref (emblem);
980 static inline ThreePixbufs *
981 get_composite_icons (const gchar *icon_code,
983 GdkPixbuf **pixbuf_open,
984 GdkPixbuf **pixbuf_close)
986 ThreePixbufs *retval;
988 if (pixbuf && !*pixbuf) {
990 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
992 *pixbuf = gdk_pixbuf_copy (icon);
998 if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
999 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
1003 if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
1004 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
1008 retval = g_slice_new0 (ThreePixbufs);
1009 if (pixbuf && *pixbuf)
1010 retval->pixbuf = g_object_ref (*pixbuf);
1012 retval->pixbuf = NULL;
1013 if (pixbuf_open && *pixbuf_open)
1014 retval->pixbuf_open = g_object_ref (*pixbuf_open);
1016 retval->pixbuf_open = NULL;
1017 if (pixbuf_close && *pixbuf_close)
1018 retval->pixbuf_close = g_object_ref (*pixbuf_close);
1020 retval->pixbuf_close = NULL;
1025 static inline ThreePixbufs *
1026 get_account_protocol_pixbufs (ModestFolderView *folder_view,
1027 ModestProtocolType protocol_type,
1030 ModestProtocol *protocol;
1031 const GdkPixbuf *pixbuf = NULL;
1032 ModestFolderViewPrivate *priv;
1034 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1036 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1039 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
1040 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
1041 priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
1042 MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
1043 MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
1044 object, FOLDER_ICON_SIZE);
1048 ThreePixbufs *retval;
1049 retval = g_slice_new0 (ThreePixbufs);
1050 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
1051 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
1052 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
1059 static inline ThreePixbufs*
1060 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
1062 TnyAccount *account = NULL;
1063 static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
1064 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
1065 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
1066 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
1067 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
1069 static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
1070 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
1071 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
1072 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
1073 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
1075 static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
1076 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1077 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1078 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1079 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1081 ThreePixbufs *retval = NULL;
1083 if (TNY_IS_ACCOUNT (instance)) {
1084 account = g_object_ref (instance);
1085 } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1086 account = tny_folder_get_account (TNY_FOLDER (instance));
1090 ModestProtocolType account_store_protocol;
1092 account_store_protocol = modest_tny_account_get_protocol_type (account);
1093 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1094 g_object_unref (account);
1100 /* Sometimes an special folder is reported by the server as
1101 NORMAL, like some versions of Dovecot */
1102 if (type == TNY_FOLDER_TYPE_NORMAL ||
1103 type == TNY_FOLDER_TYPE_UNKNOWN) {
1104 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1107 /* It's not enough with check the folder type. We need to
1108 ensure that we're not giving a special folder icon to a
1109 normal folder with the same name than a special folder */
1110 if (TNY_IS_FOLDER (instance) &&
1111 get_cmp_pos (type, TNY_FOLDER (instance)) == 4)
1112 type = TNY_FOLDER_TYPE_NORMAL;
1114 /* Remote folders should not be treated as special folders */
1115 if (TNY_IS_FOLDER_STORE (instance) &&
1116 !TNY_IS_ACCOUNT (instance) &&
1117 type != TNY_FOLDER_TYPE_INBOX &&
1118 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1119 #ifdef MODEST_TOOLKIT_HILDON2
1120 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1123 &anorm_pixbuf_close);
1125 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1127 &normal_pixbuf_open,
1128 &normal_pixbuf_close);
1134 case TNY_FOLDER_TYPE_INVALID:
1135 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1138 case TNY_FOLDER_TYPE_ROOT:
1139 if (TNY_IS_ACCOUNT (instance)) {
1141 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1142 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1145 &avirt_pixbuf_close);
1147 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1149 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1150 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1153 &ammc_pixbuf_close);
1155 retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1158 &anorm_pixbuf_close);
1163 case TNY_FOLDER_TYPE_INBOX:
1164 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1167 &inbox_pixbuf_close);
1169 case TNY_FOLDER_TYPE_OUTBOX:
1170 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1172 &outbox_pixbuf_open,
1173 &outbox_pixbuf_close);
1175 case TNY_FOLDER_TYPE_JUNK:
1176 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1179 &junk_pixbuf_close);
1181 case TNY_FOLDER_TYPE_SENT:
1182 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1185 &sent_pixbuf_close);
1187 case TNY_FOLDER_TYPE_TRASH:
1188 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1191 &trash_pixbuf_close);
1193 case TNY_FOLDER_TYPE_DRAFTS:
1194 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1197 &draft_pixbuf_close);
1199 case TNY_FOLDER_TYPE_ARCHIVE:
1200 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1205 case TNY_FOLDER_TYPE_NORMAL:
1207 /* Memory card folders could have an special icon */
1208 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1209 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1214 retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1216 &normal_pixbuf_open,
1217 &normal_pixbuf_close);
1226 free_pixbufs (ThreePixbufs *pixbufs)
1228 if (pixbufs->pixbuf)
1229 g_object_unref (pixbufs->pixbuf);
1230 if (pixbufs->pixbuf_open)
1231 g_object_unref (pixbufs->pixbuf_open);
1232 if (pixbufs->pixbuf_close)
1233 g_object_unref (pixbufs->pixbuf_close);
1234 g_slice_free (ThreePixbufs, pixbufs);
1238 icon_cell_data (GtkTreeViewColumn *column,
1239 GtkCellRenderer *renderer,
1240 GtkTreeModel *tree_model,
1244 GObject *rendobj = NULL, *instance = NULL;
1245 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1246 gboolean has_children;
1247 ThreePixbufs *pixbufs;
1248 ModestFolderView *folder_view = (ModestFolderView *) data;
1250 rendobj = (GObject *) renderer;
1252 gtk_tree_model_get (tree_model, iter,
1254 INSTANCE_COLUMN, &instance,
1260 has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1261 pixbufs = get_folder_icons (folder_view, type, instance);
1262 g_object_unref (instance);
1265 g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1268 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1269 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1272 free_pixbufs (pixbufs);
1276 add_columns (GtkWidget *treeview)
1278 GtkTreeViewColumn *column;
1279 GtkCellRenderer *renderer;
1280 GtkTreeSelection *sel;
1281 ModestFolderViewPrivate *priv;
1283 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1286 column = gtk_tree_view_column_new ();
1288 /* Set icon and text render function */
1289 renderer = gtk_cell_renderer_pixbuf_new();
1290 #ifdef MODEST_TOOLKIT_HILDON2
1291 g_object_set (renderer,
1292 "xpad", MODEST_MARGIN_DEFAULT,
1293 "ypad", MODEST_MARGIN_DEFAULT,
1296 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1297 gtk_tree_view_column_set_cell_data_func(column, renderer,
1298 icon_cell_data, treeview, NULL);
1300 renderer = gtk_cell_renderer_text_new();
1301 g_object_set (renderer,
1302 #ifdef MODEST_TOOLKIT_HILDON2
1303 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1304 "ypad", MODEST_MARGIN_DEFAULT,
1305 "xpad", MODEST_MARGIN_DEFAULT,
1307 "ellipsize", PANGO_ELLIPSIZE_END,
1309 "ellipsize-set", TRUE, NULL);
1310 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1311 gtk_tree_view_column_set_cell_data_func(column, renderer,
1312 text_cell_data, treeview, NULL);
1314 priv->messages_renderer = gtk_cell_renderer_text_new ();
1315 g_object_set (priv->messages_renderer,
1316 #ifdef MODEST_TOOLKIT_HILDON2
1318 "ypad", MODEST_MARGIN_DEFAULT,
1319 "xpad", MODEST_MARGIN_DOUBLE,
1321 "scale", PANGO_SCALE_X_SMALL,
1324 "alignment", PANGO_ALIGN_RIGHT,
1328 gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1329 gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1330 messages_cell_data, treeview, NULL);
1332 /* Set selection mode */
1333 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1334 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1336 /* Set treeview appearance */
1337 gtk_tree_view_column_set_spacing (column, 2);
1338 gtk_tree_view_column_set_resizable (column, TRUE);
1339 gtk_tree_view_column_set_fixed_width (column, TRUE);
1340 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1341 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1344 gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1348 modest_folder_view_init (ModestFolderView *obj)
1350 ModestFolderViewPrivate *priv;
1353 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1355 priv->timer_expander = 0;
1356 priv->account_store = NULL;
1358 priv->do_refresh = TRUE;
1359 priv->style = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1360 priv->cur_folder_store = NULL;
1361 priv->visible_account_id = NULL;
1362 priv->mailbox = NULL;
1363 priv->folder_to_select = NULL;
1364 priv->outbox_deleted_handler = 0;
1365 priv->reexpand = TRUE;
1366 priv->signal_handlers = 0;
1367 #ifdef MODEST_TOOLKIT_HILDON2
1368 priv->live_search = NULL;
1371 /* Initialize the local account name */
1372 conf = modest_runtime_get_conf();
1373 priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1375 /* Init email clipboard */
1376 priv->clipboard = modest_runtime_get_email_clipboard ();
1377 priv->hidding_ids = NULL;
1378 priv->n_selected = 0;
1379 priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1380 priv->reselect = FALSE;
1381 priv->show_non_move = TRUE;
1382 priv->list_to_move = NULL;
1383 priv->show_message_count = TRUE;
1385 /* Build treeview */
1386 add_columns (GTK_WIDGET (obj));
1388 /* Setup drag and drop */
1389 setup_drag_and_drop (GTK_TREE_VIEW(obj));
1391 /* Connect signals */
1392 g_signal_connect (G_OBJECT (obj),
1394 G_CALLBACK (on_key_pressed), NULL);
1396 priv->display_name_changed_signal =
1397 g_signal_connect (modest_runtime_get_account_mgr (),
1398 "display_name_changed",
1399 G_CALLBACK (on_display_name_changed),
1403 * Track changes in the local account name (in the device it
1404 * will be the device name)
1406 priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1408 G_CALLBACK(on_configuration_key_changed),
1411 gdk_color_parse ("000", &priv->active_color);
1414 g_signal_connect (G_OBJECT (obj), "notify::style",
1415 G_CALLBACK (on_notify_style), (gpointer) obj);
1419 tny_account_store_view_init (gpointer g, gpointer iface_data)
1421 TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1423 klass->set_account_store = modest_folder_view_set_account_store;
1427 modest_folder_view_dispose (GObject *obj)
1429 ModestFolderViewPrivate *priv;
1430 GtkTreeModel *model = NULL;
1432 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1434 get_inner_models (MODEST_FOLDER_VIEW (obj),
1435 NULL, NULL, &model);
1437 #ifdef MODEST_TOOLKIT_HILDON2
1438 if (priv->signal_handlers) {
1439 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1440 priv->signal_handlers = NULL;
1444 /* Free external references */
1445 if (priv->account_store) {
1446 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1447 priv->account_inserted_signal);
1448 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1449 priv->account_removed_signal);
1450 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1451 priv->account_changed_signal);
1452 g_object_unref (G_OBJECT(priv->account_store));
1453 priv->account_store = NULL;
1457 g_object_unref (G_OBJECT (priv->query));
1461 if (priv->folder_to_select) {
1462 g_object_unref (G_OBJECT(priv->folder_to_select));
1463 priv->folder_to_select = NULL;
1466 if (priv->cur_folder_store) {
1467 g_object_unref (priv->cur_folder_store);
1468 priv->cur_folder_store = NULL;
1471 if (priv->list_to_move) {
1472 g_object_unref (priv->list_to_move);
1473 priv->list_to_move = NULL;
1476 G_OBJECT_CLASS(parent_class)->dispose (obj);
1480 modest_folder_view_finalize (GObject *obj)
1482 ModestFolderViewPrivate *priv;
1483 GtkTreeSelection *sel;
1484 TnyAccount *local_account;
1486 g_return_if_fail (obj);
1488 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1490 if (priv->timer_expander != 0) {
1491 g_source_remove (priv->timer_expander);
1492 priv->timer_expander = 0;
1495 local_account = (TnyAccount *)
1496 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1497 if (local_account) {
1498 if (g_signal_handler_is_connected (local_account,
1499 priv->outbox_deleted_handler))
1500 g_signal_handler_disconnect (local_account,
1501 priv->outbox_deleted_handler);
1502 g_object_unref (local_account);
1505 if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (),
1506 priv->display_name_changed_signal)) {
1507 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1508 priv->display_name_changed_signal);
1509 priv->display_name_changed_signal = 0;
1512 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1514 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1516 g_free (priv->local_account_name);
1517 g_free (priv->visible_account_id);
1518 g_free (priv->mailbox);
1520 if (priv->conf_key_signal) {
1521 g_signal_handler_disconnect (modest_runtime_get_conf (),
1522 priv->conf_key_signal);
1523 priv->conf_key_signal = 0;
1526 /* Clear hidding array created by cut operation */
1527 _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1529 gdk_color_parse ("000", &priv->active_color);
1531 G_OBJECT_CLASS(parent_class)->finalize (obj);
1536 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1538 ModestFolderViewPrivate *priv;
1541 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1542 g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1544 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1545 device = tny_account_store_get_device (account_store);
1547 if (G_UNLIKELY (priv->account_store)) {
1549 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1550 priv->account_inserted_signal))
1551 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1552 priv->account_inserted_signal);
1553 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1554 priv->account_removed_signal))
1555 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1556 priv->account_removed_signal);
1557 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1558 priv->account_changed_signal))
1559 g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1560 priv->account_changed_signal);
1561 g_object_unref (G_OBJECT (priv->account_store));
1564 priv->account_store = g_object_ref (G_OBJECT (account_store));
1566 priv->account_removed_signal =
1567 g_signal_connect (G_OBJECT(account_store), "account_removed",
1568 G_CALLBACK (on_account_removed), self);
1570 priv->account_inserted_signal =
1571 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1572 G_CALLBACK (on_account_inserted), self);
1574 priv->account_changed_signal =
1575 g_signal_connect (G_OBJECT(account_store), "account_changed",
1576 G_CALLBACK (on_account_changed), self);
1578 modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1579 priv->reselect = FALSE;
1580 modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1582 g_object_unref (G_OBJECT (device));
1586 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1589 ModestFolderView *self;
1590 GtkTreeModel *model, *filter_model;
1593 self = MODEST_FOLDER_VIEW (user_data);
1595 if (!get_inner_models (self, &filter_model, NULL, &model))
1598 /* Remove outbox from model */
1599 outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1600 tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1601 g_object_unref (outbox);
1604 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1608 on_account_inserted (TnyAccountStore *account_store,
1609 TnyAccount *account,
1612 ModestFolderViewPrivate *priv;
1613 GtkTreeModel *model, *filter_model;
1615 /* Ignore transport account insertions, we're not showing them
1616 in the folder view */
1617 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1620 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1623 /* If we're adding a new account, and there is no previous
1624 one, we need to select the visible server account */
1625 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1626 !priv->visible_account_id)
1627 modest_widget_memory_restore (modest_runtime_get_conf(),
1628 G_OBJECT (user_data),
1629 MODEST_CONF_FOLDER_VIEW_KEY);
1633 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1634 &filter_model, NULL, &model))
1637 /* Insert the account in the model */
1638 tny_list_append (TNY_LIST (model), G_OBJECT (account));
1640 /* When the model is a list store (plain representation) the
1641 outbox is not a child of any account so we have to manually
1642 delete it because removing the local folders account won't
1643 delete it (because tny_folder_get_account() is not defined
1644 for a merge folder */
1645 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1646 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1648 priv->outbox_deleted_handler =
1649 g_signal_connect (account,
1651 G_CALLBACK (on_outbox_deleted_cb),
1655 /* Refilter the model */
1656 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1661 same_account_selected (ModestFolderView *self,
1662 TnyAccount *account)
1664 ModestFolderViewPrivate *priv;
1665 gboolean same_account = FALSE;
1667 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1669 if (priv->cur_folder_store) {
1670 TnyAccount *selected_folder_account = NULL;
1672 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1673 selected_folder_account =
1674 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1676 selected_folder_account =
1677 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1680 if (selected_folder_account == account)
1681 same_account = TRUE;
1683 g_object_unref (selected_folder_account);
1685 return same_account;
1690 * Selects the first inbox or the local account in an idle
1693 on_idle_select_first_inbox_or_local (gpointer user_data)
1695 ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1697 gdk_threads_enter ();
1698 modest_folder_view_select_first_inbox_or_local (self);
1699 gdk_threads_leave ();
1705 on_account_changed (TnyAccountStore *account_store,
1706 TnyAccount *tny_account,
1709 ModestFolderView *self;
1710 ModestFolderViewPrivate *priv;
1711 GtkTreeModel *model, *filter_model;
1712 GtkTreeSelection *sel;
1713 gboolean same_account;
1715 /* Ignore transport account insertions, we're not showing them
1716 in the folder view */
1717 if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1720 self = MODEST_FOLDER_VIEW (user_data);
1721 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1723 /* Get the inner model */
1724 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1725 &filter_model, NULL, &model))
1728 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1730 /* Invalidate the cur_folder_store only if the selected folder
1731 belongs to the account that is being removed */
1732 same_account = same_account_selected (self, tny_account);
1734 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1735 gtk_tree_selection_unselect_all (sel);
1738 /* Remove the account from the model */
1739 tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1741 /* Insert the account in the model */
1742 tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1744 /* Refilter the model */
1745 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1747 /* Select the first INBOX if the currently selected folder
1748 belongs to the account that is being deleted */
1749 if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1750 g_idle_add (on_idle_select_first_inbox_or_local, self);
1754 on_account_removed (TnyAccountStore *account_store,
1755 TnyAccount *account,
1758 ModestFolderView *self = NULL;
1759 ModestFolderViewPrivate *priv;
1760 GtkTreeModel *model, *filter_model;
1761 GtkTreeSelection *sel = NULL;
1762 gboolean same_account = FALSE;
1764 /* Ignore transport account removals, we're not showing them
1765 in the folder view */
1766 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1769 if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1770 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1774 self = MODEST_FOLDER_VIEW (user_data);
1775 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1777 /* Invalidate the cur_folder_store only if the selected folder
1778 belongs to the account that is being removed */
1779 same_account = same_account_selected (self, account);
1781 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1782 gtk_tree_selection_unselect_all (sel);
1785 /* Invalidate row to select only if the folder to select
1786 belongs to the account that is being removed*/
1787 if (priv->folder_to_select) {
1788 TnyAccount *folder_to_select_account = NULL;
1790 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1791 if (folder_to_select_account == account) {
1792 modest_folder_view_disable_next_folder_selection (self);
1793 g_object_unref (priv->folder_to_select);
1794 priv->folder_to_select = NULL;
1796 g_object_unref (folder_to_select_account);
1799 if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1800 &filter_model, NULL, &model))
1803 /* Disconnect the signal handler */
1804 if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1805 MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1806 if (g_signal_handler_is_connected (account,
1807 priv->outbox_deleted_handler))
1808 g_signal_handler_disconnect (account,
1809 priv->outbox_deleted_handler);
1812 /* Remove the account from the model */
1813 tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1815 /* If the removed account is the currently viewed one then
1816 clear the configuration value. The new visible account will be the default account */
1817 if (priv->visible_account_id &&
1818 !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1820 /* Clear the current visible account_id */
1821 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1822 modest_folder_view_set_mailbox (self, NULL);
1824 /* Call the restore method, this will set the new visible account */
1825 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1826 MODEST_CONF_FOLDER_VIEW_KEY);
1829 /* Refilter the model */
1830 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1832 /* Select the first INBOX if the currently selected folder
1833 belongs to the account that is being deleted */
1835 g_idle_add (on_idle_select_first_inbox_or_local, self);
1839 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1841 GtkTreeViewColumn *col;
1843 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1845 col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1847 g_printerr ("modest: failed get column for title\n");
1851 gtk_tree_view_column_set_title (col, title);
1852 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1857 modest_folder_view_on_map (ModestFolderView *self,
1858 GdkEventExpose *event,
1861 ModestFolderViewPrivate *priv;
1863 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1865 /* This won't happen often */
1866 if (G_UNLIKELY (priv->reselect)) {
1867 /* Select the first inbox or the local account if not found */
1869 /* TODO: this could cause a lock at startup, so we
1870 comment it for the moment. We know that this will
1871 be a bug, because the INBOX is not selected, but we
1872 need to rewrite some parts of Modest to avoid the
1873 deathlock situation */
1874 /* TODO: check if this is still the case */
1875 priv->reselect = FALSE;
1876 modest_folder_view_select_first_inbox_or_local (self);
1877 /* Notify the display name observers */
1878 g_signal_emit (G_OBJECT(self),
1879 signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1883 if (priv->reexpand) {
1884 expand_root_items (self);
1885 priv->reexpand = FALSE;
1892 modest_folder_view_new (TnyFolderStoreQuery *query)
1894 return modest_folder_view_new_full (query, TRUE);
1898 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1901 ModestFolderViewPrivate *priv;
1902 GtkTreeSelection *sel;
1904 self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW,
1905 #ifdef MODEST_TOOLKIT_HILDON2
1906 "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1909 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1912 priv->query = g_object_ref (query);
1914 priv->do_refresh = do_refresh;
1916 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1917 priv->changed_signal = g_signal_connect (sel, "changed",
1918 G_CALLBACK (on_selection_changed), self);
1920 g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1922 g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1924 return GTK_WIDGET(self);
1927 /* this feels dirty; any other way to expand all the root items? */
1929 expand_root_items (ModestFolderView *self)
1932 GtkTreeModel *model;
1935 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1936 path = gtk_tree_path_new_first ();
1938 /* all folders should have child items, so.. */
1940 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1941 gtk_tree_path_next (path);
1942 } while (gtk_tree_model_get_iter (model, &iter, path));
1944 gtk_tree_path_free (path);
1948 is_parent_of (TnyFolder *a, TnyFolder *b)
1951 gboolean retval = FALSE;
1953 a_id = tny_folder_get_id (a);
1955 gchar *string_to_match;
1958 string_to_match = g_strconcat (a_id, "/", NULL);
1959 b_id = tny_folder_get_id (b);
1960 retval = g_str_has_prefix (b_id, string_to_match);
1961 g_free (string_to_match);
1967 typedef struct _ForeachFolderInfo {
1970 } ForeachFolderInfo;
1973 foreach_folder_with_id (GtkTreeModel *model,
1978 ForeachFolderInfo *info;
1981 info = (ForeachFolderInfo *) data;
1982 gtk_tree_model_get (model, iter,
1983 INSTANCE_COLUMN, &instance,
1986 if (TNY_IS_FOLDER (instance)) {
1989 id = tny_folder_get_id (TNY_FOLDER (instance));
1991 collate = g_utf8_collate_key (id, -1);
1992 info->found = !strcmp (info->needle, collate);
1998 g_object_unref (instance);
2006 has_folder_with_id (ModestFolderView *self, const gchar *id)
2008 GtkTreeModel *model;
2009 ForeachFolderInfo info = {NULL, FALSE};
2011 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2012 info.needle = g_utf8_collate_key (id, -1);
2014 gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
2015 g_free (info.needle);
2021 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
2024 gboolean retval = FALSE;
2026 a_id = tny_folder_get_id (a);
2029 b_id = tny_folder_get_id (b);
2032 const gchar *last_bar;
2033 gchar *string_to_match;
2034 last_bar = g_strrstr (b_id, "/");
2039 string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
2040 retval = has_folder_with_id (self, string_to_match);
2041 g_free (string_to_match);
2049 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
2051 ModestFolderViewPrivate *priv;
2052 TnyIterator *iterator;
2053 gboolean retval = TRUE;
2055 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
2056 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2058 for (iterator = tny_list_create_iterator (priv->list_to_move);
2059 retval && !tny_iterator_is_done (iterator);
2060 tny_iterator_next (iterator)) {
2062 instance = tny_iterator_get_current (iterator);
2063 if (instance == (GObject *) folder) {
2065 } else if (TNY_IS_FOLDER (instance)) {
2066 retval = !is_parent_of (TNY_FOLDER (instance), folder);
2068 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
2071 g_object_unref (instance);
2073 g_object_unref (iterator);
2080 * We use this function to implement the
2081 * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
2082 * account in this case, and the local folders.
2085 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2087 ModestFolderViewPrivate *priv;
2088 gboolean retval = TRUE;
2089 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2090 GObject *instance = NULL;
2091 const gchar *id = NULL;
2093 gboolean found = FALSE;
2094 gboolean cleared = FALSE;
2095 ModestTnyFolderRules rules = 0;
2098 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
2099 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
2101 gtk_tree_model_get (model, iter,
2102 NAME_COLUMN, &fname,
2104 INSTANCE_COLUMN, &instance,
2107 /* Do not show if there is no instance, this could indeed
2108 happen when the model is being modified while it's being
2109 drawn. This could occur for example when moving folders
2116 if (TNY_IS_ACCOUNT (instance)) {
2117 TnyAccount *acc = TNY_ACCOUNT (instance);
2118 const gchar *account_id = tny_account_get_id (acc);
2120 /* If it isn't a special folder,
2121 * don't show it unless it is the visible account: */
2122 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2123 !modest_tny_account_is_virtual_local_folders (acc) &&
2124 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2126 /* Show only the visible account id */
2127 if (priv->visible_account_id) {
2128 if (strcmp (account_id, priv->visible_account_id))
2135 /* Never show these to the user. They are merged into one folder
2136 * in the local-folders account instead: */
2137 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2140 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2141 /* Only show special folders for current account if needed */
2142 if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2143 TnyAccount *account;
2145 account = tny_folder_get_account (TNY_FOLDER (instance));
2147 if (TNY_IS_ACCOUNT (account)) {
2148 const gchar *account_id = tny_account_get_id (account);
2150 if (!modest_tny_account_is_virtual_local_folders (account) &&
2151 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2152 /* Show only the visible account id */
2153 if (priv->visible_account_id) {
2154 if (strcmp (account_id, priv->visible_account_id)) {
2156 } else if (priv->mailbox) {
2157 /* Filter mailboxes */
2158 if (!g_str_has_prefix (fname, priv->mailbox)) {
2160 } else if (!strcmp (fname, priv->mailbox)) {
2161 /* Hide mailbox parent */
2167 g_object_unref (account);
2174 /* Check hiding (if necessary) */
2175 cleared = modest_email_clipboard_cleared (priv->clipboard);
2176 if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2177 id = tny_folder_get_id (TNY_FOLDER(instance));
2178 if (priv->hidding_ids != NULL)
2179 for (i=0; i < priv->n_selected && !found; i++)
2180 if (priv->hidding_ids[i] != NULL && id != NULL)
2181 found = (!strcmp (priv->hidding_ids[i], id));
2186 /* If this is a move to dialog, hide Sent, Outbox and Drafts
2187 folder as no message can be move there according to UI specs */
2188 if (retval && !priv->show_non_move) {
2189 if (priv->list_to_move &&
2190 tny_list_get_length (priv->list_to_move) > 0 &&
2191 TNY_IS_FOLDER (instance)) {
2192 retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2194 if (retval && TNY_IS_FOLDER (instance) &&
2195 modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2197 case TNY_FOLDER_TYPE_OUTBOX:
2198 case TNY_FOLDER_TYPE_SENT:
2199 case TNY_FOLDER_TYPE_DRAFTS:
2202 case TNY_FOLDER_TYPE_UNKNOWN:
2203 case TNY_FOLDER_TYPE_NORMAL:
2204 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2205 if (type == TNY_FOLDER_TYPE_INVALID)
2206 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2208 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2209 type == TNY_FOLDER_TYPE_SENT
2210 || type == TNY_FOLDER_TYPE_DRAFTS)
2217 if (retval && TNY_IS_ACCOUNT (instance) &&
2218 modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2219 ModestProtocolType protocol_type;
2221 protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2222 retval = !modest_protocol_registry_protocol_type_has_tag
2223 (modest_runtime_get_protocol_registry (),
2225 MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2229 /* apply special filters */
2230 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2231 if (TNY_IS_ACCOUNT (instance))
2235 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2236 if (TNY_IS_FOLDER (instance))
2240 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2241 if (TNY_IS_ACCOUNT (instance)) {
2242 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2244 } else if (TNY_IS_FOLDER (instance)) {
2245 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2250 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2251 if (TNY_IS_ACCOUNT (instance)) {
2252 if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2254 } else if (TNY_IS_FOLDER (instance)) {
2255 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2260 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2261 /* A mailbox is a fake folder with an @ in the middle of the name */
2262 if (!TNY_IS_FOLDER (instance) ||
2263 !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2266 const gchar *folder_name;
2267 folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2268 if (!folder_name || strchr (folder_name, '@') == NULL)
2274 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2275 if (TNY_IS_FOLDER (instance)) {
2276 /* Check folder rules */
2277 ModestTnyFolderRules rules;
2279 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2280 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2281 } else if (TNY_IS_ACCOUNT (instance)) {
2282 if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2290 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2291 if (TNY_IS_FOLDER (instance)) {
2292 TnyFolderType guess_type;
2294 if (TNY_FOLDER_TYPE_NORMAL) {
2295 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2301 case TNY_FOLDER_TYPE_OUTBOX:
2302 case TNY_FOLDER_TYPE_SENT:
2303 case TNY_FOLDER_TYPE_DRAFTS:
2304 case TNY_FOLDER_TYPE_ARCHIVE:
2305 case TNY_FOLDER_TYPE_INBOX:
2308 case TNY_FOLDER_TYPE_UNKNOWN:
2309 case TNY_FOLDER_TYPE_NORMAL:
2315 } else if (TNY_IS_ACCOUNT (instance)) {
2320 if (retval && TNY_IS_FOLDER (instance)) {
2321 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2324 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2325 if (TNY_IS_FOLDER (instance)) {
2326 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2327 } else if (TNY_IS_ACCOUNT (instance)) {
2332 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2333 if (TNY_IS_FOLDER (instance)) {
2334 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2335 } else if (TNY_IS_ACCOUNT (instance)) {
2340 if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2341 if (TNY_IS_FOLDER (instance)) {
2342 retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2343 } else if (TNY_IS_ACCOUNT (instance)) {
2348 #ifdef MODEST_TOOLKIT_HILDON2
2349 if (retval && (priv->live_search)) {
2350 const gchar *needle;
2351 needle = hildon_live_search_get_text (HILDON_LIVE_SEARCH (priv->live_search));
2352 if (needle && needle[0] != '\0') {
2356 haystack = g_strdup (fname);
2357 if (type != TNY_FOLDER_TYPE_ROOT) {
2358 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
2359 modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
2361 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2362 type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
2363 if (type != TNY_FOLDER_TYPE_UNKNOWN) {
2365 haystack = g_strdup (modest_local_folder_info_get_type_display_name (type));
2370 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
2372 haystack = g_strdup (priv->local_account_name);
2373 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
2375 haystack = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
2379 if (type == TNY_FOLDER_TYPE_INBOX &&
2380 g_str_has_suffix (haystack, "Inbox")) {
2382 haystack = g_strdup (_("mcen_me_folder_inbox"));
2384 format_compact_style (&haystack, instance, priv->mailbox, FALSE,
2385 priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL, NULL);
2386 retval = modest_text_utils_live_search_find (haystack, needle);
2393 g_object_unref (instance);
2401 modest_folder_view_update_model (ModestFolderView *self,
2402 TnyAccountStore *account_store)
2404 ModestFolderViewPrivate *priv;
2405 GtkTreeModel *model;
2406 GtkTreeModel *filter_model = NULL, *sortable = NULL;
2408 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2409 g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2412 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2414 /* Notify that there is no folder selected */
2415 g_signal_emit (G_OBJECT(self),
2416 signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2418 if (priv->cur_folder_store) {
2419 g_object_unref (priv->cur_folder_store);
2420 priv->cur_folder_store = NULL;
2423 /* FIXME: the local accounts are not shown when the query
2424 selects only the subscribed folders */
2425 #ifdef MODEST_TOOLKIT_HILDON2
2426 TnyGtkFolderListStoreFlags flags;
2427 flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2428 if (priv->do_refresh)
2429 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2431 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2432 model = tny_gtk_folder_list_store_new_with_flags (NULL,
2434 tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2435 MODEST_FOLDER_PATH_SEPARATOR);
2437 model = tny_gtk_folder_store_tree_model_new (NULL);
2440 /* When the model is a list store (plain representation) the
2441 outbox is not a child of any account so we have to manually
2442 delete it because removing the local folders account won't
2443 delete it (because tny_folder_get_account() is not defined
2444 for a merge folder */
2445 if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2446 TnyAccount *account;
2447 ModestTnyAccountStore *acc_store;
2449 acc_store = modest_runtime_get_account_store ();
2450 account = modest_tny_account_store_get_local_folders_account (acc_store);
2452 if (g_signal_handler_is_connected (account,
2453 priv->outbox_deleted_handler))
2454 g_signal_handler_disconnect (account,
2455 priv->outbox_deleted_handler);
2457 priv->outbox_deleted_handler =
2458 g_signal_connect (account,
2460 G_CALLBACK (on_outbox_deleted_cb),
2462 g_object_unref (account);
2465 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2466 /* Get the accounts */
2467 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2469 TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2471 if (priv->visible_account_id) {
2472 TnyAccount *account;
2474 /* Add local folders account */
2475 account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2478 tny_list_append (TNY_LIST (model), (GObject *) account);
2479 g_object_unref (account);
2482 account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2485 tny_list_append (TNY_LIST (model), (GObject *) account);
2486 g_object_unref (account);
2489 /* Add visible account */
2490 account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2491 MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2492 priv->visible_account_id);
2494 tny_list_append (TNY_LIST (model), (GObject *) account);
2495 g_object_unref (account);
2497 g_warning ("You need to set an account first");
2498 g_object_unref (model);
2502 g_warning ("You need to set an account first");
2503 g_object_unref (model);
2508 sortable = gtk_tree_model_sort_new_with_model (model);
2509 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2511 GTK_SORT_ASCENDING);
2512 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2514 cmp_rows, NULL, NULL);
2516 /* Create filter model */
2517 filter_model = gtk_tree_model_filter_new (sortable, NULL);
2518 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2523 GtkTreeModel *old_tny_model = NULL;
2524 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2525 if (priv->signal_handlers > 0) {
2526 priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2527 G_OBJECT (old_tny_model),
2528 "activity-changed");
2533 gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2534 #ifndef MODEST_TOOLKIT_HILDON2
2535 g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2536 (GCallback) on_row_inserted_maybe_select_folder, self);
2539 #ifdef MODEST_TOOLKIT_HILDON2
2540 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2543 G_CALLBACK (on_activity_changed),
2547 g_object_unref (model);
2548 g_object_unref (filter_model);
2549 g_object_unref (sortable);
2551 /* Force a reselection of the INBOX next time the widget is shown */
2552 priv->reselect = TRUE;
2559 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2561 GtkTreeModel *model = NULL;
2562 TnyFolderStore *folder = NULL;
2564 ModestFolderView *tree_view = NULL;
2565 ModestFolderViewPrivate *priv = NULL;
2566 gboolean selected = FALSE;
2568 g_return_if_fail (sel);
2569 g_return_if_fail (user_data);
2571 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2573 selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2575 tree_view = MODEST_FOLDER_VIEW (user_data);
2578 gtk_tree_model_get (model, &iter,
2579 INSTANCE_COLUMN, &folder,
2582 /* If the folder is the same do not notify */
2583 if (folder && priv->cur_folder_store == folder) {
2584 g_object_unref (folder);
2589 /* Current folder was unselected */
2590 if (priv->cur_folder_store) {
2591 /* We must do this firstly because a libtinymail-camel
2592 implementation detail. If we issue the signal
2593 before doing the sync_async, then that signal could
2594 cause (and it actually does it) a free of the
2595 summary of the folder (because the main window will
2596 clear the headers view */
2597 #ifndef MODEST_TOOLKIT_HILDON2
2598 if (TNY_IS_FOLDER(priv->cur_folder_store))
2599 tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2600 FALSE, NULL, NULL, NULL);
2603 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2604 priv->cur_folder_store, FALSE);
2606 g_object_unref (priv->cur_folder_store);
2607 priv->cur_folder_store = NULL;
2610 /* New current references */
2611 priv->cur_folder_store = folder;
2613 /* New folder has been selected. Do not notify if there is
2614 nothing new selected */
2616 g_signal_emit (G_OBJECT(tree_view),
2617 signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2618 0, priv->cur_folder_store, TRUE);
2623 on_row_activated (GtkTreeView *treeview,
2624 GtkTreePath *treepath,
2625 GtkTreeViewColumn *column,
2628 GtkTreeModel *model = NULL;
2629 TnyFolderStore *folder = NULL;
2631 ModestFolderView *self = NULL;
2632 ModestFolderViewPrivate *priv = NULL;
2634 g_return_if_fail (treeview);
2635 g_return_if_fail (user_data);
2637 self = MODEST_FOLDER_VIEW (user_data);
2638 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2640 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2642 if (!gtk_tree_model_get_iter (model, &iter, treepath))
2645 gtk_tree_model_get (model, &iter,
2646 INSTANCE_COLUMN, &folder,
2649 g_signal_emit (G_OBJECT(self),
2650 signals[FOLDER_ACTIVATED_SIGNAL],
2653 #ifdef MODEST_TOOLKIT_HILDON2
2654 HildonUIMode ui_mode;
2655 g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2656 if (ui_mode == HILDON_UI_MODE_NORMAL) {
2657 if (priv->cur_folder_store)
2658 g_object_unref (priv->cur_folder_store);
2659 priv->cur_folder_store = g_object_ref (folder);
2663 g_object_unref (folder);
2667 modest_folder_view_get_selected (ModestFolderView *self)
2669 ModestFolderViewPrivate *priv;
2671 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2673 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2674 if (priv->cur_folder_store)
2675 g_object_ref (priv->cur_folder_store);
2677 return priv->cur_folder_store;
2681 get_cmp_rows_type_pos (GObject *folder)
2683 /* Remote accounts -> Local account -> MMC account .*/
2686 if (TNY_IS_ACCOUNT (folder) &&
2687 modest_tny_account_is_virtual_local_folders (
2688 TNY_ACCOUNT (folder))) {
2690 } else if (TNY_IS_ACCOUNT (folder)) {
2691 TnyAccount *account = TNY_ACCOUNT (folder);
2692 const gchar *account_id = tny_account_get_id (account);
2693 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2699 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2700 return -1; /* Should never happen */
2705 inbox_is_special (TnyFolderStore *folder_store)
2707 gboolean is_special = TRUE;
2709 if (TNY_IS_FOLDER (folder_store)) {
2713 gchar *last_inbox_bar;
2715 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2716 downcase = g_utf8_strdown (id, -1);
2717 last_bar = g_strrstr (downcase, "/");
2719 last_inbox_bar = g_strrstr (downcase, "inbox/");
2720 if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2731 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2733 TnyAccount *account;
2734 gboolean is_special;
2735 /* Inbox, Outbox, Drafts, Sent, User */
2738 if (!TNY_IS_FOLDER (folder_store))
2741 case TNY_FOLDER_TYPE_INBOX:
2743 account = tny_folder_get_account (folder_store);
2744 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2746 /* In inbox case we need to know if the inbox is really the top
2747 * inbox of the account, or if it's a submailbox inbox. To do
2748 * this we'll apply an heuristic rule: Find last "/" and check
2749 * if it's preceeded by another Inbox */
2750 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2751 g_object_unref (account);
2752 return is_special?0:4;
2755 case TNY_FOLDER_TYPE_OUTBOX:
2756 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2758 case TNY_FOLDER_TYPE_DRAFTS:
2760 account = tny_folder_get_account (folder_store);
2761 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2762 g_object_unref (account);
2763 return is_special?1:4;
2766 case TNY_FOLDER_TYPE_SENT:
2768 account = tny_folder_get_account (folder_store);
2769 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2770 g_object_unref (account);
2771 return is_special?3:4;
2780 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2782 const gchar *a1_name, *a2_name;
2784 a1_name = tny_account_get_name (a1);
2785 a2_name = tny_account_get_name (a2);
2787 return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2791 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2793 TnyAccount *a1 = NULL, *a2 = NULL;
2796 if (TNY_IS_ACCOUNT (s1)) {
2797 a1 = TNY_ACCOUNT (g_object_ref (s1));
2798 } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2799 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2802 if (TNY_IS_ACCOUNT (s2)) {
2803 a2 = TNY_ACCOUNT (g_object_ref (s2));
2804 } else if (!TNY_IS_MERGE_FOLDER (s2)) {
2805 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2822 /* First we sort with the type of account */
2823 cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2827 cmp = compare_account_names (a1, a2);
2831 g_object_unref (a1);
2833 g_object_unref (a2);
2839 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2841 gint is_account1, is_account2;
2843 is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2844 is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2846 return is_account2 - is_account1;
2850 compare_folders (const gchar *name1, const gchar *name2)
2852 const gchar *separator1, *separator2;
2853 const gchar *next1, *next2;
2857 if (name1 == NULL || name1[0] == '\0')
2859 if (name2 == NULL || name2[0] == '\0')
2862 separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2864 top1 = g_strndup (name1, separator1 - name1);
2866 top1 = g_strdup (name1);
2869 separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2871 top2 = g_strndup (name2, separator2 - name2);
2873 top2 = g_strdup (name2);
2877 cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2884 if (separator1 == NULL && separator2 == NULL)
2887 next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2888 next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2890 return compare_folders (next1, next2);
2895 * This function orders the mail accounts according to these rules:
2896 * 1st - remote accounts
2897 * 2nd - local account
2901 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2905 gchar *name1 = NULL;
2906 gchar *name2 = NULL;
2907 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2908 TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2909 GObject *folder1 = NULL;
2910 GObject *folder2 = NULL;
2912 gtk_tree_model_get (tree_model, iter1,
2913 NAME_COLUMN, &name1,
2915 INSTANCE_COLUMN, &folder1,
2917 gtk_tree_model_get (tree_model, iter2,
2918 NAME_COLUMN, &name2,
2919 TYPE_COLUMN, &type2,
2920 INSTANCE_COLUMN, &folder2,
2923 /* Return if we get no folder. This could happen when folder
2924 operations are happening. The model is updated after the
2925 folder copy/move actually occurs, so there could be
2926 situations where the model to be drawn is not correct */
2927 if (!folder1 || !folder2)
2930 /* Sort by type. First the special folders, then the archives */
2931 cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2935 /* Now we sort using the account of each folder */
2936 if (TNY_IS_FOLDER_STORE (folder1) &&
2937 TNY_IS_FOLDER_STORE (folder2)) {
2938 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2942 /* Each group is preceeded by its account */
2943 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2948 /* Pure sort by name */
2949 cmp = compare_folders (name1, name2);
2952 g_object_unref(G_OBJECT(folder1));
2954 g_object_unref(G_OBJECT(folder2));
2962 /*****************************************************************************/
2963 /* DRAG and DROP stuff */
2964 /*****************************************************************************/
2966 * This function fills the #GtkSelectionData with the row and the
2967 * model that has been dragged. It's called when this widget is a
2968 * source for dnd after the event drop happened
2971 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2972 guint info, guint time, gpointer data)
2974 GtkTreeSelection *selection;
2975 GtkTreeModel *model;
2977 GtkTreePath *source_row;
2979 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2980 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2982 source_row = gtk_tree_model_get_path (model, &iter);
2983 gtk_tree_set_row_drag_data (selection_data,
2987 gtk_tree_path_free (source_row);
2991 typedef struct _DndHelper {
2992 ModestFolderView *folder_view;
2993 gboolean delete_source;
2994 GtkTreePath *source_row;
2998 dnd_helper_destroyer (DndHelper *helper)
3000 /* Free the helper */
3001 gtk_tree_path_free (helper->source_row);
3002 g_slice_free (DndHelper, helper);
3006 xfer_folder_cb (ModestMailOperation *mail_op,
3007 TnyFolder *new_folder,
3011 /* Select the folder */
3012 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
3018 /* get the folder for the row the treepath refers to. */
3019 /* folder must be unref'd */
3020 static TnyFolderStore *
3021 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
3024 TnyFolderStore *folder = NULL;
3026 if (gtk_tree_model_get_iter (model,&iter, path))
3027 gtk_tree_model_get (model, &iter,
3028 INSTANCE_COLUMN, &folder,
3035 * This function is used by drag_data_received_cb to manage drag and
3036 * drop of a header, i.e, and drag from the header view to the folder
3040 drag_and_drop_from_header_view (GtkTreeModel *source_model,
3041 GtkTreeModel *dest_model,
3042 GtkTreePath *dest_row,
3043 GtkSelectionData *selection_data)
3045 TnyList *headers = NULL;
3046 TnyFolder *folder = NULL, *src_folder = NULL;
3047 TnyFolderType folder_type;
3048 GtkTreeIter source_iter, dest_iter;
3049 ModestWindowMgr *mgr = NULL;
3050 ModestWindow *main_win = NULL;
3051 gchar **uris, **tmp;
3053 /* Build the list of headers */
3054 mgr = modest_runtime_get_window_mgr ();
3055 headers = tny_simple_list_new ();
3056 uris = modest_dnd_selection_data_get_paths (selection_data);
3059 while (*tmp != NULL) {
3062 gboolean first = TRUE;
3065 path = gtk_tree_path_new_from_string (*tmp);
3066 gtk_tree_model_get_iter (source_model, &source_iter, path);
3067 gtk_tree_model_get (source_model, &source_iter,
3068 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
3071 /* Do not enable d&d of headers already opened */
3072 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
3073 tny_list_append (headers, G_OBJECT (header));
3075 if (G_UNLIKELY (first)) {
3076 src_folder = tny_header_get_folder (header);
3080 /* Free and go on */
3081 gtk_tree_path_free (path);
3082 g_object_unref (header);
3087 /* This could happen ig we perform a d&d very quickly over the
3088 same row that row could dissapear because message is
3090 if (!TNY_IS_FOLDER (src_folder))
3093 /* Get the target folder */
3094 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3095 gtk_tree_model_get (dest_model, &dest_iter,
3099 if (!folder || !TNY_IS_FOLDER(folder)) {
3100 /* g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
3104 folder_type = modest_tny_folder_guess_folder_type (folder);
3105 if (folder_type == TNY_FOLDER_TYPE_INVALID) {
3106 /* g_warning ("%s: invalid target folder", __FUNCTION__); */
3107 goto cleanup; /* cannot move messages there */
3110 if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3111 /* g_warning ("folder not writable"); */
3112 goto cleanup; /* verboten! */
3115 /* Ask for confirmation to move */
3116 main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
3118 g_warning ("%s: BUG: no main window found", __FUNCTION__);
3122 /* Transfer messages */
3123 modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
3128 if (G_IS_OBJECT (src_folder))
3129 g_object_unref (src_folder);
3130 if (G_IS_OBJECT(folder))
3131 g_object_unref (G_OBJECT (folder));
3132 if (G_IS_OBJECT(headers))
3133 g_object_unref (headers);
3137 TnyFolderStore *src_folder;
3138 TnyFolderStore *dst_folder;
3139 ModestFolderView *folder_view;
3144 dnd_folder_info_destroyer (DndFolderInfo *info)
3146 if (info->src_folder)
3147 g_object_unref (info->src_folder);
3148 if (info->dst_folder)
3149 g_object_unref (info->dst_folder);
3150 g_slice_free (DndFolderInfo, info);
3154 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
3155 GtkWindow *parent_window,
3156 TnyAccount *account)
3159 modest_ui_actions_on_account_connection_error (parent_window, account);
3161 /* Free the helper & info */
3162 dnd_helper_destroyer (info->helper);
3163 dnd_folder_info_destroyer (info);
3167 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
3169 GtkWindow *parent_window,
3170 TnyAccount *account,
3173 DndFolderInfo *info = NULL;
3174 ModestMailOperation *mail_op;
3176 info = (DndFolderInfo *) user_data;
3178 if (err || canceled) {
3179 dnd_on_connection_failed_destroyer (info, parent_window, account);
3183 /* Do the mail operation */
3184 mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
3185 modest_ui_actions_move_folder_error_handler,
3186 info->src_folder, NULL);
3188 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3191 /* Transfer the folder */
3192 modest_mail_operation_xfer_folder (mail_op,
3193 TNY_FOLDER (info->src_folder),
3195 info->helper->delete_source,
3197 info->helper->folder_view);
3200 g_object_unref (G_OBJECT (mail_op));
3201 dnd_helper_destroyer (info->helper);
3202 dnd_folder_info_destroyer (info);
3207 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3209 GtkWindow *parent_window,
3210 TnyAccount *account,
3213 DndFolderInfo *info = NULL;
3215 info = (DndFolderInfo *) user_data;
3217 if (err || canceled) {
3218 dnd_on_connection_failed_destroyer (info, parent_window, account);
3222 /* Connect to source folder and perform the copy/move */
3223 modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3225 drag_and_drop_from_folder_view_src_folder_performer,
3230 * This function is used by drag_data_received_cb to manage drag and
3231 * drop of a folder, i.e, and drag from the folder view to the same
3235 drag_and_drop_from_folder_view (GtkTreeModel *source_model,
3236 GtkTreeModel *dest_model,
3237 GtkTreePath *dest_row,
3238 GtkSelectionData *selection_data,
3241 GtkTreeIter dest_iter, iter;
3242 TnyFolderStore *dest_folder = NULL;
3243 TnyFolderStore *folder = NULL;
3244 gboolean forbidden = FALSE;
3246 DndFolderInfo *info = NULL;
3248 win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3250 g_warning ("%s: BUG: no main window", __FUNCTION__);
3251 dnd_helper_destroyer (helper);
3256 /* check the folder rules for the destination */
3257 folder = tree_path_to_folder (dest_model, dest_row);
3258 if (TNY_IS_FOLDER(folder)) {
3259 ModestTnyFolderRules rules =
3260 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3261 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3262 } else if (TNY_IS_FOLDER_STORE(folder)) {
3263 /* enable local root as destination for folders */
3264 if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3265 !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3268 g_object_unref (folder);
3271 /* check the folder rules for the source */
3272 folder = tree_path_to_folder (source_model, helper->source_row);
3273 if (TNY_IS_FOLDER(folder)) {
3274 ModestTnyFolderRules rules =
3275 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3276 forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3279 g_object_unref (folder);
3283 /* Check if the drag is possible */
3284 if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3286 modest_platform_run_information_dialog ((GtkWindow *) win,
3287 _("mail_in_ui_folder_move_target_error"),
3289 /* Restore the previous selection */
3290 folder = tree_path_to_folder (source_model, helper->source_row);
3292 if (TNY_IS_FOLDER (folder))
3293 modest_folder_view_select_folder (helper->folder_view,
3294 TNY_FOLDER (folder), FALSE);
3295 g_object_unref (folder);
3297 dnd_helper_destroyer (helper);
3302 gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3303 gtk_tree_model_get (dest_model, &dest_iter,
3306 gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3307 gtk_tree_model_get (source_model, &iter,
3311 /* Create the info for the performer */
3312 info = g_slice_new0 (DndFolderInfo);
3313 info->src_folder = g_object_ref (folder);
3314 info->dst_folder = g_object_ref (dest_folder);
3315 info->helper = helper;
3317 /* Connect to the destination folder and perform the copy/move */
3318 modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3320 drag_and_drop_from_folder_view_dst_folder_performer,
3324 g_object_unref (dest_folder);
3325 g_object_unref (folder);
3329 * This function receives the data set by the "drag-data-get" signal
3330 * handler. This information comes within the #GtkSelectionData. This
3331 * function will manage both the drags of folders of the treeview and
3332 * drags of headers of the header view widget.
3335 on_drag_data_received (GtkWidget *widget,
3336 GdkDragContext *context,
3339 GtkSelectionData *selection_data,
3344 GtkWidget *source_widget;
3345 GtkTreeModel *dest_model, *source_model;
3346 GtkTreePath *source_row, *dest_row;
3347 GtkTreeViewDropPosition pos;
3348 gboolean delete_source = FALSE;
3349 gboolean success = FALSE;
3351 /* Do not allow further process */
3352 g_signal_stop_emission_by_name (widget, "drag-data-received");
3353 source_widget = gtk_drag_get_source_widget (context);
3355 /* Get the action */
3356 if (context->action == GDK_ACTION_MOVE) {
3357 delete_source = TRUE;
3359 /* Notify that there is no folder selected. We need to
3360 do this in order to update the headers view (and
3361 its monitors, because when moving, the old folder
3362 won't longer exist. We can not wait for the end of
3363 the operation, because the operation won't start if
3364 the folder is in use */
3365 if (source_widget == widget) {
3366 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3367 gtk_tree_selection_unselect_all (sel);
3371 /* Check if the get_data failed */
3372 if (selection_data == NULL || selection_data->length < 0)
3375 /* Select the destination model */
3376 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3378 /* Get the path to the destination row. Can not call
3379 gtk_tree_view_get_drag_dest_row() because the source row
3380 is not selected anymore */
3381 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3384 /* Only allow drops IN other rows */
3386 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3387 pos == GTK_TREE_VIEW_DROP_AFTER)
3391 /* Drags from the header view */
3392 if (source_widget != widget) {
3393 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3395 drag_and_drop_from_header_view (source_model,
3400 DndHelper *helper = NULL;
3402 /* Get the source model and row */
3403 gtk_tree_get_row_drag_data (selection_data,
3407 /* Create the helper */
3408 helper = g_slice_new0 (DndHelper);
3409 helper->delete_source = delete_source;
3410 helper->source_row = gtk_tree_path_copy (source_row);
3411 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3413 drag_and_drop_from_folder_view (source_model,
3419 gtk_tree_path_free (source_row);
3423 gtk_tree_path_free (dest_row);
3426 /* Finish the drag and drop */
3427 gtk_drag_finish (context, success, FALSE, time);
3431 * We define a "drag-drop" signal handler because we do not want to
3432 * use the default one, because the default one always calls
3433 * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3434 * signal handler, because there we have all the information available
3435 * to know if the dnd was a success or not.
3438 drag_drop_cb (GtkWidget *widget,
3439 GdkDragContext *context,
3447 if (!context->targets)
3450 /* Check if we're dragging a folder row */
3451 target = gtk_drag_dest_find_target (widget, context, NULL);
3453 /* Request the data from the source. */
3454 gtk_drag_get_data(widget, context, target, time);
3460 * This function expands a node of a tree view if it's not expanded
3461 * yet. Not sure why it needs the threads stuff, but gtk+`example code
3462 * does that, so that's why they're here.
3465 expand_row_timeout (gpointer data)
3467 GtkTreeView *tree_view = data;
3468 GtkTreePath *dest_path = NULL;
3469 GtkTreeViewDropPosition pos;
3470 gboolean result = FALSE;
3472 gdk_threads_enter ();
3474 gtk_tree_view_get_drag_dest_row (tree_view,
3479 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3480 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3481 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3482 gtk_tree_path_free (dest_path);
3486 gtk_tree_path_free (dest_path);
3491 gdk_threads_leave ();
3497 * This function is called whenever the pointer is moved over a widget
3498 * while dragging some data. It installs a timeout that will expand a
3499 * node of the treeview if not expanded yet. This function also calls
3500 * gdk_drag_status in order to set the suggested action that will be
3501 * used by the "drag-data-received" signal handler to know if we
3502 * should do a move or just a copy of the data.
3505 on_drag_motion (GtkWidget *widget,
3506 GdkDragContext *context,
3512 GtkTreeViewDropPosition pos;
3513 GtkTreePath *dest_row;
3514 GtkTreeModel *dest_model;
3515 ModestFolderViewPrivate *priv;
3516 GdkDragAction suggested_action;
3517 gboolean valid_location = FALSE;
3518 TnyFolderStore *folder = NULL;
3520 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3522 if (priv->timer_expander != 0) {
3523 g_source_remove (priv->timer_expander);
3524 priv->timer_expander = 0;
3527 gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3532 /* Do not allow drops between folders */
3534 pos == GTK_TREE_VIEW_DROP_BEFORE ||
3535 pos == GTK_TREE_VIEW_DROP_AFTER) {
3536 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3537 gdk_drag_status(context, 0, time);
3538 valid_location = FALSE;
3541 valid_location = TRUE;
3544 /* Check that the destination folder is writable */
3545 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3546 folder = tree_path_to_folder (dest_model, dest_row);
3547 if (folder && TNY_IS_FOLDER (folder)) {
3548 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3550 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3551 valid_location = FALSE;
3556 /* Expand the selected row after 1/2 second */
3557 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3558 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3560 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3562 /* Select the desired action. By default we pick MOVE */
3563 suggested_action = GDK_ACTION_MOVE;
3565 if (context->actions == GDK_ACTION_COPY)
3566 gdk_drag_status(context, GDK_ACTION_COPY, time);
3567 else if (context->actions == GDK_ACTION_MOVE)
3568 gdk_drag_status(context, GDK_ACTION_MOVE, time);
3569 else if (context->actions & suggested_action)
3570 gdk_drag_status(context, suggested_action, time);
3572 gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3576 g_object_unref (folder);
3578 gtk_tree_path_free (dest_row);
3580 g_signal_stop_emission_by_name (widget, "drag-motion");
3582 return valid_location;
3586 * This function sets the treeview as a source and a target for dnd
3587 * events. It also connects all the requirede signals.
3590 setup_drag_and_drop (GtkTreeView *self)
3592 /* Set up the folder view as a dnd destination. Set only the
3593 highlight flag, otherwise gtk will have a different
3595 #ifdef MODEST_TOOLKIT_HILDON2
3598 gtk_drag_dest_set (GTK_WIDGET (self),
3599 GTK_DEST_DEFAULT_HIGHLIGHT,
3600 folder_view_drag_types,
3601 G_N_ELEMENTS (folder_view_drag_types),
3602 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3604 g_signal_connect (G_OBJECT (self),
3605 "drag_data_received",
3606 G_CALLBACK (on_drag_data_received),
3610 /* Set up the treeview as a dnd source */
3611 gtk_drag_source_set (GTK_WIDGET (self),
3613 folder_view_drag_types,
3614 G_N_ELEMENTS (folder_view_drag_types),
3615 GDK_ACTION_MOVE | GDK_ACTION_COPY);
3617 g_signal_connect (G_OBJECT (self),
3619 G_CALLBACK (on_drag_motion),
3622 g_signal_connect (G_OBJECT (self),
3624 G_CALLBACK (on_drag_data_get),
3627 g_signal_connect (G_OBJECT (self),
3629 G_CALLBACK (drag_drop_cb),
3634 * This function manages the navigation through the folders using the
3635 * keyboard or the hardware keys in the device
3638 on_key_pressed (GtkWidget *self,
3642 GtkTreeSelection *selection;
3644 GtkTreeModel *model;
3645 gboolean retval = FALSE;
3647 /* Up and Down are automatically managed by the treeview */
3648 if (event->keyval == GDK_Return) {
3649 /* Expand/Collapse the selected row */
3650 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3651 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3654 path = gtk_tree_model_get_path (model, &iter);
3656 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3657 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3659 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3660 gtk_tree_path_free (path);
3662 /* No further processing */
3670 * We listen to the changes in the local folder account name key,
3671 * because we want to show the right name in the view. The local
3672 * folder account name corresponds to the device name in the Maemo
3673 * version. We do this because we do not want to query gconf on each
3674 * tree view refresh. It's better to cache it and change whenever
3678 on_configuration_key_changed (ModestConf* conf,
3680 ModestConfEvent event,
3681 ModestConfNotificationId id,
3682 ModestFolderView *self)
3684 ModestFolderViewPrivate *priv;
3687 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3688 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3690 if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3691 g_free (priv->local_account_name);
3693 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3694 priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3696 priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3697 MODEST_CONF_DEVICE_NAME, NULL);
3699 /* Force a redraw */
3700 #if GTK_CHECK_VERSION(2, 8, 0)
3701 GtkTreeViewColumn * tree_column;
3703 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3705 gtk_tree_view_column_queue_resize (tree_column);
3707 gtk_widget_queue_draw (GTK_WIDGET (self));
3713 modest_folder_view_set_style (ModestFolderView *self,
3714 ModestFolderViewStyle style)
3716 ModestFolderViewPrivate *priv;
3718 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3719 g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3720 style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3722 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3725 priv->style = style;
3729 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3730 const gchar *account_id)
3732 ModestFolderViewPrivate *priv;
3733 GtkTreeModel *model;
3735 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3737 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3739 /* This will be used by the filter_row callback,
3740 * to decided which rows to show: */
3741 if (priv->visible_account_id) {
3742 g_free (priv->visible_account_id);
3743 priv->visible_account_id = NULL;
3746 priv->visible_account_id = g_strdup (account_id);
3749 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3750 if (GTK_IS_TREE_MODEL_FILTER (model))
3751 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3753 modest_folder_view_update_model(self,
3754 (TnyAccountStore *) modest_runtime_get_account_store());
3756 /* Save settings to gconf */
3757 modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3758 MODEST_CONF_FOLDER_VIEW_KEY);
3760 /* Notify observers */
3761 g_signal_emit (G_OBJECT(self),
3762 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3767 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3769 ModestFolderViewPrivate *priv;
3771 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3773 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3775 return (const gchar *) priv->visible_account_id;
3779 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3783 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3785 gtk_tree_model_get (model, iter,
3789 gboolean result = FALSE;
3790 if (type == TNY_FOLDER_TYPE_INBOX) {
3794 *inbox_iter = *iter;
3798 if (gtk_tree_model_iter_children (model, &child, iter)) {
3799 if (find_inbox_iter (model, &child, inbox_iter))
3803 } while (gtk_tree_model_iter_next (model, iter));
3812 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3814 #ifndef MODEST_TOOLKIT_HILDON2
3815 GtkTreeModel *model;
3816 GtkTreeIter iter, inbox_iter;
3817 GtkTreeSelection *sel;
3818 GtkTreePath *path = NULL;
3820 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3822 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3826 expand_root_items (self);
3827 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3829 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3830 g_warning ("%s: model is empty", __FUNCTION__);
3834 if (find_inbox_iter (model, &iter, &inbox_iter))
3835 path = gtk_tree_model_get_path (model, &inbox_iter);
3837 path = gtk_tree_path_new_first ();
3839 /* Select the row and free */
3840 gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3841 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3842 gtk_tree_path_free (path);
3845 gtk_widget_grab_focus (GTK_WIDGET(self));
3852 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3857 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3858 TnyFolder* a_folder;
3861 gtk_tree_model_get (model, iter,
3862 INSTANCE_COLUMN, &a_folder,
3868 if (folder == a_folder) {
3869 g_object_unref (a_folder);
3870 *folder_iter = *iter;
3873 g_object_unref (a_folder);
3875 if (gtk_tree_model_iter_children (model, &child, iter)) {
3876 if (find_folder_iter (model, &child, folder_iter, folder))
3880 } while (gtk_tree_model_iter_next (model, iter));
3885 #ifndef MODEST_TOOLKIT_HILDON2
3887 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3890 ModestFolderView *self)
3892 ModestFolderViewPrivate *priv = NULL;
3893 GtkTreeSelection *sel;
3894 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3895 GObject *instance = NULL;
3897 if (!MODEST_IS_FOLDER_VIEW(self))
3900 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3902 priv->reexpand = TRUE;
3904 gtk_tree_model_get (tree_model, iter,
3906 INSTANCE_COLUMN, &instance,
3912 if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3913 priv->folder_to_select = g_object_ref (instance);
3915 g_object_unref (instance);
3917 if (priv->folder_to_select) {
3919 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3922 path = gtk_tree_model_get_path (tree_model, iter);
3923 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3925 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3927 gtk_tree_selection_select_iter (sel, iter);
3928 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3930 gtk_tree_path_free (path);
3934 modest_folder_view_disable_next_folder_selection (self);
3936 /* Refilter the model */
3937 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3943 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3945 ModestFolderViewPrivate *priv;
3947 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3949 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3951 if (priv->folder_to_select)
3952 g_object_unref(priv->folder_to_select);
3954 priv->folder_to_select = NULL;
3958 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3959 gboolean after_change)
3961 GtkTreeModel *model;
3962 GtkTreeIter iter, folder_iter;
3963 GtkTreeSelection *sel;
3964 ModestFolderViewPrivate *priv = NULL;
3966 g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3967 g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3969 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3972 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3973 gtk_tree_selection_unselect_all (sel);
3975 if (priv->folder_to_select)
3976 g_object_unref(priv->folder_to_select);
3977 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3981 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3986 /* Refilter the model, before selecting the folder */
3987 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3989 if (!gtk_tree_model_get_iter_first (model, &iter)) {
3990 g_warning ("%s: model is empty", __FUNCTION__);
3994 if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3997 path = gtk_tree_model_get_path (model, &folder_iter);
3998 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
4000 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
4001 gtk_tree_selection_select_iter (sel, &folder_iter);
4002 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
4004 gtk_tree_path_free (path);
4012 modest_folder_view_copy_selection (ModestFolderView *self)
4014 g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
4016 /* Copy selection */
4017 _clipboard_set_selected_data (self, FALSE);
4021 modest_folder_view_cut_selection (ModestFolderView *folder_view)
4023 ModestFolderViewPrivate *priv = NULL;
4024 GtkTreeModel *model = NULL;
4025 const gchar **hidding = NULL;
4026 guint i, n_selected;
4028 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4029 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4031 /* Copy selection */
4032 if (!_clipboard_set_selected_data (folder_view, TRUE))
4035 /* Get hidding ids */
4036 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
4038 /* Clear hidding array created by previous cut operation */
4039 _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
4041 /* Copy hidding array */
4042 priv->n_selected = n_selected;
4043 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
4044 for (i=0; i < n_selected; i++)
4045 priv->hidding_ids[i] = g_strdup(hidding[i]);
4047 /* Hide cut folders */
4048 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
4049 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
4053 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
4054 ModestFolderView *folder_view_dst)
4056 GtkTreeModel *filter_model = NULL;
4057 GtkTreeModel *model = NULL;
4058 GtkTreeModel *new_filter_model = NULL;
4059 GtkTreeModel *old_tny_model = NULL;
4060 GtkTreeModel *new_tny_model = NULL;
4061 ModestFolderViewPrivate *dst_priv;
4063 g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
4064 g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
4066 dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
4067 if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
4068 new_tny_model = NULL;
4071 if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
4072 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
4073 G_OBJECT (old_tny_model),
4074 "activity-changed");
4076 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
4077 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
4079 /* Build new filter model */
4080 new_filter_model = gtk_tree_model_filter_new (model, NULL);
4081 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
4088 /* Set copied model */
4089 gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
4090 #ifndef MODEST_TOOLKIT_HILDON2
4091 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
4092 G_OBJECT(new_filter_model), "row-inserted",
4093 (GCallback) on_row_inserted_maybe_select_folder,
4096 #ifdef MODEST_TOOLKIT_HILDON2
4097 if (new_tny_model) {
4098 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
4099 G_OBJECT (new_tny_model),
4101 G_CALLBACK (on_activity_changed),
4107 g_object_unref (new_filter_model);
4111 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
4114 GtkTreeModel *model = NULL;
4115 ModestFolderViewPrivate* priv;
4117 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4119 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4120 priv->show_non_move = show;
4121 /* modest_folder_view_update_model(folder_view, */
4122 /* TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
4124 /* Hide special folders */
4125 model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
4126 if (GTK_IS_TREE_MODEL_FILTER (model)) {
4127 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
4132 modest_folder_view_show_message_count (ModestFolderView *folder_view,
4135 ModestFolderViewPrivate* priv;
4137 g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
4139 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4140 priv->show_message_count = show;
4142 g_object_set (G_OBJECT (priv->messages_renderer),
4143 "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4147 /* Returns FALSE if it did not selected anything */
4149 _clipboard_set_selected_data (ModestFolderView *folder_view,
4152 ModestFolderViewPrivate *priv = NULL;
4153 TnyFolderStore *folder = NULL;
4154 gboolean retval = FALSE;
4156 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
4157 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4159 /* Set selected data on clipboard */
4160 g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
4161 folder = modest_folder_view_get_selected (folder_view);
4163 /* Do not allow to select an account */
4164 if (TNY_IS_FOLDER (folder)) {
4165 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
4170 g_object_unref (folder);
4176 _clear_hidding_filter (ModestFolderView *folder_view)
4178 ModestFolderViewPrivate *priv;
4181 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4182 priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
4184 if (priv->hidding_ids != NULL) {
4185 for (i=0; i < priv->n_selected; i++)
4186 g_free (priv->hidding_ids[i]);
4187 g_free(priv->hidding_ids);
4193 on_display_name_changed (ModestAccountMgr *mgr,
4194 const gchar *account,
4197 ModestFolderView *self;
4199 self = MODEST_FOLDER_VIEW (user_data);
4201 /* Force a redraw */
4202 #if GTK_CHECK_VERSION(2, 8, 0)
4203 GtkTreeViewColumn * tree_column;
4205 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4207 gtk_tree_view_column_queue_resize (tree_column);
4209 gtk_widget_queue_draw (GTK_WIDGET (self));
4214 modest_folder_view_set_cell_style (ModestFolderView *self,
4215 ModestFolderViewCellStyle cell_style)
4217 ModestFolderViewPrivate *priv = NULL;
4219 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4220 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4222 priv->cell_style = cell_style;
4224 g_object_set (G_OBJECT (priv->messages_renderer),
4225 "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4228 gtk_widget_queue_draw (GTK_WIDGET (self));
4232 update_style (ModestFolderView *self)
4234 ModestFolderViewPrivate *priv;
4235 GdkColor style_color, style_active_color;
4236 PangoAttrList *attr_list;
4238 PangoAttribute *attr;
4240 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4241 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4245 attr_list = pango_attr_list_new ();
4246 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4247 gdk_color_parse ("grey", &style_color);
4249 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4250 pango_attr_list_insert (attr_list, attr);
4253 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4255 "SmallSystemFont", NULL,
4258 attr = pango_attr_font_desc_new (pango_font_description_copy
4259 (style->font_desc));
4260 pango_attr_list_insert (attr_list, attr);
4262 g_object_set (G_OBJECT (priv->messages_renderer),
4263 "foreground-gdk", &style_color,
4264 "foreground-set", TRUE,
4265 "attributes", attr_list,
4267 pango_attr_list_unref (attr_list);
4270 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4271 priv->active_color = style_active_color;
4273 gdk_color_parse ("000", &(priv->active_color));
4278 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4280 if (strcmp ("style", spec->name) == 0) {
4281 update_style (MODEST_FOLDER_VIEW (obj));
4282 gtk_widget_queue_draw (GTK_WIDGET (obj));
4287 modest_folder_view_set_filter (ModestFolderView *self,
4288 ModestFolderViewFilter filter)
4290 ModestFolderViewPrivate *priv;
4291 GtkTreeModel *filter_model;
4293 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4294 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4296 priv->filter |= filter;
4298 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4299 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4300 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4305 modest_folder_view_unset_filter (ModestFolderView *self,
4306 ModestFolderViewFilter filter)
4308 ModestFolderViewPrivate *priv;
4309 GtkTreeModel *filter_model;
4311 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4312 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4314 priv->filter &= ~filter;
4316 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4317 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4318 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4323 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4324 ModestTnyFolderRules rules)
4326 GtkTreeModel *filter_model;
4328 gboolean fulfil = FALSE;
4330 if (!get_inner_models (self, &filter_model, NULL, NULL))
4333 if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4337 TnyFolderStore *folder;
4339 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4341 if (TNY_IS_FOLDER (folder)) {
4342 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4343 /* Folder rules are negative: non_writable, non_deletable... */
4344 if (!(folder_rules & rules))
4347 g_object_unref (folder);
4350 } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4356 modest_folder_view_set_list_to_move (ModestFolderView *self,
4359 ModestFolderViewPrivate *priv;
4361 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4362 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4364 if (priv->list_to_move)
4365 g_object_unref (priv->list_to_move);
4368 g_object_ref (list);
4370 priv->list_to_move = list;
4374 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4376 ModestFolderViewPrivate *priv;
4378 g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4379 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4382 g_free (priv->mailbox);
4384 priv->mailbox = g_strdup (mailbox);
4386 /* Notify observers */
4387 g_signal_emit (G_OBJECT(self),
4388 signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4389 priv->visible_account_id);
4393 modest_folder_view_get_mailbox (ModestFolderView *self)
4395 ModestFolderViewPrivate *priv;
4397 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4398 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4400 return (const gchar *) priv->mailbox;
4404 modest_folder_view_get_activity (ModestFolderView *self)
4406 ModestFolderViewPrivate *priv;
4407 GtkTreeModel *inner_model;
4409 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4410 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4411 g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4413 if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4414 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4420 #ifdef MODEST_TOOLKIT_HILDON2
4422 on_activity_changed (TnyGtkFolderListStore *store,
4424 ModestFolderView *folder_view)
4426 ModestFolderViewPrivate *priv;
4428 g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4429 g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4430 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4432 g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
4438 modest_folder_view_get_model_tny_list (ModestFolderView *self)
4440 GtkTreeModel *model;
4446 if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
4447 ret_value = TNY_LIST (model);
4448 g_object_ref (ret_value);
4455 #ifdef MODEST_TOOLKIT_HILDON2
4457 on_live_search_refilter (HildonLiveSearch *livesearch,
4458 ModestFolderView *self)
4460 GtkTreeModel *filter_model;
4462 if (get_inner_models (self, &filter_model, NULL, NULL))
4463 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
4469 modest_folder_view_setup_live_search (ModestFolderView *self)
4471 ModestFolderViewPrivate *priv;
4473 g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4474 priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4475 priv->live_search = hildon_live_search_new ();
4477 g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), self);
4479 return priv->live_search;