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