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