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