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