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