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