Drop old non hildon2 code in folder view
[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 static gboolean
2888 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2889 {
2890         do {
2891                 GtkTreeIter child;
2892                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2893
2894                 gtk_tree_model_get (model, iter,
2895                                     TYPE_COLUMN,
2896                                     &type, -1);
2897
2898                 gboolean result = FALSE;
2899                 if (type == TNY_FOLDER_TYPE_INBOX) {
2900                         result = TRUE;
2901                 }
2902                 if (result) {
2903                         *inbox_iter = *iter;
2904                         return TRUE;
2905                 }
2906
2907                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2908                         if (find_inbox_iter (model, &child, inbox_iter))
2909                                 return TRUE;
2910                 }
2911
2912         } while (gtk_tree_model_iter_next (model, iter));
2913
2914         return FALSE;
2915 }
2916
2917 /* recursive */
2918 static gboolean
2919 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2920                   TnyFolder* folder)
2921 {
2922         do {
2923                 GtkTreeIter child;
2924                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2925                 TnyFolder* a_folder;
2926                 gchar *name = NULL;
2927
2928                 gtk_tree_model_get (model, iter,
2929                                     INSTANCE_COLUMN, &a_folder,
2930                                     NAME_COLUMN, &name,
2931                                     TYPE_COLUMN, &type,
2932                                     -1);
2933                 g_free (name);
2934
2935                 if (folder == a_folder) {
2936                         g_object_unref (a_folder);
2937                         *folder_iter = *iter;
2938                         return TRUE;
2939                 }
2940                 g_object_unref (a_folder);
2941
2942                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2943                         if (find_folder_iter (model, &child, folder_iter, folder))
2944                                 return TRUE;
2945                 }
2946
2947         } while (gtk_tree_model_iter_next (model, iter));
2948
2949         return FALSE;
2950 }
2951
2952
2953 void
2954 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2955 {
2956         ModestFolderViewPrivate *priv;
2957
2958         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2959
2960         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2961
2962         if (priv->folder_to_select)
2963                 g_object_unref(priv->folder_to_select);
2964
2965         priv->folder_to_select = NULL;
2966 }
2967
2968 gboolean
2969 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2970                                   gboolean after_change)
2971 {
2972         GtkTreeModel *model;
2973         GtkTreeIter iter, folder_iter;
2974         GtkTreeSelection *sel;
2975         ModestFolderViewPrivate *priv = NULL;
2976
2977         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2978         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
2979
2980         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2981
2982         if (after_change) {
2983                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2984                 gtk_tree_selection_unselect_all (sel);
2985
2986                 if (priv->folder_to_select)
2987                         g_object_unref(priv->folder_to_select);
2988                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2989                 return TRUE;
2990         }
2991
2992         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2993         if (!model)
2994                 return FALSE;
2995
2996
2997         /* Refilter the model, before selecting the folder */
2998         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2999
3000         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3001                 g_warning ("%s: model is empty", __FUNCTION__);
3002                 return FALSE;
3003         }
3004
3005         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3006                 GtkTreePath *path;
3007
3008                 path = gtk_tree_model_get_path (model, &folder_iter);
3009                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3010
3011                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3012                 gtk_tree_selection_select_iter (sel, &folder_iter);
3013                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3014
3015                 gtk_tree_path_free (path);
3016                 return TRUE;
3017         }
3018         return FALSE;
3019 }
3020
3021
3022 void
3023 modest_folder_view_copy_selection (ModestFolderView *self)
3024 {
3025         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3026
3027         /* Copy selection */
3028         _clipboard_set_selected_data (self, FALSE);
3029 }
3030
3031 void
3032 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3033 {
3034         ModestFolderViewPrivate *priv = NULL;
3035         GtkTreeModel *model = NULL;
3036         const gchar **hidding = NULL;
3037         guint i, n_selected;
3038
3039         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3040         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3041
3042         /* Copy selection */
3043         if (!_clipboard_set_selected_data (folder_view, TRUE))
3044                 return;
3045
3046         /* Get hidding ids */
3047         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3048
3049         /* Clear hidding array created by previous cut operation */
3050         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3051
3052         /* Copy hidding array */
3053         priv->n_selected = n_selected;
3054         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3055         for (i=0; i < n_selected; i++)
3056                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3057
3058         /* Hide cut folders */
3059         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3060         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3061 }
3062
3063 void
3064 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3065                                ModestFolderView *folder_view_dst)
3066 {
3067         GtkTreeModel *filter_model = NULL;
3068         GtkTreeModel *model = NULL;
3069         GtkTreeModel *new_filter_model = NULL;
3070         GtkTreeModel *old_tny_model = NULL;
3071         GtkTreeModel *new_tny_model = NULL;
3072         ModestFolderViewPrivate *dst_priv;
3073
3074         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3075         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3076
3077         dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3078         if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3079                 new_tny_model = NULL;
3080
3081         /* Get src model*/
3082         if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3083                 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3084                                               G_OBJECT (old_tny_model),
3085                                               "activity-changed");
3086         }
3087         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3088         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3089
3090         /* Build new filter model */
3091         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3092         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3093                                                 filter_row,
3094                                                 folder_view_dst,
3095                                                 NULL);
3096
3097
3098
3099         /* Set copied model */
3100         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3101         if (new_tny_model) {
3102                 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3103                                                                        G_OBJECT (new_tny_model),
3104                                                                        "activity-changed",
3105                                                                        G_CALLBACK (on_activity_changed),
3106                                                                        folder_view_dst);
3107         }
3108
3109         /* Free */
3110         g_object_unref (new_filter_model);
3111 }
3112
3113 void
3114 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3115                                           gboolean show)
3116 {
3117         GtkTreeModel *model = NULL;
3118         ModestFolderViewPrivate* priv;
3119
3120         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3121
3122         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3123         priv->show_non_move = show;
3124 /*      modest_folder_view_update_model(folder_view, */
3125 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3126
3127         /* Hide special folders */
3128         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3129         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3130                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3131         }
3132 }
3133
3134 void
3135 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3136                                           gboolean show)
3137 {
3138         ModestFolderViewPrivate* priv;
3139
3140         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3141
3142         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3143         priv->show_message_count = show;
3144
3145         g_object_set (G_OBJECT (priv->messages_renderer),
3146                       "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3147                       NULL);
3148 }
3149
3150 /* Returns FALSE if it did not selected anything */
3151 static gboolean
3152 _clipboard_set_selected_data (ModestFolderView *folder_view,
3153                               gboolean delete)
3154 {
3155         ModestFolderViewPrivate *priv = NULL;
3156         TnyFolderStore *folder = NULL;
3157         gboolean retval = FALSE;
3158
3159         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3160         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3161
3162         /* Set selected data on clipboard   */
3163         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3164         folder = modest_folder_view_get_selected (folder_view);
3165
3166         /* Do not allow to select an account */
3167         if (TNY_IS_FOLDER (folder)) {
3168                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3169                 retval = TRUE;
3170         }
3171
3172         /* Free */
3173         g_object_unref (folder);
3174
3175         return retval;
3176 }
3177
3178 static void
3179 _clear_hidding_filter (ModestFolderView *folder_view)
3180 {
3181         ModestFolderViewPrivate *priv;
3182         guint i;
3183
3184         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3185         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3186
3187         if (priv->hidding_ids != NULL) {
3188                 for (i=0; i < priv->n_selected; i++)
3189                         g_free (priv->hidding_ids[i]);
3190                 g_free(priv->hidding_ids);
3191         }
3192 }
3193
3194
3195 static void
3196 on_display_name_changed (ModestAccountMgr *mgr,
3197                          const gchar *account,
3198                          gpointer user_data)
3199 {
3200         ModestFolderView *self;
3201
3202         self = MODEST_FOLDER_VIEW (user_data);
3203
3204         /* Force a redraw */
3205 #if GTK_CHECK_VERSION(2, 8, 0)
3206         GtkTreeViewColumn * tree_column;
3207
3208         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3209                                                 NAME_COLUMN);
3210         gtk_tree_view_column_queue_resize (tree_column);
3211 #else
3212         gtk_widget_queue_draw (GTK_WIDGET (self));
3213 #endif
3214 }
3215
3216 void 
3217 modest_folder_view_set_cell_style (ModestFolderView *self,
3218                                    ModestFolderViewCellStyle cell_style)
3219 {
3220         ModestFolderViewPrivate *priv = NULL;
3221
3222         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3223         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3224
3225         priv->cell_style = cell_style;
3226
3227         g_object_set (G_OBJECT (priv->messages_renderer),
3228                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3229                       NULL);
3230         
3231         gtk_widget_queue_draw (GTK_WIDGET (self));
3232 }
3233
3234 static void
3235 update_style (ModestFolderView *self)
3236 {
3237         ModestFolderViewPrivate *priv;
3238         GdkColor style_color, style_active_color;
3239         PangoAttrList *attr_list;
3240         GtkStyle *style;
3241         PangoAttribute *attr;
3242
3243         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3244         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3245
3246         /* Set color */
3247
3248         attr_list = pango_attr_list_new ();
3249
3250         if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3251                 gdk_color_parse ("grey", &style_color);
3252         }
3253         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3254         pango_attr_list_insert (attr_list, attr);
3255         
3256         /* set font */
3257         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3258                                            (GTK_WIDGET(self)),
3259                                            "SmallSystemFont", NULL,
3260                                            G_TYPE_NONE);
3261         if (style) {
3262                 attr = pango_attr_font_desc_new (pango_font_description_copy
3263                                                  (style->font_desc));
3264                 pango_attr_list_insert (attr_list, attr);
3265
3266                 g_object_set (G_OBJECT (priv->messages_renderer),
3267                               "foreground-gdk", &style_color,
3268                               "foreground-set", TRUE,
3269                               "attributes", attr_list,
3270                               NULL);
3271                 pango_attr_list_unref (attr_list);
3272         }
3273
3274         if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3275                 priv->active_color = style_active_color;
3276         } else {
3277                 gdk_color_parse ("000", &(priv->active_color));
3278         }
3279 }
3280
3281 static void 
3282 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3283 {
3284         if (strcmp ("style", spec->name) == 0) {
3285                 update_style (MODEST_FOLDER_VIEW (obj));
3286                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3287         } 
3288 }
3289
3290 void 
3291 modest_folder_view_set_filter (ModestFolderView *self,
3292                                ModestFolderViewFilter filter)
3293 {
3294         ModestFolderViewPrivate *priv;
3295         GtkTreeModel *filter_model;
3296
3297         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3298         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3299
3300         priv->filter |= filter;
3301
3302         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3303         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3304                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3305         }
3306 }
3307
3308 void 
3309 modest_folder_view_unset_filter (ModestFolderView *self,
3310                                  ModestFolderViewFilter filter)
3311 {
3312         ModestFolderViewPrivate *priv;
3313         GtkTreeModel *filter_model;
3314
3315         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3316         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3317
3318         priv->filter &= ~filter;
3319
3320         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3321         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3322                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3323         }
3324 }
3325
3326 gboolean
3327 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3328                                              ModestTnyFolderRules rules)
3329 {
3330         GtkTreeModel *filter_model;
3331         GtkTreeIter iter;
3332         gboolean fulfil = FALSE;
3333
3334         if (!get_inner_models (self, &filter_model, NULL, NULL))
3335                 return FALSE;
3336
3337         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3338                 return FALSE;
3339
3340         do {
3341                 TnyFolderStore *folder;
3342
3343                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3344                 if (folder) {
3345                         if (TNY_IS_FOLDER (folder)) {
3346                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3347                                 /* Folder rules are negative: non_writable, non_deletable... */
3348                                 if (!(folder_rules & rules))
3349                                         fulfil = TRUE;
3350                         }
3351                         g_object_unref (folder);
3352                 }
3353
3354         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3355
3356         return fulfil;
3357 }
3358
3359 void 
3360 modest_folder_view_set_list_to_move (ModestFolderView *self,
3361                                      TnyList *list)
3362 {
3363         ModestFolderViewPrivate *priv;
3364
3365         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3366         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3367
3368         if (priv->list_to_move)
3369                 g_object_unref (priv->list_to_move);
3370
3371         if (list)
3372                 g_object_ref (list);
3373
3374         priv->list_to_move = list;
3375 }
3376
3377 void
3378 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3379 {
3380         ModestFolderViewPrivate *priv;
3381
3382         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3383         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3384
3385         if (priv->mailbox)
3386                 g_free (priv->mailbox);
3387
3388         priv->mailbox = g_strdup (mailbox);
3389
3390         /* Notify observers */
3391         g_signal_emit (G_OBJECT(self),
3392                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3393                        priv->visible_account_id);
3394 }
3395
3396 const gchar *
3397 modest_folder_view_get_mailbox (ModestFolderView *self)
3398 {
3399         ModestFolderViewPrivate *priv;
3400
3401         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3402         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3403
3404         return (const gchar *) priv->mailbox;
3405 }
3406
3407 gboolean 
3408 modest_folder_view_get_activity (ModestFolderView *self)
3409 {
3410         ModestFolderViewPrivate *priv;
3411         GtkTreeModel *inner_model;
3412
3413         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3414         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3415         g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
3416
3417         if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3418                 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3419         } else {
3420                 return FALSE;
3421         }
3422 }
3423
3424 static void
3425 on_activity_changed (TnyGtkFolderListStore *store,
3426                      gboolean activity,
3427                      ModestFolderView *folder_view)
3428 {
3429         ModestFolderViewPrivate *priv;
3430
3431         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3432         g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3433         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3434
3435         g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3436                        activity);
3437 }
3438
3439 TnyList *
3440 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3441 {
3442         GtkTreeModel *model;
3443         TnyList *ret_value;
3444
3445         ret_value = NULL;
3446         model = NULL;
3447
3448         if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3449                 ret_value = TNY_LIST (model);
3450                 g_object_ref (ret_value);
3451         }
3452
3453         return ret_value;
3454
3455 }