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