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