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