Fix modest_utils_show_dialog_and_forget for gtk and modest shell
[modest] / src / widgets / modest-folder-view.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
16  *
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.
28  */
29
30 #include <glib/gi18n.h>
31 #include <string.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
67 /* Folder view drag types */
68 const GtkTargetEntry folder_view_drag_types[] =
69 {
70         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
71         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
72 };
73
74 /* Default icon sizes for Fremantle style are different */
75 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
76
77 /* Column names depending on we use list store or tree store */
78 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
79 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
80 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
81 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
82 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
83
84 /* 'private'/'protected' functions */
85 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
86 static void modest_folder_view_init        (ModestFolderView *obj);
87 static void modest_folder_view_finalize    (GObject *obj);
88 static void modest_folder_view_dispose     (GObject *obj);
89
90 static void         tny_account_store_view_init (gpointer g,
91                                                  gpointer iface_data);
92
93 static void         modest_folder_view_set_account_store (TnyAccountStoreView *self,
94                                                           TnyAccountStore     *account_store);
95
96 static void         on_selection_changed   (GtkTreeSelection *sel,
97                                             gpointer data);
98
99 static void         on_row_activated       (GtkTreeView *treeview,
100                                             GtkTreePath *path,
101                                             GtkTreeViewColumn *column,
102                                             gpointer userdata);
103
104 static void         on_account_removed     (TnyAccountStore *self,
105                                             TnyAccount *account,
106                                             gpointer user_data);
107
108 static void         on_account_inserted    (TnyAccountStore *self,
109                                             TnyAccount *account,
110                                             gpointer user_data);
111
112 static void         on_account_changed    (TnyAccountStore *self,
113                                             TnyAccount *account,
114                                             gpointer user_data);
115
116 static gint         cmp_rows               (GtkTreeModel *tree_model,
117                                             GtkTreeIter *iter1,
118                                             GtkTreeIter *iter2,
119                                             gpointer user_data);
120
121 static gboolean     filter_row             (GtkTreeModel *model,
122                                             GtkTreeIter *iter,
123                                             gpointer data);
124
125 static gboolean     on_key_pressed         (GtkWidget *self,
126                                             GdkEventKey *event,
127                                             gpointer user_data);
128
129 static void         on_configuration_key_changed  (ModestConf* conf,
130                                                    const gchar *key,
131                                                    ModestConfEvent event,
132                                                    ModestConfNotificationId notification_id,
133                                                    ModestFolderView *self);
134
135 static void         expand_root_items (ModestFolderView *self);
136
137 static gboolean     _clipboard_set_selected_data (ModestFolderView *folder_view,
138                                                   gboolean delete);
139
140 static void         _clear_hidding_filter (ModestFolderView *folder_view);
141
142 static void         on_display_name_changed (ModestAccountMgr *self,
143                                              const gchar *account,
144                                              gpointer user_data);
145 static void         update_style (ModestFolderView *self);
146 static void         on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
147 static gint         get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
148 static gboolean     inbox_is_special (TnyFolderStore *folder_store);
149
150 static gboolean     get_inner_models        (ModestFolderView *self,
151                                              GtkTreeModel **filter_model,
152                                              GtkTreeModel **sort_model,
153                                              GtkTreeModel **tny_model);
154 static void on_activity_changed (TnyGtkFolderListStore *store,
155                                  gboolean activity,
156                                  ModestFolderView *folder_view);
157
158 enum {
159         FOLDER_SELECTION_CHANGED_SIGNAL,
160         FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
161         FOLDER_ACTIVATED_SIGNAL,
162         VISIBLE_ACCOUNT_CHANGED_SIGNAL,
163         ACTIVITY_CHANGED_SIGNAL,
164         LAST_SIGNAL
165 };
166
167 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
168 struct _ModestFolderViewPrivate {
169         TnyAccountStore      *account_store;
170         TnyFolderStore       *cur_folder_store;
171
172         TnyFolder            *folder_to_select; /* folder to select after the next update */
173
174         gulong                changed_signal;
175         gulong                account_inserted_signal;
176         gulong                account_removed_signal;
177         gulong                account_changed_signal;
178         gulong                conf_key_signal;
179         gulong                display_name_changed_signal;
180
181         /* not unref this object, its a singlenton */
182         ModestEmailClipboard *clipboard;
183
184         /* Filter tree model */
185         gchar **hidding_ids;
186         guint n_selected;
187         ModestFolderViewFilter filter;
188
189         TnyFolderStoreQuery  *query;
190         gboolean              do_refresh;
191         guint                 timer_expander;
192
193         gchar                *local_account_name;
194         gchar                *visible_account_id;
195         gchar                *mailbox;
196         ModestFolderViewStyle style;
197         ModestFolderViewCellStyle cell_style;
198         gboolean show_message_count;
199
200         gboolean  reselect; /* we use this to force a reselection of the INBOX */
201         gboolean  show_non_move;
202         TnyList   *list_to_move;
203         gboolean  reexpand; /* next time we expose, we'll expand all root folders */
204
205         GtkCellRenderer *messages_renderer;
206
207         gulong                outbox_deleted_handler;
208
209         GSList   *signal_handlers;
210         GdkColor active_color;
211 };
212 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                       \
213         (G_TYPE_INSTANCE_GET_PRIVATE((o),                       \
214                                      MODEST_TYPE_FOLDER_VIEW,   \
215                                      ModestFolderViewPrivate))
216 /* globals */
217 static GObjectClass *parent_class = NULL;
218
219 static guint signals[LAST_SIGNAL] = {0};
220
221 GType
222 modest_folder_view_get_type (void)
223 {
224         static GType my_type = 0;
225         if (!my_type) {
226                 static const GTypeInfo my_info = {
227                         sizeof(ModestFolderViewClass),
228                         NULL,           /* base init */
229                         NULL,           /* base finalize */
230                         (GClassInitFunc) modest_folder_view_class_init,
231                         NULL,           /* class finalize */
232                         NULL,           /* class data */
233                         sizeof(ModestFolderView),
234                         1,              /* n_preallocs */
235                         (GInstanceInitFunc) modest_folder_view_init,
236                         NULL
237                 };
238
239                 static const GInterfaceInfo tny_account_store_view_info = {
240                         (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
241                         NULL,         /* interface_finalize */
242                         NULL          /* interface_data */
243                 };
244
245
246                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
247                                                   "ModestFolderView",
248                                                   &my_info, 0);
249
250                 g_type_add_interface_static (my_type,
251                                              TNY_TYPE_ACCOUNT_STORE_VIEW,
252                                              &tny_account_store_view_info);
253         }
254         return my_type;
255 }
256
257 static void
258 modest_folder_view_class_init (ModestFolderViewClass *klass)
259 {
260         GObjectClass *gobject_class;
261         GtkTreeViewClass *treeview_class;
262         gobject_class = (GObjectClass*) klass;
263         treeview_class = (GtkTreeViewClass*) klass;
264
265         parent_class            = g_type_class_peek_parent (klass);
266         gobject_class->finalize = modest_folder_view_finalize;
267         gobject_class->dispose  = modest_folder_view_dispose;
268
269         g_type_class_add_private (gobject_class,
270                                   sizeof(ModestFolderViewPrivate));
271
272         signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
273                 g_signal_new ("folder_selection_changed",
274                               G_TYPE_FROM_CLASS (gobject_class),
275                               G_SIGNAL_RUN_FIRST,
276                               G_STRUCT_OFFSET (ModestFolderViewClass,
277                                                folder_selection_changed),
278                               NULL, NULL,
279                               modest_marshal_VOID__POINTER_BOOLEAN,
280                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
281
282         /*
283          * This signal is emitted whenever the currently selected
284          * folder display name is computed. Note that the name could
285          * be different to the folder name, because we could append
286          * the unread messages count to the folder name to build the
287          * folder display name
288          */
289         signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
290                 g_signal_new ("folder-display-name-changed",
291                               G_TYPE_FROM_CLASS (gobject_class),
292                               G_SIGNAL_RUN_FIRST,
293                               G_STRUCT_OFFSET (ModestFolderViewClass,
294                                                folder_display_name_changed),
295                               NULL, NULL,
296                               g_cclosure_marshal_VOID__STRING,
297                               G_TYPE_NONE, 1, G_TYPE_STRING);
298
299         signals[FOLDER_ACTIVATED_SIGNAL] =
300                 g_signal_new ("folder_activated",
301                               G_TYPE_FROM_CLASS (gobject_class),
302                               G_SIGNAL_RUN_FIRST,
303                               G_STRUCT_OFFSET (ModestFolderViewClass,
304                                                folder_activated),
305                               NULL, NULL,
306                               g_cclosure_marshal_VOID__POINTER,
307                               G_TYPE_NONE, 1, G_TYPE_POINTER);
308
309         /*
310          * Emitted whenever the visible account changes
311          */
312         signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
313                 g_signal_new ("visible-account-changed",
314                               G_TYPE_FROM_CLASS (gobject_class),
315                               G_SIGNAL_RUN_FIRST,
316                               G_STRUCT_OFFSET (ModestFolderViewClass,
317                                                visible_account_changed),
318                               NULL, NULL,
319                               g_cclosure_marshal_VOID__STRING,
320                               G_TYPE_NONE, 1, G_TYPE_STRING);
321
322         /*
323          * Emitted when the underlying GtkListStore is updating data
324          */
325         signals[ACTIVITY_CHANGED_SIGNAL] =
326                 g_signal_new ("activity-changed",
327                               G_TYPE_FROM_CLASS (gobject_class),
328                               G_SIGNAL_RUN_FIRST,
329                               G_STRUCT_OFFSET (ModestFolderViewClass,
330                                                activity_changed),
331                               NULL, NULL,
332                               g_cclosure_marshal_VOID__BOOLEAN,
333                               G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
334
335         treeview_class->select_cursor_parent = NULL;
336
337 #ifdef MODEST_TOOLKIT_HILDON2
338         gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
339         
340 #endif
341
342 }
343
344 /* Retrieves the filter, sort and tny models of the folder view. If
345    any of these does not exist then it returns FALSE */
346 static gboolean
347 get_inner_models (ModestFolderView *self, 
348                   GtkTreeModel **filter_model,
349                   GtkTreeModel **sort_model,
350                   GtkTreeModel **tny_model)
351 {
352         GtkTreeModel *s_model, *f_model, *t_model;
353
354         f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
355         if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
356                 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
357                 return FALSE;
358         }
359
360         s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
361         if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
362                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
363                 return FALSE;
364         }
365
366         t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
367
368         /* Assign values */
369         if (filter_model)
370                 *filter_model = f_model;
371         if (sort_model)
372                 *sort_model = s_model;
373         if (tny_model)
374                 *tny_model = t_model;
375
376         return TRUE;
377 }
378
379 /* Simplify checks for NULLs: */
380 static gboolean
381 strings_are_equal (const gchar *a, const gchar *b)
382 {
383         if (!a && !b)
384                 return TRUE;
385         if (a && b)
386         {
387                 return (strcmp (a, b) == 0);
388         }
389         else
390                 return FALSE;
391 }
392
393 static gboolean
394 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path,  GtkTreeIter *iter, gpointer data)
395 {
396         GObject *instance = NULL;
397
398         gtk_tree_model_get (model, iter,
399                             INSTANCE_COLUMN, &instance,
400                             -1);
401
402         if (!instance)
403                 return FALSE; /* keep walking */
404
405         if (!TNY_IS_ACCOUNT (instance)) {
406                 g_object_unref (instance);
407                 return FALSE; /* keep walking */
408         }
409
410         /* Check if this is the looked-for account: */
411         TnyAccount *this_account = TNY_ACCOUNT (instance);
412         TnyAccount *account = TNY_ACCOUNT (data);
413
414         const gchar *this_account_id = tny_account_get_id(this_account);
415         const gchar *account_id = tny_account_get_id(account);
416         g_object_unref (instance);
417         instance = NULL;
418
419         /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
420         if (strings_are_equal(this_account_id, account_id)) {
421                 /* Tell the model that the data has changed, so that
422                  * it calls the cell_data_func callbacks again: */
423                 /* TODO: This does not seem to actually cause the new string to be shown: */
424                 gtk_tree_model_row_changed (model, path, iter);
425
426                 return TRUE; /* stop walking */
427         }
428
429         return FALSE; /* keep walking */
430 }
431
432 typedef struct
433 {
434         ModestFolderView *self;
435         gchar *previous_name;
436 } GetMmcAccountNameData;
437
438 static void
439 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
440 {
441         /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
442
443         GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
444
445         if (!strings_are_equal (
446                 tny_account_get_name(TNY_ACCOUNT(account)),
447                 data->previous_name)) {
448
449                 /* Tell the model that the data has changed, so that
450                  * it calls the cell_data_func callbacks again: */
451                 ModestFolderView *self = data->self;
452                 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
453                 if (model)
454                         gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
455         }
456
457         g_free (data->previous_name);
458         g_slice_free (GetMmcAccountNameData, data);
459 }
460
461 static void
462 convert_parent_folders_to_dots (gchar **item_name)
463 {
464         gint n_parents = 0;
465         gint n_inbox_parents = 0;
466         gchar *c;
467         gchar *path_start;
468         gchar *last_separator;
469
470         if (item_name == NULL)
471                 return;
472
473         path_start = *item_name;
474         for (c = *item_name; *c != '\0'; c++) {
475                 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
476                         gchar *compare;
477                         if (c != path_start) {
478                                 compare = g_strndup (path_start, c - path_start);
479                                 compare = g_strstrip (compare);
480                                 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
481                                         n_inbox_parents++;
482                                 }
483                                 g_free (compare);
484                         }
485                         n_parents++;
486                         path_start = c + 1;
487                 }
488         }
489
490         last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
491         if (last_separator != NULL) {
492                 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
493         }
494
495         if (n_parents > 0) {
496                 GString *buffer;
497                 gint i;
498
499                 buffer = g_string_new ("");
500                 for (i = 0; i < n_parents - n_inbox_parents; i++) {
501                         buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
502                 }
503                 buffer = g_string_append (buffer, last_separator);
504                 g_free (*item_name);
505                 *item_name = g_string_free (buffer, FALSE);
506         }
507
508 }
509
510 static void
511 format_compact_style (gchar **item_name,
512                       GObject *instance,
513                       const gchar *mailbox,
514                       gboolean bold,
515                       gboolean multiaccount,
516                       gboolean *use_markup)
517 {
518         TnyFolder *folder;
519         gboolean is_special;
520         TnyFolderType folder_type;
521
522         if (!TNY_IS_FOLDER (instance))
523                 return;
524
525         folder = (TnyFolder *) instance;
526
527         folder_type = tny_folder_get_folder_type (folder);
528         is_special = (get_cmp_pos (folder_type, folder)!= 4);
529
530         if (mailbox) {
531                 /* Remove mailbox prefix if any */
532                 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
533                 if (g_str_has_prefix (*item_name, prefix)) {
534                         gchar *new_item_name = g_strdup (*item_name + strlen (prefix));
535                         g_free (*item_name);
536                         *item_name = new_item_name;
537                 }
538         }
539
540         if (!is_special || multiaccount) {
541                 TnyAccount *account = tny_folder_get_account (folder);
542                 const gchar *folder_name;
543                 gboolean concat_folder_name = FALSE;
544                 GString *buffer;
545
546                 /* Should not happen */
547                 if (account == NULL)
548                         return;
549
550                 /* convert parent folders to dots */
551                 convert_parent_folders_to_dots  (item_name);
552
553                 folder_name = tny_folder_get_name (folder);
554                 if (g_str_has_suffix (*item_name, folder_name)) {
555                         gchar *offset = g_strrstr (*item_name, folder_name);
556                         *offset = '\0';
557                         concat_folder_name = TRUE;
558                 }
559
560                 buffer = g_string_new ("");
561
562                 buffer = g_string_append (buffer, *item_name);
563                 if (concat_folder_name) {
564                         if (!is_special && folder_type == TNY_FOLDER_TYPE_DRAFTS) {
565                                 buffer = g_string_append (buffer, folder_name);
566                                 /* TODO: append a sensitive string to the remote drafts to
567                                  * be able to know it's the remote one */
568 /*                              buffer = g_string_append (buffer, " (TODO:remote)"); */
569                         } else {
570                                 buffer = g_string_append (buffer, folder_name);
571                         }
572                 }
573                 g_free (*item_name);
574                 g_object_unref (account);
575
576                 *item_name = g_string_free (buffer, FALSE);
577                 *use_markup = FALSE;
578         } else {
579                 *use_markup = FALSE;
580         }
581 }
582
583 static void
584 text_cell_data  (GtkTreeViewColumn *column,
585                  GtkCellRenderer *renderer,
586                  GtkTreeModel *tree_model,
587                  GtkTreeIter *iter,
588                  gpointer data)
589 {
590         ModestFolderViewPrivate *priv;
591         GObject *rendobj = (GObject *) renderer;
592         gchar *fname = NULL;
593         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
594         GObject *instance = NULL;
595         gboolean use_markup = FALSE;
596
597         gtk_tree_model_get (tree_model, iter,
598                             NAME_COLUMN, &fname,
599                             TYPE_COLUMN, &type,
600                             INSTANCE_COLUMN, &instance,
601                             -1);
602         if (!fname || !instance)
603                 goto end;
604
605         ModestFolderView *self = MODEST_FOLDER_VIEW (data);
606         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
607
608         gchar *item_name = NULL;
609         gint item_weight = 400;
610
611         if (type != TNY_FOLDER_TYPE_ROOT) {
612                 gint number = 0;
613                 gboolean drafts;
614                 gboolean is_local;
615
616                 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
617                         modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
618
619                 if (is_local) {
620                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
621                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
622                                 g_free (fname);
623                                 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
624                         }
625                 } else {
626                         /* Sometimes an special folder is reported by the server as
627                            NORMAL, like some versions of Dovecot */
628                         if (type == TNY_FOLDER_TYPE_NORMAL ||
629                             type == TNY_FOLDER_TYPE_UNKNOWN) {
630                                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
631                         }
632                 }
633
634                 /* note: we cannot reliably get the counts from the
635                  * tree model, we need to use explicit calls on
636                  * tny_folder for some reason. Select the number to
637                  * show: the unread or unsent messages. in case of
638                  * outbox/drafts, show all */
639                 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
640                                  (type == TNY_FOLDER_TYPE_OUTBOX) ||
641                                  (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
642                         number = tny_folder_get_all_count (TNY_FOLDER(instance));
643                         drafts = TRUE;
644                 } else {
645                         number = tny_folder_get_unread_count (TNY_FOLDER(instance));
646                         drafts = FALSE;
647                 }
648
649                 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
650                         item_name = g_strdup (fname);
651                         if (number > 0) {
652                                 item_weight = 800;
653                         } else {
654                                 item_weight = 400;
655                         }
656                 } else {
657                         /* Use bold font style if there are unread or unset messages */
658                         if (number > 0) {
659                                 if (priv->show_message_count) {
660                                         item_name = g_strdup_printf ("%s (%d)", fname, number);
661                                 } else {
662                                         item_name = g_strdup (fname);
663                                 }
664                                 item_weight = 800;
665                         } else {
666                                 item_name = g_strdup (fname);
667                                 item_weight = 400;
668                         }
669                 }
670
671         } else if (TNY_IS_ACCOUNT (instance)) {
672                 /* If it's a server account */
673                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
674                         item_name = g_strdup (priv->local_account_name);
675                         item_weight = 800;
676                 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
677                         /* fname is only correct when the items are first
678                          * added to the model, not when the account is
679                          * changed later, so get the name from the account
680                          * instance: */
681                         item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
682                         item_weight = 800;
683                 } else {
684                         item_name = g_strdup (fname);
685                         item_weight = 800;
686                 }
687         }
688
689         /* Convert INBOX */
690         if (type == TNY_FOLDER_TYPE_INBOX &&
691             g_str_has_suffix (fname, "Inbox")) {
692                 g_free (item_name);
693                 item_name = g_strdup (_("mcen_me_folder_inbox"));
694         }
695
696         if (!item_name)
697                 item_name = g_strdup ("unknown");
698
699         if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
700                 gboolean multiaccount;
701
702                 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
703                 /* Convert item_name to markup */
704                 format_compact_style (&item_name, instance, priv->mailbox,
705                                       item_weight == 800, 
706                                       multiaccount, &use_markup);
707         }
708
709         if (item_name && item_weight) {
710                 /* Set the name in the treeview cell: */
711                 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 && 
712                     (priv->active_color.red != 0 || priv->active_color.blue != 0 || priv->active_color.green != 0)) {
713                         g_object_set (rendobj, 
714                                       "text", item_name, 
715                                       "weight-set", FALSE,
716                                       "foreground-set", TRUE,
717                                       "foreground-gdk", &(priv->active_color),
718                                       NULL);
719                 } else {
720                         g_object_set (rendobj, 
721                                       "text", item_name,
722                                       "foreground-set", FALSE,
723                                       "weight-set", TRUE, 
724                                       "weight", item_weight,
725                                       NULL);
726                 }
727
728                 /* Notify display name observers */
729                 /* TODO: What listens for this signal, and how can it use only the new name? */
730                 if (((GObject *) priv->cur_folder_store) == instance) {
731                         g_signal_emit (G_OBJECT(self),
732                                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
733                                                item_name);
734                 }
735                 g_free (item_name);
736
737         }
738
739         /* If it is a Memory card account, make sure that we have the correct name.
740          * This function will be trigerred again when the name has been retrieved: */
741         if (TNY_IS_STORE_ACCOUNT (instance) &&
742                 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
743
744                 /* Get the account name asynchronously: */
745                 GetMmcAccountNameData *callback_data =
746                         g_slice_new0(GetMmcAccountNameData);
747                 callback_data->self = self;
748
749                 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
750                 if (name)
751                         callback_data->previous_name = g_strdup (name);
752
753                 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
754                                                          on_get_mmc_account_name, callback_data);
755         }
756  end:
757         if (instance)
758                 g_object_unref (G_OBJECT (instance));
759         if (fname)
760                 g_free (fname);
761 }
762
763 static void
764 messages_cell_data  (GtkTreeViewColumn *column,
765                  GtkCellRenderer *renderer,
766                  GtkTreeModel *tree_model,
767                  GtkTreeIter *iter,
768                  gpointer data)
769 {
770         ModestFolderView *self; 
771         ModestFolderViewPrivate *priv;
772         GObject *rendobj = (GObject *) renderer;
773         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
774         GObject *instance = NULL;
775         gchar *item_name = NULL;
776
777         gtk_tree_model_get (tree_model, iter,
778                             TYPE_COLUMN, &type,
779                             INSTANCE_COLUMN, &instance,
780                             -1);
781         if (!instance)
782                 goto end;
783
784         self = MODEST_FOLDER_VIEW (data);
785         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
786
787
788         if (type != TNY_FOLDER_TYPE_ROOT) {
789                 gint number = 0;
790                 gboolean drafts;
791                 gboolean is_local;
792
793                 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
794                         modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
795
796                 if (is_local) {
797                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
798                 } else {
799                         /* Sometimes an special folder is reported by the server as
800                            NORMAL, like some versions of Dovecot */
801                         if (type == TNY_FOLDER_TYPE_NORMAL ||
802                             type == TNY_FOLDER_TYPE_UNKNOWN) {
803                                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
804                         }
805                 }
806
807                 /* note: we cannot reliably get the counts from the tree model, we need
808                  * to use explicit calls on tny_folder for some reason.
809                  */
810                 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
811                 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
812                                  (type == TNY_FOLDER_TYPE_OUTBOX) ||
813                                  (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
814                         number = tny_folder_get_all_count (TNY_FOLDER(instance));
815                         drafts = TRUE;
816                 } else {
817                         number = tny_folder_get_unread_count (TNY_FOLDER(instance));
818                         drafts = FALSE;
819                 }
820
821                 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
822                         item_name =
823                                 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
824                                                            (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
825                                                            number), number);
826                 }
827         }
828
829         if (!item_name)
830                 item_name = g_strdup ("");
831
832         if (item_name) {
833                 /* Set the name in the treeview cell: */
834                 g_object_set (rendobj,"text", item_name, NULL);
835
836                 g_free (item_name);
837
838         }
839
840  end:
841         if (instance)
842                 g_object_unref (G_OBJECT (instance));
843 }
844
845
846 typedef struct {
847         GdkPixbuf *pixbuf;
848         GdkPixbuf *pixbuf_open;
849         GdkPixbuf *pixbuf_close;
850 } ThreePixbufs;
851
852
853 static inline GdkPixbuf *
854 get_composite_pixbuf (const gchar *icon_name,
855                       const gint size,
856                       GdkPixbuf *base_pixbuf)
857 {
858         GdkPixbuf *emblem, *retval = NULL;
859
860         emblem = modest_platform_get_icon (icon_name, size);
861         if (emblem) {
862                 retval = gdk_pixbuf_copy (base_pixbuf);
863                 gdk_pixbuf_composite (emblem, retval, 0, 0,
864                                       MIN (gdk_pixbuf_get_width (emblem),
865                                            gdk_pixbuf_get_width (retval)),
866                                       MIN (gdk_pixbuf_get_height (emblem),
867                                            gdk_pixbuf_get_height (retval)),
868                                                   0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
869                 g_object_unref (emblem);
870         }
871         return retval;
872 }
873
874 static inline ThreePixbufs *
875 get_composite_icons (const gchar *icon_code,
876                      GdkPixbuf **pixbuf,
877                      GdkPixbuf **pixbuf_open,
878                      GdkPixbuf **pixbuf_close)
879 {
880         ThreePixbufs *retval;
881
882         if (pixbuf && !*pixbuf) {
883                 GdkPixbuf *icon;
884                 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
885                 if (icon) {
886                         *pixbuf = gdk_pixbuf_copy (icon);
887                 } else {
888                         *pixbuf = NULL;
889                 }
890         }
891
892         if (pixbuf_open && !*pixbuf_open && pixbuf && *pixbuf)
893                 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
894                                                      FOLDER_ICON_SIZE,
895                                                      *pixbuf);
896
897         if (pixbuf_close && !*pixbuf_close && pixbuf && *pixbuf)
898                 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
899                                                       FOLDER_ICON_SIZE,
900                                                       *pixbuf);
901
902         retval = g_slice_new0 (ThreePixbufs);
903         if (pixbuf && *pixbuf)
904                 retval->pixbuf = g_object_ref (*pixbuf);
905         else
906                 retval->pixbuf = NULL;
907         if (pixbuf_open && *pixbuf_open)
908                 retval->pixbuf_open = g_object_ref (*pixbuf_open);
909         else
910                 retval->pixbuf_open = NULL;
911         if (pixbuf_close && *pixbuf_close)
912                 retval->pixbuf_close = g_object_ref (*pixbuf_close);
913         else
914                 retval->pixbuf_close = NULL;
915
916         return retval;
917 }
918
919 static inline ThreePixbufs *
920 get_account_protocol_pixbufs (ModestFolderView *folder_view,
921                               ModestProtocolType protocol_type,
922                               GObject *object)
923 {
924         ModestProtocol *protocol;
925         const GdkPixbuf *pixbuf = NULL;
926         ModestFolderViewPrivate *priv;
927
928         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
929
930         protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
931                                                                   protocol_type);
932
933         if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
934                 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol), 
935                                                            priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
936                                                            MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
937                                                            MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
938                                                            object, FOLDER_ICON_SIZE);
939         }
940
941         if (pixbuf) {
942                 ThreePixbufs *retval;
943                 retval = g_slice_new0 (ThreePixbufs);
944                 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
945                 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
946                 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
947                 return retval;
948         } else {
949                 return NULL;
950         }
951 }
952
953 static inline ThreePixbufs*
954 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
955 {
956         TnyAccount *account = NULL;
957         static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
958                 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
959                 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
960                 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
961                 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
962
963         static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
964                 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
965                 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
966                 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
967                 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
968
969         static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
970                 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
971                 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
972                 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
973                 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
974
975         ThreePixbufs *retval = NULL;
976
977         if (TNY_IS_ACCOUNT (instance)) {
978                 account = g_object_ref (instance);
979         } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
980                 account = tny_folder_get_account (TNY_FOLDER (instance));
981         }
982
983         if (account) {
984                 ModestProtocolType account_store_protocol;
985
986                 account_store_protocol = modest_tny_account_get_protocol_type (account);
987                 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
988                 g_object_unref (account);
989         }
990
991         if (retval)
992                 return retval;
993
994         /* Sometimes an special folder is reported by the server as
995            NORMAL, like some versions of Dovecot */
996         if (type == TNY_FOLDER_TYPE_NORMAL ||
997             type == TNY_FOLDER_TYPE_UNKNOWN) {
998                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
999         }
1000
1001         /* It's not enough with check the folder type. We need to
1002            ensure that we're not giving a special folder icon to a
1003            normal folder with the same name than a special folder */
1004         if (TNY_IS_FOLDER (instance) &&
1005             get_cmp_pos (type, TNY_FOLDER (instance)) ==  4)
1006                 type = TNY_FOLDER_TYPE_NORMAL;
1007
1008         /* Remote folders should not be treated as special folders */
1009         if (TNY_IS_FOLDER_STORE (instance) &&
1010             !TNY_IS_ACCOUNT (instance) &&
1011             type != TNY_FOLDER_TYPE_INBOX &&
1012             modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1013                 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1014                                             &anorm_pixbuf,
1015                                             &anorm_pixbuf_open,
1016                                             &anorm_pixbuf_close);
1017         }
1018
1019         switch (type) {
1020
1021         case TNY_FOLDER_TYPE_INVALID:
1022                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1023                 break;
1024
1025         case TNY_FOLDER_TYPE_ROOT:
1026                 if (TNY_IS_ACCOUNT (instance)) {
1027
1028                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1029                                 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1030                                                               &avirt_pixbuf,
1031                                                               &avirt_pixbuf_open,
1032                                                               &avirt_pixbuf_close);
1033                         } else {
1034                                 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1035
1036                                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1037                                         retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1038                                                                       &ammc_pixbuf,
1039                                                                       &ammc_pixbuf_open,
1040                                                                       &ammc_pixbuf_close);
1041                                 } else {
1042                                         retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1043                                                                       &anorm_pixbuf,
1044                                                                       &anorm_pixbuf_open,
1045                                                                       &anorm_pixbuf_close);
1046                                 }
1047                         }
1048                 }
1049                 break;
1050         case TNY_FOLDER_TYPE_INBOX:
1051                 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1052                                               &inbox_pixbuf,
1053                                               &inbox_pixbuf_open,
1054                                               &inbox_pixbuf_close);
1055                 break;
1056         case TNY_FOLDER_TYPE_OUTBOX:
1057                 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1058                                               &outbox_pixbuf,
1059                                               &outbox_pixbuf_open,
1060                                               &outbox_pixbuf_close);
1061                 break;
1062         case TNY_FOLDER_TYPE_JUNK:
1063                 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1064                                               &junk_pixbuf,
1065                                               &junk_pixbuf_open,
1066                                               &junk_pixbuf_close);
1067                 break;
1068         case TNY_FOLDER_TYPE_SENT:
1069                 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1070                                               &sent_pixbuf,
1071                                               &sent_pixbuf_open,
1072                                               &sent_pixbuf_close);
1073                 break;
1074         case TNY_FOLDER_TYPE_TRASH:
1075                 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1076                                               &trash_pixbuf,
1077                                               &trash_pixbuf_open,
1078                                               &trash_pixbuf_close);
1079                 break;
1080         case TNY_FOLDER_TYPE_DRAFTS:
1081                 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1082                                               &draft_pixbuf,
1083                                               &draft_pixbuf_open,
1084                                               &draft_pixbuf_close);
1085                 break;
1086         case TNY_FOLDER_TYPE_ARCHIVE:
1087                 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1088                                               &mmc_pixbuf,
1089                                               &mmc_pixbuf_open,
1090                                               &mmc_pixbuf_close);
1091                 break;
1092         case TNY_FOLDER_TYPE_NORMAL:
1093         default:
1094                 /* Memory card folders could have an special icon */
1095                 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1096                         retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1097                                                       &mmc_pixbuf,
1098                                                       &mmc_pixbuf_open,
1099                                                       &mmc_pixbuf_close);
1100                 } else {
1101                         retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1102                                                       &normal_pixbuf,
1103                                                       &normal_pixbuf_open,
1104                                                       &normal_pixbuf_close);
1105                 }
1106                 break;
1107         }
1108
1109         return retval;
1110 }
1111
1112 static void
1113 free_pixbufs (ThreePixbufs *pixbufs)
1114 {
1115         if (pixbufs->pixbuf)
1116                 g_object_unref (pixbufs->pixbuf);
1117         if (pixbufs->pixbuf_open)
1118                 g_object_unref (pixbufs->pixbuf_open);
1119         if (pixbufs->pixbuf_close)
1120                 g_object_unref (pixbufs->pixbuf_close);
1121         g_slice_free (ThreePixbufs, pixbufs);
1122 }
1123
1124 static void
1125 icon_cell_data  (GtkTreeViewColumn *column,
1126                  GtkCellRenderer *renderer,
1127                  GtkTreeModel *tree_model,
1128                  GtkTreeIter *iter,
1129                  gpointer data)
1130 {
1131         GObject *rendobj = NULL, *instance = NULL;
1132         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1133         gboolean has_children;
1134         ThreePixbufs *pixbufs;
1135         ModestFolderView *folder_view = (ModestFolderView *) data;
1136
1137         rendobj = (GObject *) renderer;
1138
1139         gtk_tree_model_get (tree_model, iter,
1140                             TYPE_COLUMN, &type,
1141                             INSTANCE_COLUMN, &instance,
1142                             -1);
1143
1144         if (!instance)
1145                 return;
1146
1147         has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1148         pixbufs = get_folder_icons (folder_view, type, instance);
1149         g_object_unref (instance);
1150
1151         /* Set pixbuf */
1152         g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1153
1154         if (has_children) {
1155                 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1156                 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1157         }
1158
1159         free_pixbufs (pixbufs);
1160 }
1161
1162 static void
1163 add_columns (GtkWidget *treeview)
1164 {
1165         GtkTreeViewColumn *column;
1166         GtkCellRenderer *renderer;
1167         GtkTreeSelection *sel;
1168         ModestFolderViewPrivate *priv;
1169
1170         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1171
1172         /* Create column */
1173         column = gtk_tree_view_column_new ();
1174
1175         /* Set icon and text render function */
1176         renderer = gtk_cell_renderer_pixbuf_new();
1177         g_object_set (renderer,
1178                       "xpad", MODEST_MARGIN_DEFAULT,
1179                       "ypad", MODEST_MARGIN_DEFAULT,
1180                       NULL);
1181         gtk_tree_view_column_pack_start (column, renderer, FALSE);
1182         gtk_tree_view_column_set_cell_data_func(column, renderer,
1183                                                 icon_cell_data, treeview, NULL);
1184
1185         renderer = gtk_cell_renderer_text_new();
1186         g_object_set (renderer, 
1187                       "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1188                       "ypad", MODEST_MARGIN_DEFAULT,
1189                       "xpad", MODEST_MARGIN_DEFAULT,
1190                       "ellipsize-set", TRUE, NULL);
1191         gtk_tree_view_column_pack_start (column, renderer, TRUE);
1192         gtk_tree_view_column_set_cell_data_func(column, renderer,
1193                                                 text_cell_data, treeview, NULL);
1194
1195         priv->messages_renderer = gtk_cell_renderer_text_new ();
1196         g_object_set (priv->messages_renderer, 
1197                       "yalign", 0.5,
1198                       "ypad", MODEST_MARGIN_DEFAULT,
1199                       "xpad", MODEST_MARGIN_DOUBLE,
1200                       "alignment", PANGO_ALIGN_RIGHT,
1201                       "align-set", TRUE,
1202                       "xalign", 1.0,
1203                       NULL);
1204         gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1205         gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1206                                                 messages_cell_data, treeview, NULL);
1207
1208         /* Set selection mode */
1209         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1210         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1211
1212         /* Set treeview appearance */
1213         gtk_tree_view_column_set_spacing (column, 2);
1214         gtk_tree_view_column_set_resizable (column, TRUE);
1215         gtk_tree_view_column_set_fixed_width (column, TRUE);
1216         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1217         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1218
1219         /* Add column */
1220         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1221 }
1222
1223 static void
1224 modest_folder_view_init (ModestFolderView *obj)
1225 {
1226         ModestFolderViewPrivate *priv;
1227         ModestConf *conf;
1228
1229         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1230
1231         priv->timer_expander = 0;
1232         priv->account_store  = NULL;
1233         priv->query          = NULL;
1234         priv->do_refresh     = TRUE;
1235         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1236         priv->cur_folder_store   = NULL;
1237         priv->visible_account_id = NULL;
1238         priv->mailbox = NULL;
1239         priv->folder_to_select = NULL;
1240         priv->outbox_deleted_handler = 0;
1241         priv->reexpand = TRUE;
1242         priv->signal_handlers = 0;
1243
1244         /* Initialize the local account name */
1245         conf = modest_runtime_get_conf();
1246         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1247
1248         /* Init email clipboard */
1249         priv->clipboard = modest_runtime_get_email_clipboard ();
1250         priv->hidding_ids = NULL;
1251         priv->n_selected = 0;
1252         priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1253         priv->reselect = FALSE;
1254         priv->show_non_move = TRUE;
1255         priv->list_to_move = NULL;
1256         priv->show_message_count = TRUE;
1257
1258         /* Build treeview */
1259         add_columns (GTK_WIDGET (obj));
1260
1261         /* Connect signals */
1262         g_signal_connect (G_OBJECT (obj),
1263                           "key-press-event",
1264                           G_CALLBACK (on_key_pressed), NULL);
1265
1266         priv->display_name_changed_signal =
1267                 g_signal_connect (modest_runtime_get_account_mgr (),
1268                                   "display_name_changed",
1269                                   G_CALLBACK (on_display_name_changed),
1270                                   obj);
1271
1272         /*
1273          * Track changes in the local account name (in the device it
1274          * will be the device name)
1275          */
1276         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1277                                                   "key_changed",
1278                                                   G_CALLBACK(on_configuration_key_changed),
1279                                                   obj);
1280
1281         gdk_color_parse ("000", &priv->active_color);
1282
1283         update_style (obj);
1284         g_signal_connect (G_OBJECT (obj), "notify::style", 
1285                           G_CALLBACK (on_notify_style), (gpointer) obj);
1286 }
1287
1288 static void
1289 tny_account_store_view_init (gpointer g, gpointer iface_data)
1290 {
1291         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1292
1293         klass->set_account_store = modest_folder_view_set_account_store;
1294 }
1295
1296 static void
1297 modest_folder_view_dispose (GObject *obj)
1298 {
1299         ModestFolderViewPrivate *priv;
1300
1301         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1302
1303         if (priv->signal_handlers) {
1304                 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1305                 priv->signal_handlers = NULL;
1306         }
1307
1308         /* Free external references */
1309         if (priv->account_store) {
1310                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1311                                              priv->account_inserted_signal);
1312                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1313                                              priv->account_removed_signal);
1314                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1315                                              priv->account_changed_signal);
1316                 g_object_unref (G_OBJECT(priv->account_store));
1317                 priv->account_store = NULL;
1318         }
1319
1320         if (priv->query) {
1321                 g_object_unref (G_OBJECT (priv->query));
1322                 priv->query = NULL;
1323         }
1324
1325         if (priv->folder_to_select) {
1326                 g_object_unref (G_OBJECT(priv->folder_to_select));
1327                 priv->folder_to_select = NULL;
1328         }
1329
1330         if (priv->cur_folder_store) {
1331                 g_object_unref (priv->cur_folder_store);
1332                 priv->cur_folder_store = NULL;
1333         }
1334
1335         if (priv->list_to_move) {
1336                 g_object_unref (priv->list_to_move);
1337                 priv->list_to_move = NULL;
1338         }
1339
1340         G_OBJECT_CLASS(parent_class)->dispose (obj);
1341 }
1342
1343 static void
1344 modest_folder_view_finalize (GObject *obj)
1345 {
1346         ModestFolderViewPrivate *priv;
1347         GtkTreeSelection    *sel;
1348         TnyAccount *local_account;
1349
1350         g_return_if_fail (obj);
1351
1352         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1353
1354         if (priv->timer_expander != 0) {
1355                 g_source_remove (priv->timer_expander);
1356                 priv->timer_expander = 0;
1357         }
1358
1359         local_account = (TnyAccount *)
1360                 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1361         if (local_account) {
1362                 if (g_signal_handler_is_connected (local_account,
1363                                                    priv->outbox_deleted_handler))
1364                         g_signal_handler_disconnect (local_account,
1365                                                      priv->outbox_deleted_handler);
1366                 g_object_unref (local_account);
1367         }
1368
1369         if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (), 
1370                                            priv->display_name_changed_signal)) {
1371                 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1372                                              priv->display_name_changed_signal);
1373                 priv->display_name_changed_signal = 0;
1374         }
1375
1376         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1377         if (sel)
1378                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1379
1380         g_free (priv->local_account_name);
1381         g_free (priv->visible_account_id);
1382         g_free (priv->mailbox);
1383
1384         if (priv->conf_key_signal) {
1385                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1386                                              priv->conf_key_signal);
1387                 priv->conf_key_signal = 0;
1388         }
1389
1390         /* Clear hidding array created by cut operation */
1391         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1392
1393         gdk_color_parse ("000", &priv->active_color);
1394
1395         G_OBJECT_CLASS(parent_class)->finalize (obj);
1396 }
1397
1398
1399 static void
1400 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1401 {
1402         ModestFolderViewPrivate *priv;
1403         TnyDevice *device;
1404
1405         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1406         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1407
1408         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1409         device = tny_account_store_get_device (account_store);
1410
1411         if (G_UNLIKELY (priv->account_store)) {
1412
1413                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1414                                                    priv->account_inserted_signal))
1415                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1416                                                      priv->account_inserted_signal);
1417                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1418                                                    priv->account_removed_signal))
1419                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1420                                                      priv->account_removed_signal);
1421                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1422                                                    priv->account_changed_signal))
1423                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1424                                                      priv->account_changed_signal);
1425                 g_object_unref (G_OBJECT (priv->account_store));
1426         }
1427
1428         priv->account_store = g_object_ref (G_OBJECT (account_store));
1429
1430         priv->account_removed_signal =
1431                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1432                                   G_CALLBACK (on_account_removed), self);
1433
1434         priv->account_inserted_signal =
1435                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1436                                   G_CALLBACK (on_account_inserted), self);
1437
1438         priv->account_changed_signal =
1439                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1440                                   G_CALLBACK (on_account_changed), self);
1441
1442         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1443         priv->reselect = FALSE;
1444
1445         g_object_unref (G_OBJECT (device));
1446 }
1447
1448 static void
1449 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1450                       gpointer user_data)
1451 {
1452         ModestFolderView *self;
1453         GtkTreeModel *model, *filter_model;
1454         TnyFolder *outbox;
1455
1456         self = MODEST_FOLDER_VIEW (user_data);
1457
1458         if (!get_inner_models (self, &filter_model, NULL, &model))
1459                 return;
1460
1461         /* Remove outbox from model */
1462         outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1463         tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1464         g_object_unref (outbox);
1465
1466         /* Refilter view */
1467         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1468 }
1469
1470 static void
1471 on_account_inserted (TnyAccountStore *account_store,
1472                      TnyAccount *account,
1473                      gpointer user_data)
1474 {
1475         ModestFolderViewPrivate *priv;
1476         GtkTreeModel *model, *filter_model;
1477
1478         /* Ignore transport account insertions, we're not showing them
1479            in the folder view */
1480         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1481                 return;
1482
1483         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1484
1485
1486         /* If we're adding a new account, and there is no previous
1487            one, we need to select the visible server account */
1488         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1489             !priv->visible_account_id)
1490                 modest_widget_memory_restore (modest_runtime_get_conf(),
1491                                               G_OBJECT (user_data),
1492                                               MODEST_CONF_FOLDER_VIEW_KEY);
1493
1494
1495         /* Get models */
1496         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1497                                &filter_model, NULL, &model))
1498                 return;
1499
1500         /* Insert the account in the model */
1501         tny_list_append (TNY_LIST (model), G_OBJECT (account));
1502
1503         /* When the model is a list store (plain representation) the
1504            outbox is not a child of any account so we have to manually
1505            delete it because removing the local folders account won't
1506            delete it (because tny_folder_get_account() is not defined
1507            for a merge folder */
1508         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1509             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1510
1511                 priv->outbox_deleted_handler =
1512                         g_signal_connect (account,
1513                                           "outbox-deleted",
1514                                           G_CALLBACK (on_outbox_deleted_cb),
1515                                           user_data);
1516         }
1517
1518         /* Refilter the model */
1519         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1520 }
1521
1522
1523 static gboolean
1524 same_account_selected (ModestFolderView *self,
1525                        TnyAccount *account)
1526 {
1527         ModestFolderViewPrivate *priv;
1528         gboolean same_account = FALSE;
1529
1530         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1531
1532         if (priv->cur_folder_store) {
1533                 TnyAccount *selected_folder_account = NULL;
1534
1535                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1536                         selected_folder_account =
1537                                 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1538                 } else {
1539                         selected_folder_account =
1540                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1541                 }
1542
1543                 if (selected_folder_account == account)
1544                         same_account = TRUE;
1545
1546                 g_object_unref (selected_folder_account);
1547         }
1548         return same_account;
1549 }
1550
1551 static void
1552 on_account_changed (TnyAccountStore *account_store,
1553                     TnyAccount *tny_account,
1554                     gpointer user_data)
1555 {
1556         ModestFolderView *self;
1557         ModestFolderViewPrivate *priv;
1558         GtkTreeModel *model, *filter_model;
1559         GtkTreeSelection *sel;
1560         gboolean same_account;
1561
1562         /* Ignore transport account insertions, we're not showing them
1563            in the folder view */
1564         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1565                 return;
1566
1567         self = MODEST_FOLDER_VIEW (user_data);
1568         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1569
1570         /* Get the inner model */
1571         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1572                                &filter_model, NULL, &model))
1573                 return;
1574
1575         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1576
1577         /* Invalidate the cur_folder_store only if the selected folder
1578            belongs to the account that is being removed */
1579         same_account = same_account_selected (self, tny_account);
1580         if (same_account) {
1581                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1582                 gtk_tree_selection_unselect_all (sel);
1583         }
1584
1585         /* Remove the account from the model */
1586         tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1587
1588         /* Insert the account in the model */
1589         tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1590
1591         /* Refilter the model */
1592         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1593
1594 }
1595
1596 static void
1597 on_account_removed (TnyAccountStore *account_store,
1598                     TnyAccount *account,
1599                     gpointer user_data)
1600 {
1601         ModestFolderView *self = NULL;
1602         ModestFolderViewPrivate *priv;
1603         GtkTreeModel *model, *filter_model;
1604         GtkTreeSelection *sel = NULL;
1605         gboolean same_account = FALSE;
1606
1607         /* Ignore transport account removals, we're not showing them
1608            in the folder view */
1609         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1610                 return;
1611
1612         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1613                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1614                 return;
1615         }
1616
1617         self = MODEST_FOLDER_VIEW (user_data);
1618         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1619
1620         /* Invalidate the cur_folder_store only if the selected folder
1621            belongs to the account that is being removed */
1622         same_account = same_account_selected (self, account);
1623         if (same_account) {
1624                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1625                 gtk_tree_selection_unselect_all (sel);
1626         }
1627
1628         /* Invalidate row to select only if the folder to select
1629            belongs to the account that is being removed*/
1630         if (priv->folder_to_select) {
1631                 TnyAccount *folder_to_select_account = NULL;
1632
1633                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1634                 if (folder_to_select_account == account) {
1635                         modest_folder_view_disable_next_folder_selection (self);
1636                         g_object_unref (priv->folder_to_select);
1637                         priv->folder_to_select = NULL;
1638                 }
1639                 g_object_unref (folder_to_select_account);
1640         }
1641
1642         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1643                                &filter_model, NULL, &model))
1644                 return;
1645
1646         /* Disconnect the signal handler */
1647         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1648             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1649                 if (g_signal_handler_is_connected (account,
1650                                                    priv->outbox_deleted_handler))
1651                         g_signal_handler_disconnect (account,
1652                                                      priv->outbox_deleted_handler);
1653         }
1654
1655         /* Remove the account from the model */
1656         tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1657
1658         /* If the removed account is the currently viewed one then
1659            clear the configuration value. The new visible account will be the default account */
1660         if (priv->visible_account_id &&
1661             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1662
1663                 /* Clear the current visible account_id */
1664                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1665                 modest_folder_view_set_mailbox (self, NULL);
1666
1667                 /* Call the restore method, this will set the new visible account */
1668                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1669                                               MODEST_CONF_FOLDER_VIEW_KEY);
1670         }
1671
1672         /* Refilter the model */
1673         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1674
1675 }
1676
1677 void
1678 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1679 {
1680         GtkTreeViewColumn *col;
1681
1682         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1683
1684         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1685         if (!col) {
1686                 g_printerr ("modest: failed get column for title\n");
1687                 return;
1688         }
1689
1690         gtk_tree_view_column_set_title (col, title);
1691         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1692                                            title != NULL);
1693 }
1694
1695 static gboolean
1696 modest_folder_view_on_map (ModestFolderView *self,
1697                            GdkEventExpose *event,
1698                            gpointer data)
1699 {
1700         ModestFolderViewPrivate *priv;
1701
1702         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1703
1704         /* This won't happen often */
1705         if (G_UNLIKELY (priv->reselect)) {
1706                 /* Select the first inbox or the local account if not found */
1707
1708                 /* TODO: this could cause a lock at startup, so we
1709                    comment it for the moment. We know that this will
1710                    be a bug, because the INBOX is not selected, but we
1711                    need to rewrite some parts of Modest to avoid the
1712                    deathlock situation */
1713                 /* TODO: check if this is still the case */
1714                 priv->reselect = FALSE;
1715                 /* Notify the display name observers */
1716                 g_signal_emit (G_OBJECT(self),
1717                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1718                                NULL);
1719         }
1720
1721         if (priv->reexpand) {
1722                 expand_root_items (self);
1723                 priv->reexpand = FALSE;
1724         }
1725
1726         return FALSE;
1727 }
1728
1729 GtkWidget*
1730 modest_folder_view_new (TnyFolderStoreQuery *query)
1731 {
1732         return modest_folder_view_new_full (query, TRUE);
1733 }
1734
1735 GtkWidget*
1736 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1737 {
1738         GObject *self;
1739         ModestFolderViewPrivate *priv;
1740         GtkTreeSelection *sel;
1741
1742         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, 
1743 #ifdef MODEST_TOOLKIT_HILDON2
1744                                        "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1745 #endif
1746                                        NULL));
1747         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1748
1749         if (query)
1750                 priv->query = g_object_ref (query);
1751
1752         priv->do_refresh = do_refresh;
1753
1754         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1755         priv->changed_signal = g_signal_connect (sel, "changed",
1756                                                  G_CALLBACK (on_selection_changed), self);
1757
1758         g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1759
1760         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1761
1762         return GTK_WIDGET(self);
1763 }
1764
1765 /* this feels dirty; any other way to expand all the root items? */
1766 static void
1767 expand_root_items (ModestFolderView *self)
1768 {
1769         GtkTreePath *path;
1770         GtkTreeModel *model;
1771         GtkTreeIter iter;
1772
1773         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1774         path = gtk_tree_path_new_first ();
1775
1776         /* all folders should have child items, so.. */
1777         do {
1778                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1779                 gtk_tree_path_next (path);
1780         } while (gtk_tree_model_get_iter (model, &iter, path));
1781
1782         gtk_tree_path_free (path);
1783 }
1784
1785 static gboolean
1786 is_parent_of (TnyFolder *a, TnyFolder *b)
1787 {
1788         const gchar *a_id;
1789         gboolean retval = FALSE;
1790
1791         a_id = tny_folder_get_id (a);
1792         if (a_id) {
1793                 gchar *string_to_match;
1794                 const gchar *b_id;
1795
1796                 string_to_match = g_strconcat (a_id, "/", NULL);
1797                 b_id = tny_folder_get_id (b);
1798                 retval = g_str_has_prefix (b_id, string_to_match);
1799                 g_free (string_to_match);
1800         }
1801         
1802         return retval;
1803 }
1804
1805 typedef struct _ForeachFolderInfo {
1806         gchar *needle;
1807         gboolean found;
1808 } ForeachFolderInfo;
1809
1810 static gboolean 
1811 foreach_folder_with_id (GtkTreeModel *model,
1812                         GtkTreePath *path,
1813                         GtkTreeIter *iter,
1814                         gpointer data)
1815 {
1816         ForeachFolderInfo *info;
1817         GObject *instance;
1818
1819         info = (ForeachFolderInfo *) data;
1820         gtk_tree_model_get (model, iter,
1821                             INSTANCE_COLUMN, &instance,
1822                             -1);
1823
1824         if (TNY_IS_FOLDER (instance)) {
1825                 const gchar *id;
1826                 gchar *collate;
1827                 id = tny_folder_get_id (TNY_FOLDER (instance));
1828                 if (id) {
1829                         collate = g_utf8_collate_key (id, -1);
1830                         info->found = !strcmp (info->needle, collate);
1831                         g_free (collate);
1832                 }
1833         }
1834
1835         if (instance)
1836                 g_object_unref (instance);
1837
1838         return info->found;
1839         
1840 }
1841
1842
1843 static gboolean
1844 has_folder_with_id (ModestFolderView *self, const gchar *id)
1845 {
1846         GtkTreeModel *model;
1847         ForeachFolderInfo info = {NULL, FALSE};
1848
1849         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1850         info.needle = g_utf8_collate_key (id, -1);
1851         
1852         gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1853         g_free (info.needle);
1854
1855         return info.found;
1856 }
1857
1858 static gboolean
1859 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1860 {
1861         const gchar *a_id;
1862         gboolean retval = FALSE;
1863
1864         a_id = tny_folder_get_id (a);
1865         if (a_id) {
1866                 const gchar *b_id;
1867                 b_id = tny_folder_get_id (b);
1868                 
1869                 if (b_id) {
1870                         const gchar *last_bar;
1871                         gchar *string_to_match;
1872                         last_bar = g_strrstr (b_id, "/");
1873                         if (last_bar)
1874                                 last_bar++;
1875                         else
1876                                 last_bar = b_id;
1877                         string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1878                         retval = has_folder_with_id (self, string_to_match);
1879                         g_free (string_to_match);
1880                 }
1881         }
1882
1883         return retval;
1884 }
1885
1886 static gboolean
1887 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1888 {
1889         ModestFolderViewPrivate *priv;
1890         TnyIterator *iterator;
1891         gboolean retval = TRUE;
1892
1893         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1894         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1895
1896         for (iterator = tny_list_create_iterator (priv->list_to_move);
1897              retval && !tny_iterator_is_done (iterator);
1898              tny_iterator_next (iterator)) {
1899                 GObject *instance;
1900                 instance = tny_iterator_get_current (iterator);
1901                 if (instance == (GObject *) folder) {
1902                         retval = FALSE;
1903                 } else if (TNY_IS_FOLDER (instance)) {
1904                         retval = !is_parent_of (TNY_FOLDER (instance), folder);
1905                         if (retval) {
1906                                 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1907                         }
1908                 }
1909                 g_object_unref (instance);
1910         }
1911         g_object_unref (iterator);
1912
1913         return retval;
1914 }
1915
1916
1917 /*
1918  * We use this function to implement the
1919  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1920  * account in this case, and the local folders.
1921  */
1922 static gboolean
1923 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1924 {
1925         ModestFolderViewPrivate *priv;
1926         gboolean retval = TRUE;
1927         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1928         GObject *instance = NULL;
1929         const gchar *id = NULL;
1930         guint i;
1931         gboolean found = FALSE;
1932         gboolean cleared = FALSE;
1933         ModestTnyFolderRules rules = 0;
1934         gchar *fname;
1935
1936         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1937         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1938
1939         gtk_tree_model_get (model, iter,
1940                             NAME_COLUMN, &fname,
1941                             TYPE_COLUMN, &type,
1942                             INSTANCE_COLUMN, &instance,
1943                             -1);
1944
1945         /* Do not show if there is no instance, this could indeed
1946            happen when the model is being modified while it's being
1947            drawn. This could occur for example when moving folders
1948            using drag&drop */
1949         if (!instance) {
1950                 g_free (fname);
1951                 return FALSE;
1952         }
1953
1954         if (TNY_IS_ACCOUNT (instance)) {
1955                 TnyAccount *acc = TNY_ACCOUNT (instance);
1956                 const gchar *account_id = tny_account_get_id (acc);
1957
1958                 /* If it isn't a special folder,
1959                  * don't show it unless it is the visible account: */
1960                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1961                     !modest_tny_account_is_virtual_local_folders (acc) &&
1962                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1963
1964                         /* Show only the visible account id */
1965                         if (priv->visible_account_id) {
1966                                 if (strcmp (account_id, priv->visible_account_id))
1967                                         retval = FALSE;
1968                         } else {
1969                                 retval = FALSE;
1970                         }
1971                 }
1972
1973                 /* Never show these to the user. They are merged into one folder
1974                  * in the local-folders account instead: */
1975                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1976                         retval = FALSE;
1977         } else {
1978                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1979                         /* Only show special folders for current account if needed */
1980                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1981                                 TnyAccount *account;
1982
1983                                 account = tny_folder_get_account (TNY_FOLDER (instance));
1984
1985                                 if (TNY_IS_ACCOUNT (account)) {
1986                                         const gchar *account_id = tny_account_get_id (account);
1987
1988                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
1989                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1990                                                 /* Show only the visible account id */
1991                                                 if (priv->visible_account_id) {
1992                                                   if (strcmp (account_id, priv->visible_account_id)) {
1993                                                           retval = FALSE;
1994                                                   } else if (priv->mailbox) {
1995                                                           /* Filter mailboxes */
1996                                                           if (!g_str_has_prefix (fname, priv->mailbox)) {
1997                                                                   retval = FALSE;
1998                                                           } else if (!strcmp (fname, priv->mailbox)) {
1999                                                                   /* Hide mailbox parent */
2000                                                                   retval = FALSE;
2001                                                           }
2002                                                   }
2003                                                 }
2004                                         }
2005                                                 g_object_unref (account);
2006                                 }
2007                         }
2008
2009                 }
2010         }
2011
2012         /* Check hiding (if necessary) */
2013         cleared = modest_email_clipboard_cleared (priv->clipboard);
2014         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2015                 id = tny_folder_get_id (TNY_FOLDER(instance));
2016                 if (priv->hidding_ids != NULL)
2017                         for (i=0; i < priv->n_selected && !found; i++)
2018                                 if (priv->hidding_ids[i] != NULL && id != NULL)
2019                                         found = (!strcmp (priv->hidding_ids[i], id));
2020
2021                 retval = !found;
2022         }
2023
2024         /* If this is a move to dialog, hide Sent, Outbox and Drafts
2025         folder as no message can be move there according to UI specs */
2026         if (retval && !priv->show_non_move) {
2027                 if (priv->list_to_move && 
2028                     tny_list_get_length (priv->list_to_move) > 0 &&
2029                     TNY_IS_FOLDER (instance)) {
2030                         retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2031                 }
2032                 if (retval && TNY_IS_FOLDER (instance) && 
2033                     modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2034                         switch (type) {
2035                         case TNY_FOLDER_TYPE_OUTBOX:
2036                         case TNY_FOLDER_TYPE_SENT:
2037                         case TNY_FOLDER_TYPE_DRAFTS:
2038                                 retval = FALSE;
2039                                 break;
2040                         case TNY_FOLDER_TYPE_UNKNOWN:
2041                         case TNY_FOLDER_TYPE_NORMAL:
2042                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2043                                 if (type == TNY_FOLDER_TYPE_INVALID)
2044                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2045                                 
2046                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2047                                     type == TNY_FOLDER_TYPE_SENT
2048                                     || type == TNY_FOLDER_TYPE_DRAFTS)
2049                                         retval = FALSE;
2050                                 break;
2051                         default:
2052                                 break;
2053                         }
2054                 }
2055                 if (retval && TNY_IS_ACCOUNT (instance) &&
2056                     modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2057                         ModestProtocolType protocol_type;
2058
2059                         protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2060                         retval  = !modest_protocol_registry_protocol_type_has_tag
2061                                 (modest_runtime_get_protocol_registry (),
2062                                  protocol_type,
2063                                  MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2064                 }
2065         }
2066
2067         /* apply special filters */
2068         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2069                 if (TNY_IS_ACCOUNT (instance))
2070                         return FALSE;
2071         }
2072
2073         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2074                 if (TNY_IS_FOLDER (instance))
2075                         return FALSE;
2076         }
2077
2078         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2079                 if (TNY_IS_ACCOUNT (instance)) {
2080                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2081                                 return FALSE;
2082                 } else if (TNY_IS_FOLDER (instance)) {
2083                         if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2084                                 return FALSE;
2085                 }
2086         }
2087
2088         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2089                 if (TNY_IS_ACCOUNT (instance)) {
2090                         if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2091                                 return FALSE;
2092                 } else if (TNY_IS_FOLDER (instance)) {
2093                         if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2094                                 return FALSE;
2095                 }
2096         }
2097
2098         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2099                 /* A mailbox is a fake folder with an @ in the middle of the name */
2100                 if (!TNY_IS_FOLDER (instance) ||
2101                     !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2102                         return FALSE;
2103                 } else {
2104                         const gchar *folder_name;
2105                         folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2106                         if (!folder_name || strchr (folder_name, '@') == NULL)
2107                                 return FALSE;
2108                 }
2109                 
2110         }
2111
2112         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2113                 if (TNY_IS_FOLDER (instance)) {
2114                         /* Check folder rules */
2115                         ModestTnyFolderRules rules;
2116
2117                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2118                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2119                 } else if (TNY_IS_ACCOUNT (instance)) {
2120                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2121                                 retval = FALSE;
2122                         } else {
2123                                 retval = TRUE;
2124                         }
2125                 }
2126         }
2127
2128         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2129                 if (TNY_IS_FOLDER (instance)) {
2130                         TnyFolderType guess_type;
2131
2132                         if (TNY_FOLDER_TYPE_NORMAL) {
2133                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2134                         } else {
2135                                 guess_type = type;
2136                         }
2137
2138                         switch (type) {
2139                         case TNY_FOLDER_TYPE_OUTBOX:
2140                         case TNY_FOLDER_TYPE_SENT:
2141                         case TNY_FOLDER_TYPE_DRAFTS:
2142                         case TNY_FOLDER_TYPE_ARCHIVE:
2143                         case TNY_FOLDER_TYPE_INBOX:
2144                                 retval = FALSE;
2145                                 break;
2146                         case TNY_FOLDER_TYPE_UNKNOWN:
2147                         case TNY_FOLDER_TYPE_NORMAL:
2148                                 break;
2149                         default:
2150                                 break;
2151                         }
2152
2153                 } else if (TNY_IS_ACCOUNT (instance)) {
2154                         retval = FALSE;
2155                 }
2156         }
2157
2158         if (retval && TNY_IS_FOLDER (instance)) {
2159                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2160         }
2161
2162         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2163                 if (TNY_IS_FOLDER (instance)) {
2164                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2165                 } else if (TNY_IS_ACCOUNT (instance)) {
2166                         retval = FALSE;
2167                 }
2168         }
2169
2170         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2171                 if (TNY_IS_FOLDER (instance)) {
2172                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2173                 } else if (TNY_IS_ACCOUNT (instance)) {
2174                         retval = FALSE;
2175                 }
2176         }
2177
2178         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2179                 if (TNY_IS_FOLDER (instance)) {
2180                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2181                 } else if (TNY_IS_ACCOUNT (instance)) {
2182                         retval = FALSE;
2183                 }
2184         }
2185
2186         /* Free */
2187         g_object_unref (instance);
2188         g_free (fname);
2189
2190         return retval;
2191 }
2192
2193
2194 gboolean
2195 modest_folder_view_update_model (ModestFolderView *self,
2196                                  TnyAccountStore *account_store)
2197 {
2198         ModestFolderViewPrivate *priv;
2199         GtkTreeModel *model;
2200         GtkTreeModel *filter_model = NULL, *sortable = NULL;
2201
2202         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2203         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2204                               FALSE);
2205
2206         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2207
2208         /* Notify that there is no folder selected */
2209         g_signal_emit (G_OBJECT(self),
2210                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2211                        NULL, FALSE);
2212         if (priv->cur_folder_store) {
2213                 g_object_unref (priv->cur_folder_store);
2214                 priv->cur_folder_store = NULL;
2215         }
2216
2217         /* FIXME: the local accounts are not shown when the query
2218            selects only the subscribed folders */
2219         TnyGtkFolderListStoreFlags flags;
2220         flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2221         if (priv->do_refresh)
2222                 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2223         else
2224                 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2225         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
2226                                                           flags);
2227         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2228                                                       MODEST_FOLDER_PATH_SEPARATOR);
2229
2230         /* When the model is a list store (plain representation) the
2231            outbox is not a child of any account so we have to manually
2232            delete it because removing the local folders account won't
2233            delete it (because tny_folder_get_account() is not defined
2234            for a merge folder */
2235         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2236                 TnyAccount *account;
2237                 ModestTnyAccountStore *acc_store;
2238
2239                 acc_store = modest_runtime_get_account_store ();
2240                 account = modest_tny_account_store_get_local_folders_account (acc_store);
2241
2242                 if (g_signal_handler_is_connected (account,
2243                                                    priv->outbox_deleted_handler))
2244                         g_signal_handler_disconnect (account,
2245                                                      priv->outbox_deleted_handler);
2246
2247                 priv->outbox_deleted_handler =
2248                         g_signal_connect (account,
2249                                           "outbox-deleted",
2250                                           G_CALLBACK (on_outbox_deleted_cb),
2251                                           self);
2252                 g_object_unref (account);
2253         }
2254
2255        if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2256                /* Get the accounts */
2257                tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2258                                                TNY_LIST (model),
2259                                                TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2260        } else {
2261                if (priv->visible_account_id) {
2262                        TnyAccount *account;
2263
2264                        /* Add local folders account */
2265                        account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2266
2267                        if (account) {
2268                                tny_list_append (TNY_LIST (model), (GObject *) account);
2269                                g_object_unref (account);
2270                        }
2271
2272                        account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2273
2274                        if (account) {
2275                                tny_list_append (TNY_LIST (model), (GObject *) account);
2276                                g_object_unref (account);
2277                        }
2278
2279                        /* Add visible account */
2280                        account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2281                                                                               MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2282                                                                               priv->visible_account_id);
2283                        if (account) {
2284                                tny_list_append (TNY_LIST (model), (GObject *) account);
2285                                g_object_unref (account);
2286                        } else {
2287                                g_warning ("You need to set an account first");
2288                                g_object_unref (model);
2289                                return FALSE;
2290                        }
2291                } else {
2292                        g_warning ("You need to set an account first");
2293                        g_object_unref (model);
2294                        return FALSE;
2295                }
2296        }
2297
2298         sortable = gtk_tree_model_sort_new_with_model (model);
2299         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2300                                               NAME_COLUMN,
2301                                               GTK_SORT_ASCENDING);
2302         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2303                                          NAME_COLUMN,
2304                                          cmp_rows, NULL, NULL);
2305
2306         /* Create filter model */
2307         filter_model = gtk_tree_model_filter_new (sortable, NULL);
2308         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2309                                                 filter_row,
2310                                                 self,
2311                                                 NULL);
2312
2313         GtkTreeModel *old_tny_model = NULL;
2314         if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2315                 if (priv->signal_handlers > 0) {
2316                         priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2317                                                                               G_OBJECT (old_tny_model), 
2318                                                                               "activity-changed");
2319                 }
2320         }
2321
2322         /* Set new model */
2323         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2324
2325         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2326                                                            G_OBJECT (model),
2327                                                            "activity-changed",
2328                                                            G_CALLBACK (on_activity_changed),
2329                                                            self);
2330
2331         g_object_unref (model);
2332         g_object_unref (filter_model);
2333         g_object_unref (sortable);
2334
2335         /* Force a reselection of the INBOX next time the widget is shown */
2336         priv->reselect = TRUE;
2337
2338         return TRUE;
2339 }
2340
2341
2342 static void
2343 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2344 {
2345         GtkTreeModel *model = NULL;
2346         TnyFolderStore *folder = NULL;
2347         GtkTreeIter iter;
2348         ModestFolderView *tree_view = NULL;
2349         ModestFolderViewPrivate *priv = NULL;
2350         gboolean selected = FALSE;
2351
2352         g_return_if_fail (sel);
2353         g_return_if_fail (user_data);
2354
2355         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2356
2357         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2358
2359         tree_view = MODEST_FOLDER_VIEW (user_data);
2360
2361         if (selected) {
2362                 gtk_tree_model_get (model, &iter,
2363                                     INSTANCE_COLUMN, &folder,
2364                                     -1);
2365
2366                 /* If the folder is the same do not notify */
2367                 if (folder && priv->cur_folder_store == folder) {
2368                         g_object_unref (folder);
2369                         return;
2370                 }
2371         }
2372
2373         /* Current folder was unselected */
2374         if (priv->cur_folder_store) {
2375                 /* We must do this firstly because a libtinymail-camel
2376                    implementation detail. If we issue the signal
2377                    before doing the sync_async, then that signal could
2378                    cause (and it actually does it) a free of the
2379                    summary of the folder (because the main window will
2380                    clear the headers view */
2381
2382                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2383                        priv->cur_folder_store, FALSE);
2384
2385                 g_object_unref (priv->cur_folder_store);
2386                 priv->cur_folder_store = NULL;
2387         }
2388
2389         /* New current references */
2390         priv->cur_folder_store = folder;
2391
2392         /* New folder has been selected. Do not notify if there is
2393            nothing new selected */
2394         if (selected) {
2395                 g_signal_emit (G_OBJECT(tree_view),
2396                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2397                                0, priv->cur_folder_store, TRUE);
2398         }
2399 }
2400
2401 static void
2402 on_row_activated (GtkTreeView *treeview,
2403                   GtkTreePath *treepath,
2404                   GtkTreeViewColumn *column,
2405                   gpointer user_data)
2406 {
2407         GtkTreeModel *model = NULL;
2408         TnyFolderStore *folder = NULL;
2409         GtkTreeIter iter;
2410         ModestFolderView *self = NULL;
2411         ModestFolderViewPrivate *priv = NULL;
2412
2413         g_return_if_fail (treeview);
2414         g_return_if_fail (user_data);
2415
2416         self = MODEST_FOLDER_VIEW (user_data);
2417         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2418
2419         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2420
2421         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2422                 return;
2423
2424         gtk_tree_model_get (model, &iter,
2425                             INSTANCE_COLUMN, &folder,
2426                             -1);
2427
2428         g_signal_emit (G_OBJECT(self),
2429                        signals[FOLDER_ACTIVATED_SIGNAL],
2430                        0, folder);
2431
2432 #ifdef MODEST_TOOLKIT_HILDON2
2433         HildonUIMode ui_mode;
2434         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2435         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2436                 if (priv->cur_folder_store)
2437                         g_object_unref (priv->cur_folder_store);
2438                 priv->cur_folder_store = g_object_ref (folder);
2439         }
2440 #endif
2441
2442         g_object_unref (folder);
2443 }
2444
2445 TnyFolderStore *
2446 modest_folder_view_get_selected (ModestFolderView *self)
2447 {
2448         ModestFolderViewPrivate *priv;
2449
2450         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2451
2452         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2453         if (priv->cur_folder_store)
2454                 g_object_ref (priv->cur_folder_store);
2455
2456         return priv->cur_folder_store;
2457 }
2458
2459 static gint
2460 get_cmp_rows_type_pos (GObject *folder)
2461 {
2462         /* Remote accounts -> Local account -> MMC account .*/
2463         /* 0, 1, 2 */
2464
2465         if (TNY_IS_ACCOUNT (folder) &&
2466                 modest_tny_account_is_virtual_local_folders (
2467                         TNY_ACCOUNT (folder))) {
2468                 return 1;
2469         } else if (TNY_IS_ACCOUNT (folder)) {
2470                 TnyAccount *account = TNY_ACCOUNT (folder);
2471                 const gchar *account_id = tny_account_get_id (account);
2472                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2473                         return 2;
2474                 else
2475                         return 0;
2476         }
2477         else {
2478                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2479                 return -1; /* Should never happen */
2480         }
2481 }
2482
2483 static gboolean
2484 inbox_is_special (TnyFolderStore *folder_store)
2485 {
2486         gboolean is_special = TRUE;
2487
2488         if (TNY_IS_FOLDER (folder_store)) {
2489                 const gchar *id;
2490                 gchar *downcase;
2491                 gchar *last_bar;
2492                 gchar *last_inbox_bar;
2493
2494                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2495                 downcase = g_utf8_strdown (id, -1);
2496                 last_bar = g_strrstr (downcase, "/");
2497                 if (last_bar) {
2498                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2499                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2500                                 is_special = FALSE;
2501                 } else {
2502                         is_special = FALSE;
2503                 }
2504                 g_free (downcase);
2505         }
2506         return is_special;
2507 }
2508
2509 static gint
2510 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2511 {
2512         TnyAccount *account;
2513         gboolean is_special;
2514         /* Inbox, Outbox, Drafts, Sent, User */
2515         /* 0, 1, 2, 3, 4 */
2516
2517         if (!TNY_IS_FOLDER (folder_store))
2518                 return 4;
2519         switch (t) {
2520         case TNY_FOLDER_TYPE_INBOX:
2521         {
2522                 account = tny_folder_get_account (folder_store);
2523                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2524
2525                 /* In inbox case we need to know if the inbox is really the top
2526                  * inbox of the account, or if it's a submailbox inbox. To do
2527                  * this we'll apply an heuristic rule: Find last "/" and check
2528                  * if it's preceeded by another Inbox */
2529                 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2530                 g_object_unref (account);
2531                 return is_special?0:4;
2532         }
2533         break;
2534         case TNY_FOLDER_TYPE_OUTBOX:
2535                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2536                 break;
2537         case TNY_FOLDER_TYPE_DRAFTS:
2538         {
2539                 account = tny_folder_get_account (folder_store);
2540                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2541                 g_object_unref (account);
2542                 return is_special?1:4;
2543         }
2544         break;
2545         case TNY_FOLDER_TYPE_SENT:
2546         {
2547                 account = tny_folder_get_account (folder_store);
2548                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2549                 g_object_unref (account);
2550                 return is_special?3:4;
2551         }
2552         break;
2553         default:
2554                 return 4;
2555         }
2556 }
2557
2558 static gint
2559 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2560 {
2561         const gchar *a1_name, *a2_name;
2562
2563         a1_name = tny_account_get_name (a1);
2564         a2_name = tny_account_get_name (a2);
2565
2566         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2567 }
2568
2569 static gint
2570 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2571 {
2572         TnyAccount *a1 = NULL, *a2 = NULL;
2573         gint cmp;
2574
2575         if (TNY_IS_ACCOUNT (s1)) {
2576                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2577         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2578                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2579         }
2580
2581         if (TNY_IS_ACCOUNT (s2)) {
2582                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2583         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2584                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2585         }
2586
2587         if (!a1 || !a2) {
2588                 if (!a1 && !a2)
2589                         cmp = 0;
2590                 else if (!a1)
2591                         cmp = 1;
2592                 else
2593                         cmp = -1;
2594                 goto finish;
2595         }
2596
2597         if (a1 == a2) {
2598                 cmp = 0;
2599                 goto finish;
2600         }
2601         /* First we sort with the type of account */
2602         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2603         if (cmp != 0)
2604                 goto finish;
2605
2606         cmp = compare_account_names (a1, a2);
2607
2608 finish:
2609         if (a1)
2610                 g_object_unref (a1);
2611         if (a2)
2612                 g_object_unref (a2);
2613
2614         return cmp;
2615 }
2616
2617 static gint
2618 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2619 {
2620         gint is_account1, is_account2;
2621
2622         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2623         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2624
2625         return is_account2 - is_account1;
2626 }
2627
2628 static gint
2629 compare_folders (const gchar *name1, const gchar *name2)
2630 {
2631         const gchar *separator1, *separator2;
2632         const gchar *next1, *next2;
2633         gchar *top1, *top2;
2634         gint cmp;
2635
2636         if (name1 == NULL || name1[0] == '\0')
2637                 return -1;
2638         if (name2 == NULL || name2[0] == '\0')
2639                 return 1;
2640
2641         separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2642         if (separator1) {
2643                 top1 = g_strndup (name1, separator1 - name1);
2644         } else {
2645                 top1 = g_strdup (name1);
2646         }
2647
2648         separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2649         if (separator2) {
2650                 top2 = g_strndup (name2, separator2 - name2);
2651         } else {
2652                 top2 = g_strdup (name2);
2653         }
2654
2655
2656         cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2657         g_free (top1);
2658         g_free (top2);
2659
2660         if (cmp != 0)
2661                 return cmp;
2662
2663         if (separator1 == NULL && separator2 == NULL)
2664                 return 0;
2665
2666         next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2667         next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2668
2669         return compare_folders (next1, next2);
2670 }
2671
2672
2673 /*
2674  * This function orders the mail accounts according to these rules:
2675  * 1st - remote accounts
2676  * 2nd - local account
2677  * 3rd - MMC account
2678  */
2679 static gint
2680 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2681           gpointer user_data)
2682 {
2683         gint cmp = 0;
2684         gchar *name1 = NULL;
2685         gchar *name2 = NULL;
2686         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2687         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2688         GObject *folder1 = NULL;
2689         GObject *folder2 = NULL;
2690
2691         gtk_tree_model_get (tree_model, iter1,
2692                             NAME_COLUMN, &name1,
2693                             TYPE_COLUMN, &type,
2694                             INSTANCE_COLUMN, &folder1,
2695                             -1);
2696         gtk_tree_model_get (tree_model, iter2,
2697                             NAME_COLUMN, &name2,
2698                             TYPE_COLUMN, &type2,
2699                             INSTANCE_COLUMN, &folder2,
2700                             -1);
2701
2702         /* Return if we get no folder. This could happen when folder
2703            operations are happening. The model is updated after the
2704            folder copy/move actually occurs, so there could be
2705            situations where the model to be drawn is not correct */
2706         if (!folder1 || !folder2)
2707                 goto finish;
2708
2709         /* Sort by type. First the special folders, then the archives */
2710         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2711         if (cmp != 0)
2712                 goto finish;
2713
2714         /* Now we sort using the account of each folder */
2715         if (TNY_IS_FOLDER_STORE (folder1) && 
2716             TNY_IS_FOLDER_STORE (folder2)) {
2717                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2718                 if (cmp != 0)
2719                         goto finish;
2720
2721                 /* Each group is preceeded by its account */
2722                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2723                 if (cmp != 0)
2724                         goto finish;
2725         }
2726
2727         /* Pure sort by name */
2728         cmp = compare_folders (name1, name2);
2729  finish:
2730         if (folder1)
2731                 g_object_unref(G_OBJECT(folder1));
2732         if (folder2)
2733                 g_object_unref(G_OBJECT(folder2));
2734
2735         g_free (name1);
2736         g_free (name2);
2737
2738         return cmp;
2739 }
2740
2741
2742 /*
2743  * This function manages the navigation through the folders using the
2744  * keyboard or the hardware keys in the device
2745  */
2746 static gboolean
2747 on_key_pressed (GtkWidget *self,
2748                 GdkEventKey *event,
2749                 gpointer user_data)
2750 {
2751         GtkTreeSelection *selection;
2752         GtkTreeIter iter;
2753         GtkTreeModel *model;
2754         gboolean retval = FALSE;
2755
2756         /* Up and Down are automatically managed by the treeview */
2757         if (event->keyval == GDK_Return) {
2758                 /* Expand/Collapse the selected row */
2759                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2760                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2761                         GtkTreePath *path;
2762
2763                         path = gtk_tree_model_get_path (model, &iter);
2764
2765                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2766                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2767                         else
2768                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2769                         gtk_tree_path_free (path);
2770                 }
2771                 /* No further processing */
2772                 retval = TRUE;
2773         }
2774
2775         return retval;
2776 }
2777
2778 /*
2779  * We listen to the changes in the local folder account name key,
2780  * because we want to show the right name in the view. The local
2781  * folder account name corresponds to the device name in the Maemo
2782  * version. We do this because we do not want to query gconf on each
2783  * tree view refresh. It's better to cache it and change whenever
2784  * necessary.
2785  */
2786 static void
2787 on_configuration_key_changed (ModestConf* conf,
2788                               const gchar *key,
2789                               ModestConfEvent event,
2790                               ModestConfNotificationId id,
2791                               ModestFolderView *self)
2792 {
2793         ModestFolderViewPrivate *priv;
2794
2795
2796         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2797         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2798
2799         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2800                 g_free (priv->local_account_name);
2801
2802                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2803                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2804                 else
2805                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2806                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2807
2808                 /* Force a redraw */
2809 #if GTK_CHECK_VERSION(2, 8, 0)
2810                 GtkTreeViewColumn * tree_column;
2811
2812                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2813                                                         NAME_COLUMN);
2814                 gtk_tree_view_column_queue_resize (tree_column);
2815 #else
2816                 gtk_widget_queue_draw (GTK_WIDGET (self));
2817 #endif
2818         }
2819 }
2820
2821 void
2822 modest_folder_view_set_style (ModestFolderView *self,
2823                               ModestFolderViewStyle style)
2824 {
2825         ModestFolderViewPrivate *priv;
2826
2827         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2828         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2829                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2830
2831         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2832
2833
2834         priv->style = style;
2835 }
2836
2837 void
2838 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2839                                                              const gchar *account_id)
2840 {
2841         ModestFolderViewPrivate *priv;
2842         GtkTreeModel *model;
2843
2844         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2845
2846         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2847
2848         /* This will be used by the filter_row callback,
2849          * to decided which rows to show: */
2850         if (priv->visible_account_id) {
2851                 g_free (priv->visible_account_id);
2852                 priv->visible_account_id = NULL;
2853         }
2854         if (account_id)
2855                 priv->visible_account_id = g_strdup (account_id);
2856
2857         /* Refilter */
2858         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2859         if (GTK_IS_TREE_MODEL_FILTER (model))
2860                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2861         else
2862                 modest_folder_view_update_model(self,
2863                                                 (TnyAccountStore *) modest_runtime_get_account_store());
2864
2865         /* Save settings to gconf */
2866         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2867                                    MODEST_CONF_FOLDER_VIEW_KEY);
2868
2869         /* Notify observers */
2870         g_signal_emit (G_OBJECT(self),
2871                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
2872                        account_id);
2873 }
2874
2875 const gchar *
2876 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2877 {
2878         ModestFolderViewPrivate *priv;
2879
2880         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2881
2882         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2883
2884         return (const gchar *) priv->visible_account_id;
2885 }
2886
2887 /* recursive */
2888 static gboolean
2889 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2890                   TnyFolder* folder)
2891 {
2892         do {
2893                 GtkTreeIter child;
2894                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2895                 TnyFolder* a_folder;
2896                 gchar *name = NULL;
2897
2898                 gtk_tree_model_get (model, iter,
2899                                     INSTANCE_COLUMN, &a_folder,
2900                                     NAME_COLUMN, &name,
2901                                     TYPE_COLUMN, &type,
2902                                     -1);
2903                 g_free (name);
2904
2905                 if (folder == a_folder) {
2906                         g_object_unref (a_folder);
2907                         *folder_iter = *iter;
2908                         return TRUE;
2909                 }
2910                 g_object_unref (a_folder);
2911
2912                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2913                         if (find_folder_iter (model, &child, folder_iter, folder))
2914                                 return TRUE;
2915                 }
2916
2917         } while (gtk_tree_model_iter_next (model, iter));
2918
2919         return FALSE;
2920 }
2921
2922
2923 void
2924 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2925 {
2926         ModestFolderViewPrivate *priv;
2927
2928         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2929
2930         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2931
2932         if (priv->folder_to_select)
2933                 g_object_unref(priv->folder_to_select);
2934
2935         priv->folder_to_select = NULL;
2936 }
2937
2938 gboolean
2939 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2940                                   gboolean after_change)
2941 {
2942         GtkTreeModel *model;
2943         GtkTreeIter iter, folder_iter;
2944         GtkTreeSelection *sel;
2945         ModestFolderViewPrivate *priv = NULL;
2946
2947         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2948         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2949
2950         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2951
2952         if (after_change) {
2953                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2954                 gtk_tree_selection_unselect_all (sel);
2955
2956                 if (priv->folder_to_select)
2957                         g_object_unref(priv->folder_to_select);
2958                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2959                 return TRUE;
2960         }
2961
2962         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2963         if (!model)
2964                 return FALSE;
2965
2966
2967         /* Refilter the model, before selecting the folder */
2968         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2969
2970         if (!gtk_tree_model_get_iter_first (model, &iter)) {
2971                 g_warning ("%s: model is empty", __FUNCTION__);
2972                 return FALSE;
2973         }
2974
2975         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2976                 GtkTreePath *path;
2977
2978                 path = gtk_tree_model_get_path (model, &folder_iter);
2979                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2980
2981                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2982                 gtk_tree_selection_select_iter (sel, &folder_iter);
2983                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2984
2985                 gtk_tree_path_free (path);
2986                 return TRUE;
2987         }
2988         return FALSE;
2989 }
2990
2991
2992 void
2993 modest_folder_view_copy_selection (ModestFolderView *self)
2994 {
2995         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2996
2997         /* Copy selection */
2998         _clipboard_set_selected_data (self, FALSE);
2999 }
3000
3001 void
3002 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3003 {
3004         ModestFolderViewPrivate *priv = NULL;
3005         GtkTreeModel *model = NULL;
3006         const gchar **hidding = NULL;
3007         guint i, n_selected;
3008
3009         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3010         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3011
3012         /* Copy selection */
3013         if (!_clipboard_set_selected_data (folder_view, TRUE))
3014                 return;
3015
3016         /* Get hidding ids */
3017         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3018
3019         /* Clear hidding array created by previous cut operation */
3020         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3021
3022         /* Copy hidding array */
3023         priv->n_selected = n_selected;
3024         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3025         for (i=0; i < n_selected; i++)
3026                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3027
3028         /* Hide cut folders */
3029         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3030         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3031 }
3032
3033 void
3034 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3035                                ModestFolderView *folder_view_dst)
3036 {
3037         GtkTreeModel *filter_model = NULL;
3038         GtkTreeModel *model = NULL;
3039         GtkTreeModel *new_filter_model = NULL;
3040         GtkTreeModel *old_tny_model = NULL;
3041         GtkTreeModel *new_tny_model = NULL;
3042         ModestFolderViewPrivate *dst_priv;
3043
3044         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3045         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3046
3047         dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3048         if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3049                 new_tny_model = NULL;
3050
3051         /* Get src model*/
3052         if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3053                 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3054                                               G_OBJECT (old_tny_model),
3055                                               "activity-changed");
3056         }
3057         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3058         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3059
3060         /* Build new filter model */
3061         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3062         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3063                                                 filter_row,
3064                                                 folder_view_dst,
3065                                                 NULL);
3066
3067
3068
3069         /* Set copied model */
3070         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3071         if (new_tny_model) {
3072                 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3073                                                                        G_OBJECT (new_tny_model),
3074                                                                        "activity-changed",
3075                                                                        G_CALLBACK (on_activity_changed),
3076                                                                        folder_view_dst);
3077         }
3078
3079         /* Free */
3080         g_object_unref (new_filter_model);
3081 }
3082
3083 void
3084 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3085                                           gboolean show)
3086 {
3087         GtkTreeModel *model = NULL;
3088         ModestFolderViewPrivate* priv;
3089
3090         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3091
3092         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3093         priv->show_non_move = show;
3094 /*      modest_folder_view_update_model(folder_view, */
3095 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3096
3097         /* Hide special folders */
3098         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3099         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3100                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3101         }
3102 }
3103
3104 void
3105 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3106                                           gboolean show)
3107 {
3108         ModestFolderViewPrivate* priv;
3109
3110         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3111
3112         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3113         priv->show_message_count = show;
3114
3115         g_object_set (G_OBJECT (priv->messages_renderer),
3116                       "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3117                       NULL);
3118 }
3119
3120 /* Returns FALSE if it did not selected anything */
3121 static gboolean
3122 _clipboard_set_selected_data (ModestFolderView *folder_view,
3123                               gboolean delete)
3124 {
3125         ModestFolderViewPrivate *priv = NULL;
3126         TnyFolderStore *folder = NULL;
3127         gboolean retval = FALSE;
3128
3129         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3130         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3131
3132         /* Set selected data on clipboard   */
3133         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3134         folder = modest_folder_view_get_selected (folder_view);
3135
3136         /* Do not allow to select an account */
3137         if (TNY_IS_FOLDER (folder)) {
3138                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3139                 retval = TRUE;
3140         }
3141
3142         /* Free */
3143         g_object_unref (folder);
3144
3145         return retval;
3146 }
3147
3148 static void
3149 _clear_hidding_filter (ModestFolderView *folder_view)
3150 {
3151         ModestFolderViewPrivate *priv;
3152         guint i;
3153
3154         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3155         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3156
3157         if (priv->hidding_ids != NULL) {
3158                 for (i=0; i < priv->n_selected; i++)
3159                         g_free (priv->hidding_ids[i]);
3160                 g_free(priv->hidding_ids);
3161         }
3162 }
3163
3164
3165 static void
3166 on_display_name_changed (ModestAccountMgr *mgr,
3167                          const gchar *account,
3168                          gpointer user_data)
3169 {
3170         ModestFolderView *self;
3171
3172         self = MODEST_FOLDER_VIEW (user_data);
3173
3174         /* Force a redraw */
3175 #if GTK_CHECK_VERSION(2, 8, 0)
3176         GtkTreeViewColumn * tree_column;
3177
3178         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3179                                                 NAME_COLUMN);
3180         gtk_tree_view_column_queue_resize (tree_column);
3181 #else
3182         gtk_widget_queue_draw (GTK_WIDGET (self));
3183 #endif
3184 }
3185
3186 void 
3187 modest_folder_view_set_cell_style (ModestFolderView *self,
3188                                    ModestFolderViewCellStyle cell_style)
3189 {
3190         ModestFolderViewPrivate *priv = NULL;
3191
3192         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3193         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3194
3195         priv->cell_style = cell_style;
3196
3197         g_object_set (G_OBJECT (priv->messages_renderer),
3198                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3199                       NULL);
3200         
3201         gtk_widget_queue_draw (GTK_WIDGET (self));
3202 }
3203
3204 static void
3205 update_style (ModestFolderView *self)
3206 {
3207         ModestFolderViewPrivate *priv;
3208         GdkColor style_color, style_active_color;
3209         PangoAttrList *attr_list;
3210         GtkStyle *style;
3211         PangoAttribute *attr;
3212
3213         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3214         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3215
3216         /* Set color */
3217
3218         attr_list = pango_attr_list_new ();
3219
3220         if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3221                 gdk_color_parse ("grey", &style_color);
3222         }
3223         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3224         pango_attr_list_insert (attr_list, attr);
3225         
3226         /* set font */
3227         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3228                                            (GTK_WIDGET(self)),
3229                                            "SmallSystemFont", NULL,
3230                                            G_TYPE_NONE);
3231         if (style) {
3232                 attr = pango_attr_font_desc_new (pango_font_description_copy
3233                                                  (style->font_desc));
3234                 pango_attr_list_insert (attr_list, attr);
3235
3236                 g_object_set (G_OBJECT (priv->messages_renderer),
3237                               "foreground-gdk", &style_color,
3238                               "foreground-set", TRUE,
3239                               "attributes", attr_list,
3240                               NULL);
3241                 pango_attr_list_unref (attr_list);
3242         }
3243
3244         if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3245                 priv->active_color = style_active_color;
3246         } else {
3247                 gdk_color_parse ("000", &(priv->active_color));
3248         }
3249 }
3250
3251 static void 
3252 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3253 {
3254         if (strcmp ("style", spec->name) == 0) {
3255                 update_style (MODEST_FOLDER_VIEW (obj));
3256                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3257         } 
3258 }
3259
3260 void 
3261 modest_folder_view_set_filter (ModestFolderView *self,
3262                                ModestFolderViewFilter filter)
3263 {
3264         ModestFolderViewPrivate *priv;
3265         GtkTreeModel *filter_model;
3266
3267         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3268         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3269
3270         priv->filter |= filter;
3271
3272         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3273         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3274                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3275         }
3276 }
3277
3278 void 
3279 modest_folder_view_unset_filter (ModestFolderView *self,
3280                                  ModestFolderViewFilter filter)
3281 {
3282         ModestFolderViewPrivate *priv;
3283         GtkTreeModel *filter_model;
3284
3285         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3286         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3287
3288         priv->filter &= ~filter;
3289
3290         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3291         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3292                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3293         }
3294 }
3295
3296 gboolean
3297 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3298                                              ModestTnyFolderRules rules)
3299 {
3300         GtkTreeModel *filter_model;
3301         GtkTreeIter iter;
3302         gboolean fulfil = FALSE;
3303
3304         if (!get_inner_models (self, &filter_model, NULL, NULL))
3305                 return FALSE;
3306
3307         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3308                 return FALSE;
3309
3310         do {
3311                 TnyFolderStore *folder;
3312
3313                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3314                 if (folder) {
3315                         if (TNY_IS_FOLDER (folder)) {
3316                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3317                                 /* Folder rules are negative: non_writable, non_deletable... */
3318                                 if (!(folder_rules & rules))
3319                                         fulfil = TRUE;
3320                         }
3321                         g_object_unref (folder);
3322                 }
3323
3324         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3325
3326         return fulfil;
3327 }
3328
3329 void 
3330 modest_folder_view_set_list_to_move (ModestFolderView *self,
3331                                      TnyList *list)
3332 {
3333         ModestFolderViewPrivate *priv;
3334
3335         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3336         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3337
3338         if (priv->list_to_move)
3339                 g_object_unref (priv->list_to_move);
3340
3341         if (list)
3342                 g_object_ref (list);
3343
3344         priv->list_to_move = list;
3345 }
3346
3347 void
3348 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3349 {
3350         ModestFolderViewPrivate *priv;
3351
3352         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3353         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3354
3355         if (priv->mailbox)
3356                 g_free (priv->mailbox);
3357
3358         priv->mailbox = g_strdup (mailbox);
3359
3360         /* Notify observers */
3361         g_signal_emit (G_OBJECT(self),
3362                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3363                        priv->visible_account_id);
3364 }
3365
3366 const gchar *
3367 modest_folder_view_get_mailbox (ModestFolderView *self)
3368 {
3369         ModestFolderViewPrivate *priv;
3370
3371         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3372         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3373
3374         return (const gchar *) priv->mailbox;
3375 }
3376
3377 gboolean 
3378 modest_folder_view_get_activity (ModestFolderView *self)
3379 {
3380         ModestFolderViewPrivate *priv;
3381         GtkTreeModel *inner_model;
3382
3383         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3384         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3385         g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
3386
3387         if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3388                 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3389         } else {
3390                 return FALSE;
3391         }
3392 }
3393
3394 static void
3395 on_activity_changed (TnyGtkFolderListStore *store,
3396                      gboolean activity,
3397                      ModestFolderView *folder_view)
3398 {
3399         ModestFolderViewPrivate *priv;
3400
3401         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3402         g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3403         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3404
3405         g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3406                        activity);
3407 }
3408
3409 TnyList *
3410 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3411 {
3412         GtkTreeModel *model;
3413         TnyList *ret_value;
3414
3415         ret_value = NULL;
3416         model = NULL;
3417
3418         if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3419                 ret_value = TNY_LIST (model);
3420                 g_object_ref (ret_value);
3421         }
3422
3423         return ret_value;
3424
3425 }