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