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