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