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