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