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