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