1d411ee8b9ca98f5810a6aee01811444025b22ed
[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         gtk_tree_view_set_rules_hint ((GtkTreeView *) treeview, TRUE);
1267
1268         /* Add column */
1269         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1270 }
1271
1272 static void
1273 modest_folder_view_init (ModestFolderView *obj)
1274 {
1275         ModestFolderViewPrivate *priv;
1276         ModestConf *conf;
1277
1278         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1279
1280         priv->timer_expander = 0;
1281         priv->account_store  = NULL;
1282         priv->query          = NULL;
1283         priv->do_refresh     = TRUE;
1284         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1285         priv->cur_folder_store   = NULL;
1286         priv->visible_account_id = NULL;
1287         priv->mailbox = NULL;
1288         priv->folder_to_select = NULL;
1289         priv->outbox_deleted_handler = 0;
1290         priv->reexpand = TRUE;
1291         priv->signal_handlers = 0;
1292
1293         /* Initialize the local account name */
1294         conf = modest_runtime_get_conf();
1295         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1296
1297         /* Init email clipboard */
1298         priv->clipboard = modest_runtime_get_email_clipboard ();
1299         priv->hidding_ids = NULL;
1300         priv->n_selected = 0;
1301         priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1302         priv->reselect = FALSE;
1303         priv->show_non_move = TRUE;
1304         priv->list_to_move = NULL;
1305         priv->show_message_count = TRUE;
1306
1307         /* Build treeview */
1308         add_columns (GTK_WIDGET (obj));
1309
1310         /* Connect signals */
1311         g_signal_connect (G_OBJECT (obj),
1312                           "key-press-event",
1313                           G_CALLBACK (on_key_pressed), NULL);
1314
1315         priv->display_name_changed_signal =
1316                 g_signal_connect (modest_runtime_get_account_mgr (),
1317                                   "display_name_changed",
1318                                   G_CALLBACK (on_display_name_changed),
1319                                   obj);
1320
1321         /*
1322          * Track changes in the local account name (in the device it
1323          * will be the device name)
1324          */
1325         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1326                                                   "key_changed",
1327                                                   G_CALLBACK(on_configuration_key_changed),
1328                                                   obj);
1329
1330         gdk_color_parse ("000", &priv->active_color);
1331
1332         update_style (obj);
1333         g_signal_connect (G_OBJECT (obj), "notify::style", 
1334                           G_CALLBACK (on_notify_style), (gpointer) obj);
1335 }
1336
1337 static void
1338 tny_account_store_view_init (gpointer g, gpointer iface_data)
1339 {
1340         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1341
1342         klass->set_account_store = modest_folder_view_set_account_store;
1343 }
1344
1345 static void
1346 modest_folder_view_dispose (GObject *obj)
1347 {
1348         ModestFolderViewPrivate *priv;
1349
1350         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1351
1352         if (priv->signal_handlers) {
1353                 modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1354                 priv->signal_handlers = NULL;
1355         }
1356
1357         /* Free external references */
1358         if (priv->account_store) {
1359                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1360                                              priv->account_inserted_signal);
1361                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1362                                              priv->account_removed_signal);
1363                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1364                                              priv->account_changed_signal);
1365                 g_object_unref (G_OBJECT(priv->account_store));
1366                 priv->account_store = NULL;
1367         }
1368
1369         if (priv->query) {
1370                 g_object_unref (G_OBJECT (priv->query));
1371                 priv->query = NULL;
1372         }
1373
1374         if (priv->folder_to_select) {
1375                 g_object_unref (G_OBJECT(priv->folder_to_select));
1376                 priv->folder_to_select = NULL;
1377         }
1378
1379         if (priv->cur_folder_store) {
1380                 g_object_unref (priv->cur_folder_store);
1381                 priv->cur_folder_store = NULL;
1382         }
1383
1384         if (priv->list_to_move) {
1385                 g_object_unref (priv->list_to_move);
1386                 priv->list_to_move = NULL;
1387         }
1388
1389         G_OBJECT_CLASS(parent_class)->dispose (obj);
1390 }
1391
1392 static void
1393 modest_folder_view_finalize (GObject *obj)
1394 {
1395         ModestFolderViewPrivate *priv;
1396         GtkTreeSelection    *sel;
1397         TnyAccount *local_account;
1398
1399         g_return_if_fail (obj);
1400
1401         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1402
1403         if (priv->timer_expander != 0) {
1404                 g_source_remove (priv->timer_expander);
1405                 priv->timer_expander = 0;
1406         }
1407
1408         local_account = (TnyAccount *)
1409                 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1410         if (local_account) {
1411                 if (g_signal_handler_is_connected (local_account,
1412                                                    priv->outbox_deleted_handler))
1413                         g_signal_handler_disconnect (local_account,
1414                                                      priv->outbox_deleted_handler);
1415                 g_object_unref (local_account);
1416         }
1417
1418         if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (), 
1419                                            priv->display_name_changed_signal)) {
1420                 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1421                                              priv->display_name_changed_signal);
1422                 priv->display_name_changed_signal = 0;
1423         }
1424
1425         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1426         if (sel)
1427                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1428
1429         g_free (priv->local_account_name);
1430         g_free (priv->visible_account_id);
1431         g_free (priv->mailbox);
1432
1433         if (priv->conf_key_signal) {
1434                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1435                                              priv->conf_key_signal);
1436                 priv->conf_key_signal = 0;
1437         }
1438
1439         /* Clear hidding array created by cut operation */
1440         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1441
1442         gdk_color_parse ("000", &priv->active_color);
1443
1444         G_OBJECT_CLASS(parent_class)->finalize (obj);
1445 }
1446
1447
1448 static void
1449 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1450 {
1451         ModestFolderViewPrivate *priv;
1452         TnyDevice *device;
1453
1454         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1455         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1456
1457         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1458         device = tny_account_store_get_device (account_store);
1459
1460         if (G_UNLIKELY (priv->account_store)) {
1461
1462                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1463                                                    priv->account_inserted_signal))
1464                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1465                                                      priv->account_inserted_signal);
1466                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1467                                                    priv->account_removed_signal))
1468                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1469                                                      priv->account_removed_signal);
1470                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1471                                                    priv->account_changed_signal))
1472                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1473                                                      priv->account_changed_signal);
1474                 g_object_unref (G_OBJECT (priv->account_store));
1475         }
1476
1477         priv->account_store = g_object_ref (G_OBJECT (account_store));
1478
1479         priv->account_removed_signal =
1480                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1481                                   G_CALLBACK (on_account_removed), self);
1482
1483         priv->account_inserted_signal =
1484                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1485                                   G_CALLBACK (on_account_inserted), self);
1486
1487         priv->account_changed_signal =
1488                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1489                                   G_CALLBACK (on_account_changed), self);
1490
1491         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1492         priv->reselect = FALSE;
1493
1494         g_object_unref (G_OBJECT (device));
1495 }
1496
1497 static void
1498 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1499                       gpointer user_data)
1500 {
1501         ModestFolderView *self;
1502         GtkTreeModel *model, *filter_model;
1503         TnyFolder *outbox;
1504
1505         self = MODEST_FOLDER_VIEW (user_data);
1506
1507         if (!get_inner_models (self, &filter_model, NULL, &model))
1508                 return;
1509
1510         /* Remove outbox from model */
1511         outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1512         tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1513         g_object_unref (outbox);
1514
1515         /* Refilter view */
1516         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1517 }
1518
1519 static void
1520 on_account_inserted (TnyAccountStore *account_store,
1521                      TnyAccount *account,
1522                      gpointer user_data)
1523 {
1524         ModestFolderViewPrivate *priv;
1525         GtkTreeModel *model, *filter_model;
1526
1527         /* Ignore transport account insertions, we're not showing them
1528            in the folder view */
1529         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1530                 return;
1531
1532         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1533
1534
1535         /* If we're adding a new account, and there is no previous
1536            one, we need to select the visible server account */
1537         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1538             !priv->visible_account_id)
1539                 modest_widget_memory_restore (modest_runtime_get_conf(),
1540                                               G_OBJECT (user_data),
1541                                               MODEST_CONF_FOLDER_VIEW_KEY);
1542
1543
1544         /* Get models */
1545         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1546                                &filter_model, NULL, &model))
1547                 return;
1548
1549         /* Insert the account in the model */
1550         tny_list_append (TNY_LIST (model), G_OBJECT (account));
1551
1552         /* When the model is a list store (plain representation) the
1553            outbox is not a child of any account so we have to manually
1554            delete it because removing the local folders account won't
1555            delete it (because tny_folder_get_account() is not defined
1556            for a merge folder */
1557         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1558             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1559
1560                 priv->outbox_deleted_handler =
1561                         g_signal_connect (account,
1562                                           "outbox-deleted",
1563                                           G_CALLBACK (on_outbox_deleted_cb),
1564                                           user_data);
1565         }
1566
1567         /* Refilter the model */
1568         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1569 }
1570
1571
1572 static gboolean
1573 same_account_selected (ModestFolderView *self,
1574                        TnyAccount *account)
1575 {
1576         ModestFolderViewPrivate *priv;
1577         gboolean same_account = FALSE;
1578
1579         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1580
1581         if (priv->cur_folder_store) {
1582                 TnyAccount *selected_folder_account = NULL;
1583
1584                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1585                         selected_folder_account =
1586                                 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1587                 } else {
1588                         selected_folder_account =
1589                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1590                 }
1591
1592                 if (selected_folder_account == account)
1593                         same_account = TRUE;
1594
1595                 g_object_unref (selected_folder_account);
1596         }
1597         return same_account;
1598 }
1599
1600 static void
1601 on_account_changed (TnyAccountStore *account_store,
1602                     TnyAccount *tny_account,
1603                     gpointer user_data)
1604 {
1605         ModestFolderView *self;
1606         ModestFolderViewPrivate *priv;
1607         GtkTreeModel *model, *filter_model;
1608         GtkTreeSelection *sel;
1609         gboolean same_account;
1610
1611         /* Ignore transport account insertions, we're not showing them
1612            in the folder view */
1613         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1614                 return;
1615
1616         self = MODEST_FOLDER_VIEW (user_data);
1617         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1618
1619         /* Get the inner model */
1620         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1621                                &filter_model, NULL, &model))
1622                 return;
1623
1624         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1625
1626         /* Invalidate the cur_folder_store only if the selected folder
1627            belongs to the account that is being removed */
1628         same_account = same_account_selected (self, tny_account);
1629         if (same_account) {
1630                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1631                 gtk_tree_selection_unselect_all (sel);
1632         }
1633
1634         /* Remove the account from the model */
1635         tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1636
1637         /* Insert the account in the model */
1638         tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1639
1640         /* Refilter the model */
1641         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1642
1643 }
1644
1645 static void
1646 on_account_removed (TnyAccountStore *account_store,
1647                     TnyAccount *account,
1648                     gpointer user_data)
1649 {
1650         ModestFolderView *self = NULL;
1651         ModestFolderViewPrivate *priv;
1652         GtkTreeModel *model, *filter_model;
1653         GtkTreeSelection *sel = NULL;
1654         gboolean same_account = FALSE;
1655
1656         /* Ignore transport account removals, we're not showing them
1657            in the folder view */
1658         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1659                 return;
1660
1661         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1662                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1663                 return;
1664         }
1665
1666         self = MODEST_FOLDER_VIEW (user_data);
1667         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1668
1669         /* Invalidate the cur_folder_store only if the selected folder
1670            belongs to the account that is being removed */
1671         same_account = same_account_selected (self, account);
1672         if (same_account) {
1673                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1674                 gtk_tree_selection_unselect_all (sel);
1675         }
1676
1677         /* Invalidate row to select only if the folder to select
1678            belongs to the account that is being removed*/
1679         if (priv->folder_to_select) {
1680                 TnyAccount *folder_to_select_account = NULL;
1681
1682                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1683                 if (folder_to_select_account == account) {
1684                         modest_folder_view_disable_next_folder_selection (self);
1685                         g_object_unref (priv->folder_to_select);
1686                         priv->folder_to_select = NULL;
1687                 }
1688                 g_object_unref (folder_to_select_account);
1689         }
1690
1691         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1692                                &filter_model, NULL, &model))
1693                 return;
1694
1695         /* Disconnect the signal handler */
1696         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1697             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1698                 if (g_signal_handler_is_connected (account,
1699                                                    priv->outbox_deleted_handler))
1700                         g_signal_handler_disconnect (account,
1701                                                      priv->outbox_deleted_handler);
1702         }
1703
1704         /* Remove the account from the model */
1705         tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1706
1707         /* If the removed account is the currently viewed one then
1708            clear the configuration value. The new visible account will be the default account */
1709         if (priv->visible_account_id &&
1710             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1711
1712                 /* Clear the current visible account_id */
1713                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1714                 modest_folder_view_set_mailbox (self, NULL);
1715
1716                 /* Call the restore method, this will set the new visible account */
1717                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1718                                               MODEST_CONF_FOLDER_VIEW_KEY);
1719         }
1720
1721         /* Refilter the model */
1722         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1723
1724 }
1725
1726 void
1727 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1728 {
1729         GtkTreeViewColumn *col;
1730
1731         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1732
1733         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1734         if (!col) {
1735                 g_printerr ("modest: failed get column for title\n");
1736                 return;
1737         }
1738
1739         gtk_tree_view_column_set_title (col, title);
1740         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1741                                            title != NULL);
1742 }
1743
1744 static gboolean
1745 modest_folder_view_on_map (ModestFolderView *self,
1746                            GdkEventExpose *event,
1747                            gpointer data)
1748 {
1749         ModestFolderViewPrivate *priv;
1750
1751         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1752
1753         /* This won't happen often */
1754         if (G_UNLIKELY (priv->reselect)) {
1755                 /* Select the first inbox or the local account if not found */
1756
1757                 /* TODO: this could cause a lock at startup, so we
1758                    comment it for the moment. We know that this will
1759                    be a bug, because the INBOX is not selected, but we
1760                    need to rewrite some parts of Modest to avoid the
1761                    deathlock situation */
1762                 /* TODO: check if this is still the case */
1763                 priv->reselect = FALSE;
1764                 /* Notify the display name observers */
1765                 g_signal_emit (G_OBJECT(self),
1766                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1767                                NULL);
1768         }
1769
1770         if (priv->reexpand) {
1771                 expand_root_items (self);
1772                 priv->reexpand = FALSE;
1773         }
1774
1775         return FALSE;
1776 }
1777
1778 GtkWidget*
1779 modest_folder_view_new (TnyFolderStoreQuery *query)
1780 {
1781         return modest_folder_view_new_full (query, TRUE);
1782 }
1783
1784 GtkWidget*
1785 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1786 {
1787         GObject *self;
1788         ModestFolderViewPrivate *priv;
1789         GtkTreeSelection *sel;
1790
1791         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, 
1792 #ifdef MODEST_TOOLKIT_HILDON2
1793                                        "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1794 #endif
1795                                        NULL));
1796         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1797
1798         if (query)
1799                 priv->query = g_object_ref (query);
1800
1801         priv->do_refresh = do_refresh;
1802
1803         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1804         priv->changed_signal = g_signal_connect (sel, "changed",
1805                                                  G_CALLBACK (on_selection_changed), self);
1806
1807         g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1808
1809         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1810
1811         /* Hide headers by default */
1812         gtk_tree_view_set_headers_visible ((GtkTreeView *)self, FALSE);
1813
1814         return GTK_WIDGET(self);
1815 }
1816
1817 /* this feels dirty; any other way to expand all the root items? */
1818 static void
1819 expand_root_items (ModestFolderView *self)
1820 {
1821         GtkTreePath *path;
1822         GtkTreeModel *model;
1823         GtkTreeIter iter;
1824
1825         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1826         path = gtk_tree_path_new_first ();
1827
1828         /* all folders should have child items, so.. */
1829         do {
1830                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1831                 gtk_tree_path_next (path);
1832         } while (gtk_tree_model_get_iter (model, &iter, path));
1833
1834         gtk_tree_path_free (path);
1835 }
1836
1837 static gboolean
1838 is_parent_of (TnyFolder *a, TnyFolder *b)
1839 {
1840         const gchar *a_id;
1841         gboolean retval = FALSE;
1842
1843         a_id = tny_folder_get_id (a);
1844         if (a_id) {
1845                 gchar *string_to_match;
1846                 const gchar *b_id;
1847
1848                 string_to_match = g_strconcat (a_id, "/", NULL);
1849                 b_id = tny_folder_get_id (b);
1850                 retval = g_str_has_prefix (b_id, string_to_match);
1851                 g_free (string_to_match);
1852         }
1853         
1854         return retval;
1855 }
1856
1857 typedef struct _ForeachFolderInfo {
1858         gchar *needle;
1859         gboolean found;
1860 } ForeachFolderInfo;
1861
1862 static gboolean 
1863 foreach_folder_with_id (GtkTreeModel *model,
1864                         GtkTreePath *path,
1865                         GtkTreeIter *iter,
1866                         gpointer data)
1867 {
1868         ForeachFolderInfo *info;
1869         GObject *instance;
1870
1871         info = (ForeachFolderInfo *) data;
1872         gtk_tree_model_get (model, iter,
1873                             INSTANCE_COLUMN, &instance,
1874                             -1);
1875
1876         if (TNY_IS_FOLDER (instance)) {
1877                 const gchar *id;
1878                 gchar *collate;
1879                 id = tny_folder_get_id (TNY_FOLDER (instance));
1880                 if (id) {
1881                         collate = g_utf8_collate_key (id, -1);
1882                         info->found = !strcmp (info->needle, collate);
1883                         g_free (collate);
1884                 }
1885         }
1886
1887         if (instance)
1888                 g_object_unref (instance);
1889
1890         return info->found;
1891         
1892 }
1893
1894
1895 static gboolean
1896 has_folder_with_id (ModestFolderView *self, const gchar *id)
1897 {
1898         GtkTreeModel *model;
1899         ForeachFolderInfo info = {NULL, FALSE};
1900
1901         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1902         info.needle = g_utf8_collate_key (id, -1);
1903         
1904         gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1905         g_free (info.needle);
1906
1907         return info.found;
1908 }
1909
1910 static gboolean
1911 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1912 {
1913         const gchar *a_id;
1914         gboolean retval = FALSE;
1915
1916         a_id = tny_folder_get_id (a);
1917         if (a_id) {
1918                 const gchar *b_id;
1919                 b_id = tny_folder_get_id (b);
1920                 
1921                 if (b_id) {
1922                         const gchar *last_bar;
1923                         gchar *string_to_match;
1924                         last_bar = g_strrstr (b_id, "/");
1925                         if (last_bar)
1926                                 last_bar++;
1927                         else
1928                                 last_bar = b_id;
1929                         string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1930                         retval = has_folder_with_id (self, string_to_match);
1931                         g_free (string_to_match);
1932                 }
1933         }
1934
1935         return retval;
1936 }
1937
1938 static gboolean
1939 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1940 {
1941         ModestFolderViewPrivate *priv;
1942         TnyIterator *iterator;
1943         gboolean retval = TRUE;
1944
1945         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1946         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1947
1948         for (iterator = tny_list_create_iterator (priv->list_to_move);
1949              retval && !tny_iterator_is_done (iterator);
1950              tny_iterator_next (iterator)) {
1951                 GObject *instance;
1952                 instance = tny_iterator_get_current (iterator);
1953                 if (instance == (GObject *) folder) {
1954                         retval = FALSE;
1955                 } else if (TNY_IS_FOLDER (instance)) {
1956                         retval = !is_parent_of (TNY_FOLDER (instance), folder);
1957                         if (retval) {
1958                                 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1959                         }
1960                 }
1961                 g_object_unref (instance);
1962         }
1963         g_object_unref (iterator);
1964
1965         return retval;
1966 }
1967
1968
1969 /*
1970  * We use this function to implement the
1971  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1972  * account in this case, and the local folders.
1973  */
1974 static gboolean
1975 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1976 {
1977         ModestFolderViewPrivate *priv;
1978         gboolean retval = TRUE;
1979         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1980         GObject *instance = NULL;
1981         const gchar *id = NULL;
1982         guint i;
1983         gboolean found = FALSE;
1984         gboolean cleared = FALSE;
1985         ModestTnyFolderRules rules = 0;
1986         gchar *fname;
1987
1988         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1989         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1990
1991         gtk_tree_model_get (model, iter,
1992                             NAME_COLUMN, &fname,
1993                             TYPE_COLUMN, &type,
1994                             INSTANCE_COLUMN, &instance,
1995                             -1);
1996
1997         /* Do not show if there is no instance, this could indeed
1998            happen when the model is being modified while it's being
1999            drawn. This could occur for example when moving folders
2000            using drag&drop */
2001         if (!instance) {
2002                 g_free (fname);
2003                 return FALSE;
2004         }
2005
2006         if (TNY_IS_ACCOUNT (instance)) {
2007                 TnyAccount *acc = TNY_ACCOUNT (instance);
2008                 const gchar *account_id = tny_account_get_id (acc);
2009
2010                 /* If it isn't a special folder,
2011                  * don't show it unless it is the visible account: */
2012                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2013                     !modest_tny_account_is_virtual_local_folders (acc) &&
2014                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2015
2016                         /* Show only the visible account id */
2017                         if (priv->visible_account_id) {
2018                                 if (strcmp (account_id, priv->visible_account_id))
2019                                         retval = FALSE;
2020                         } else {
2021                                 retval = FALSE;
2022                         }
2023                 }
2024
2025                 /* Never show these to the user. They are merged into one folder
2026                  * in the local-folders account instead: */
2027                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2028                         retval = FALSE;
2029         } else {
2030                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2031                         /* Only show special folders for current account if needed */
2032                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2033                                 TnyAccount *account;
2034
2035                                 account = tny_folder_get_account (TNY_FOLDER (instance));
2036
2037                                 if (TNY_IS_ACCOUNT (account)) {
2038                                         const gchar *account_id = tny_account_get_id (account);
2039
2040                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
2041                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2042                                                 /* Show only the visible account id */
2043                                                 if (priv->visible_account_id) {
2044                                                   if (strcmp (account_id, priv->visible_account_id)) {
2045                                                           retval = FALSE;
2046                                                   } else if (priv->mailbox) {
2047                                                           /* Filter mailboxes */
2048                                                           if (!g_str_has_prefix (fname, priv->mailbox)) {
2049                                                                   retval = FALSE;
2050                                                           } else if (!strcmp (fname, priv->mailbox)) {
2051                                                                   /* Hide mailbox parent */
2052                                                                   retval = FALSE;
2053                                                           }
2054                                                   }
2055                                                 }
2056                                         }
2057                                                 g_object_unref (account);
2058                                 }
2059                         }
2060
2061                 }
2062         }
2063
2064         /* Check hiding (if necessary) */
2065         cleared = modest_email_clipboard_cleared (priv->clipboard);
2066         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2067                 id = tny_folder_get_id (TNY_FOLDER(instance));
2068                 if (priv->hidding_ids != NULL)
2069                         for (i=0; i < priv->n_selected && !found; i++)
2070                                 if (priv->hidding_ids[i] != NULL && id != NULL)
2071                                         found = (!strcmp (priv->hidding_ids[i], id));
2072
2073                 retval = !found;
2074         }
2075
2076         /* If this is a move to dialog, hide Sent, Outbox and Drafts
2077         folder as no message can be move there according to UI specs */
2078         if (retval && !priv->show_non_move) {
2079                 if (priv->list_to_move && 
2080                     tny_list_get_length (priv->list_to_move) > 0 &&
2081                     TNY_IS_FOLDER (instance)) {
2082                         retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2083                 }
2084                 if (retval && TNY_IS_FOLDER (instance) && 
2085                     modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2086                         switch (type) {
2087                         case TNY_FOLDER_TYPE_OUTBOX:
2088                         case TNY_FOLDER_TYPE_SENT:
2089                         case TNY_FOLDER_TYPE_DRAFTS:
2090                                 retval = FALSE;
2091                                 break;
2092                         case TNY_FOLDER_TYPE_UNKNOWN:
2093                         case TNY_FOLDER_TYPE_NORMAL:
2094                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2095                                 if (type == TNY_FOLDER_TYPE_INVALID)
2096                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2097                                 
2098                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2099                                     type == TNY_FOLDER_TYPE_SENT
2100                                     || type == TNY_FOLDER_TYPE_DRAFTS)
2101                                         retval = FALSE;
2102                                 break;
2103                         default:
2104                                 break;
2105                         }
2106                 }
2107                 if (retval && TNY_IS_ACCOUNT (instance) &&
2108                     modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2109                         ModestProtocolType protocol_type;
2110
2111                         protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2112                         retval  = !modest_protocol_registry_protocol_type_has_tag
2113                                 (modest_runtime_get_protocol_registry (),
2114                                  protocol_type,
2115                                  MODEST_PROTOCOL_REGISTRY_STORE_FORBID_INCOMING_XFERS);
2116                 }
2117         }
2118
2119         /* apply special filters */
2120         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2121                 if (TNY_IS_ACCOUNT (instance))
2122                         return FALSE;
2123         }
2124
2125         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2126                 if (TNY_IS_FOLDER (instance))
2127                         return FALSE;
2128         }
2129
2130         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2131                 if (TNY_IS_ACCOUNT (instance)) {
2132                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2133                                 return FALSE;
2134                 } else if (TNY_IS_FOLDER (instance)) {
2135                         if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2136                                 return FALSE;
2137                 }
2138         }
2139
2140         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2141                 if (TNY_IS_ACCOUNT (instance)) {
2142                         if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2143                                 return FALSE;
2144                 } else if (TNY_IS_FOLDER (instance)) {
2145                         if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2146                                 return FALSE;
2147                 }
2148         }
2149
2150         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2151                 /* A mailbox is a fake folder with an @ in the middle of the name */
2152                 if (!TNY_IS_FOLDER (instance) ||
2153                     !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2154                         return FALSE;
2155                 } else {
2156                         const gchar *folder_name;
2157                         folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2158                         if (!folder_name || strchr (folder_name, '@') == NULL)
2159                                 return FALSE;
2160                 }
2161                 
2162         }
2163
2164         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2165                 if (TNY_IS_FOLDER (instance)) {
2166                         /* Check folder rules */
2167                         ModestTnyFolderRules rules;
2168
2169                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2170                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2171                 } else if (TNY_IS_ACCOUNT (instance)) {
2172                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2173                                 retval = FALSE;
2174                         } else {
2175                                 retval = TRUE;
2176                         }
2177                 }
2178         }
2179
2180         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2181                 if (TNY_IS_FOLDER (instance)) {
2182                         TnyFolderType guess_type;
2183
2184                         if (TNY_FOLDER_TYPE_NORMAL) {
2185                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2186                         } else {
2187                                 guess_type = type;
2188                         }
2189
2190                         switch (type) {
2191                         case TNY_FOLDER_TYPE_OUTBOX:
2192                         case TNY_FOLDER_TYPE_SENT:
2193                         case TNY_FOLDER_TYPE_DRAFTS:
2194                         case TNY_FOLDER_TYPE_ARCHIVE:
2195                         case TNY_FOLDER_TYPE_INBOX:
2196                                 retval = FALSE;
2197                                 break;
2198                         case TNY_FOLDER_TYPE_UNKNOWN:
2199                         case TNY_FOLDER_TYPE_NORMAL:
2200                                 break;
2201                         default:
2202                                 break;
2203                         }
2204
2205                 } else if (TNY_IS_ACCOUNT (instance)) {
2206                         retval = FALSE;
2207                 }
2208         }
2209
2210         if (retval && TNY_IS_FOLDER (instance)) {
2211                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2212         }
2213
2214         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2215                 if (TNY_IS_FOLDER (instance)) {
2216                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2217                 } else if (TNY_IS_ACCOUNT (instance)) {
2218                         retval = FALSE;
2219                 }
2220         }
2221
2222         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2223                 if (TNY_IS_FOLDER (instance)) {
2224                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2225                 } else if (TNY_IS_ACCOUNT (instance)) {
2226                         retval = FALSE;
2227                 }
2228         }
2229
2230         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2231                 if (TNY_IS_FOLDER (instance)) {
2232                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2233                 } else if (TNY_IS_ACCOUNT (instance)) {
2234                         retval = FALSE;
2235                 }
2236         }
2237
2238         /* Free */
2239         g_object_unref (instance);
2240         g_free (fname);
2241
2242         return retval;
2243 }
2244
2245
2246 gboolean
2247 modest_folder_view_update_model (ModestFolderView *self,
2248                                  TnyAccountStore *account_store)
2249 {
2250         ModestFolderViewPrivate *priv;
2251         GtkTreeModel *model;
2252         GtkTreeModel *filter_model = NULL, *sortable = NULL;
2253
2254         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2255         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2256                               FALSE);
2257
2258         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2259
2260         /* Notify that there is no folder selected */
2261         g_signal_emit (G_OBJECT(self),
2262                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2263                        NULL, FALSE);
2264         if (priv->cur_folder_store) {
2265                 g_object_unref (priv->cur_folder_store);
2266                 priv->cur_folder_store = NULL;
2267         }
2268
2269         /* FIXME: the local accounts are not shown when the query
2270            selects only the subscribed folders */
2271         TnyGtkFolderListStoreFlags flags;
2272         flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2273         if (priv->do_refresh)
2274                 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_DELAYED_REFRESH;
2275         else
2276                 flags |= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2277         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
2278                                                           flags);
2279         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2280                                                       MODEST_FOLDER_PATH_SEPARATOR);
2281
2282         /* When the model is a list store (plain representation) the
2283            outbox is not a child of any account so we have to manually
2284            delete it because removing the local folders account won't
2285            delete it (because tny_folder_get_account() is not defined
2286            for a merge folder */
2287         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2288                 TnyAccount *account;
2289                 ModestTnyAccountStore *acc_store;
2290
2291                 acc_store = modest_runtime_get_account_store ();
2292                 account = modest_tny_account_store_get_local_folders_account (acc_store);
2293
2294                 if (g_signal_handler_is_connected (account,
2295                                                    priv->outbox_deleted_handler))
2296                         g_signal_handler_disconnect (account,
2297                                                      priv->outbox_deleted_handler);
2298
2299                 priv->outbox_deleted_handler =
2300                         g_signal_connect (account,
2301                                           "outbox-deleted",
2302                                           G_CALLBACK (on_outbox_deleted_cb),
2303                                           self);
2304                 g_object_unref (account);
2305         }
2306
2307        if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL) {
2308                /* Get the accounts */
2309                tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2310                                                TNY_LIST (model),
2311                                                TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2312        } else {
2313                if (priv->visible_account_id) {
2314                        TnyAccount *account;
2315
2316                        /* Add local folders account */
2317                        account = modest_tny_account_store_get_local_folders_account ((ModestTnyAccountStore *) account_store);
2318
2319                        if (account) {
2320                                tny_list_append (TNY_LIST (model), (GObject *) account);
2321                                g_object_unref (account);
2322                        }
2323
2324                        account = modest_tny_account_store_get_mmc_folders_account ((ModestTnyAccountStore *) account_store);
2325
2326                        if (account) {
2327                                tny_list_append (TNY_LIST (model), (GObject *) account);
2328                                g_object_unref (account);
2329                        }
2330
2331                        /* Add visible account */
2332                        account = modest_tny_account_store_get_tny_account_by ((ModestTnyAccountStore *) account_store,
2333                                                                               MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
2334                                                                               priv->visible_account_id);
2335                        if (account) {
2336                                tny_list_append (TNY_LIST (model), (GObject *) account);
2337                                g_object_unref (account);
2338                        } else {
2339                                g_warning ("You need to set an account first");
2340                                g_object_unref (model);
2341                                return FALSE;
2342                        }
2343                } else {
2344                        g_warning ("You need to set an account first");
2345                        g_object_unref (model);
2346                        return FALSE;
2347                }
2348        }
2349
2350         sortable = gtk_tree_model_sort_new_with_model (model);
2351         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2352                                               NAME_COLUMN,
2353                                               GTK_SORT_ASCENDING);
2354         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2355                                          NAME_COLUMN,
2356                                          cmp_rows, NULL, NULL);
2357
2358         /* Create filter model */
2359         filter_model = gtk_tree_model_filter_new (sortable, NULL);
2360         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2361                                                 filter_row,
2362                                                 self,
2363                                                 NULL);
2364
2365         GtkTreeModel *old_tny_model = NULL;
2366         if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2367                 if (priv->signal_handlers > 0) {
2368                         priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2369                                                                               G_OBJECT (old_tny_model), 
2370                                                                               "activity-changed");
2371                 }
2372         }
2373
2374         /* Set new model */
2375         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2376
2377         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2378                                                            G_OBJECT (model),
2379                                                            "activity-changed",
2380                                                            G_CALLBACK (on_activity_changed),
2381                                                            self);
2382
2383         g_object_unref (model);
2384         g_object_unref (filter_model);
2385         g_object_unref (sortable);
2386
2387         /* Force a reselection of the INBOX next time the widget is shown */
2388         priv->reselect = TRUE;
2389
2390         return TRUE;
2391 }
2392
2393
2394 static void
2395 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2396 {
2397         GtkTreeModel *model = NULL;
2398         TnyFolderStore *folder = NULL;
2399         GtkTreeIter iter;
2400         ModestFolderView *tree_view = NULL;
2401         ModestFolderViewPrivate *priv = NULL;
2402         gboolean selected = FALSE;
2403
2404         g_return_if_fail (sel);
2405         g_return_if_fail (user_data);
2406
2407         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2408
2409         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2410
2411         tree_view = MODEST_FOLDER_VIEW (user_data);
2412
2413         if (selected) {
2414                 gtk_tree_model_get (model, &iter,
2415                                     INSTANCE_COLUMN, &folder,
2416                                     -1);
2417
2418                 /* If the folder is the same do not notify */
2419                 if (folder && priv->cur_folder_store == folder) {
2420                         g_object_unref (folder);
2421                         return;
2422                 }
2423         }
2424
2425         /* Current folder was unselected */
2426         if (priv->cur_folder_store) {
2427                 /* We must do this firstly because a libtinymail-camel
2428                    implementation detail. If we issue the signal
2429                    before doing the sync_async, then that signal could
2430                    cause (and it actually does it) a free of the
2431                    summary of the folder (because the main window will
2432                    clear the headers view */
2433
2434                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2435                        priv->cur_folder_store, FALSE);
2436
2437                 g_object_unref (priv->cur_folder_store);
2438                 priv->cur_folder_store = NULL;
2439         }
2440
2441         /* New current references */
2442         priv->cur_folder_store = folder;
2443
2444         /* New folder has been selected. Do not notify if there is
2445            nothing new selected */
2446         if (selected) {
2447                 g_signal_emit (G_OBJECT(tree_view),
2448                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2449                                0, priv->cur_folder_store, TRUE);
2450         }
2451 }
2452
2453 static void
2454 on_row_activated (GtkTreeView *treeview,
2455                   GtkTreePath *treepath,
2456                   GtkTreeViewColumn *column,
2457                   gpointer user_data)
2458 {
2459         GtkTreeModel *model = NULL;
2460         TnyFolderStore *folder = NULL;
2461         GtkTreeIter iter;
2462         ModestFolderView *self = NULL;
2463         ModestFolderViewPrivate *priv = NULL;
2464
2465         g_return_if_fail (treeview);
2466         g_return_if_fail (user_data);
2467
2468         self = MODEST_FOLDER_VIEW (user_data);
2469         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2470
2471         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2472
2473         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2474                 return;
2475
2476         gtk_tree_model_get (model, &iter,
2477                             INSTANCE_COLUMN, &folder,
2478                             -1);
2479
2480         g_signal_emit (G_OBJECT(self),
2481                        signals[FOLDER_ACTIVATED_SIGNAL],
2482                        0, folder);
2483
2484 #ifdef MODEST_TOOLKIT_HILDON2
2485         HildonUIMode ui_mode;
2486         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2487         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2488                 if (priv->cur_folder_store)
2489                         g_object_unref (priv->cur_folder_store);
2490                 priv->cur_folder_store = g_object_ref (folder);
2491         }
2492 #endif
2493
2494         g_object_unref (folder);
2495 }
2496
2497 TnyFolderStore *
2498 modest_folder_view_get_selected (ModestFolderView *self)
2499 {
2500         ModestFolderViewPrivate *priv;
2501
2502         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2503
2504         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2505         if (priv->cur_folder_store)
2506                 g_object_ref (priv->cur_folder_store);
2507
2508         return priv->cur_folder_store;
2509 }
2510
2511 static gint
2512 get_cmp_rows_type_pos (GObject *folder)
2513 {
2514         /* Remote accounts -> Local account -> MMC account .*/
2515         /* 0, 1, 2 */
2516
2517         if (TNY_IS_ACCOUNT (folder) &&
2518                 modest_tny_account_is_virtual_local_folders (
2519                         TNY_ACCOUNT (folder))) {
2520                 return 1;
2521         } else if (TNY_IS_ACCOUNT (folder)) {
2522                 TnyAccount *account = TNY_ACCOUNT (folder);
2523                 const gchar *account_id = tny_account_get_id (account);
2524                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2525                         return 2;
2526                 else
2527                         return 0;
2528         }
2529         else {
2530                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2531                 return -1; /* Should never happen */
2532         }
2533 }
2534
2535 static gboolean
2536 inbox_is_special (TnyFolderStore *folder_store)
2537 {
2538         gboolean is_special = TRUE;
2539
2540         if (TNY_IS_FOLDER (folder_store)) {
2541                 const gchar *id;
2542                 gchar *downcase;
2543                 gchar *last_bar;
2544                 gchar *last_inbox_bar;
2545
2546                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2547                 downcase = g_utf8_strdown (id, -1);
2548                 last_bar = g_strrstr (downcase, "/");
2549                 if (last_bar) {
2550                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2551                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2552                                 is_special = FALSE;
2553                 } else {
2554                         is_special = FALSE;
2555                 }
2556                 g_free (downcase);
2557         }
2558         return is_special;
2559 }
2560
2561 static gint
2562 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2563 {
2564         TnyAccount *account;
2565         gboolean is_special;
2566         /* Inbox, Outbox, Drafts, Sent, User */
2567         /* 0, 1, 2, 3, 4 */
2568
2569         if (!TNY_IS_FOLDER (folder_store))
2570                 return 4;
2571         switch (t) {
2572         case TNY_FOLDER_TYPE_INBOX:
2573         {
2574                 account = tny_folder_get_account (folder_store);
2575                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2576
2577                 /* In inbox case we need to know if the inbox is really the top
2578                  * inbox of the account, or if it's a submailbox inbox. To do
2579                  * this we'll apply an heuristic rule: Find last "/" and check
2580                  * if it's preceeded by another Inbox */
2581                 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2582                 g_object_unref (account);
2583                 return is_special?0:4;
2584         }
2585         break;
2586         case TNY_FOLDER_TYPE_OUTBOX:
2587                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2588                 break;
2589         case TNY_FOLDER_TYPE_DRAFTS:
2590         {
2591                 account = tny_folder_get_account (folder_store);
2592                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2593                 g_object_unref (account);
2594                 return is_special?1:4;
2595         }
2596         break;
2597         case TNY_FOLDER_TYPE_SENT:
2598         {
2599                 account = tny_folder_get_account (folder_store);
2600                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2601                 g_object_unref (account);
2602                 return is_special?3:4;
2603         }
2604         break;
2605         default:
2606                 return 4;
2607         }
2608 }
2609
2610 static gint
2611 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2612 {
2613         const gchar *a1_name, *a2_name;
2614
2615         a1_name = tny_account_get_name (a1);
2616         a2_name = tny_account_get_name (a2);
2617
2618         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2619 }
2620
2621 static gint
2622 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2623 {
2624         TnyAccount *a1 = NULL, *a2 = NULL;
2625         gint cmp;
2626
2627         if (TNY_IS_ACCOUNT (s1)) {
2628                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2629         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2630                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2631         }
2632
2633         if (TNY_IS_ACCOUNT (s2)) {
2634                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2635         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2636                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2637         }
2638
2639         if (!a1 || !a2) {
2640                 if (!a1 && !a2)
2641                         cmp = 0;
2642                 else if (!a1)
2643                         cmp = 1;
2644                 else
2645                         cmp = -1;
2646                 goto finish;
2647         }
2648
2649         if (a1 == a2) {
2650                 cmp = 0;
2651                 goto finish;
2652         }
2653         /* First we sort with the type of account */
2654         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2655         if (cmp != 0)
2656                 goto finish;
2657
2658         cmp = compare_account_names (a1, a2);
2659
2660 finish:
2661         if (a1)
2662                 g_object_unref (a1);
2663         if (a2)
2664                 g_object_unref (a2);
2665
2666         return cmp;
2667 }
2668
2669 static gint
2670 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2671 {
2672         gint is_account1, is_account2;
2673
2674         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2675         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2676
2677         return is_account2 - is_account1;
2678 }
2679
2680 static gint
2681 compare_folders (const gchar *name1, const gchar *name2)
2682 {
2683         const gchar *separator1, *separator2;
2684         const gchar *next1, *next2;
2685         gchar *top1, *top2;
2686         gint cmp;
2687
2688         if (name1 == NULL || name1[0] == '\0')
2689                 return -1;
2690         if (name2 == NULL || name2[0] == '\0')
2691                 return 1;
2692
2693         separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2694         if (separator1) {
2695                 top1 = g_strndup (name1, separator1 - name1);
2696         } else {
2697                 top1 = g_strdup (name1);
2698         }
2699
2700         separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2701         if (separator2) {
2702                 top2 = g_strndup (name2, separator2 - name2);
2703         } else {
2704                 top2 = g_strdup (name2);
2705         }
2706
2707
2708         cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2709         g_free (top1);
2710         g_free (top2);
2711
2712         if (cmp != 0)
2713                 return cmp;
2714
2715         if (separator1 == NULL && separator2 == NULL)
2716                 return 0;
2717
2718         next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2719         next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2720
2721         return compare_folders (next1, next2);
2722 }
2723
2724
2725 /*
2726  * This function orders the mail accounts according to these rules:
2727  * 1st - remote accounts
2728  * 2nd - local account
2729  * 3rd - MMC account
2730  */
2731 static gint
2732 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2733           gpointer user_data)
2734 {
2735         gint cmp = 0;
2736         gchar *name1 = NULL;
2737         gchar *name2 = NULL;
2738         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2739         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2740         GObject *folder1 = NULL;
2741         GObject *folder2 = NULL;
2742
2743         gtk_tree_model_get (tree_model, iter1,
2744                             NAME_COLUMN, &name1,
2745                             TYPE_COLUMN, &type,
2746                             INSTANCE_COLUMN, &folder1,
2747                             -1);
2748         gtk_tree_model_get (tree_model, iter2,
2749                             NAME_COLUMN, &name2,
2750                             TYPE_COLUMN, &type2,
2751                             INSTANCE_COLUMN, &folder2,
2752                             -1);
2753
2754         /* Return if we get no folder. This could happen when folder
2755            operations are happening. The model is updated after the
2756            folder copy/move actually occurs, so there could be
2757            situations where the model to be drawn is not correct */
2758         if (!folder1 || !folder2)
2759                 goto finish;
2760
2761         /* Sort by type. First the special folders, then the archives */
2762         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2763         if (cmp != 0)
2764                 goto finish;
2765
2766         /* Now we sort using the account of each folder */
2767         if (TNY_IS_FOLDER_STORE (folder1) && 
2768             TNY_IS_FOLDER_STORE (folder2)) {
2769                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2770                 if (cmp != 0)
2771                         goto finish;
2772
2773                 /* Each group is preceeded by its account */
2774                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2775                 if (cmp != 0)
2776                         goto finish;
2777         }
2778
2779         /* Pure sort by name */
2780         cmp = compare_folders (name1, name2);
2781  finish:
2782         if (folder1)
2783                 g_object_unref(G_OBJECT(folder1));
2784         if (folder2)
2785                 g_object_unref(G_OBJECT(folder2));
2786
2787         g_free (name1);
2788         g_free (name2);
2789
2790         return cmp;
2791 }
2792
2793
2794 /*
2795  * This function manages the navigation through the folders using the
2796  * keyboard or the hardware keys in the device
2797  */
2798 static gboolean
2799 on_key_pressed (GtkWidget *self,
2800                 GdkEventKey *event,
2801                 gpointer user_data)
2802 {
2803         GtkTreeSelection *selection;
2804         GtkTreeIter iter;
2805         GtkTreeModel *model;
2806         gboolean retval = FALSE;
2807
2808         /* Up and Down are automatically managed by the treeview */
2809         if (event->keyval == GDK_Return) {
2810                 /* Expand/Collapse the selected row */
2811                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2812                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2813                         GtkTreePath *path;
2814
2815                         path = gtk_tree_model_get_path (model, &iter);
2816
2817                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2818                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2819                         else
2820                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2821                         gtk_tree_path_free (path);
2822                 }
2823                 /* No further processing */
2824                 retval = TRUE;
2825         }
2826
2827         return retval;
2828 }
2829
2830 /*
2831  * We listen to the changes in the local folder account name key,
2832  * because we want to show the right name in the view. The local
2833  * folder account name corresponds to the device name in the Maemo
2834  * version. We do this because we do not want to query gconf on each
2835  * tree view refresh. It's better to cache it and change whenever
2836  * necessary.
2837  */
2838 static void
2839 on_configuration_key_changed (ModestConf* conf,
2840                               const gchar *key,
2841                               ModestConfEvent event,
2842                               ModestConfNotificationId id,
2843                               ModestFolderView *self)
2844 {
2845         ModestFolderViewPrivate *priv;
2846
2847
2848         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2849         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2850
2851         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2852                 g_free (priv->local_account_name);
2853
2854                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2855                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2856                 else
2857                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2858                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2859
2860                 /* Force a redraw */
2861 #if GTK_CHECK_VERSION(2, 8, 0)
2862                 GtkTreeViewColumn * tree_column;
2863
2864                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2865                                                         NAME_COLUMN);
2866                 gtk_tree_view_column_queue_resize (tree_column);
2867 #else
2868                 gtk_widget_queue_draw (GTK_WIDGET (self));
2869 #endif
2870         }
2871 }
2872
2873 void
2874 modest_folder_view_set_style (ModestFolderView *self,
2875                               ModestFolderViewStyle style)
2876 {
2877         ModestFolderViewPrivate *priv;
2878
2879         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2880         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2881                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2882
2883         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2884
2885
2886         priv->style = style;
2887 }
2888
2889 void
2890 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2891                                                              const gchar *account_id)
2892 {
2893         ModestFolderViewPrivate *priv;
2894         GtkTreeModel *model;
2895
2896         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2897
2898         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2899
2900         /* This will be used by the filter_row callback,
2901          * to decided which rows to show: */
2902         if (priv->visible_account_id) {
2903                 g_free (priv->visible_account_id);
2904                 priv->visible_account_id = NULL;
2905         }
2906         if (account_id)
2907                 priv->visible_account_id = g_strdup (account_id);
2908
2909         /* Refilter */
2910         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2911         if (GTK_IS_TREE_MODEL_FILTER (model))
2912                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2913         else
2914                 modest_folder_view_update_model(self,
2915                                                 (TnyAccountStore *) modest_runtime_get_account_store());
2916
2917         /* Save settings to gconf */
2918         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2919                                    MODEST_CONF_FOLDER_VIEW_KEY);
2920
2921         /* Notify observers */
2922         g_signal_emit (G_OBJECT(self),
2923                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
2924                        account_id);
2925 }
2926
2927 const gchar *
2928 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2929 {
2930         ModestFolderViewPrivate *priv;
2931
2932         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2933
2934         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2935
2936         return (const gchar *) priv->visible_account_id;
2937 }
2938
2939 /* recursive */
2940 static gboolean
2941 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2942                   TnyFolder* folder)
2943 {
2944         do {
2945                 GtkTreeIter child;
2946                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2947                 TnyFolder* a_folder;
2948                 gchar *name = NULL;
2949
2950                 gtk_tree_model_get (model, iter,
2951                                     INSTANCE_COLUMN, &a_folder,
2952                                     NAME_COLUMN, &name,
2953                                     TYPE_COLUMN, &type,
2954                                     -1);
2955                 g_free (name);
2956
2957                 if (folder == a_folder) {
2958                         g_object_unref (a_folder);
2959                         *folder_iter = *iter;
2960                         return TRUE;
2961                 }
2962                 g_object_unref (a_folder);
2963
2964                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2965                         if (find_folder_iter (model, &child, folder_iter, folder))
2966                                 return TRUE;
2967                 }
2968
2969         } while (gtk_tree_model_iter_next (model, iter));
2970
2971         return FALSE;
2972 }
2973
2974
2975 void
2976 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
2977 {
2978         ModestFolderViewPrivate *priv;
2979
2980         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2981
2982         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2983
2984         if (priv->folder_to_select)
2985                 g_object_unref(priv->folder_to_select);
2986
2987         priv->folder_to_select = NULL;
2988 }
2989
2990 gboolean
2991 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
2992                                   gboolean after_change)
2993 {
2994         GtkTreeModel *model;
2995         GtkTreeIter iter, folder_iter;
2996         GtkTreeSelection *sel;
2997         ModestFolderViewPrivate *priv = NULL;
2998
2999         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3000         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3001
3002         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3003
3004         if (after_change) {
3005                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3006                 gtk_tree_selection_unselect_all (sel);
3007
3008                 if (priv->folder_to_select)
3009                         g_object_unref(priv->folder_to_select);
3010                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3011                 return TRUE;
3012         }
3013
3014         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3015         if (!model)
3016                 return FALSE;
3017
3018
3019         /* Refilter the model, before selecting the folder */
3020         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3021
3022         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3023                 g_warning ("%s: model is empty", __FUNCTION__);
3024                 return FALSE;
3025         }
3026
3027         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3028                 GtkTreePath *path;
3029
3030                 path = gtk_tree_model_get_path (model, &folder_iter);
3031                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3032
3033                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3034                 gtk_tree_selection_select_iter (sel, &folder_iter);
3035                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3036
3037                 gtk_tree_path_free (path);
3038                 return TRUE;
3039         }
3040         return FALSE;
3041 }
3042
3043
3044 void
3045 modest_folder_view_copy_selection (ModestFolderView *self)
3046 {
3047         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3048
3049         /* Copy selection */
3050         _clipboard_set_selected_data (self, FALSE);
3051 }
3052
3053 void
3054 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3055 {
3056         ModestFolderViewPrivate *priv = NULL;
3057         GtkTreeModel *model = NULL;
3058         const gchar **hidding = NULL;
3059         guint i, n_selected;
3060
3061         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3062         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3063
3064         /* Copy selection */
3065         if (!_clipboard_set_selected_data (folder_view, TRUE))
3066                 return;
3067
3068         /* Get hidding ids */
3069         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3070
3071         /* Clear hidding array created by previous cut operation */
3072         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3073
3074         /* Copy hidding array */
3075         priv->n_selected = n_selected;
3076         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3077         for (i=0; i < n_selected; i++)
3078                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3079
3080         /* Hide cut folders */
3081         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3082         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3083 }
3084
3085 void
3086 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3087                                ModestFolderView *folder_view_dst)
3088 {
3089         GtkTreeModel *filter_model = NULL;
3090         GtkTreeModel *model = NULL;
3091         GtkTreeModel *new_filter_model = NULL;
3092         GtkTreeModel *old_tny_model = NULL;
3093         GtkTreeModel *new_tny_model = NULL;
3094         ModestFolderViewPrivate *dst_priv;
3095
3096         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3097         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3098
3099         dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3100         if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3101                 new_tny_model = NULL;
3102
3103         /* Get src model*/
3104         if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3105                 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3106                                               G_OBJECT (old_tny_model),
3107                                               "activity-changed");
3108         }
3109         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3110         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3111
3112         /* Build new filter model */
3113         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3114         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3115                                                 filter_row,
3116                                                 folder_view_dst,
3117                                                 NULL);
3118
3119
3120
3121         /* Set copied model */
3122         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3123         if (new_tny_model) {
3124                 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3125                                                                        G_OBJECT (new_tny_model),
3126                                                                        "activity-changed",
3127                                                                        G_CALLBACK (on_activity_changed),
3128                                                                        folder_view_dst);
3129         }
3130
3131         /* Free */
3132         g_object_unref (new_filter_model);
3133 }
3134
3135 void
3136 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3137                                           gboolean show)
3138 {
3139         GtkTreeModel *model = NULL;
3140         ModestFolderViewPrivate* priv;
3141
3142         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3143
3144         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3145         priv->show_non_move = show;
3146 /*      modest_folder_view_update_model(folder_view, */
3147 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3148
3149         /* Hide special folders */
3150         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3151         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3152                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3153         }
3154 }
3155
3156 void
3157 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3158                                           gboolean show)
3159 {
3160         ModestFolderViewPrivate* priv;
3161
3162         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3163
3164         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3165         priv->show_message_count = show;
3166
3167         g_object_set (G_OBJECT (priv->messages_renderer),
3168                       "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3169                       NULL);
3170 }
3171
3172 /* Returns FALSE if it did not selected anything */
3173 static gboolean
3174 _clipboard_set_selected_data (ModestFolderView *folder_view,
3175                               gboolean delete)
3176 {
3177         ModestFolderViewPrivate *priv = NULL;
3178         TnyFolderStore *folder = NULL;
3179         gboolean retval = FALSE;
3180
3181         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3182         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3183
3184         /* Set selected data on clipboard   */
3185         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3186         folder = modest_folder_view_get_selected (folder_view);
3187
3188         /* Do not allow to select an account */
3189         if (TNY_IS_FOLDER (folder)) {
3190                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3191                 retval = TRUE;
3192         }
3193
3194         /* Free */
3195         g_object_unref (folder);
3196
3197         return retval;
3198 }
3199
3200 static void
3201 _clear_hidding_filter (ModestFolderView *folder_view)
3202 {
3203         ModestFolderViewPrivate *priv;
3204         guint i;
3205
3206         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3207         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3208
3209         if (priv->hidding_ids != NULL) {
3210                 for (i=0; i < priv->n_selected; i++)
3211                         g_free (priv->hidding_ids[i]);
3212                 g_free(priv->hidding_ids);
3213         }
3214 }
3215
3216
3217 static void
3218 on_display_name_changed (ModestAccountMgr *mgr,
3219                          const gchar *account,
3220                          gpointer user_data)
3221 {
3222         ModestFolderView *self;
3223
3224         self = MODEST_FOLDER_VIEW (user_data);
3225
3226         /* Force a redraw */
3227 #if GTK_CHECK_VERSION(2, 8, 0)
3228         GtkTreeViewColumn * tree_column;
3229
3230         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3231                                                 NAME_COLUMN);
3232         gtk_tree_view_column_queue_resize (tree_column);
3233 #else
3234         gtk_widget_queue_draw (GTK_WIDGET (self));
3235 #endif
3236 }
3237
3238 void 
3239 modest_folder_view_set_cell_style (ModestFolderView *self,
3240                                    ModestFolderViewCellStyle cell_style)
3241 {
3242         ModestFolderViewPrivate *priv = NULL;
3243
3244         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3245         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3246
3247         priv->cell_style = cell_style;
3248
3249         g_object_set (G_OBJECT (priv->messages_renderer),
3250                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3251                       NULL);
3252         
3253         gtk_widget_queue_draw (GTK_WIDGET (self));
3254 }
3255
3256 static void
3257 update_style (ModestFolderView *self)
3258 {
3259         ModestFolderViewPrivate *priv;
3260         GdkColor style_color, style_active_color;
3261         PangoAttrList *attr_list;
3262         GtkStyle *style;
3263         PangoAttribute *attr;
3264
3265         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3266         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3267
3268         /* Set color */
3269
3270         attr_list = pango_attr_list_new ();
3271
3272         if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
3273                 gdk_color_parse ("grey", &style_color);
3274         }
3275         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3276         pango_attr_list_insert (attr_list, attr);
3277         
3278         /* set font */
3279         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3280                                            (GTK_WIDGET(self)),
3281                                            "SmallSystemFont", NULL,
3282                                            G_TYPE_NONE);
3283         if (style) {
3284                 attr = pango_attr_font_desc_new (pango_font_description_copy
3285                                                  (style->font_desc));
3286                 pango_attr_list_insert (attr_list, attr);
3287
3288                 g_object_set (G_OBJECT (priv->messages_renderer),
3289                               "foreground-gdk", &style_color,
3290                               "foreground-set", TRUE,
3291                               "attributes", attr_list,
3292                               NULL);
3293                 pango_attr_list_unref (attr_list);
3294         }
3295
3296         if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
3297                 priv->active_color = style_active_color;
3298         } else {
3299                 gdk_color_parse ("000", &(priv->active_color));
3300         }
3301 }
3302
3303 static void 
3304 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3305 {
3306         if (strcmp ("style", spec->name) == 0) {
3307                 update_style (MODEST_FOLDER_VIEW (obj));
3308                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3309         } 
3310 }
3311
3312 void 
3313 modest_folder_view_set_filter (ModestFolderView *self,
3314                                ModestFolderViewFilter filter)
3315 {
3316         ModestFolderViewPrivate *priv;
3317         GtkTreeModel *filter_model;
3318
3319         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3320         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3321
3322         priv->filter |= filter;
3323
3324         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3325         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3326                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3327         }
3328 }
3329
3330 void 
3331 modest_folder_view_unset_filter (ModestFolderView *self,
3332                                  ModestFolderViewFilter filter)
3333 {
3334         ModestFolderViewPrivate *priv;
3335         GtkTreeModel *filter_model;
3336
3337         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3338         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3339
3340         priv->filter &= ~filter;
3341
3342         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3343         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3344                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3345         }
3346 }
3347
3348 gboolean
3349 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3350                                              ModestTnyFolderRules rules)
3351 {
3352         GtkTreeModel *filter_model;
3353         GtkTreeIter iter;
3354         gboolean fulfil = FALSE;
3355
3356         if (!get_inner_models (self, &filter_model, NULL, NULL))
3357                 return FALSE;
3358
3359         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3360                 return FALSE;
3361
3362         do {
3363                 TnyFolderStore *folder;
3364
3365                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3366                 if (folder) {
3367                         if (TNY_IS_FOLDER (folder)) {
3368                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3369                                 /* Folder rules are negative: non_writable, non_deletable... */
3370                                 if (!(folder_rules & rules))
3371                                         fulfil = TRUE;
3372                         }
3373                         g_object_unref (folder);
3374                 }
3375
3376         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3377
3378         return fulfil;
3379 }
3380
3381 void 
3382 modest_folder_view_set_list_to_move (ModestFolderView *self,
3383                                      TnyList *list)
3384 {
3385         ModestFolderViewPrivate *priv;
3386
3387         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3388         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3389
3390         if (priv->list_to_move)
3391                 g_object_unref (priv->list_to_move);
3392
3393         if (list)
3394                 g_object_ref (list);
3395
3396         priv->list_to_move = list;
3397 }
3398
3399 void
3400 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3401 {
3402         ModestFolderViewPrivate *priv;
3403
3404         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3405         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3406
3407         if (priv->mailbox)
3408                 g_free (priv->mailbox);
3409
3410         priv->mailbox = g_strdup (mailbox);
3411
3412         /* Notify observers */
3413         g_signal_emit (G_OBJECT(self),
3414                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3415                        priv->visible_account_id);
3416 }
3417
3418 const gchar *
3419 modest_folder_view_get_mailbox (ModestFolderView *self)
3420 {
3421         ModestFolderViewPrivate *priv;
3422
3423         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3424         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3425
3426         return (const gchar *) priv->mailbox;
3427 }
3428
3429 gboolean 
3430 modest_folder_view_get_activity (ModestFolderView *self)
3431 {
3432         ModestFolderViewPrivate *priv;
3433         GtkTreeModel *inner_model;
3434
3435         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
3436         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3437
3438         if (!get_inner_models (self, NULL, NULL, &inner_model))
3439                 return FALSE;
3440
3441         if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
3442                 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
3443         } else {
3444                 return FALSE;
3445         }
3446 }
3447
3448 static void
3449 on_activity_changed (TnyGtkFolderListStore *store,
3450                      gboolean activity,
3451                      ModestFolderView *folder_view)
3452 {
3453         ModestFolderViewPrivate *priv;
3454
3455         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3456         g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
3457         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3458
3459         g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
3460                        activity);
3461 }
3462
3463 TnyList *
3464 modest_folder_view_get_model_tny_list (ModestFolderView *self)
3465 {
3466         GtkTreeModel *model;
3467         TnyList *ret_value;
3468
3469         ret_value = NULL;
3470         model = NULL;
3471
3472         if (get_inner_models (MODEST_FOLDER_VIEW (self), NULL, NULL, (GtkTreeModel **) &model)) {
3473                 ret_value = TNY_LIST (model);
3474                 g_object_ref (ret_value);
3475         }
3476
3477         return ret_value;
3478
3479 }