8c4e346af0ad955143b63d470197e8026512b422
[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 #ifdef MODEST_TOOLKIT_HILDON2
76 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
77 #else
78 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
79 #endif
80
81 /* Column names depending on we use list store or tree store */
82 #ifdef MODEST_TOOLKIT_HILDON2
83 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
84 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
85 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
86 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
87 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
88 #else
89 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
90 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
91 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
92 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
93 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
94 #endif
95
96 /* 'private'/'protected' functions */
97 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
98 static void modest_folder_view_init        (ModestFolderView *obj);
99 static void modest_folder_view_finalize    (GObject *obj);
100 static void modest_folder_view_dispose     (GObject *obj);
101
102 static void         tny_account_store_view_init (gpointer g,
103                                                  gpointer iface_data);
104
105 static void         modest_folder_view_set_account_store (TnyAccountStoreView *self,
106                                                           TnyAccountStore     *account_store);
107
108 static void         on_selection_changed   (GtkTreeSelection *sel,
109                                             gpointer data);
110
111 static void         on_row_activated       (GtkTreeView *treeview,
112                                             GtkTreePath *path,
113                                             GtkTreeViewColumn *column,
114                                             gpointer userdata);
115
116 static void         on_account_removed     (TnyAccountStore *self,
117                                             TnyAccount *account,
118                                             gpointer user_data);
119
120 static void         on_account_inserted    (TnyAccountStore *self,
121                                             TnyAccount *account,
122                                             gpointer user_data);
123
124 static void         on_account_changed    (TnyAccountStore *self,
125                                             TnyAccount *account,
126                                             gpointer user_data);
127
128 static gint         cmp_rows               (GtkTreeModel *tree_model,
129                                             GtkTreeIter *iter1,
130                                             GtkTreeIter *iter2,
131                                             gpointer user_data);
132
133 static gboolean     filter_row             (GtkTreeModel *model,
134                                             GtkTreeIter *iter,
135                                             gpointer data);
136
137 static gboolean     on_key_pressed         (GtkWidget *self,
138                                             GdkEventKey *event,
139                                             gpointer user_data);
140
141 static void         on_configuration_key_changed  (ModestConf* conf,
142                                                    const gchar *key,
143                                                    ModestConfEvent event,
144                                                    ModestConfNotificationId notification_id,
145                                                    ModestFolderView *self);
146
147 /* DnD functions */
148 static void         on_drag_data_get       (GtkWidget *widget,
149                                             GdkDragContext *context,
150                                             GtkSelectionData *selection_data,
151                                             guint info,
152                                             guint time,
153                                             gpointer data);
154
155 static void         on_drag_data_received  (GtkWidget *widget,
156                                             GdkDragContext *context,
157                                             gint x,
158                                             gint y,
159                                             GtkSelectionData *selection_data,
160                                             guint info,
161                                             guint time,
162                                             gpointer data);
163
164 static gboolean     on_drag_motion         (GtkWidget      *widget,
165                                             GdkDragContext *context,
166                                             gint            x,
167                                             gint            y,
168                                             guint           time,
169                                             gpointer        user_data);
170
171 static void         expand_root_items (ModestFolderView *self);
172
173 static gint         expand_row_timeout     (gpointer data);
174
175 static void         setup_drag_and_drop    (GtkTreeView *self);
176
177 static gboolean     _clipboard_set_selected_data (ModestFolderView *folder_view,
178                                                   gboolean delete);
179
180 static void         _clear_hidding_filter (ModestFolderView *folder_view);
181
182 #ifndef MODEST_TOOLKIT_HILDON2
183 static void         on_row_inserted_maybe_select_folder (GtkTreeModel     *tree_model,
184                                                          GtkTreePath      *path,
185                                                          GtkTreeIter      *iter,
186                                                          ModestFolderView *self);
187 #endif
188
189 static void         on_display_name_changed (ModestAccountMgr *self,
190                                              const gchar *account,
191                                              gpointer user_data);
192 static void         update_style (ModestFolderView *self);
193 static void         on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
194 static gint         get_cmp_pos (TnyFolderType t, TnyFolder *folder_store);
195 static gboolean     inbox_is_special (TnyFolderStore *folder_store);
196
197 static gboolean     get_inner_models        (ModestFolderView *self,
198                                              GtkTreeModel **filter_model,
199                                              GtkTreeModel **sort_model,
200                                              GtkTreeModel **tny_model);
201 #ifdef MODEST_TOOLKIT_HILDON2
202 static void on_activity_changed (TnyGtkFolderListStore *store,
203                                  gboolean activity,
204                                  ModestFolderView *folder_view);
205 #endif
206
207 enum {
208         FOLDER_SELECTION_CHANGED_SIGNAL,
209         FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
210         FOLDER_ACTIVATED_SIGNAL,
211         VISIBLE_ACCOUNT_CHANGED_SIGNAL,
212         ACTIVITY_CHANGED_SIGNAL,
213         LAST_SIGNAL
214 };
215
216 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
217 struct _ModestFolderViewPrivate {
218         TnyAccountStore      *account_store;
219         TnyFolderStore       *cur_folder_store;
220
221         TnyFolder            *folder_to_select; /* folder to select after the next update */
222
223         /* not unref this object, its a singlenton */
224         ModestEmailClipboard *clipboard;
225
226         /* Filter tree model */
227         gchar **hidding_ids;
228         guint n_selected;
229         ModestFolderViewFilter filter;
230
231         TnyFolderStoreQuery  *query;
232         gboolean              do_refresh;
233         guint                 timer_expander;
234
235         gchar                *local_account_name;
236         gchar                *visible_account_id;
237         gchar                *mailbox;
238         ModestFolderViewStyle style;
239         ModestFolderViewCellStyle cell_style;
240         gboolean show_message_count;
241
242         gboolean  reselect; /* we use this to force a reselection of the INBOX */
243         gboolean  show_non_move;
244         TnyList   *list_to_move;
245         gboolean  reexpand; /* next time we expose, we'll expand all root folders */
246
247         GtkCellRenderer *messages_renderer;
248
249         GSList   *signal_handlers;
250         GdkColor *active_color;
251 };
252 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                       \
253         (G_TYPE_INSTANCE_GET_PRIVATE((o),                       \
254                                      MODEST_TYPE_FOLDER_VIEW,   \
255                                      ModestFolderViewPrivate))
256 /* globals */
257 static GObjectClass *parent_class = NULL;
258
259 static guint signals[LAST_SIGNAL] = {0};
260
261 GType
262 modest_folder_view_get_type (void)
263 {
264         static GType my_type = 0;
265         if (!my_type) {
266                 static const GTypeInfo my_info = {
267                         sizeof(ModestFolderViewClass),
268                         NULL,           /* base init */
269                         NULL,           /* base finalize */
270                         (GClassInitFunc) modest_folder_view_class_init,
271                         NULL,           /* class finalize */
272                         NULL,           /* class data */
273                         sizeof(ModestFolderView),
274                         1,              /* n_preallocs */
275                         (GInstanceInitFunc) modest_folder_view_init,
276                         NULL
277                 };
278
279                 static const GInterfaceInfo tny_account_store_view_info = {
280                         (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
281                         NULL,         /* interface_finalize */
282                         NULL          /* interface_data */
283                 };
284
285
286                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
287                                                   "ModestFolderView",
288                                                   &my_info, 0);
289
290                 g_type_add_interface_static (my_type,
291                                              TNY_TYPE_ACCOUNT_STORE_VIEW,
292                                              &tny_account_store_view_info);
293         }
294         return my_type;
295 }
296
297 static void
298 modest_folder_view_class_init (ModestFolderViewClass *klass)
299 {
300         GObjectClass *gobject_class;
301         GtkTreeViewClass *treeview_class;
302         gobject_class = (GObjectClass*) klass;
303         treeview_class = (GtkTreeViewClass*) klass;
304
305         parent_class            = g_type_class_peek_parent (klass);
306         gobject_class->finalize = modest_folder_view_finalize;
307         gobject_class->finalize = modest_folder_view_dispose;
308
309         g_type_class_add_private (gobject_class,
310                                   sizeof(ModestFolderViewPrivate));
311
312         signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
313                 g_signal_new ("folder_selection_changed",
314                               G_TYPE_FROM_CLASS (gobject_class),
315                               G_SIGNAL_RUN_FIRST,
316                               G_STRUCT_OFFSET (ModestFolderViewClass,
317                                                folder_selection_changed),
318                               NULL, NULL,
319                               modest_marshal_VOID__POINTER_BOOLEAN,
320                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
321
322         /*
323          * This signal is emitted whenever the currently selected
324          * folder display name is computed. Note that the name could
325          * be different to the folder name, because we could append
326          * the unread messages count to the folder name to build the
327          * folder display name
328          */
329         signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
330                 g_signal_new ("folder-display-name-changed",
331                               G_TYPE_FROM_CLASS (gobject_class),
332                               G_SIGNAL_RUN_FIRST,
333                               G_STRUCT_OFFSET (ModestFolderViewClass,
334                                                folder_display_name_changed),
335                               NULL, NULL,
336                               g_cclosure_marshal_VOID__STRING,
337                               G_TYPE_NONE, 1, G_TYPE_STRING);
338
339         signals[FOLDER_ACTIVATED_SIGNAL] =
340                 g_signal_new ("folder_activated",
341                               G_TYPE_FROM_CLASS (gobject_class),
342                               G_SIGNAL_RUN_FIRST,
343                               G_STRUCT_OFFSET (ModestFolderViewClass,
344                                                folder_activated),
345                               NULL, NULL,
346                               g_cclosure_marshal_VOID__POINTER,
347                               G_TYPE_NONE, 1, G_TYPE_POINTER);
348
349         /*
350          * Emitted whenever the visible account changes
351          */
352         signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL] =
353                 g_signal_new ("visible-account-changed",
354                               G_TYPE_FROM_CLASS (gobject_class),
355                               G_SIGNAL_RUN_FIRST,
356                               G_STRUCT_OFFSET (ModestFolderViewClass,
357                                                visible_account_changed),
358                               NULL, NULL,
359                               g_cclosure_marshal_VOID__STRING,
360                               G_TYPE_NONE, 1, G_TYPE_STRING);
361
362         /*
363          * Emitted when the underlying GtkListStore is updating data
364          */
365         signals[ACTIVITY_CHANGED_SIGNAL] =
366                 g_signal_new ("activity-changed",
367                               G_TYPE_FROM_CLASS (gobject_class),
368                               G_SIGNAL_RUN_FIRST,
369                               G_STRUCT_OFFSET (ModestFolderViewClass,
370                                                activity_changed),
371                               NULL, NULL,
372                               g_cclosure_marshal_VOID__BOOLEAN,
373                               G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
374
375         treeview_class->select_cursor_parent = NULL;
376
377 #ifdef MODEST_TOOLKIT_HILDON2
378         gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
379         
380 #endif
381
382 }
383
384 /* Retrieves the filter, sort and tny models of the folder view. If
385    any of these does not exist then it returns FALSE */
386 static gboolean
387 get_inner_models (ModestFolderView *self, 
388                   GtkTreeModel **filter_model,
389                   GtkTreeModel **sort_model,
390                   GtkTreeModel **tny_model)
391 {
392         GtkTreeModel *s_model, *f_model, *t_model;
393
394         f_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
395         if (!GTK_IS_TREE_MODEL_FILTER(f_model)) {
396                 g_debug ("%s: emtpy model or not filter model", __FUNCTION__);
397                 return FALSE;
398         }
399
400         s_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (f_model));
401         if (!GTK_IS_TREE_MODEL_SORT(s_model)) {
402                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
403                 return FALSE;
404         }
405
406         t_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (s_model));
407
408         /* Assign values */
409         if (filter_model)
410                 *filter_model = f_model;
411         if (sort_model)
412                 *sort_model = s_model;
413         if (tny_model)
414                 *tny_model = t_model;
415
416         return TRUE;
417 }
418
419 /* Simplify checks for NULLs: */
420 static gboolean
421 strings_are_equal (const gchar *a, const gchar *b)
422 {
423         if (!a && !b)
424                 return TRUE;
425         if (a && b)
426         {
427                 return (strcmp (a, b) == 0);
428         }
429         else
430                 return FALSE;
431 }
432
433 static gboolean
434 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path,  GtkTreeIter *iter, gpointer data)
435 {
436         GObject *instance = NULL;
437
438         gtk_tree_model_get (model, iter,
439                             INSTANCE_COLUMN, &instance,
440                             -1);
441
442         if (!instance)
443                 return FALSE; /* keep walking */
444
445         if (!TNY_IS_ACCOUNT (instance)) {
446                 g_object_unref (instance);
447                 return FALSE; /* keep walking */
448         }
449
450         /* Check if this is the looked-for account: */
451         TnyAccount *this_account = TNY_ACCOUNT (instance);
452         TnyAccount *account = TNY_ACCOUNT (data);
453
454         const gchar *this_account_id = tny_account_get_id(this_account);
455         const gchar *account_id = tny_account_get_id(account);
456         g_object_unref (instance);
457         instance = NULL;
458
459         /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
460         if (strings_are_equal(this_account_id, account_id)) {
461                 /* Tell the model that the data has changed, so that
462                  * it calls the cell_data_func callbacks again: */
463                 /* TODO: This does not seem to actually cause the new string to be shown: */
464                 gtk_tree_model_row_changed (model, path, iter);
465
466                 return TRUE; /* stop walking */
467         }
468
469         return FALSE; /* keep walking */
470 }
471
472 typedef struct
473 {
474         ModestFolderView *self;
475         gchar *previous_name;
476 } GetMmcAccountNameData;
477
478 static void
479 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
480 {
481         /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
482
483         GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
484
485         if (!strings_are_equal (
486                 tny_account_get_name(TNY_ACCOUNT(account)),
487                 data->previous_name)) {
488
489                 /* Tell the model that the data has changed, so that
490                  * it calls the cell_data_func callbacks again: */
491                 ModestFolderView *self = data->self;
492                 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
493                 if (model)
494                         gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
495         }
496
497         g_free (data->previous_name);
498         g_slice_free (GetMmcAccountNameData, data);
499 }
500
501 static void
502 convert_parent_folders_to_dots (gchar **item_name)
503 {
504         gint n_parents = 0;
505         gint n_inbox_parents = 0;
506         gchar *c;
507         gchar *path_start;
508         gchar *last_separator;
509
510         if (item_name == NULL)
511                 return;
512
513         path_start = *item_name;
514         for (c = *item_name; *c != '\0'; c++) {
515                 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
516                         gchar *compare;
517                         if (c != path_start) {
518                                 compare = g_strndup (path_start, c - path_start);
519                                 compare = g_strstrip (compare);
520                                 if (g_ascii_strcasecmp (compare, "inbox") == 0) {
521                                         n_inbox_parents++;
522                                 }
523                                 g_free (compare);
524                         }
525                         n_parents++;
526                         path_start = c + 1;
527                 }
528         }
529
530         last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
531         if (last_separator != NULL) {
532                 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
533         }
534
535         if (n_parents > 0) {
536                 GString *buffer;
537                 gint i;
538
539                 buffer = g_string_new ("");
540                 for (i = 0; i < n_parents - n_inbox_parents; i++) {
541                         buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
542                 }
543                 buffer = g_string_append (buffer, last_separator);
544                 g_free (*item_name);
545                 *item_name = g_string_free (buffer, FALSE);
546         }
547
548 }
549
550 static void
551 format_compact_style (gchar **item_name,
552                       GObject *instance,
553                       const gchar *mailbox,
554                       gboolean bold,
555                       gboolean multiaccount,
556                       gboolean *use_markup)
557 {
558         TnyFolder *folder;
559         gboolean is_special;
560         TnyFolderType folder_type;
561
562         if (!TNY_IS_FOLDER (instance))
563                 return;
564
565         folder = (TnyFolder *) instance;
566
567         folder_type = tny_folder_get_folder_type (folder);
568         is_special = (get_cmp_pos (folder_type, folder)!= 4);
569
570         if (mailbox) {
571                 /* Remove mailbox prefix if any */
572                 gchar *prefix = g_strconcat (mailbox, MODEST_FOLDER_PATH_SEPARATOR, NULL);
573                 if (g_str_has_prefix (*item_name, prefix)) {
574                         gchar *new_item_name;
575
576                         new_item_name = g_strdup (*item_name + strlen (prefix));
577                         if (!g_ascii_strcasecmp (new_item_name, "Inbox")) {
578                                 g_free (new_item_name);
579                                 new_item_name = g_strdup (_("mcen_me_folder_inbox"));
580                         }
581                         g_free (*item_name);
582                         *item_name = new_item_name;
583                 }
584         } else if (!g_ascii_strcasecmp (*item_name, "Inbox")) {
585
586                 g_free (*item_name);
587                 *item_name = g_strdup (_("mcen_me_folder_inbox"));
588         }
589
590         if (!is_special || multiaccount) {
591                 TnyAccount *account = tny_folder_get_account (folder);
592                 const gchar *folder_name;
593                 gboolean concat_folder_name = FALSE;
594                 GString *buffer;
595
596                 /* Should not happen */
597                 if (account == NULL)
598                         return;
599
600                 /* convert parent folders to dots */
601                 convert_parent_folders_to_dots  (item_name);
602
603                 folder_name = tny_folder_get_name (folder);
604                 if (g_str_has_suffix (*item_name, folder_name)) {
605                         gchar *offset = g_strrstr (*item_name, folder_name);
606                         *offset = '\0';
607                         concat_folder_name = TRUE;
608                 }
609
610                 buffer = g_string_new ("");
611
612                 buffer = g_string_append (buffer, *item_name);
613                 if (concat_folder_name) {
614                         buffer = g_string_append (buffer, folder_name);
615                 }
616                 g_free (*item_name);
617                 g_object_unref (account);
618
619                 *item_name = g_string_free (buffer, FALSE);
620                 *use_markup = FALSE;
621         } else {
622                 *use_markup = FALSE;
623         }
624 }
625
626 static void
627 text_cell_data  (GtkTreeViewColumn *column,
628                  GtkCellRenderer *renderer,
629                  GtkTreeModel *tree_model,
630                  GtkTreeIter *iter,
631                  gpointer data)
632 {
633         ModestFolderViewPrivate *priv;
634         GObject *rendobj = (GObject *) renderer;
635         gchar *fname = NULL;
636         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
637         GObject *instance = NULL;
638         gboolean use_markup = FALSE;
639
640         gtk_tree_model_get (tree_model, iter,
641                             NAME_COLUMN, &fname,
642                             TYPE_COLUMN, &type,
643                             INSTANCE_COLUMN, &instance,
644                             -1);
645         if (!fname || !instance)
646                 goto end;
647
648         ModestFolderView *self = MODEST_FOLDER_VIEW (data);
649         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
650
651         gchar *item_name = NULL;
652         gint item_weight = 400;
653
654         if (type != TNY_FOLDER_TYPE_ROOT) {
655                 gint number = 0;
656                 gboolean drafts;
657                 gboolean is_local;
658
659                 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
660                         modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
661
662                 if (is_local) {
663                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
664                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
665                                 g_free (fname);
666                                 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
667                         }
668                 } else {
669                         /* Sometimes an special folder is reported by the server as
670                            NORMAL, like some versions of Dovecot */
671                         if (type == TNY_FOLDER_TYPE_NORMAL ||
672                             type == TNY_FOLDER_TYPE_UNKNOWN) {
673                                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
674                         }
675                 }
676
677                 /* note: we cannot reliably get the counts from the
678                  * tree model, we need to use explicit calls on
679                  * tny_folder for some reason. Select the number to
680                  * show: the unread or unsent messages. in case of
681                  * outbox/drafts, show all */
682                 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
683                                  (type == TNY_FOLDER_TYPE_OUTBOX) ||
684                                  (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
685                         number = tny_folder_get_all_count (TNY_FOLDER(instance));
686                         drafts = TRUE;
687                 } else {
688                         number = tny_folder_get_unread_count (TNY_FOLDER(instance));
689                         drafts = FALSE;
690                 }
691
692                 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
693                         item_name = g_strdup (fname);
694                         if (number > 0) {
695                                 item_weight = 800;
696                         } else {
697                                 item_weight = 400;
698                         }
699                 } else {
700                         /* Use bold font style if there are unread or unset messages */
701                         if (number > 0) {
702                                 if (priv->show_message_count) {
703                                         item_name = g_strdup_printf ("%s (%d)", fname, number);
704                                 } else {
705                                         item_name = g_strdup (fname);
706                                 }
707                                 item_weight = 800;
708                         } else {
709                                 item_name = g_strdup (fname);
710                                 item_weight = 400;
711                         }
712                 }
713
714         } else if (TNY_IS_ACCOUNT (instance)) {
715                 /* If it's a server account */
716                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
717                         item_name = g_strdup (priv->local_account_name);
718                         item_weight = 800;
719                 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
720                         /* fname is only correct when the items are first
721                          * added to the model, not when the account is
722                          * changed later, so get the name from the account
723                          * instance: */
724                         item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
725                         item_weight = 800;
726                 } else {
727                         item_name = g_strdup (fname);
728                         item_weight = 800;
729                 }
730         }
731
732         if (!item_name)
733                 item_name = g_strdup ("unknown");
734
735         if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
736                 gboolean multiaccount;
737
738                 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
739                 /* Convert item_name to markup */
740                 format_compact_style (&item_name, instance, priv->mailbox,
741                                       item_weight == 800, 
742                                       multiaccount, &use_markup);
743         }
744
745         if (item_name && item_weight) {
746                 /* Set the name in the treeview cell: */
747                 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && item_weight == 800 && priv->active_color) {
748                         g_object_set (rendobj, 
749                                       "text", item_name, 
750                                       "weight-set", FALSE,
751                                       "foreground-set", TRUE,
752                                       "foreground-gdk", priv->active_color,
753                                       NULL);
754                 } else {
755                         g_object_set (rendobj, 
756                                       "text", item_name,
757                                       "foreground-set", FALSE,
758                                       "weight-set", TRUE, 
759                                       "weight", item_weight,
760                                       NULL);
761                 }
762
763                 /* Notify display name observers */
764                 /* TODO: What listens for this signal, and how can it use only the new name? */
765                 if (((GObject *) priv->cur_folder_store) == instance) {
766                         g_signal_emit (G_OBJECT(self),
767                                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
768                                                item_name);
769                 }
770                 g_free (item_name);
771
772         }
773
774         /* If it is a Memory card account, make sure that we have the correct name.
775          * This function will be trigerred again when the name has been retrieved: */
776         if (TNY_IS_STORE_ACCOUNT (instance) &&
777                 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
778
779                 /* Get the account name asynchronously: */
780                 GetMmcAccountNameData *callback_data =
781                         g_slice_new0(GetMmcAccountNameData);
782                 callback_data->self = self;
783
784                 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
785                 if (name)
786                         callback_data->previous_name = g_strdup (name);
787
788                 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
789                                                          on_get_mmc_account_name, callback_data);
790         }
791  end:
792         if (instance)
793                 g_object_unref (G_OBJECT (instance));
794         if (fname)
795                 g_free (fname);
796 }
797
798 static void
799 messages_cell_data  (GtkTreeViewColumn *column,
800                  GtkCellRenderer *renderer,
801                  GtkTreeModel *tree_model,
802                  GtkTreeIter *iter,
803                  gpointer data)
804 {
805         ModestFolderView *self; 
806         ModestFolderViewPrivate *priv;
807         GObject *rendobj = (GObject *) renderer;
808         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
809         GObject *instance = NULL;
810         gchar *item_name = NULL;
811
812         gtk_tree_model_get (tree_model, iter,
813                             TYPE_COLUMN, &type,
814                             INSTANCE_COLUMN, &instance,
815                             -1);
816         if (!instance)
817                 goto end;
818
819         self = MODEST_FOLDER_VIEW (data);
820         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
821
822
823         if (type != TNY_FOLDER_TYPE_ROOT) {
824                 gint number = 0;
825                 gboolean drafts;
826                 gboolean is_local;
827
828                 is_local = modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
829                         modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance));
830
831                 if (is_local) {
832                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
833                 } else {
834                         /* Sometimes an special folder is reported by the server as
835                            NORMAL, like some versions of Dovecot */
836                         if (type == TNY_FOLDER_TYPE_NORMAL ||
837                             type == TNY_FOLDER_TYPE_UNKNOWN) {
838                                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
839                         }
840                 }
841
842                 /* note: we cannot reliably get the counts from the tree model, we need
843                  * to use explicit calls on tny_folder for some reason.
844                  */
845                 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
846                 if (is_local && ((type == TNY_FOLDER_TYPE_DRAFTS) ||
847                                  (type == TNY_FOLDER_TYPE_OUTBOX) ||
848                                  (type == TNY_FOLDER_TYPE_MERGE))) { /* _OUTBOX actually returns _MERGE... */
849                         number = tny_folder_get_all_count (TNY_FOLDER(instance));
850                         drafts = TRUE;
851                 } else {
852                         number = tny_folder_get_unread_count (TNY_FOLDER(instance));
853                         drafts = FALSE;
854                 }
855
856                 if ((priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) && (number > 0)) {
857                         item_name =
858                                 g_strdup_printf (ngettext ((drafts) ? "mcen_ti_message" : "mcen_va_new_message",
859                                                            (drafts) ? "mcen_ti_messages" : "mcen_va_new_messages",
860                                                            number), number);
861                 }
862         }
863
864         if (!item_name)
865                 item_name = g_strdup ("");
866
867         if (item_name) {
868                 /* Set the name in the treeview cell: */
869                 g_object_set (rendobj,"text", item_name, NULL);
870
871                 g_free (item_name);
872
873         }
874
875  end:
876         if (instance)
877                 g_object_unref (G_OBJECT (instance));
878 }
879
880
881 typedef struct {
882         GdkPixbuf *pixbuf;
883         GdkPixbuf *pixbuf_open;
884         GdkPixbuf *pixbuf_close;
885 } ThreePixbufs;
886
887
888 static inline GdkPixbuf *
889 get_composite_pixbuf (const gchar *icon_name,
890                       const gint size,
891                       GdkPixbuf *base_pixbuf)
892 {
893         GdkPixbuf *emblem, *retval = NULL;
894
895         emblem = modest_platform_get_icon (icon_name, size);
896         if (emblem) {
897                 retval = gdk_pixbuf_copy (base_pixbuf);
898                 gdk_pixbuf_composite (emblem, retval, 0, 0,
899                                       MIN (gdk_pixbuf_get_width (emblem),
900                                            gdk_pixbuf_get_width (retval)),
901                                       MIN (gdk_pixbuf_get_height (emblem),
902                                            gdk_pixbuf_get_height (retval)),
903                                                   0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
904                 g_object_unref (emblem);
905         }
906         return retval;
907 }
908
909 static inline ThreePixbufs *
910 get_composite_icons (const gchar *icon_code,
911                      GdkPixbuf **pixbuf,
912                      GdkPixbuf **pixbuf_open,
913                      GdkPixbuf **pixbuf_close)
914 {
915         ThreePixbufs *retval;
916
917         if (!*pixbuf) {
918                 GdkPixbuf *icon;
919                 icon = modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE);
920                 if (icon) {
921                         *pixbuf = gdk_pixbuf_copy (icon);
922                 } else {
923                         *pixbuf = NULL;
924                 }
925         }
926
927         if (!*pixbuf_open && pixbuf && *pixbuf)
928                 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
929                                                      FOLDER_ICON_SIZE,
930                                                      *pixbuf);
931
932         if (!*pixbuf_close && pixbuf && *pixbuf)
933                 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
934                                                       FOLDER_ICON_SIZE,
935                                                       *pixbuf);
936
937         retval = g_slice_new0 (ThreePixbufs);
938         if (*pixbuf)
939                 retval->pixbuf = g_object_ref (*pixbuf);
940         if (*pixbuf_open)
941                 retval->pixbuf_open = g_object_ref (*pixbuf_open);
942         if (*pixbuf_close)
943                 retval->pixbuf_close = g_object_ref (*pixbuf_close);
944
945         return retval;
946 }
947
948 static inline ThreePixbufs *
949 get_account_protocol_pixbufs (ModestFolderView *folder_view,
950                               ModestProtocolType protocol_type,
951                               GObject *object)
952 {
953         ModestProtocol *protocol;
954         const GdkPixbuf *pixbuf = NULL;
955         ModestFolderViewPrivate *priv;
956
957         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
958
959         protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
960                                                                   protocol_type);
961
962         if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
963                 pixbuf = modest_account_protocol_get_icon (MODEST_ACCOUNT_PROTOCOL (protocol), 
964                                                            priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES?
965                                                            MODEST_ACCOUNT_PROTOCOL_ICON_MAILBOX:
966                                                            MODEST_ACCOUNT_PROTOCOL_ICON_FOLDER,
967                                                            object, FOLDER_ICON_SIZE);
968         }
969
970         if (pixbuf) {
971                 ThreePixbufs *retval;
972                 retval = g_slice_new0 (ThreePixbufs);
973                 retval->pixbuf = g_object_ref ((GObject *) pixbuf);
974                 retval->pixbuf_open = g_object_ref ((GObject *) pixbuf);
975                 retval->pixbuf_close = g_object_ref ((GObject *) pixbuf);
976                 return retval;
977         } else {
978                 return NULL;
979         }
980 }
981
982 static inline ThreePixbufs*
983 get_folder_icons (ModestFolderView *folder_view, TnyFolderType type, GObject *instance)
984 {
985         TnyAccount *account = NULL;
986         static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
987                 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
988                 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
989                 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, *mmc_pixbuf = NULL,
990                 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
991
992         static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
993                 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
994                 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
995                 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, *mmc_pixbuf_open = NULL,
996                 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
997
998         static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
999                 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
1000                 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
1001                 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, *mmc_pixbuf_close = NULL,
1002                 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
1003
1004         ThreePixbufs *retval = NULL;
1005
1006         if (TNY_IS_ACCOUNT (instance)) {
1007                 account = g_object_ref (instance);
1008         } else if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1009                 account = tny_folder_get_account (TNY_FOLDER (instance));
1010         }
1011
1012         if (account) {
1013                 ModestProtocolType account_store_protocol;
1014
1015                 account_store_protocol = modest_tny_account_get_protocol_type (account);
1016                 retval = get_account_protocol_pixbufs (folder_view, account_store_protocol, instance);
1017                 g_object_unref (account);
1018         }
1019
1020         if (retval)
1021                 return retval;
1022
1023         /* Sometimes an special folder is reported by the server as
1024            NORMAL, like some versions of Dovecot */
1025         if (type == TNY_FOLDER_TYPE_NORMAL ||
1026             type == TNY_FOLDER_TYPE_UNKNOWN) {
1027                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1028         }
1029
1030         /* It's not enough with check the folder type. We need to
1031            ensure that we're not giving a special folder icon to a
1032            normal folder with the same name than a special folder */
1033         if (TNY_IS_FOLDER (instance) &&
1034             get_cmp_pos (type, TNY_FOLDER (instance)) ==  4)
1035                 type = TNY_FOLDER_TYPE_NORMAL;
1036
1037         /* Remote folders should not be treated as special folders */
1038         if (TNY_IS_FOLDER_STORE (instance) &&
1039             !TNY_IS_ACCOUNT (instance) &&
1040             type != TNY_FOLDER_TYPE_INBOX &&
1041             modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1042 #ifdef MODEST_TOOLKIT_HILDON2
1043                 return get_composite_icons (MODEST_FOLDER_ICON_REMOTE_FOLDER,
1044                                             &anorm_pixbuf,
1045                                             &anorm_pixbuf_open,
1046                                             &anorm_pixbuf_close);
1047 #else
1048                 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1049                                             &normal_pixbuf,
1050                                             &normal_pixbuf_open,
1051                                             &normal_pixbuf_close);
1052 #endif
1053         }
1054
1055         switch (type) {
1056
1057         case TNY_FOLDER_TYPE_INVALID:
1058                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1059                 break;
1060
1061         case TNY_FOLDER_TYPE_ROOT:
1062                 if (TNY_IS_ACCOUNT (instance)) {
1063
1064                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
1065                                 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
1066                                                               &avirt_pixbuf,
1067                                                               &avirt_pixbuf_open,
1068                                                               &avirt_pixbuf_close);
1069                         } else {
1070                                 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
1071
1072                                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1073                                         retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
1074                                                                       &ammc_pixbuf,
1075                                                                       &ammc_pixbuf_open,
1076                                                                       &ammc_pixbuf_close);
1077                                 } else {
1078                                         retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
1079                                                                       &anorm_pixbuf,
1080                                                                       &anorm_pixbuf_open,
1081                                                                       &anorm_pixbuf_close);
1082                                 }
1083                         }
1084                 }
1085                 break;
1086         case TNY_FOLDER_TYPE_INBOX:
1087                 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
1088                                               &inbox_pixbuf,
1089                                               &inbox_pixbuf_open,
1090                                               &inbox_pixbuf_close);
1091                 break;
1092         case TNY_FOLDER_TYPE_OUTBOX:
1093                 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
1094                                               &outbox_pixbuf,
1095                                               &outbox_pixbuf_open,
1096                                               &outbox_pixbuf_close);
1097                 break;
1098         case TNY_FOLDER_TYPE_JUNK:
1099                 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
1100                                               &junk_pixbuf,
1101                                               &junk_pixbuf_open,
1102                                               &junk_pixbuf_close);
1103                 break;
1104         case TNY_FOLDER_TYPE_SENT:
1105                 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
1106                                               &sent_pixbuf,
1107                                               &sent_pixbuf_open,
1108                                               &sent_pixbuf_close);
1109                 break;
1110         case TNY_FOLDER_TYPE_TRASH:
1111                 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
1112                                               &trash_pixbuf,
1113                                               &trash_pixbuf_open,
1114                                               &trash_pixbuf_close);
1115                 break;
1116         case TNY_FOLDER_TYPE_DRAFTS:
1117                 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
1118                                               &draft_pixbuf,
1119                                               &draft_pixbuf_open,
1120                                               &draft_pixbuf_close);
1121                 break;
1122         case TNY_FOLDER_TYPE_ARCHIVE:
1123                 retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1124                                               &mmc_pixbuf,
1125                                               &mmc_pixbuf_open,
1126                                               &mmc_pixbuf_close);
1127                 break;
1128         case TNY_FOLDER_TYPE_NORMAL:
1129         default:
1130                 /* Memory card folders could have an special icon */
1131                 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
1132                         retval = get_composite_icons (MODEST_FOLDER_ICON_MMC_FOLDER,
1133                                                       &mmc_pixbuf,
1134                                                       &mmc_pixbuf_open,
1135                                                       &mmc_pixbuf_close);
1136                 } else {
1137                         retval = get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
1138                                                       &normal_pixbuf,
1139                                                       &normal_pixbuf_open,
1140                                                       &normal_pixbuf_close);
1141                 }
1142                 break;
1143         }
1144
1145         return retval;
1146 }
1147
1148 static void
1149 free_pixbufs (ThreePixbufs *pixbufs)
1150 {
1151         if (pixbufs->pixbuf)
1152                 g_object_unref (pixbufs->pixbuf);
1153         if (pixbufs->pixbuf_open)
1154                 g_object_unref (pixbufs->pixbuf_open);
1155         if (pixbufs->pixbuf_close)
1156                 g_object_unref (pixbufs->pixbuf_close);
1157         g_slice_free (ThreePixbufs, pixbufs);
1158 }
1159
1160 static void
1161 icon_cell_data  (GtkTreeViewColumn *column,
1162                  GtkCellRenderer *renderer,
1163                  GtkTreeModel *tree_model,
1164                  GtkTreeIter *iter,
1165                  gpointer data)
1166 {
1167         GObject *rendobj = NULL, *instance = NULL;
1168         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1169         gboolean has_children;
1170         ThreePixbufs *pixbufs;
1171         ModestFolderView *folder_view = (ModestFolderView *) data;
1172
1173         rendobj = (GObject *) renderer;
1174
1175         gtk_tree_model_get (tree_model, iter,
1176                             TYPE_COLUMN, &type,
1177                             INSTANCE_COLUMN, &instance,
1178                             -1);
1179
1180         if (!instance)
1181                 return;
1182
1183         has_children = gtk_tree_model_iter_has_child (tree_model, iter);
1184         pixbufs = get_folder_icons (folder_view, type, instance);
1185         g_object_unref (instance);
1186
1187         /* Set pixbuf */
1188         g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
1189
1190         if (has_children) {
1191                 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
1192                 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
1193         }
1194
1195         free_pixbufs (pixbufs);
1196 }
1197
1198 static void
1199 add_columns (GtkWidget *treeview)
1200 {
1201         GtkTreeViewColumn *column;
1202         GtkCellRenderer *renderer;
1203         GtkTreeSelection *sel;
1204         ModestFolderViewPrivate *priv;
1205
1206         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1207
1208         /* Create column */
1209         column = gtk_tree_view_column_new ();
1210
1211         /* Set icon and text render function */
1212         renderer = gtk_cell_renderer_pixbuf_new();
1213 #ifdef MODEST_TOOLKIT_HILDON2
1214         g_object_set (renderer,
1215                       "xpad", MODEST_MARGIN_DEFAULT,
1216                       "ypad", MODEST_MARGIN_DEFAULT,
1217                       NULL);
1218 #endif
1219         gtk_tree_view_column_pack_start (column, renderer, FALSE);
1220         gtk_tree_view_column_set_cell_data_func(column, renderer,
1221                                                 icon_cell_data, treeview, NULL);
1222
1223         renderer = gtk_cell_renderer_text_new();
1224         g_object_set (renderer, 
1225 #ifdef MODEST_TOOLKIT_HILDON2
1226                       "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1227                       "ypad", MODEST_MARGIN_DEFAULT,
1228                       "xpad", MODEST_MARGIN_DEFAULT,
1229 #else
1230                       "ellipsize", PANGO_ELLIPSIZE_END,
1231 #endif
1232                       "ellipsize-set", TRUE, NULL);
1233         gtk_tree_view_column_pack_start (column, renderer, TRUE);
1234         gtk_tree_view_column_set_cell_data_func(column, renderer,
1235                                                 text_cell_data, treeview, NULL);
1236
1237         priv->messages_renderer = gtk_cell_renderer_text_new ();
1238         g_object_set (priv->messages_renderer, 
1239 #ifdef MODEST_TOOLKIT_HILDON2
1240                       "yalign", 0.5,
1241                       "ypad", MODEST_MARGIN_DEFAULT,
1242                       "xpad", MODEST_MARGIN_DOUBLE,
1243 #else
1244                       "scale", PANGO_SCALE_X_SMALL,
1245                       "scale-set", TRUE,
1246 #endif
1247                       "alignment", PANGO_ALIGN_RIGHT,
1248                       "align-set", TRUE,
1249                       "xalign", 1.0,
1250                       NULL);
1251         gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1252         gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1253                                                 messages_cell_data, treeview, NULL);
1254
1255         /* Set selection mode */
1256         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1257         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1258
1259         /* Set treeview appearance */
1260         gtk_tree_view_column_set_spacing (column, 2);
1261         gtk_tree_view_column_set_resizable (column, TRUE);
1262         gtk_tree_view_column_set_fixed_width (column, TRUE);
1263         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1264         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1265
1266         /* Add column */
1267         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1268 }
1269
1270 static void
1271 modest_folder_view_init (ModestFolderView *obj)
1272 {
1273         ModestFolderViewPrivate *priv;
1274         ModestConf *conf;
1275
1276         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1277
1278         priv->timer_expander = 0;
1279         priv->account_store  = NULL;
1280         priv->query          = NULL;
1281         priv->do_refresh     = TRUE;
1282         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1283         priv->cur_folder_store   = NULL;
1284         priv->visible_account_id = NULL;
1285         priv->mailbox = NULL;
1286         priv->folder_to_select = NULL;
1287         priv->reexpand = TRUE;
1288         priv->signal_handlers = 0;
1289
1290         /* Initialize the local account name */
1291         conf = modest_runtime_get_conf();
1292         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1293
1294         /* Init email clipboard */
1295         priv->clipboard = modest_runtime_get_email_clipboard ();
1296         priv->hidding_ids = NULL;
1297         priv->n_selected = 0;
1298         priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1299         priv->reselect = FALSE;
1300         priv->show_non_move = TRUE;
1301         priv->list_to_move = NULL;
1302         priv->show_message_count = TRUE;
1303
1304         /* Build treeview */
1305         add_columns (GTK_WIDGET (obj));
1306
1307         /* Setup drag and drop */
1308         setup_drag_and_drop (GTK_TREE_VIEW(obj));
1309
1310         /* Connect signals */
1311         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1312                                                            G_OBJECT (obj), "key-press-event",
1313                                                            G_CALLBACK (on_key_pressed), NULL);
1314
1315         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1316                                                            (GObject*) 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->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1326                                                            G_OBJECT(conf),
1327                                                            "key_changed",
1328                                                            G_CALLBACK(on_configuration_key_changed),
1329                                                            obj);
1330
1331         priv->active_color = NULL;
1332
1333         update_style (obj);
1334         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1335                                                            G_OBJECT (obj), "notify::style",
1336                                                            G_CALLBACK (on_notify_style), (gpointer) obj);
1337 }
1338
1339 static void
1340 tny_account_store_view_init (gpointer g, gpointer iface_data)
1341 {
1342         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1343
1344         klass->set_account_store = modest_folder_view_set_account_store;
1345 }
1346
1347 static void
1348 modest_folder_view_dispose (GObject *obj)
1349 {
1350         static gboolean disposed = FALSE;
1351         ModestFolderViewPrivate *priv;
1352
1353         if (disposed)
1354                 return;
1355
1356         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (obj);
1357
1358 #ifdef MODEST_TOOLKIT_HILDON2
1359         modest_signal_mgr_disconnect_all_and_destroy (priv->signal_handlers);
1360 #endif
1361
1362         /* Free external references */
1363         if (priv->account_store) {
1364                 g_object_unref (G_OBJECT(priv->account_store));
1365                 priv->account_store = NULL;
1366         }
1367
1368         if (priv->query) {
1369                 g_object_unref (G_OBJECT (priv->query));
1370                 priv->query = NULL;
1371         }
1372
1373         if (priv->folder_to_select) {
1374                 g_object_unref (G_OBJECT(priv->folder_to_select));
1375                 priv->folder_to_select = NULL;
1376         }
1377
1378         if (priv->cur_folder_store) {
1379                 g_object_unref (priv->cur_folder_store);
1380                 priv->cur_folder_store = NULL;
1381         }
1382
1383         if (priv->list_to_move) {
1384                 g_object_unref (priv->list_to_move);
1385                 priv->list_to_move = NULL;
1386         }
1387 }
1388
1389 static void
1390 modest_folder_view_finalize (GObject *obj)
1391 {
1392         ModestFolderViewPrivate *priv;
1393
1394         g_return_if_fail (obj);
1395
1396         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1397
1398         if (priv->active_color) {
1399                 gdk_color_free (priv->active_color);
1400                 priv->active_color = NULL;
1401         }
1402
1403         if (priv->timer_expander != 0) {
1404                 g_source_remove (priv->timer_expander);
1405                 priv->timer_expander = 0;
1406         }
1407
1408         g_free (priv->local_account_name);
1409         g_free (priv->visible_account_id);
1410         g_free (priv->mailbox);
1411
1412         /* Clear hidding array created by cut operation */
1413         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1414
1415         G_OBJECT_CLASS(parent_class)->finalize (obj);
1416 }
1417
1418
1419 static void
1420 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1421 {
1422         ModestFolderViewPrivate *priv;
1423         TnyDevice *device;
1424
1425         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1426         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1427
1428         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1429         device = tny_account_store_get_device (account_store);
1430
1431         if (G_UNLIKELY (priv->account_store)) {
1432
1433                 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1434                                                     G_OBJECT (priv->account_store),
1435                                                     "account_inserted"))
1436                         priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1437                                                                               G_OBJECT (priv->account_store),
1438                                                                               "account_inserted");
1439                 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1440                                                     G_OBJECT (priv->account_store),
1441                                                     "account_removed"))
1442                         priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1443                                                                               G_OBJECT (priv->account_store),
1444                                                                               "account_removed");
1445                 if (modest_signal_mgr_is_connected (priv->signal_handlers,
1446                                                     G_OBJECT (priv->account_store),
1447                                                     "account_changed"))
1448                         priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1449                                                                               G_OBJECT (priv->account_store),
1450                                                                               "account_changed");
1451                 g_object_unref (G_OBJECT (priv->account_store));
1452         }
1453
1454         priv->account_store = g_object_ref (G_OBJECT (account_store));
1455
1456         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1457                                                            G_OBJECT(account_store), "account_removed",
1458                                                            G_CALLBACK (on_account_removed), self);
1459
1460         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1461                                                            G_OBJECT(account_store), "account_inserted",
1462                                                            G_CALLBACK (on_account_inserted), self);
1463
1464         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1465                                                            G_OBJECT(account_store), "account_changed",
1466                                                            G_CALLBACK (on_account_changed), self);
1467
1468         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1469         priv->reselect = FALSE;
1470         modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1471
1472         g_object_unref (G_OBJECT (device));
1473 }
1474
1475 static void
1476 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1477                       gpointer user_data)
1478 {
1479         ModestFolderView *self;
1480         GtkTreeModel *model, *filter_model;
1481         TnyFolder *outbox;
1482
1483         self = MODEST_FOLDER_VIEW (user_data);
1484
1485         if (!get_inner_models (self, &filter_model, NULL, &model))
1486                 return;
1487
1488         /* Remove outbox from model */
1489         outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1490         tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1491         g_object_unref (outbox);
1492
1493         /* Refilter view */
1494         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1495 }
1496
1497 static void
1498 on_account_inserted (TnyAccountStore *account_store,
1499                      TnyAccount *account,
1500                      gpointer user_data)
1501 {
1502         ModestFolderViewPrivate *priv;
1503         GtkTreeModel *model, *filter_model;
1504
1505         /* Ignore transport account insertions, we're not showing them
1506            in the folder view */
1507         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1508                 return;
1509
1510         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1511
1512
1513         /* If we're adding a new account, and there is no previous
1514            one, we need to select the visible server account */
1515         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1516             !priv->visible_account_id)
1517                 modest_widget_memory_restore (modest_runtime_get_conf(),
1518                                               G_OBJECT (user_data),
1519                                               MODEST_CONF_FOLDER_VIEW_KEY);
1520
1521
1522         /* Get models */
1523         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1524                                &filter_model, NULL, &model))
1525                 return;
1526
1527         /* Insert the account in the model */
1528         tny_list_append (TNY_LIST (model), G_OBJECT (account));
1529
1530         /* When the model is a list store (plain representation) the
1531            outbox is not a child of any account so we have to manually
1532            delete it because removing the local folders account won't
1533            delete it (because tny_folder_get_account() is not defined
1534            for a merge folder */
1535         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1536             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1537                 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1538                                                                    (GObject*) account, "outbox-deleted",
1539                                                                    G_CALLBACK (on_outbox_deleted_cb),
1540                                                                    user_data);
1541         }
1542
1543         /* Refilter the model */
1544         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1545 }
1546
1547
1548 static gboolean
1549 same_account_selected (ModestFolderView *self,
1550                        TnyAccount *account)
1551 {
1552         ModestFolderViewPrivate *priv;
1553         gboolean same_account = FALSE;
1554
1555         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1556
1557         if (priv->cur_folder_store) {
1558                 TnyAccount *selected_folder_account = NULL;
1559
1560                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1561                         selected_folder_account =
1562                                 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1563                 } else {
1564                         selected_folder_account =
1565                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1566                 }
1567
1568                 if (selected_folder_account == account)
1569                         same_account = TRUE;
1570
1571                 g_object_unref (selected_folder_account);
1572         }
1573         return same_account;
1574 }
1575
1576 /**
1577  *
1578  * Selects the first inbox or the local account in an idle
1579  */
1580 static gboolean
1581 on_idle_select_first_inbox_or_local (gpointer user_data)
1582 {
1583         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1584
1585         gdk_threads_enter ();
1586         modest_folder_view_select_first_inbox_or_local (self);
1587         gdk_threads_leave ();
1588
1589         return FALSE;
1590 }
1591
1592 static void
1593 on_account_changed (TnyAccountStore *account_store,
1594                     TnyAccount *tny_account,
1595                     gpointer user_data)
1596 {
1597         ModestFolderView *self;
1598         ModestFolderViewPrivate *priv;
1599         GtkTreeModel *model, *filter_model;
1600         GtkTreeSelection *sel;
1601         gboolean same_account;
1602
1603         /* Ignore transport account insertions, we're not showing them
1604            in the folder view */
1605         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1606                 return;
1607
1608         self = MODEST_FOLDER_VIEW (user_data);
1609         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1610
1611         /* Get the inner model */
1612         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1613                                &filter_model, NULL, &model))
1614                 return;
1615
1616         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1617
1618         /* Invalidate the cur_folder_store only if the selected folder
1619            belongs to the account that is being removed */
1620         same_account = same_account_selected (self, tny_account);
1621         if (same_account) {
1622                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1623                 gtk_tree_selection_unselect_all (sel);
1624         }
1625
1626         /* Remove the account from the model */
1627         tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1628
1629         /* Insert the account in the model */
1630         tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1631
1632         /* Refilter the model */
1633         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1634
1635         /* Select the first INBOX if the currently selected folder
1636            belongs to the account that is being deleted */
1637         if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1638                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1639 }
1640
1641 static void
1642 on_account_removed (TnyAccountStore *account_store,
1643                     TnyAccount *account,
1644                     gpointer user_data)
1645 {
1646         ModestFolderView *self = NULL;
1647         ModestFolderViewPrivate *priv;
1648         GtkTreeModel *model, *filter_model;
1649         GtkTreeSelection *sel = NULL;
1650         gboolean same_account = FALSE;
1651
1652         /* Ignore transport account removals, we're not showing them
1653            in the folder view */
1654         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1655                 return;
1656
1657         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1658                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1659                 return;
1660         }
1661
1662         self = MODEST_FOLDER_VIEW (user_data);
1663         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1664
1665         /* Invalidate the cur_folder_store only if the selected folder
1666            belongs to the account that is being removed */
1667         same_account = same_account_selected (self, account);
1668         if (same_account) {
1669                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1670                 gtk_tree_selection_unselect_all (sel);
1671         }
1672
1673         /* Invalidate row to select only if the folder to select
1674            belongs to the account that is being removed*/
1675         if (priv->folder_to_select) {
1676                 TnyAccount *folder_to_select_account = NULL;
1677
1678                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1679                 if (folder_to_select_account == account) {
1680                         modest_folder_view_disable_next_folder_selection (self);
1681                         g_object_unref (priv->folder_to_select);
1682                         priv->folder_to_select = NULL;
1683                 }
1684                 g_object_unref (folder_to_select_account);
1685         }
1686
1687         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1688                                &filter_model, NULL, &model))
1689                 return;
1690
1691         /* Disconnect the signal handler */
1692         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1693             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1694                 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject*) account, "outbox-deleted"))
1695                         priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
1696                                                                               (GObject *) account,
1697                                                                               "outbox-deleted");
1698         }
1699
1700         /* Remove the account from the model */
1701         tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1702
1703         /* If the removed account is the currently viewed one then
1704            clear the configuration value. The new visible account will be the default account */
1705         if (priv->visible_account_id &&
1706             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1707
1708                 /* Clear the current visible account_id */
1709                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1710                 modest_folder_view_set_mailbox (self, NULL);
1711
1712                 /* Call the restore method, this will set the new visible account */
1713                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1714                                               MODEST_CONF_FOLDER_VIEW_KEY);
1715         }
1716
1717         /* Refilter the model */
1718         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1719
1720         /* Select the first INBOX if the currently selected folder
1721            belongs to the account that is being deleted */
1722         if (same_account)
1723                 g_idle_add (on_idle_select_first_inbox_or_local, self);
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                 modest_folder_view_select_first_inbox_or_local (self);
1765                 /* Notify the display name observers */
1766                 g_signal_emit (G_OBJECT(self),
1767                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1768                                NULL);
1769         }
1770
1771         if (priv->reexpand) {
1772                 expand_root_items (self);
1773                 priv->reexpand = FALSE;
1774         }
1775
1776         return FALSE;
1777 }
1778
1779 GtkWidget*
1780 modest_folder_view_new (TnyFolderStoreQuery *query)
1781 {
1782         return modest_folder_view_new_full (query, TRUE);
1783 }
1784
1785 GtkWidget*
1786 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1787 {
1788         GObject *self;
1789         ModestFolderViewPrivate *priv;
1790         GtkTreeSelection *sel;
1791
1792         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, 
1793 #ifdef MODEST_TOOLKIT_HILDON2
1794                                        "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1795 #endif
1796                                        NULL));
1797         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1798
1799         if (query)
1800                 priv->query = g_object_ref (query);
1801
1802         priv->do_refresh = do_refresh;
1803
1804         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1805         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1806                                                            (GObject*) sel, "changed",
1807                                                            G_CALLBACK (on_selection_changed), self);
1808
1809         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1810                                                            self, "row-activated",
1811                                                            G_CALLBACK (on_row_activated), self);
1812
1813         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
1814                                                            self, "expose-event", 
1815                                                            G_CALLBACK (modest_folder_view_on_map), NULL);
1816
1817         return GTK_WIDGET(self);
1818 }
1819
1820 /* this feels dirty; any other way to expand all the root items? */
1821 static void
1822 expand_root_items (ModestFolderView *self)
1823 {
1824         GtkTreePath *path;
1825         GtkTreeModel *model;
1826         GtkTreeIter iter;
1827
1828         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1829         path = gtk_tree_path_new_first ();
1830
1831         /* all folders should have child items, so.. */
1832         do {
1833                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1834                 gtk_tree_path_next (path);
1835         } while (gtk_tree_model_get_iter (model, &iter, path));
1836
1837         gtk_tree_path_free (path);
1838 }
1839
1840 static gboolean
1841 is_parent_of (TnyFolder *a, TnyFolder *b)
1842 {
1843         const gchar *a_id;
1844         gboolean retval = FALSE;
1845
1846         a_id = tny_folder_get_id (a);
1847         if (a_id) {
1848                 gchar *string_to_match;
1849                 const gchar *b_id;
1850
1851                 string_to_match = g_strconcat (a_id, "/", NULL);
1852                 b_id = tny_folder_get_id (b);
1853                 retval = g_str_has_prefix (b_id, string_to_match);
1854                 g_free (string_to_match);
1855         }
1856         
1857         return retval;
1858 }
1859
1860 typedef struct _ForeachFolderInfo {
1861         gchar *needle;
1862         gboolean found;
1863 } ForeachFolderInfo;
1864
1865 static gboolean 
1866 foreach_folder_with_id (GtkTreeModel *model,
1867                         GtkTreePath *path,
1868                         GtkTreeIter *iter,
1869                         gpointer data)
1870 {
1871         ForeachFolderInfo *info;
1872         GObject *instance;
1873
1874         info = (ForeachFolderInfo *) data;
1875         gtk_tree_model_get (model, iter,
1876                             INSTANCE_COLUMN, &instance,
1877                             -1);
1878
1879         if (TNY_IS_FOLDER (instance)) {
1880                 const gchar *id;
1881                 gchar *collate;
1882                 id = tny_folder_get_id (TNY_FOLDER (instance));
1883                 if (id) {
1884                         collate = g_utf8_collate_key (id, -1);
1885                         info->found = !strcmp (info->needle, collate);
1886                         g_free (collate);
1887                 }
1888         }
1889
1890         if (instance)
1891                 g_object_unref (instance);
1892
1893         return info->found;
1894         
1895 }
1896
1897
1898 static gboolean
1899 has_folder_with_id (ModestFolderView *self, const gchar *id)
1900 {
1901         GtkTreeModel *model;
1902         ForeachFolderInfo info = {NULL, FALSE};
1903
1904         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1905         info.needle = g_utf8_collate_key (id, -1);
1906         
1907         gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1908         g_free (info.needle);
1909
1910         return info.found;
1911 }
1912
1913 static gboolean
1914 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1915 {
1916         const gchar *a_id;
1917         gboolean retval = FALSE;
1918
1919         a_id = tny_folder_get_id (a);
1920         if (a_id) {
1921                 const gchar *b_id;
1922                 b_id = tny_folder_get_id (b);
1923                 
1924                 if (b_id) {
1925                         const gchar *last_bar;
1926                         gchar *string_to_match;
1927                         last_bar = g_strrstr (b_id, "/");
1928                         if (last_bar)
1929                                 last_bar++;
1930                         else
1931                                 last_bar = b_id;
1932                         string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1933                         retval = has_folder_with_id (self, string_to_match);
1934                         g_free (string_to_match);
1935                 }
1936         }
1937
1938         return retval;
1939 }
1940
1941 static gboolean
1942 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1943 {
1944         ModestFolderViewPrivate *priv;
1945         TnyIterator *iterator;
1946         gboolean retval = TRUE;
1947
1948         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1949         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1950
1951         for (iterator = tny_list_create_iterator (priv->list_to_move);
1952              retval && !tny_iterator_is_done (iterator);
1953              tny_iterator_next (iterator)) {
1954                 GObject *instance;
1955                 instance = tny_iterator_get_current (iterator);
1956                 if (instance == (GObject *) folder) {
1957                         retval = FALSE;
1958                 } else if (TNY_IS_FOLDER (instance)) {
1959                         retval = !is_parent_of (TNY_FOLDER (instance), folder);
1960                         if (retval) {
1961                                 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1962                         }
1963                 }
1964                 g_object_unref (instance);
1965         }
1966         g_object_unref (iterator);
1967
1968         return retval;
1969 }
1970
1971
1972 /*
1973  * We use this function to implement the
1974  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1975  * account in this case, and the local folders.
1976  */
1977 static gboolean
1978 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1979 {
1980         ModestFolderViewPrivate *priv;
1981         gboolean retval = TRUE;
1982         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1983         GObject *instance = NULL;
1984         const gchar *id = NULL;
1985         guint i;
1986         gboolean found = FALSE;
1987         gboolean cleared = FALSE;
1988         ModestTnyFolderRules rules = 0;
1989         gchar *fname;
1990
1991         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1992         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1993
1994         gtk_tree_model_get (model, iter,
1995                             NAME_COLUMN, &fname,
1996                             TYPE_COLUMN, &type,
1997                             INSTANCE_COLUMN, &instance,
1998                             -1);
1999
2000         /* Do not show if there is no instance, this could indeed
2001            happen when the model is being modified while it's being
2002            drawn. This could occur for example when moving folders
2003            using drag&drop */
2004         if (!instance) {
2005                 g_free (fname);
2006                 return FALSE;
2007         }
2008
2009         if (TNY_IS_ACCOUNT (instance)) {
2010                 TnyAccount *acc = TNY_ACCOUNT (instance);
2011                 const gchar *account_id = tny_account_get_id (acc);
2012
2013                 /* If it isn't a special folder,
2014                  * don't show it unless it is the visible account: */
2015                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2016                     !modest_tny_account_is_virtual_local_folders (acc) &&
2017                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2018
2019                         /* Show only the visible account id */
2020                         if (priv->visible_account_id) {
2021                                 if (strcmp (account_id, priv->visible_account_id))
2022                                         retval = FALSE;
2023                         } else {
2024                                 retval = FALSE;
2025                         }
2026                 }
2027
2028                 /* Never show these to the user. They are merged into one folder
2029                  * in the local-folders account instead: */
2030                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2031                         retval = FALSE;
2032         } else {
2033                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2034                         /* Only show special folders for current account if needed */
2035                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2036                                 TnyAccount *account;
2037
2038                                 account = tny_folder_get_account (TNY_FOLDER (instance));
2039
2040                                 if (TNY_IS_ACCOUNT (account)) {
2041                                         const gchar *account_id = tny_account_get_id (account);
2042
2043                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
2044                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2045                                                 /* Show only the visible account id */
2046                                                 if (priv->visible_account_id) {
2047                                                   if (strcmp (account_id, priv->visible_account_id)) {
2048                                                           retval = FALSE;
2049                                                   } else if (priv->mailbox) {
2050                                                           /* Filter mailboxes */
2051                                                           if (!g_str_has_prefix (fname, priv->mailbox)) {
2052                                                                   retval = FALSE;
2053                                                           } else if (!strcmp (fname, priv->mailbox)) {
2054                                                                   /* Hide mailbox parent */
2055                                                                   retval = FALSE;
2056                                                           }
2057                                                   }
2058                                                 }
2059                                         }
2060                                                 g_object_unref (account);
2061                                 }
2062                         }
2063
2064                 }
2065         }
2066
2067         /* Check hiding (if necessary) */
2068         cleared = modest_email_clipboard_cleared (priv->clipboard);
2069         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2070                 id = tny_folder_get_id (TNY_FOLDER(instance));
2071                 if (priv->hidding_ids != NULL)
2072                         for (i=0; i < priv->n_selected && !found; i++)
2073                                 if (priv->hidding_ids[i] != NULL && id != NULL)
2074                                         found = (!strcmp (priv->hidding_ids[i], id));
2075
2076                 retval = !found;
2077         }
2078
2079         /* If this is a move to dialog, hide Sent, Outbox and Drafts
2080         folder as no message can be move there according to UI specs */
2081         if (retval && !priv->show_non_move) {
2082                 if (priv->list_to_move && 
2083                     tny_list_get_length (priv->list_to_move) > 0 &&
2084                     TNY_IS_FOLDER (instance)) {
2085                         retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2086                 }
2087                 if (retval && TNY_IS_FOLDER (instance) && 
2088                     modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2089                         switch (type) {
2090                         case TNY_FOLDER_TYPE_OUTBOX:
2091                         case TNY_FOLDER_TYPE_SENT:
2092                         case TNY_FOLDER_TYPE_DRAFTS:
2093                                 retval = FALSE;
2094                                 break;
2095                         case TNY_FOLDER_TYPE_UNKNOWN:
2096                         case TNY_FOLDER_TYPE_NORMAL:
2097                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2098                                 if (type == TNY_FOLDER_TYPE_INVALID)
2099                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2100                                 
2101                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2102                                     type == TNY_FOLDER_TYPE_SENT
2103                                     || type == TNY_FOLDER_TYPE_DRAFTS)
2104                                         retval = FALSE;
2105                                 break;
2106                         default:
2107                                 break;
2108                         }
2109                 }
2110                 if (retval && TNY_IS_ACCOUNT (instance) &&
2111                     modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2112                         ModestProtocolType protocol_type;
2113
2114                         protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2115                         retval  = !modest_protocol_registry_protocol_type_has_tag 
2116                                 (modest_runtime_get_protocol_registry (),
2117                                  protocol_type,
2118                                  MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2119                 }
2120         }
2121
2122         /* apply special filters */
2123         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2124                 if (TNY_IS_ACCOUNT (instance))
2125                         return FALSE;
2126         }
2127
2128         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2129                 if (TNY_IS_FOLDER (instance))
2130                         return FALSE;
2131         }
2132
2133         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2134                 if (TNY_IS_ACCOUNT (instance)) {
2135                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2136                                 return FALSE;
2137                 } else if (TNY_IS_FOLDER (instance)) {
2138                         if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2139                                 return FALSE;
2140                 }
2141         }
2142
2143         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2144                 if (TNY_IS_ACCOUNT (instance)) {
2145                         if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2146                                 return FALSE;
2147                 } else if (TNY_IS_FOLDER (instance)) {
2148                         if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2149                                 return FALSE;
2150                 }
2151         }
2152
2153         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2154                 /* A mailbox is a fake folder with an @ in the middle of the name */
2155                 if (!TNY_IS_FOLDER (instance) ||
2156                     !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2157                         return FALSE;
2158                 } else {
2159                         const gchar *folder_name;
2160                         folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2161                         if (!folder_name || strchr (folder_name, '@') == NULL)
2162                                 return FALSE;
2163                 }
2164                 
2165         }
2166
2167         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2168                 if (TNY_IS_FOLDER (instance)) {
2169                         /* Check folder rules */
2170                         ModestTnyFolderRules rules;
2171
2172                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2173                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2174                 } else if (TNY_IS_ACCOUNT (instance)) {
2175                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2176                                 retval = FALSE;
2177                         } else {
2178                                 retval = TRUE;
2179                         }
2180                 }
2181         }
2182
2183         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2184                 if (TNY_IS_FOLDER (instance)) {
2185                         TnyFolderType guess_type;
2186
2187                         if (TNY_FOLDER_TYPE_NORMAL) {
2188                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2189                         } else {
2190                                 guess_type = type;
2191                         }
2192
2193                         switch (type) {
2194                         case TNY_FOLDER_TYPE_OUTBOX:
2195                         case TNY_FOLDER_TYPE_SENT:
2196                         case TNY_FOLDER_TYPE_DRAFTS:
2197                         case TNY_FOLDER_TYPE_ARCHIVE:
2198                         case TNY_FOLDER_TYPE_INBOX:
2199                                 retval = FALSE;
2200                                 break;
2201                         case TNY_FOLDER_TYPE_UNKNOWN:
2202                         case TNY_FOLDER_TYPE_NORMAL:
2203                                 break;
2204                         default:
2205                                 break;
2206                         }
2207
2208                 } else if (TNY_IS_ACCOUNT (instance)) {
2209                         retval = FALSE;
2210                 }
2211         }
2212
2213         if (retval && TNY_IS_FOLDER (instance)) {
2214                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2215         }
2216
2217         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2218                 if (TNY_IS_FOLDER (instance)) {
2219                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2220                 } else if (TNY_IS_ACCOUNT (instance)) {
2221                         retval = FALSE;
2222                 }
2223         }
2224
2225         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2226                 if (TNY_IS_FOLDER (instance)) {
2227                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2228                 } else if (TNY_IS_ACCOUNT (instance)) {
2229                         retval = FALSE;
2230                 }
2231         }
2232
2233         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2234                 if (TNY_IS_FOLDER (instance)) {
2235                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2236                 } else if (TNY_IS_ACCOUNT (instance)) {
2237                         retval = FALSE;
2238                 }
2239         }
2240
2241         /* Free */
2242         g_object_unref (instance);
2243         g_free (fname);
2244
2245         return retval;
2246 }
2247
2248
2249 gboolean
2250 modest_folder_view_update_model (ModestFolderView *self,
2251                                  TnyAccountStore *account_store)
2252 {
2253         ModestFolderViewPrivate *priv;
2254         GtkTreeModel *model;
2255         GtkTreeModel *filter_model = NULL, *sortable = NULL, *old_tny_model;
2256
2257         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2258         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2259                               FALSE);
2260
2261         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2262
2263         /* Notify that there is no folder selected */
2264         g_signal_emit (G_OBJECT(self),
2265                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2266                        NULL, FALSE);
2267         if (priv->cur_folder_store) {
2268                 g_object_unref (priv->cur_folder_store);
2269                 priv->cur_folder_store = NULL;
2270         }
2271
2272         /* FIXME: the local accounts are not shown when the query
2273            selects only the subscribed folders */
2274 #ifdef MODEST_TOOLKIT_HILDON2
2275         TnyGtkFolderListStoreFlags flags;
2276         flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2277         if (!priv->do_refresh)
2278                 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2279         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
2280                                                           flags);
2281         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2282                                                       MODEST_FOLDER_PATH_SEPARATOR);
2283 #else
2284         model = tny_gtk_folder_store_tree_model_new (NULL);
2285 #endif
2286
2287         /* When the model is a list store (plain representation) the
2288            outbox is not a child of any account so we have to manually
2289            delete it because removing the local folders account won't
2290            delete it (because tny_folder_get_account() is not defined
2291            for a merge folder */
2292         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2293                 TnyAccount *account;
2294                 ModestTnyAccountStore *acc_store;
2295
2296                 acc_store = modest_runtime_get_account_store ();
2297                 account = modest_tny_account_store_get_local_folders_account (acc_store);
2298
2299                 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) account,
2300                                                     "outbox-deleted"))
2301                         priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2302                                                                               (GObject *) account,
2303                                                                               "outbox-deleted");
2304
2305                 priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2306                                                                    (GObject*) account, "outbox-deleted",
2307                                                                    G_CALLBACK (on_outbox_deleted_cb),
2308                                                                    self);
2309                 g_object_unref (account);
2310         }
2311
2312         /* Get the accounts: */
2313         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2314                                         TNY_LIST (model),
2315                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2316
2317         sortable = gtk_tree_model_sort_new_with_model (model);
2318         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2319                                               NAME_COLUMN,
2320                                               GTK_SORT_ASCENDING);
2321         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2322                                          NAME_COLUMN,
2323                                          cmp_rows, NULL, NULL);
2324
2325         /* Create filter model */
2326         filter_model = gtk_tree_model_filter_new (sortable, NULL);
2327         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2328                                                 filter_row,
2329                                                 self,
2330                                                 NULL);
2331
2332         if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2333                 if (modest_signal_mgr_is_connected (priv->signal_handlers, (GObject *) old_tny_model,
2334                                                     "activity-changed"))
2335                         priv->signal_handlers = modest_signal_mgr_disconnect (priv->signal_handlers,
2336                                                                               G_OBJECT (old_tny_model),
2337                                                                               "activity-changed");
2338         }
2339
2340         /* Set new model */
2341         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2342 #ifndef MODEST_TOOLKIT_HILDON2
2343         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2344                                                            G_OBJECT(filter_model), "row-inserted",
2345                                                            (GCallback) on_row_inserted_maybe_select_folder, self);
2346 #endif
2347
2348 #ifdef MODEST_TOOLKIT_HILDON2
2349         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
2350                                                            G_OBJECT (model),
2351                                                            "activity-changed",
2352                                                            G_CALLBACK (on_activity_changed), 
2353                                                            self);
2354 #endif
2355
2356         g_object_unref (model);
2357         g_object_unref (filter_model);
2358         g_object_unref (sortable);
2359
2360         /* Force a reselection of the INBOX next time the widget is shown */
2361         priv->reselect = TRUE;
2362
2363         return TRUE;
2364 }
2365
2366
2367 static void
2368 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2369 {
2370         GtkTreeModel *model = NULL;
2371         TnyFolderStore *folder = NULL;
2372         GtkTreeIter iter;
2373         ModestFolderView *tree_view = NULL;
2374         ModestFolderViewPrivate *priv = NULL;
2375         gboolean selected = FALSE;
2376
2377         g_return_if_fail (sel);
2378         g_return_if_fail (user_data);
2379
2380         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2381
2382         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2383
2384         tree_view = MODEST_FOLDER_VIEW (user_data);
2385
2386         if (selected) {
2387                 gtk_tree_model_get (model, &iter,
2388                                     INSTANCE_COLUMN, &folder,
2389                                     -1);
2390
2391                 /* If the folder is the same do not notify */
2392                 if (folder && priv->cur_folder_store == folder) {
2393                         g_object_unref (folder);
2394                         return;
2395                 }
2396         }
2397
2398         /* Current folder was unselected */
2399         if (priv->cur_folder_store) {
2400                 /* We must do this firstly because a libtinymail-camel
2401                    implementation detail. If we issue the signal
2402                    before doing the sync_async, then that signal could
2403                    cause (and it actually does it) a free of the
2404                    summary of the folder (because the main window will
2405                    clear the headers view */
2406 #ifndef MODEST_TOOLKIT_HILDON2
2407                 if (TNY_IS_FOLDER(priv->cur_folder_store))
2408                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2409                                                FALSE, NULL, NULL, NULL);
2410 #endif
2411
2412                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2413                        priv->cur_folder_store, FALSE);
2414
2415                 g_object_unref (priv->cur_folder_store);
2416                 priv->cur_folder_store = NULL;
2417         }
2418
2419         /* New current references */
2420         priv->cur_folder_store = folder;
2421
2422         /* New folder has been selected. Do not notify if there is
2423            nothing new selected */
2424         if (selected) {
2425                 g_signal_emit (G_OBJECT(tree_view),
2426                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2427                                0, priv->cur_folder_store, TRUE);
2428         }
2429 }
2430
2431 static void
2432 on_row_activated (GtkTreeView *treeview,
2433                   GtkTreePath *treepath,
2434                   GtkTreeViewColumn *column,
2435                   gpointer user_data)
2436 {
2437         GtkTreeModel *model = NULL;
2438         TnyFolderStore *folder = NULL;
2439         GtkTreeIter iter;
2440         ModestFolderView *self = NULL;
2441         ModestFolderViewPrivate *priv = NULL;
2442
2443         g_return_if_fail (treeview);
2444         g_return_if_fail (user_data);
2445
2446         self = MODEST_FOLDER_VIEW (user_data);
2447         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2448
2449         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2450
2451         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2452                 return;
2453
2454         gtk_tree_model_get (model, &iter,
2455                             INSTANCE_COLUMN, &folder,
2456                             -1);
2457
2458         g_signal_emit (G_OBJECT(self),
2459                        signals[FOLDER_ACTIVATED_SIGNAL],
2460                        0, folder);
2461
2462 #ifdef MODEST_TOOLKIT_HILDON2
2463         HildonUIMode ui_mode;
2464         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2465         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2466                 if (priv->cur_folder_store)
2467                         g_object_unref (priv->cur_folder_store);
2468                 priv->cur_folder_store = g_object_ref (folder);
2469         }
2470 #endif
2471
2472         g_object_unref (folder);
2473 }
2474
2475 TnyFolderStore *
2476 modest_folder_view_get_selected (ModestFolderView *self)
2477 {
2478         ModestFolderViewPrivate *priv;
2479
2480         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2481
2482         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2483         if (priv->cur_folder_store)
2484                 g_object_ref (priv->cur_folder_store);
2485
2486         return priv->cur_folder_store;
2487 }
2488
2489 static gint
2490 get_cmp_rows_type_pos (GObject *folder)
2491 {
2492         /* Remote accounts -> Local account -> MMC account .*/
2493         /* 0, 1, 2 */
2494
2495         if (TNY_IS_ACCOUNT (folder) &&
2496                 modest_tny_account_is_virtual_local_folders (
2497                         TNY_ACCOUNT (folder))) {
2498                 return 1;
2499         } else if (TNY_IS_ACCOUNT (folder)) {
2500                 TnyAccount *account = TNY_ACCOUNT (folder);
2501                 const gchar *account_id = tny_account_get_id (account);
2502                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2503                         return 2;
2504                 else
2505                         return 0;
2506         }
2507         else {
2508                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2509                 return -1; /* Should never happen */
2510         }
2511 }
2512
2513 static gboolean
2514 inbox_is_special (TnyFolderStore *folder_store)
2515 {
2516         gboolean is_special = TRUE;
2517
2518         if (TNY_IS_FOLDER (folder_store)) {
2519                 const gchar *id;
2520                 gchar *downcase;
2521                 gchar *last_bar;
2522                 gchar *last_inbox_bar;
2523
2524                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2525                 downcase = g_utf8_strdown (id, -1);
2526                 last_bar = g_strrstr (downcase, "/");
2527                 if (last_bar) {
2528                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2529                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2530                                 is_special = FALSE;
2531                 } else {
2532                         is_special = FALSE;
2533                 }
2534                 g_free (downcase);
2535         }
2536         return is_special;
2537 }
2538
2539 static gint
2540 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2541 {
2542         TnyAccount *account;
2543         gboolean is_special;
2544         /* Inbox, Outbox, Drafts, Sent, User */
2545         /* 0, 1, 2, 3, 4 */
2546
2547         if (!TNY_IS_FOLDER (folder_store))
2548                 return 4;
2549         switch (t) {
2550         case TNY_FOLDER_TYPE_INBOX:
2551         {
2552                 account = tny_folder_get_account (folder_store);
2553                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2554
2555                 /* In inbox case we need to know if the inbox is really the top
2556                  * inbox of the account, or if it's a submailbox inbox. To do
2557                  * this we'll apply an heuristic rule: Find last "/" and check
2558                  * if it's preceeded by another Inbox */
2559                 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2560                 g_object_unref (account);
2561                 return is_special?0:4;
2562         }
2563         break;
2564         case TNY_FOLDER_TYPE_OUTBOX:
2565                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2566                 break;
2567         case TNY_FOLDER_TYPE_DRAFTS:
2568         {
2569                 account = tny_folder_get_account (folder_store);
2570                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2571                 g_object_unref (account);
2572                 return is_special?1:4;
2573         }
2574         break;
2575         case TNY_FOLDER_TYPE_SENT:
2576         {
2577                 account = tny_folder_get_account (folder_store);
2578                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2579                 g_object_unref (account);
2580                 return is_special?3:4;
2581         }
2582         break;
2583         default:
2584                 return 4;
2585         }
2586 }
2587
2588 static gint
2589 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2590 {
2591         const gchar *a1_name, *a2_name;
2592
2593         a1_name = tny_account_get_name (a1);
2594         a2_name = tny_account_get_name (a2);
2595
2596         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2597 }
2598
2599 static gint
2600 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2601 {
2602         TnyAccount *a1 = NULL, *a2 = NULL;
2603         gint cmp;
2604
2605         if (TNY_IS_ACCOUNT (s1)) {
2606                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2607         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2608                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2609         }
2610
2611         if (TNY_IS_ACCOUNT (s2)) {
2612                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2613         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2614                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2615         }
2616
2617         if (!a1 || !a2) {
2618                 if (!a1 && !a2)
2619                         cmp = 0;
2620                 else if (!a1)
2621                         cmp = 1;
2622                 else
2623                         cmp = -1;
2624                 goto finish;
2625         }
2626
2627         if (a1 == a2) {
2628                 cmp = 0;
2629                 goto finish;
2630         }
2631         /* First we sort with the type of account */
2632         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2633         if (cmp != 0)
2634                 goto finish;
2635
2636         cmp = compare_account_names (a1, a2);
2637
2638 finish:
2639         if (a1)
2640                 g_object_unref (a1);
2641         if (a2)
2642                 g_object_unref (a2);
2643
2644         return cmp;
2645 }
2646
2647 static gint
2648 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2649 {
2650         gint is_account1, is_account2;
2651
2652         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2653         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2654
2655         return is_account2 - is_account1;
2656 }
2657
2658 static gint
2659 compare_folders (const gchar *name1, const gchar *name2)
2660 {
2661         const gchar *separator1, *separator2;
2662         const gchar *next1, *next2;
2663         gchar *top1, *top2;
2664         gint cmp;
2665
2666         if (name1 == NULL || name1[0] == '\0')
2667                 return -1;
2668         if (name2 == NULL || name2[0] == '\0')
2669                 return 1;
2670
2671         separator1 = strstr (name1, MODEST_FOLDER_PATH_SEPARATOR);
2672         if (separator1) {
2673                 top1 = g_strndup (name1, separator1 - name1);
2674         } else {
2675                 top1 = g_strdup (name1);
2676         }
2677
2678         separator2 = strstr (name2, MODEST_FOLDER_PATH_SEPARATOR);
2679         if (separator2) {
2680                 top2 = g_strndup (name2, separator2 - name2);
2681         } else {
2682                 top2 = g_strdup (name2);
2683         }
2684
2685
2686         cmp = modest_text_utils_utf8_strcmp (top1, top2, TRUE);
2687         g_free (top1);
2688         g_free (top2);
2689
2690         if (cmp != 0)
2691                 return cmp;
2692
2693         if (separator1 == NULL && separator2 == NULL)
2694                 return 0;
2695
2696         next1 = (separator1 != NULL)?separator1 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2697         next2 = (separator2 != NULL)?separator2 + strlen (MODEST_FOLDER_PATH_SEPARATOR):NULL;
2698
2699         return compare_folders (next1, next2);
2700 }
2701
2702
2703 /*
2704  * This function orders the mail accounts according to these rules:
2705  * 1st - remote accounts
2706  * 2nd - local account
2707  * 3rd - MMC account
2708  */
2709 static gint
2710 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2711           gpointer user_data)
2712 {
2713         gint cmp = 0;
2714         gchar *name1 = NULL;
2715         gchar *name2 = NULL;
2716         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2717         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2718         GObject *folder1 = NULL;
2719         GObject *folder2 = NULL;
2720
2721         gtk_tree_model_get (tree_model, iter1,
2722                             NAME_COLUMN, &name1,
2723                             TYPE_COLUMN, &type,
2724                             INSTANCE_COLUMN, &folder1,
2725                             -1);
2726         gtk_tree_model_get (tree_model, iter2,
2727                             NAME_COLUMN, &name2,
2728                             TYPE_COLUMN, &type2,
2729                             INSTANCE_COLUMN, &folder2,
2730                             -1);
2731
2732         /* Return if we get no folder. This could happen when folder
2733            operations are happening. The model is updated after the
2734            folder copy/move actually occurs, so there could be
2735            situations where the model to be drawn is not correct */
2736         if (!folder1 || !folder2)
2737                 goto finish;
2738
2739         /* Sort by type. First the special folders, then the archives */
2740         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2741         if (cmp != 0)
2742                 goto finish;
2743
2744         /* Now we sort using the account of each folder */
2745         if (TNY_IS_FOLDER_STORE (folder1) && 
2746             TNY_IS_FOLDER_STORE (folder2)) {
2747                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2748                 if (cmp != 0)
2749                         goto finish;
2750
2751                 /* Each group is preceeded by its account */
2752                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2753                 if (cmp != 0)
2754                         goto finish;
2755         }
2756
2757         /* Pure sort by name */
2758         cmp = compare_folders (name1, name2);
2759  finish:
2760         if (folder1)
2761                 g_object_unref(G_OBJECT(folder1));
2762         if (folder2)
2763                 g_object_unref(G_OBJECT(folder2));
2764
2765         g_free (name1);
2766         g_free (name2);
2767
2768         return cmp;
2769 }
2770
2771 /*****************************************************************************/
2772 /*                        DRAG and DROP stuff                                */
2773 /*****************************************************************************/
2774 /*
2775  * This function fills the #GtkSelectionData with the row and the
2776  * model that has been dragged. It's called when this widget is a
2777  * source for dnd after the event drop happened
2778  */
2779 static void
2780 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2781                   guint info, guint time, gpointer data)
2782 {
2783         GtkTreeSelection *selection;
2784         GtkTreeModel *model;
2785         GtkTreeIter iter;
2786         GtkTreePath *source_row;
2787
2788         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2789         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2790
2791                 source_row = gtk_tree_model_get_path (model, &iter);
2792                 gtk_tree_set_row_drag_data (selection_data,
2793                                             model,
2794                                             source_row);
2795
2796                 gtk_tree_path_free (source_row);
2797         }
2798 }
2799
2800 typedef struct _DndHelper {
2801         ModestFolderView *folder_view;
2802         gboolean delete_source;
2803         GtkTreePath *source_row;
2804 } DndHelper;
2805
2806 static void
2807 dnd_helper_destroyer (DndHelper *helper)
2808 {
2809         /* Free the helper */
2810         gtk_tree_path_free (helper->source_row);
2811         g_slice_free (DndHelper, helper);
2812 }
2813
2814 static void
2815 xfer_folder_cb (ModestMailOperation *mail_op,
2816                 TnyFolder *new_folder,
2817                 gpointer user_data)
2818 {
2819         if (new_folder) {
2820                 /* Select the folder */
2821                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2822                                                   new_folder, FALSE);
2823         }
2824 }
2825
2826
2827 /* get the folder for the row the treepath refers to. */
2828 /* folder must be unref'd */
2829 static TnyFolderStore *
2830 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2831 {
2832         GtkTreeIter iter;
2833         TnyFolderStore *folder = NULL;
2834
2835         if (gtk_tree_model_get_iter (model,&iter, path))
2836                 gtk_tree_model_get (model, &iter,
2837                                     INSTANCE_COLUMN, &folder,
2838                                     -1);
2839         return folder;
2840 }
2841
2842
2843 /*
2844  * This function is used by drag_data_received_cb to manage drag and
2845  * drop of a header, i.e, and drag from the header view to the folder
2846  * view.
2847  */
2848 static void
2849 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2850                                 GtkTreeModel *dest_model,
2851                                 GtkTreePath  *dest_row,
2852                                 GtkSelectionData *selection_data)
2853 {
2854         TnyList *headers = NULL;
2855         TnyFolder *folder = NULL, *src_folder = NULL;
2856         TnyFolderType folder_type;
2857         GtkTreeIter source_iter, dest_iter;
2858         ModestWindowMgr *mgr = NULL;
2859         ModestWindow *main_win = NULL;
2860         gchar **uris, **tmp;
2861
2862         /* Build the list of headers */
2863         mgr = modest_runtime_get_window_mgr ();
2864         headers = tny_simple_list_new ();
2865         uris = modest_dnd_selection_data_get_paths (selection_data);
2866         tmp = uris;
2867
2868         while (*tmp != NULL) {
2869                 TnyHeader *header;
2870                 GtkTreePath *path;
2871                 gboolean first = TRUE;
2872
2873                 /* Get header */
2874                 path = gtk_tree_path_new_from_string (*tmp);
2875                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2876                 gtk_tree_model_get (source_model, &source_iter,
2877                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2878                                     &header, -1);
2879
2880                 /* Do not enable d&d of headers already opened */
2881                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2882                         tny_list_append (headers, G_OBJECT (header));
2883
2884                 if (G_UNLIKELY (first)) {
2885                         src_folder = tny_header_get_folder (header);
2886                         first = FALSE;
2887                 }
2888
2889                 /* Free and go on */
2890                 gtk_tree_path_free (path);
2891                 g_object_unref (header);
2892                 tmp++;
2893         }
2894         g_strfreev (uris);
2895
2896         /* This could happen ig we perform a d&d very quickly over the
2897            same row that row could dissapear because message is
2898            transferred */
2899         if (!TNY_IS_FOLDER (src_folder))
2900                 goto cleanup;
2901
2902         /* Get the target folder */
2903         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2904         gtk_tree_model_get (dest_model, &dest_iter,
2905                             INSTANCE_COLUMN,
2906                             &folder, -1);
2907
2908         if (!folder || !TNY_IS_FOLDER(folder)) {
2909 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2910                 goto cleanup;
2911         }
2912
2913         folder_type = modest_tny_folder_guess_folder_type (folder);
2914         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2915 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2916                 goto cleanup;  /* cannot move messages there */
2917         }
2918
2919         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2920 /*              g_warning ("folder not writable"); */
2921                 goto cleanup; /* verboten! */
2922         }
2923
2924         /* Ask for confirmation to move */
2925         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2926         if (!main_win) {
2927                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2928                 goto cleanup;
2929         }
2930
2931         /* Transfer messages */
2932         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2933                                                     headers, folder);
2934
2935         /* Frees */
2936 cleanup:
2937         if (G_IS_OBJECT (src_folder))
2938                 g_object_unref (src_folder);
2939         if (G_IS_OBJECT(folder))
2940                 g_object_unref (G_OBJECT (folder));
2941         if (G_IS_OBJECT(headers))
2942                 g_object_unref (headers);
2943 }
2944
2945 typedef struct {
2946         TnyFolderStore *src_folder;
2947         TnyFolderStore *dst_folder;
2948         ModestFolderView *folder_view;
2949         DndHelper *helper;
2950 } DndFolderInfo;
2951
2952 static void
2953 dnd_folder_info_destroyer (DndFolderInfo *info)
2954 {
2955         if (info->src_folder)
2956                 g_object_unref (info->src_folder);
2957         if (info->dst_folder)
2958                 g_object_unref (info->dst_folder);
2959         g_slice_free (DndFolderInfo, info);
2960 }
2961
2962 static void
2963 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2964                                     GtkWindow *parent_window,
2965                                     TnyAccount *account)
2966 {
2967         /* Show error */
2968         modest_ui_actions_on_account_connection_error (parent_window, account);
2969
2970         /* Free the helper & info */
2971         dnd_helper_destroyer (info->helper);
2972         dnd_folder_info_destroyer (info);
2973 }
2974
2975 static void
2976 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2977                                                      GError *err,
2978                                                      GtkWindow *parent_window,
2979                                                      TnyAccount *account,
2980                                                      gpointer user_data)
2981 {
2982         DndFolderInfo *info = NULL;
2983         ModestMailOperation *mail_op;
2984
2985         info = (DndFolderInfo *) user_data;
2986
2987         if (err || canceled) {
2988                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2989                 return;
2990         }
2991
2992         /* Do the mail operation */
2993         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2994                                                                  modest_ui_actions_move_folder_error_handler,
2995                                                                  info->src_folder, NULL);
2996
2997         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2998                                          mail_op);
2999
3000         /* Transfer the folder */
3001         modest_mail_operation_xfer_folder (mail_op,
3002                                            TNY_FOLDER (info->src_folder),
3003                                            info->dst_folder,
3004                                            info->helper->delete_source,
3005                                            xfer_folder_cb,
3006                                            info->helper->folder_view);
3007
3008         /* Frees */
3009         g_object_unref (G_OBJECT (mail_op));
3010         dnd_helper_destroyer (info->helper);
3011         dnd_folder_info_destroyer (info);
3012 }
3013
3014
3015 static void
3016 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
3017                                                      GError *err,
3018                                                      GtkWindow *parent_window,
3019                                                      TnyAccount *account,
3020                                                      gpointer user_data)
3021 {
3022         DndFolderInfo *info = NULL;
3023
3024         info = (DndFolderInfo *) user_data;
3025
3026         if (err || canceled) {
3027                 dnd_on_connection_failed_destroyer (info, parent_window, account);
3028                 return;
3029         }
3030
3031         /* Connect to source folder and perform the copy/move */
3032         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
3033                                                        info->src_folder,
3034                                                        drag_and_drop_from_folder_view_src_folder_performer,
3035                                                        info);
3036 }
3037
3038 /*
3039  * This function is used by drag_data_received_cb to manage drag and
3040  * drop of a folder, i.e, and drag from the folder view to the same
3041  * folder view.
3042  */
3043 static void
3044 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
3045                                 GtkTreeModel     *dest_model,
3046                                 GtkTreePath      *dest_row,
3047                                 GtkSelectionData *selection_data,
3048                                 DndHelper        *helper)
3049 {
3050         GtkTreeIter dest_iter, iter;
3051         TnyFolderStore *dest_folder = NULL;
3052         TnyFolderStore *folder = NULL;
3053         gboolean forbidden = FALSE;
3054         ModestWindow *win;
3055         DndFolderInfo *info = NULL;
3056
3057         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3058         if (!win) {
3059                 g_warning ("%s: BUG: no main window", __FUNCTION__);
3060                 dnd_helper_destroyer (helper);
3061                 return;
3062         }
3063
3064         if (!forbidden) {
3065                 /* check the folder rules for the destination */
3066                 folder = tree_path_to_folder (dest_model, dest_row);
3067                 if (TNY_IS_FOLDER(folder)) {
3068                         ModestTnyFolderRules rules =
3069                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3070                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3071                 } else if (TNY_IS_FOLDER_STORE(folder)) {
3072                         /* enable local root as destination for folders */
3073                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3074                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3075                                 forbidden = TRUE;
3076                 }
3077                 g_object_unref (folder);
3078         }
3079         if (!forbidden) {
3080                 /* check the folder rules for the source */
3081                 folder = tree_path_to_folder (source_model, helper->source_row);
3082                 if (TNY_IS_FOLDER(folder)) {
3083                         ModestTnyFolderRules rules =
3084                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3085                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
3086                 } else
3087                         forbidden = TRUE;
3088                 g_object_unref (folder);
3089         }
3090
3091
3092         /* Check if the drag is possible */
3093         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3094                 /* Show error */
3095                 modest_platform_run_information_dialog ((GtkWindow *) win, 
3096                                                         _("mail_in_ui_folder_move_target_error"), 
3097                                                         FALSE);
3098                 /* Restore the previous selection */
3099                 folder = tree_path_to_folder (source_model, helper->source_row);
3100                 if (folder) {
3101                         if (TNY_IS_FOLDER (folder))
3102                                 modest_folder_view_select_folder (helper->folder_view, 
3103                                                                   TNY_FOLDER (folder), FALSE);
3104                         g_object_unref (folder);
3105                 }
3106                 dnd_helper_destroyer (helper);
3107                 return;
3108         }
3109
3110         /* Get data */
3111         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3112         gtk_tree_model_get (dest_model, &dest_iter,
3113                             INSTANCE_COLUMN,
3114                             &dest_folder, -1);
3115         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3116         gtk_tree_model_get (source_model, &iter,
3117                             INSTANCE_COLUMN,
3118                             &folder, -1);
3119
3120         /* Create the info for the performer */
3121         info = g_slice_new0 (DndFolderInfo);
3122         info->src_folder = g_object_ref (folder);
3123         info->dst_folder = g_object_ref (dest_folder);
3124         info->helper = helper;
3125
3126         /* Connect to the destination folder and perform the copy/move */
3127         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3128                                                        dest_folder,
3129                                                        drag_and_drop_from_folder_view_dst_folder_performer,
3130                                                        info);
3131
3132         /* Frees */
3133         g_object_unref (dest_folder);
3134         g_object_unref (folder);
3135 }
3136
3137 /*
3138  * This function receives the data set by the "drag-data-get" signal
3139  * handler. This information comes within the #GtkSelectionData. This
3140  * function will manage both the drags of folders of the treeview and
3141  * drags of headers of the header view widget.
3142  */
3143 static void
3144 on_drag_data_received (GtkWidget *widget,
3145                        GdkDragContext *context,
3146                        gint x,
3147                        gint y,
3148                        GtkSelectionData *selection_data,
3149                        guint target_type,
3150                        guint time,
3151                        gpointer data)
3152 {
3153         GtkWidget *source_widget;
3154         GtkTreeModel *dest_model, *source_model;
3155         GtkTreePath *source_row, *dest_row;
3156         GtkTreeViewDropPosition pos;
3157         gboolean delete_source = FALSE;
3158         gboolean success = FALSE;
3159
3160         /* Do not allow further process */
3161         g_signal_stop_emission_by_name (widget, "drag-data-received");
3162         source_widget = gtk_drag_get_source_widget (context);
3163
3164         /* Get the action */
3165         if (context->action == GDK_ACTION_MOVE) {
3166                 delete_source = TRUE;
3167
3168                 /* Notify that there is no folder selected. We need to
3169                    do this in order to update the headers view (and
3170                    its monitors, because when moving, the old folder
3171                    won't longer exist. We can not wait for the end of
3172                    the operation, because the operation won't start if
3173                    the folder is in use */
3174                 if (source_widget == widget) {
3175                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3176                         gtk_tree_selection_unselect_all (sel);
3177                 }
3178         }
3179
3180         /* Check if the get_data failed */
3181         if (selection_data == NULL || selection_data->length < 0)
3182                 goto end;
3183
3184         /* Select the destination model */
3185         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3186
3187         /* Get the path to the destination row. Can not call
3188            gtk_tree_view_get_drag_dest_row() because the source row
3189            is not selected anymore */
3190         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3191                                            &dest_row, &pos);
3192
3193         /* Only allow drops IN other rows */
3194         if (!dest_row ||
3195             pos == GTK_TREE_VIEW_DROP_BEFORE ||
3196             pos == GTK_TREE_VIEW_DROP_AFTER)
3197                 goto end;
3198
3199         success = TRUE;
3200         /* Drags from the header view */
3201         if (source_widget != widget) {
3202                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3203
3204                 drag_and_drop_from_header_view (source_model,
3205                                                 dest_model,
3206                                                 dest_row,
3207                                                 selection_data);
3208         } else {
3209                 DndHelper *helper = NULL;
3210
3211                 /* Get the source model and row */
3212                 gtk_tree_get_row_drag_data (selection_data,
3213                                             &source_model,
3214                                             &source_row);
3215
3216                 /* Create the helper */
3217                 helper = g_slice_new0 (DndHelper);
3218                 helper->delete_source = delete_source;
3219                 helper->source_row = gtk_tree_path_copy (source_row);
3220                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3221
3222                 drag_and_drop_from_folder_view (source_model,
3223                                                 dest_model,
3224                                                 dest_row,
3225                                                 selection_data,
3226                                                 helper);
3227
3228                 gtk_tree_path_free (source_row);
3229         }
3230
3231         /* Frees */
3232         gtk_tree_path_free (dest_row);
3233
3234  end:
3235         /* Finish the drag and drop */
3236         gtk_drag_finish (context, success, FALSE, time);
3237 }
3238
3239 /*
3240  * We define a "drag-drop" signal handler because we do not want to
3241  * use the default one, because the default one always calls
3242  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3243  * signal handler, because there we have all the information available
3244  * to know if the dnd was a success or not.
3245  */
3246 static gboolean
3247 drag_drop_cb (GtkWidget      *widget,
3248               GdkDragContext *context,
3249               gint            x,
3250               gint            y,
3251               guint           time,
3252               gpointer        user_data)
3253 {
3254         gpointer target;
3255
3256         if (!context->targets)
3257                 return FALSE;
3258
3259         /* Check if we're dragging a folder row */
3260         target = gtk_drag_dest_find_target (widget, context, NULL);
3261
3262         /* Request the data from the source. */
3263         gtk_drag_get_data(widget, context, target, time);
3264
3265     return TRUE;
3266 }
3267
3268 /*
3269  * This function expands a node of a tree view if it's not expanded
3270  * yet. Not sure why it needs the threads stuff, but gtk+`example code
3271  * does that, so that's why they're here.
3272  */
3273 static gint
3274 expand_row_timeout (gpointer data)
3275 {
3276         GtkTreeView *tree_view = data;
3277         GtkTreePath *dest_path = NULL;
3278         GtkTreeViewDropPosition pos;
3279         gboolean result = FALSE;
3280
3281         gdk_threads_enter ();
3282
3283         gtk_tree_view_get_drag_dest_row (tree_view,
3284                                          &dest_path,
3285                                          &pos);
3286
3287         if (dest_path &&
3288             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3289              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3290                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3291                 gtk_tree_path_free (dest_path);
3292         }
3293         else {
3294                 if (dest_path)
3295                         gtk_tree_path_free (dest_path);
3296
3297                 result = TRUE;
3298         }
3299
3300         gdk_threads_leave ();
3301
3302         return result;
3303 }
3304
3305 /*
3306  * This function is called whenever the pointer is moved over a widget
3307  * while dragging some data. It installs a timeout that will expand a
3308  * node of the treeview if not expanded yet. This function also calls
3309  * gdk_drag_status in order to set the suggested action that will be
3310  * used by the "drag-data-received" signal handler to know if we
3311  * should do a move or just a copy of the data.
3312  */
3313 static gboolean
3314 on_drag_motion (GtkWidget      *widget,
3315                 GdkDragContext *context,
3316                 gint            x,
3317                 gint            y,
3318                 guint           time,
3319                 gpointer        user_data)
3320 {
3321         GtkTreeViewDropPosition pos;
3322         GtkTreePath *dest_row;
3323         GtkTreeModel *dest_model;
3324         ModestFolderViewPrivate *priv;
3325         GdkDragAction suggested_action;
3326         gboolean valid_location = FALSE;
3327         TnyFolderStore *folder = NULL;
3328
3329         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3330
3331         if (priv->timer_expander != 0) {
3332                 g_source_remove (priv->timer_expander);
3333                 priv->timer_expander = 0;
3334         }
3335
3336         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3337                                            x, y,
3338                                            &dest_row,
3339                                            &pos);
3340
3341         /* Do not allow drops between folders */
3342         if (!dest_row ||
3343             pos == GTK_TREE_VIEW_DROP_BEFORE ||
3344             pos == GTK_TREE_VIEW_DROP_AFTER) {
3345                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3346                 gdk_drag_status(context, 0, time);
3347                 valid_location = FALSE;
3348                 goto out;
3349         } else {
3350                 valid_location = TRUE;
3351         }
3352
3353         /* Check that the destination folder is writable */
3354         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3355         folder = tree_path_to_folder (dest_model, dest_row);
3356         if (folder && TNY_IS_FOLDER (folder)) {
3357                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3358
3359                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3360                         valid_location = FALSE;
3361                         goto out;
3362                 }
3363         }
3364
3365         /* Expand the selected row after 1/2 second */
3366         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3367                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3368         }
3369         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3370
3371         /* Select the desired action. By default we pick MOVE */
3372         suggested_action = GDK_ACTION_MOVE;
3373
3374         if (context->actions == GDK_ACTION_COPY)
3375             gdk_drag_status(context, GDK_ACTION_COPY, time);
3376         else if (context->actions == GDK_ACTION_MOVE)
3377             gdk_drag_status(context, GDK_ACTION_MOVE, time);
3378         else if (context->actions & suggested_action)
3379             gdk_drag_status(context, suggested_action, time);
3380         else
3381             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3382
3383  out:
3384         if (folder)
3385                 g_object_unref (folder);
3386         if (dest_row) {
3387                 gtk_tree_path_free (dest_row);
3388         }
3389         g_signal_stop_emission_by_name (widget, "drag-motion");
3390
3391         return valid_location;
3392 }
3393
3394 /*
3395  * This function sets the treeview as a source and a target for dnd
3396  * events. It also connects all the requirede signals.
3397  */
3398 static void
3399 setup_drag_and_drop (GtkTreeView *self)
3400 {
3401         /* Set up the folder view as a dnd destination. Set only the
3402            highlight flag, otherwise gtk will have a different
3403            behaviour */
3404 #ifdef MODEST_TOOLKIT_HILDON2
3405         return;
3406 #endif
3407         ModestFolderViewPrivate *priv;
3408
3409         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3410
3411         gtk_drag_dest_set (GTK_WIDGET (self),
3412                            GTK_DEST_DEFAULT_HIGHLIGHT,
3413                            folder_view_drag_types,
3414                            G_N_ELEMENTS (folder_view_drag_types),
3415                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
3416
3417         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3418                                                            G_OBJECT (self), "drag_data_received",
3419                                                            G_CALLBACK (on_drag_data_received), NULL);
3420
3421
3422         /* Set up the treeview as a dnd source */
3423         gtk_drag_source_set (GTK_WIDGET (self),
3424                              GDK_BUTTON1_MASK,
3425                              folder_view_drag_types,
3426                              G_N_ELEMENTS (folder_view_drag_types),
3427                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
3428
3429         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3430                                                            G_OBJECT (self), "drag_motion",
3431                                                            G_CALLBACK (on_drag_motion), NULL);
3432
3433         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3434                                                            G_OBJECT (self), "drag_data_get",
3435                                                            G_CALLBACK (on_drag_data_get), NULL);
3436
3437         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3438                                                            G_OBJECT (self), "drag_drop",
3439                                                            G_CALLBACK (drag_drop_cb), NULL);
3440 }
3441
3442 /*
3443  * This function manages the navigation through the folders using the
3444  * keyboard or the hardware keys in the device
3445  */
3446 static gboolean
3447 on_key_pressed (GtkWidget *self,
3448                 GdkEventKey *event,
3449                 gpointer user_data)
3450 {
3451         GtkTreeSelection *selection;
3452         GtkTreeIter iter;
3453         GtkTreeModel *model;
3454         gboolean retval = FALSE;
3455
3456         /* Up and Down are automatically managed by the treeview */
3457         if (event->keyval == GDK_Return) {
3458                 /* Expand/Collapse the selected row */
3459                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3460                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3461                         GtkTreePath *path;
3462
3463                         path = gtk_tree_model_get_path (model, &iter);
3464
3465                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3466                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3467                         else
3468                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3469                         gtk_tree_path_free (path);
3470                 }
3471                 /* No further processing */
3472                 retval = TRUE;
3473         }
3474
3475         return retval;
3476 }
3477
3478 /*
3479  * We listen to the changes in the local folder account name key,
3480  * because we want to show the right name in the view. The local
3481  * folder account name corresponds to the device name in the Maemo
3482  * version. We do this because we do not want to query gconf on each
3483  * tree view refresh. It's better to cache it and change whenever
3484  * necessary.
3485  */
3486 static void
3487 on_configuration_key_changed (ModestConf* conf,
3488                               const gchar *key,
3489                               ModestConfEvent event,
3490                               ModestConfNotificationId id,
3491                               ModestFolderView *self)
3492 {
3493         ModestFolderViewPrivate *priv;
3494
3495
3496         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3497         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3498
3499         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3500                 g_free (priv->local_account_name);
3501
3502                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3503                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3504                 else
3505                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3506                                                                            MODEST_CONF_DEVICE_NAME, NULL);
3507
3508                 /* Force a redraw */
3509 #if GTK_CHECK_VERSION(2, 8, 0)
3510                 GtkTreeViewColumn * tree_column;
3511
3512                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3513                                                         NAME_COLUMN);
3514                 gtk_tree_view_column_queue_resize (tree_column);
3515 #else
3516                 gtk_widget_queue_draw (GTK_WIDGET (self));
3517 #endif
3518         }
3519 }
3520
3521 void
3522 modest_folder_view_set_style (ModestFolderView *self,
3523                               ModestFolderViewStyle style)
3524 {
3525         ModestFolderViewPrivate *priv;
3526
3527         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3528         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3529                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3530
3531         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3532
3533
3534         priv->style = style;
3535 }
3536
3537 void
3538 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3539                                                              const gchar *account_id)
3540 {
3541         ModestFolderViewPrivate *priv;
3542         GtkTreeModel *model;
3543
3544         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3545
3546         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3547
3548         /* This will be used by the filter_row callback,
3549          * to decided which rows to show: */
3550         if (priv->visible_account_id) {
3551                 g_free (priv->visible_account_id);
3552                 priv->visible_account_id = NULL;
3553         }
3554         if (account_id)
3555                 priv->visible_account_id = g_strdup (account_id);
3556
3557         /* Refilter */
3558         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3559         if (GTK_IS_TREE_MODEL_FILTER (model))
3560                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3561
3562         /* Save settings to gconf */
3563         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3564                                    MODEST_CONF_FOLDER_VIEW_KEY);
3565
3566         /* Notify observers */
3567         g_signal_emit (G_OBJECT(self),
3568                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3569                        account_id);
3570 }
3571
3572 const gchar *
3573 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3574 {
3575         ModestFolderViewPrivate *priv;
3576
3577         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3578
3579         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3580
3581         return (const gchar *) priv->visible_account_id;
3582 }
3583
3584 static gboolean
3585 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3586 {
3587         do {
3588                 GtkTreeIter child;
3589                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3590
3591                 gtk_tree_model_get (model, iter,
3592                                     TYPE_COLUMN,
3593                                     &type, -1);
3594
3595                 gboolean result = FALSE;
3596                 if (type == TNY_FOLDER_TYPE_INBOX) {
3597                         result = TRUE;
3598                 }
3599                 if (result) {
3600                         *inbox_iter = *iter;
3601                         return TRUE;
3602                 }
3603
3604                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3605                         if (find_inbox_iter (model, &child, inbox_iter))
3606                                 return TRUE;
3607                 }
3608
3609         } while (gtk_tree_model_iter_next (model, iter));
3610
3611         return FALSE;
3612 }
3613
3614
3615
3616
3617 void
3618 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3619 {
3620 #ifndef MODEST_TOOLKIT_HILDON2
3621         GtkTreeModel *model;
3622         GtkTreeIter iter, inbox_iter;
3623         GtkTreeSelection *sel;
3624         GtkTreePath *path = NULL;
3625
3626         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3627
3628         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3629         if (!model)
3630                 return;
3631
3632         expand_root_items (self);
3633         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3634
3635         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3636                 g_warning ("%s: model is empty", __FUNCTION__);
3637                 return;
3638         }
3639
3640         if (find_inbox_iter (model, &iter, &inbox_iter))
3641                 path = gtk_tree_model_get_path (model, &inbox_iter);
3642         else
3643                 path = gtk_tree_path_new_first ();
3644
3645         /* Select the row and free */
3646         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3647         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3648         gtk_tree_path_free (path);
3649
3650         /* set focus */
3651         gtk_widget_grab_focus (GTK_WIDGET(self));
3652 #endif
3653 }
3654
3655
3656 /* recursive */
3657 static gboolean
3658 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3659                   TnyFolder* folder)
3660 {
3661         do {
3662                 GtkTreeIter child;
3663                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3664                 TnyFolder* a_folder;
3665                 gchar *name = NULL;
3666
3667                 gtk_tree_model_get (model, iter,
3668                                     INSTANCE_COLUMN, &a_folder,
3669                                     NAME_COLUMN, &name,
3670                                     TYPE_COLUMN, &type,
3671                                     -1);
3672                 g_free (name);
3673
3674                 if (folder == a_folder) {
3675                         g_object_unref (a_folder);
3676                         *folder_iter = *iter;
3677                         return TRUE;
3678                 }
3679                 g_object_unref (a_folder);
3680
3681                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3682                         if (find_folder_iter (model, &child, folder_iter, folder))
3683                                 return TRUE;
3684                 }
3685
3686         } while (gtk_tree_model_iter_next (model, iter));
3687
3688         return FALSE;
3689 }
3690
3691 #ifndef MODEST_TOOLKIT_HILDON2
3692 static void
3693 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3694                                      GtkTreePath *path,
3695                                      GtkTreeIter *iter,
3696                                      ModestFolderView *self)
3697 {
3698         ModestFolderViewPrivate *priv = NULL;
3699         GtkTreeSelection *sel;
3700         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3701         GObject *instance = NULL;
3702
3703         if (!MODEST_IS_FOLDER_VIEW(self))
3704                 return;
3705
3706         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3707
3708         priv->reexpand = TRUE;
3709
3710         gtk_tree_model_get (tree_model, iter,
3711                             TYPE_COLUMN, &type,
3712                             INSTANCE_COLUMN, &instance,
3713                             -1);
3714
3715         if (!instance)
3716                 return;
3717
3718         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3719                 priv->folder_to_select = g_object_ref (instance);
3720         }
3721         g_object_unref (instance);
3722
3723         if (priv->folder_to_select) {
3724
3725                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3726                                                        FALSE)) {
3727                         GtkTreePath *path;
3728                         path = gtk_tree_model_get_path (tree_model, iter);
3729                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3730
3731                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3732
3733                         gtk_tree_selection_select_iter (sel, iter);
3734                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3735
3736                         gtk_tree_path_free (path);
3737                 }
3738
3739                 /* Disable next */
3740                 modest_folder_view_disable_next_folder_selection (self);
3741
3742                 /* Refilter the model */
3743                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3744         }
3745 }
3746 #endif
3747
3748 void
3749 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3750 {
3751         ModestFolderViewPrivate *priv;
3752
3753         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3754
3755         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3756
3757         if (priv->folder_to_select)
3758                 g_object_unref(priv->folder_to_select);
3759
3760         priv->folder_to_select = NULL;
3761 }
3762
3763 gboolean
3764 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3765                                   gboolean after_change)
3766 {
3767         GtkTreeModel *model;
3768         GtkTreeIter iter, folder_iter;
3769         GtkTreeSelection *sel;
3770         ModestFolderViewPrivate *priv = NULL;
3771
3772         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3773         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3774
3775         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3776
3777         if (after_change) {
3778                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3779                 gtk_tree_selection_unselect_all (sel);
3780
3781                 if (priv->folder_to_select)
3782                         g_object_unref(priv->folder_to_select);
3783                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3784                 return TRUE;
3785         }
3786
3787         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3788         if (!model)
3789                 return FALSE;
3790
3791
3792         /* Refilter the model, before selecting the folder */
3793         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3794
3795         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3796                 g_warning ("%s: model is empty", __FUNCTION__);
3797                 return FALSE;
3798         }
3799
3800         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3801                 GtkTreePath *path;
3802
3803                 path = gtk_tree_model_get_path (model, &folder_iter);
3804                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3805
3806                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3807                 gtk_tree_selection_select_iter (sel, &folder_iter);
3808                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3809
3810                 gtk_tree_path_free (path);
3811                 return TRUE;
3812         }
3813         return FALSE;
3814 }
3815
3816
3817 void
3818 modest_folder_view_copy_selection (ModestFolderView *self)
3819 {
3820         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3821
3822         /* Copy selection */
3823         _clipboard_set_selected_data (self, FALSE);
3824 }
3825
3826 void
3827 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3828 {
3829         ModestFolderViewPrivate *priv = NULL;
3830         GtkTreeModel *model = NULL;
3831         const gchar **hidding = NULL;
3832         guint i, n_selected;
3833
3834         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3835         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3836
3837         /* Copy selection */
3838         if (!_clipboard_set_selected_data (folder_view, TRUE))
3839                 return;
3840
3841         /* Get hidding ids */
3842         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3843
3844         /* Clear hidding array created by previous cut operation */
3845         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3846
3847         /* Copy hidding array */
3848         priv->n_selected = n_selected;
3849         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3850         for (i=0; i < n_selected; i++)
3851                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3852
3853         /* Hide cut folders */
3854         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3855         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3856 }
3857
3858 void
3859 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3860                                ModestFolderView *folder_view_dst)
3861 {
3862         GtkTreeModel *filter_model = NULL;
3863         GtkTreeModel *model = NULL;
3864         GtkTreeModel *new_filter_model = NULL;
3865         GtkTreeModel *old_tny_model = NULL;
3866         GtkTreeModel *new_tny_model = NULL;
3867         ModestFolderViewPrivate *dst_priv;
3868
3869         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3870         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3871
3872         dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3873         if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3874                 new_tny_model = NULL;
3875
3876         /* Get src model*/
3877         if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3878                 modest_signal_mgr_disconnect (dst_priv->signal_handlers,
3879                                               G_OBJECT (old_tny_model),
3880                                               "activity-changed");
3881         }
3882         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3883         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3884
3885         /* Build new filter model */
3886         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3887         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3888                                                 filter_row,
3889                                                 folder_view_dst,
3890                                                 NULL);
3891
3892
3893
3894         /* Set copied model */
3895         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3896 #ifndef MODEST_TOOLKIT_HILDON2
3897         priv->signal_handlers = modest_signal_mgr_connect (priv->signal_handlers,
3898                                                            G_OBJECT(new_filter_model), "row-inserted",
3899                                                            (GCallback) on_row_inserted_maybe_select_folder,
3900                                                            folder_view_dst);
3901 #endif
3902 #ifdef MODEST_TOOLKIT_HILDON2
3903         if (new_tny_model) {
3904                 dst_priv->signal_handlers = modest_signal_mgr_connect (dst_priv->signal_handlers,
3905                                                                        G_OBJECT (new_tny_model),
3906                                                                        "activity-changed",
3907                                                                        G_CALLBACK (on_activity_changed),
3908                                                                        folder_view_dst);
3909         }
3910 #endif
3911
3912         /* Free */
3913         g_object_unref (new_filter_model);
3914 }
3915
3916 void
3917 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3918                                           gboolean show)
3919 {
3920         GtkTreeModel *model = NULL;
3921         ModestFolderViewPrivate* priv;
3922
3923         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3924
3925         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3926         priv->show_non_move = show;
3927 /*      modest_folder_view_update_model(folder_view, */
3928 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3929
3930         /* Hide special folders */
3931         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3932         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3933                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3934         }
3935 }
3936
3937 void
3938 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3939                                           gboolean show)
3940 {
3941         ModestFolderViewPrivate* priv;
3942
3943         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3944
3945         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3946         priv->show_message_count = show;
3947
3948         g_object_set (G_OBJECT (priv->messages_renderer),
3949                       "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3950                       NULL);
3951 }
3952
3953 /* Returns FALSE if it did not selected anything */
3954 static gboolean
3955 _clipboard_set_selected_data (ModestFolderView *folder_view,
3956                               gboolean delete)
3957 {
3958         ModestFolderViewPrivate *priv = NULL;
3959         TnyFolderStore *folder = NULL;
3960         gboolean retval = FALSE;
3961
3962         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3963         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3964
3965         /* Set selected data on clipboard   */
3966         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3967         folder = modest_folder_view_get_selected (folder_view);
3968
3969         /* Do not allow to select an account */
3970         if (TNY_IS_FOLDER (folder)) {
3971                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3972                 retval = TRUE;
3973         }
3974
3975         /* Free */
3976         g_object_unref (folder);
3977
3978         return retval;
3979 }
3980
3981 static void
3982 _clear_hidding_filter (ModestFolderView *folder_view)
3983 {
3984         ModestFolderViewPrivate *priv;
3985         guint i;
3986
3987         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3988         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3989
3990         if (priv->hidding_ids != NULL) {
3991                 for (i=0; i < priv->n_selected; i++)
3992                         g_free (priv->hidding_ids[i]);
3993                 g_free(priv->hidding_ids);
3994         }
3995 }
3996
3997
3998 static void
3999 on_display_name_changed (ModestAccountMgr *mgr,
4000                          const gchar *account,
4001                          gpointer user_data)
4002 {
4003         ModestFolderView *self;
4004
4005         self = MODEST_FOLDER_VIEW (user_data);
4006
4007         /* Force a redraw */
4008 #if GTK_CHECK_VERSION(2, 8, 0)
4009         GtkTreeViewColumn * tree_column;
4010
4011         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
4012                                                 NAME_COLUMN);
4013         gtk_tree_view_column_queue_resize (tree_column);
4014 #else
4015         gtk_widget_queue_draw (GTK_WIDGET (self));
4016 #endif
4017 }
4018
4019 void 
4020 modest_folder_view_set_cell_style (ModestFolderView *self,
4021                                    ModestFolderViewCellStyle cell_style)
4022 {
4023         ModestFolderViewPrivate *priv = NULL;
4024
4025         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4026         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4027
4028         priv->cell_style = cell_style;
4029
4030         g_object_set (G_OBJECT (priv->messages_renderer),
4031                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
4032                       NULL);
4033         
4034         gtk_widget_queue_draw (GTK_WIDGET (self));
4035 }
4036
4037 static void
4038 update_style (ModestFolderView *self)
4039 {
4040         ModestFolderViewPrivate *priv;
4041         GdkColor style_color, style_active_color;
4042         PangoAttrList *attr_list;
4043         GtkStyle *style;
4044         PangoAttribute *attr;
4045
4046         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4047         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4048
4049         /* Set color */
4050
4051         attr_list = pango_attr_list_new ();
4052         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
4053                 gdk_color_parse ("grey", &style_color);
4054         }
4055         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4056         pango_attr_list_insert (attr_list, attr);
4057         
4058         /* set font */
4059         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4060                                            (GTK_WIDGET(self)),
4061                                            "SmallSystemFont", NULL,
4062                                            G_TYPE_NONE);
4063         if (style) {
4064                 attr = pango_attr_font_desc_new (pango_font_description_copy
4065                                                  (style->font_desc));
4066                 pango_attr_list_insert (attr_list, attr);
4067
4068                 g_object_set (G_OBJECT (priv->messages_renderer),
4069                               "foreground-gdk", &style_color,
4070                               "foreground-set", TRUE,
4071                               "attributes", attr_list,
4072                               NULL);
4073                 pango_attr_list_unref (attr_list);
4074         }
4075         if (priv->active_color)
4076                 gdk_color_free (priv->active_color);
4077
4078         if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4079                 priv->active_color = gdk_color_copy (&style_active_color);
4080         }
4081 }
4082
4083 static void 
4084 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4085 {
4086         if (strcmp ("style", spec->name) == 0) {
4087                 update_style (MODEST_FOLDER_VIEW (obj));
4088                 gtk_widget_queue_draw (GTK_WIDGET (obj));
4089         } 
4090 }
4091
4092 void 
4093 modest_folder_view_set_filter (ModestFolderView *self,
4094                                ModestFolderViewFilter filter)
4095 {
4096         ModestFolderViewPrivate *priv;
4097         GtkTreeModel *filter_model;
4098
4099         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4100         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4101
4102         priv->filter |= filter;
4103
4104         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4105         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4106                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
4107         }
4108 }
4109
4110 void 
4111 modest_folder_view_unset_filter (ModestFolderView *self,
4112                                  ModestFolderViewFilter filter)
4113 {
4114         ModestFolderViewPrivate *priv;
4115         GtkTreeModel *filter_model;
4116
4117         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4118         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4119
4120         priv->filter &= ~filter;
4121
4122         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4123         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4124                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
4125         }
4126 }
4127
4128 gboolean
4129 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4130                                              ModestTnyFolderRules rules)
4131 {
4132         GtkTreeModel *filter_model;
4133         GtkTreeIter iter;
4134         gboolean fulfil = FALSE;
4135
4136         if (!get_inner_models (self, &filter_model, NULL, NULL))
4137                 return FALSE;
4138
4139         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4140                 return FALSE;
4141
4142         do {
4143                 TnyFolderStore *folder;
4144
4145                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4146                 if (folder) {
4147                         if (TNY_IS_FOLDER (folder)) {
4148                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4149                                 /* Folder rules are negative: non_writable, non_deletable... */
4150                                 if (!(folder_rules & rules))
4151                                         fulfil = TRUE;
4152                         }
4153                         g_object_unref (folder);
4154                 }
4155
4156         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4157
4158         return fulfil;
4159 }
4160
4161 void 
4162 modest_folder_view_set_list_to_move (ModestFolderView *self,
4163                                      TnyList *list)
4164 {
4165         ModestFolderViewPrivate *priv;
4166
4167         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4168         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4169
4170         if (priv->list_to_move)
4171                 g_object_unref (priv->list_to_move);
4172
4173         if (list)
4174                 g_object_ref (list);
4175
4176         priv->list_to_move = list;
4177 }
4178
4179 void
4180 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4181 {
4182         ModestFolderViewPrivate *priv;
4183
4184         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4185         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4186
4187         if (priv->mailbox)
4188                 g_free (priv->mailbox);
4189
4190         priv->mailbox = g_strdup (mailbox);
4191
4192         /* Notify observers */
4193         g_signal_emit (G_OBJECT(self),
4194                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4195                        priv->visible_account_id);
4196 }
4197
4198 const gchar *
4199 modest_folder_view_get_mailbox (ModestFolderView *self)
4200 {
4201         ModestFolderViewPrivate *priv;
4202
4203         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4204         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4205
4206         return (const gchar *) priv->mailbox;
4207 }
4208
4209 gboolean 
4210 modest_folder_view_get_activity (ModestFolderView *self)
4211 {
4212         ModestFolderViewPrivate *priv;
4213         GtkTreeModel *inner_model;
4214
4215         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4216         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4217         g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4218
4219         if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4220                 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4221         } else {
4222                 return FALSE;
4223         }
4224 }
4225
4226 #ifdef MODEST_TOOLKIT_HILDON2
4227 static void
4228 on_activity_changed (TnyGtkFolderListStore *store,
4229                      gboolean activity,
4230                      ModestFolderView *folder_view)
4231 {
4232         ModestFolderViewPrivate *priv;
4233
4234         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4235         g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4236         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4237
4238         g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
4239                        activity);
4240 }
4241 #endif