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