8ce851d7a52ccf06256255fe1df1cfe5dee81248
[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         gtk_tree_view_column_pack_start (column, renderer, FALSE);
1066         gtk_tree_view_column_set_cell_data_func(column, renderer,
1067                                                 icon_cell_data, treeview, NULL);
1068
1069         renderer = gtk_cell_renderer_text_new();
1070         g_object_set (renderer, 
1071 #ifdef MODEST_TOOLKIT_HILDON2
1072                       "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1073 #else
1074                       "ellipsize", PANGO_ELLIPSIZE_END,
1075 #endif
1076                       "ellipsize-set", TRUE, NULL);
1077         gtk_tree_view_column_pack_start (column, renderer, TRUE);
1078         gtk_tree_view_column_set_cell_data_func(column, renderer,
1079                                                 text_cell_data, treeview, NULL);
1080
1081         priv->messages_renderer = gtk_cell_renderer_text_new ();
1082         g_object_set (priv->messages_renderer, 
1083 #ifndef MODEST_TOOLKIT_HILDON2
1084                       "scale", PANGO_SCALE_X_SMALL,
1085                       "scale-set", TRUE,
1086 #endif
1087                       "alignment", PANGO_ALIGN_RIGHT,
1088                       "align-set", TRUE,
1089                       "xalign", 1.0,
1090                       NULL);
1091         gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1092         gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1093                                                 messages_cell_data, treeview, NULL);
1094
1095         /* Set selection mode */
1096         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1097         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1098
1099         /* Set treeview appearance */
1100         gtk_tree_view_column_set_spacing (column, 2);
1101         gtk_tree_view_column_set_resizable (column, TRUE);
1102         gtk_tree_view_column_set_fixed_width (column, TRUE);
1103         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1104         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1105
1106         /* Add column */
1107         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1108 }
1109
1110 static void
1111 modest_folder_view_init (ModestFolderView *obj)
1112 {
1113         ModestFolderViewPrivate *priv;
1114         ModestConf *conf;
1115
1116         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1117
1118         priv->timer_expander = 0;
1119         priv->account_store  = NULL;
1120         priv->query          = NULL;
1121         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1122         priv->cur_folder_store   = NULL;
1123         priv->visible_account_id = NULL;
1124         priv->folder_to_select = NULL;
1125         priv->outbox_deleted_handler = 0;
1126         priv->reexpand = TRUE;
1127
1128         /* Initialize the local account name */
1129         conf = modest_runtime_get_conf();
1130         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1131
1132         /* Init email clipboard */
1133         priv->clipboard = modest_runtime_get_email_clipboard ();
1134         priv->hidding_ids = NULL;
1135         priv->n_selected = 0;
1136         priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1137         priv->reselect = FALSE;
1138         priv->show_non_move = TRUE;
1139
1140         /* Build treeview */
1141         add_columns (GTK_WIDGET (obj));
1142
1143         /* Setup drag and drop */
1144         setup_drag_and_drop (GTK_TREE_VIEW(obj));
1145
1146         /* Connect signals */
1147         g_signal_connect (G_OBJECT (obj),
1148                           "key-press-event",
1149                           G_CALLBACK (on_key_pressed), NULL);
1150
1151         priv->display_name_changed_signal =
1152                 g_signal_connect (modest_runtime_get_account_mgr (),
1153                                   "display_name_changed",
1154                                   G_CALLBACK (on_display_name_changed),
1155                                   obj);
1156
1157         /*
1158          * Track changes in the local account name (in the device it
1159          * will be the device name)
1160          */
1161         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1162                                                   "key_changed",
1163                                                   G_CALLBACK(on_configuration_key_changed),
1164                                                   obj);
1165
1166         update_style (obj);
1167         g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1168
1169
1170 }
1171
1172 static void
1173 tny_account_store_view_init (gpointer g, gpointer iface_data)
1174 {
1175         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1176
1177         klass->set_account_store = modest_folder_view_set_account_store;
1178 }
1179
1180 static void
1181 modest_folder_view_finalize (GObject *obj)
1182 {
1183         ModestFolderViewPrivate *priv;
1184         GtkTreeSelection    *sel;
1185         TnyAccount *local_account;
1186
1187         g_return_if_fail (obj);
1188
1189         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1190
1191         if (priv->timer_expander != 0) {
1192                 g_source_remove (priv->timer_expander);
1193                 priv->timer_expander = 0;
1194         }
1195
1196         local_account = (TnyAccount *)
1197                 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1198         if (local_account) {
1199                 if (g_signal_handler_is_connected (local_account,
1200                                                    priv->outbox_deleted_handler))
1201                         g_signal_handler_disconnect (local_account,
1202                                                      priv->outbox_deleted_handler);
1203                 g_object_unref (local_account);
1204         }
1205
1206         if (priv->account_store) {
1207                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1208                                              priv->account_inserted_signal);
1209                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1210                                              priv->account_removed_signal);
1211                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1212                                              priv->account_changed_signal);
1213                 g_object_unref (G_OBJECT(priv->account_store));
1214                 priv->account_store = NULL;
1215         }
1216
1217         if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (), 
1218                                            priv->display_name_changed_signal)) {
1219                 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1220                                              priv->display_name_changed_signal);
1221                 priv->display_name_changed_signal = 0;
1222         }
1223
1224         if (priv->query) {
1225                 g_object_unref (G_OBJECT (priv->query));
1226                 priv->query = NULL;
1227         }
1228
1229         if (priv->folder_to_select) {
1230                 g_object_unref (G_OBJECT(priv->folder_to_select));
1231                 priv->folder_to_select = NULL;
1232         }
1233
1234         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1235         if (sel)
1236                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1237
1238         g_free (priv->local_account_name);
1239         g_free (priv->visible_account_id);
1240
1241         if (priv->conf_key_signal) {
1242                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1243                                              priv->conf_key_signal);
1244                 priv->conf_key_signal = 0;
1245         }
1246
1247         if (priv->cur_folder_store) {
1248                 g_object_unref (priv->cur_folder_store);
1249                 priv->cur_folder_store = NULL;
1250         }
1251
1252         /* Clear hidding array created by cut operation */
1253         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1254
1255         G_OBJECT_CLASS(parent_class)->finalize (obj);
1256 }
1257
1258
1259 static void
1260 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1261 {
1262         ModestFolderViewPrivate *priv;
1263         TnyDevice *device;
1264
1265         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1266         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1267
1268         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1269         device = tny_account_store_get_device (account_store);
1270
1271         if (G_UNLIKELY (priv->account_store)) {
1272
1273                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1274                                                    priv->account_inserted_signal))
1275                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1276                                                      priv->account_inserted_signal);
1277                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1278                                                    priv->account_removed_signal))
1279                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1280                                                      priv->account_removed_signal);
1281                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1282                                                    priv->account_changed_signal))
1283                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1284                                                      priv->account_changed_signal);
1285                 g_object_unref (G_OBJECT (priv->account_store));
1286         }
1287
1288         priv->account_store = g_object_ref (G_OBJECT (account_store));
1289
1290         priv->account_removed_signal =
1291                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1292                                   G_CALLBACK (on_account_removed), self);
1293
1294         priv->account_inserted_signal =
1295                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1296                                   G_CALLBACK (on_account_inserted), self);
1297
1298         priv->account_changed_signal =
1299                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1300                                   G_CALLBACK (on_account_changed), self);
1301
1302         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1303         priv->reselect = FALSE;
1304         modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1305
1306         g_object_unref (G_OBJECT (device));
1307 }
1308
1309 static void
1310 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1311                       gpointer user_data)
1312 {
1313         ModestFolderView *self;
1314         GtkTreeModel *model, *filter_model;
1315         TnyFolder *outbox;
1316
1317         self = MODEST_FOLDER_VIEW (user_data);
1318
1319         if (!get_inner_models (self, &filter_model, NULL, &model))
1320                 return;
1321
1322         /* Remove outbox from model */
1323         outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1324         tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1325         g_object_unref (outbox);
1326
1327         /* Refilter view */
1328         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1329 }
1330
1331 static void
1332 on_account_inserted (TnyAccountStore *account_store,
1333                      TnyAccount *account,
1334                      gpointer user_data)
1335 {
1336         ModestFolderViewPrivate *priv;
1337         GtkTreeModel *model, *filter_model;
1338
1339         /* Ignore transport account insertions, we're not showing them
1340            in the folder view */
1341         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1342                 return;
1343
1344         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1345
1346
1347         /* If we're adding a new account, and there is no previous
1348            one, we need to select the visible server account */
1349         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1350             !priv->visible_account_id)
1351                 modest_widget_memory_restore (modest_runtime_get_conf(),
1352                                               G_OBJECT (user_data),
1353                                               MODEST_CONF_FOLDER_VIEW_KEY);
1354
1355
1356         /* Get models */
1357         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1358                                &filter_model, NULL, &model))
1359                 return;
1360
1361         /* Insert the account in the model */
1362         tny_list_append (TNY_LIST (model), G_OBJECT (account));
1363
1364         /* When the model is a list store (plain representation) the
1365            outbox is not a child of any account so we have to manually
1366            delete it because removing the local folders account won't
1367            delete it (because tny_folder_get_account() is not defined
1368            for a merge folder */
1369         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1370             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1371
1372                 priv->outbox_deleted_handler =
1373                         g_signal_connect (account,
1374                                           "outbox-deleted",
1375                                           G_CALLBACK (on_outbox_deleted_cb),
1376                                           user_data);
1377         }
1378
1379         /* Refilter the model */
1380         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1381 }
1382
1383
1384 static gboolean
1385 same_account_selected (ModestFolderView *self,
1386                        TnyAccount *account)
1387 {
1388         ModestFolderViewPrivate *priv;
1389         gboolean same_account = FALSE;
1390
1391         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1392
1393         if (priv->cur_folder_store) {
1394                 TnyAccount *selected_folder_account = NULL;
1395
1396                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1397                         selected_folder_account =
1398                                 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1399                 } else {
1400                         selected_folder_account =
1401                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1402                 }
1403
1404                 if (selected_folder_account == account)
1405                         same_account = TRUE;
1406
1407                 g_object_unref (selected_folder_account);
1408         }
1409         return same_account;
1410 }
1411
1412 /**
1413  *
1414  * Selects the first inbox or the local account in an idle
1415  */
1416 static gboolean
1417 on_idle_select_first_inbox_or_local (gpointer user_data)
1418 {
1419         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1420
1421         gdk_threads_enter ();
1422         modest_folder_view_select_first_inbox_or_local (self);
1423         gdk_threads_leave ();
1424
1425         return FALSE;
1426 }
1427
1428 static void
1429 on_account_changed (TnyAccountStore *account_store,
1430                     TnyAccount *tny_account,
1431                     gpointer user_data)
1432 {
1433         ModestFolderView *self;
1434         ModestFolderViewPrivate *priv;
1435         GtkTreeModel *model, *filter_model;
1436         GtkTreeSelection *sel;
1437         gboolean same_account;
1438
1439         /* Ignore transport account insertions, we're not showing them
1440            in the folder view */
1441         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1442                 return;
1443
1444         self = MODEST_FOLDER_VIEW (user_data);
1445         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1446
1447         /* Get the inner model */
1448         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1449                                &filter_model, NULL, &model))
1450                 return;
1451
1452         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1453
1454         /* Invalidate the cur_folder_store only if the selected folder
1455            belongs to the account that is being removed */
1456         same_account = same_account_selected (self, tny_account);
1457         if (same_account) {
1458                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1459                 gtk_tree_selection_unselect_all (sel);
1460         }
1461
1462         /* Remove the account from the model */
1463         tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1464
1465         /* Insert the account in the model */
1466         tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1467
1468         /* Refilter the model */
1469         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1470
1471         /* Select the first INBOX if the currently selected folder
1472            belongs to the account that is being deleted */
1473         if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1474                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1475 }
1476
1477 static void
1478 on_account_removed (TnyAccountStore *account_store,
1479                     TnyAccount *account,
1480                     gpointer user_data)
1481 {
1482         ModestFolderView *self = NULL;
1483         ModestFolderViewPrivate *priv;
1484         GtkTreeModel *model, *filter_model;
1485         GtkTreeSelection *sel = NULL;
1486         gboolean same_account = FALSE;
1487
1488         /* Ignore transport account removals, we're not showing them
1489            in the folder view */
1490         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1491                 return;
1492
1493         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1494                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1495                 return;
1496         }
1497
1498         self = MODEST_FOLDER_VIEW (user_data);
1499         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1500
1501         /* Invalidate the cur_folder_store only if the selected folder
1502            belongs to the account that is being removed */
1503         same_account = same_account_selected (self, account);
1504         if (same_account) {
1505                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1506                 gtk_tree_selection_unselect_all (sel);
1507         }
1508
1509         /* Invalidate row to select only if the folder to select
1510            belongs to the account that is being removed*/
1511         if (priv->folder_to_select) {
1512                 TnyAccount *folder_to_select_account = NULL;
1513
1514                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1515                 if (folder_to_select_account == account) {
1516                         modest_folder_view_disable_next_folder_selection (self);
1517                         g_object_unref (priv->folder_to_select);
1518                         priv->folder_to_select = NULL;
1519                 }
1520                 g_object_unref (folder_to_select_account);
1521         }
1522
1523         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1524                                &filter_model, NULL, &model))
1525                 return;
1526
1527         /* Disconnect the signal handler */
1528         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1529             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1530                 if (g_signal_handler_is_connected (account,
1531                                                    priv->outbox_deleted_handler))
1532                         g_signal_handler_disconnect (account,
1533                                                      priv->outbox_deleted_handler);
1534         }
1535
1536         /* Remove the account from the model */
1537         tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1538
1539         /* If the removed account is the currently viewed one then
1540            clear the configuration value. The new visible account will be the default account */
1541         if (priv->visible_account_id &&
1542             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1543
1544                 /* Clear the current visible account_id */
1545                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1546
1547                 /* Call the restore method, this will set the new visible account */
1548                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1549                                               MODEST_CONF_FOLDER_VIEW_KEY);
1550         }
1551
1552         /* Refilter the model */
1553         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1554
1555         /* Select the first INBOX if the currently selected folder
1556            belongs to the account that is being deleted */
1557         if (same_account)
1558                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1559 }
1560
1561 void
1562 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1563 {
1564         GtkTreeViewColumn *col;
1565
1566         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1567
1568         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1569         if (!col) {
1570                 g_printerr ("modest: failed get column for title\n");
1571                 return;
1572         }
1573
1574         gtk_tree_view_column_set_title (col, title);
1575         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1576                                            title != NULL);
1577 }
1578
1579 static gboolean
1580 modest_folder_view_on_map (ModestFolderView *self,
1581                            GdkEventExpose *event,
1582                            gpointer data)
1583 {
1584         ModestFolderViewPrivate *priv;
1585
1586         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1587
1588         /* This won't happen often */
1589         if (G_UNLIKELY (priv->reselect)) {
1590                 /* Select the first inbox or the local account if not found */
1591
1592                 /* TODO: this could cause a lock at startup, so we
1593                    comment it for the moment. We know that this will
1594                    be a bug, because the INBOX is not selected, but we
1595                    need to rewrite some parts of Modest to avoid the
1596                    deathlock situation */
1597                 /* TODO: check if this is still the case */
1598                 priv->reselect = FALSE;
1599                 modest_folder_view_select_first_inbox_or_local (self);
1600                 /* Notify the display name observers */
1601                 g_signal_emit (G_OBJECT(self),
1602                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1603                                NULL);
1604         }
1605
1606         if (priv->reexpand) {
1607                 expand_root_items (self);
1608                 priv->reexpand = FALSE;
1609         }
1610
1611         return FALSE;
1612 }
1613
1614 GtkWidget*
1615 modest_folder_view_new (TnyFolderStoreQuery *query)
1616 {
1617         GObject *self;
1618         ModestFolderViewPrivate *priv;
1619         GtkTreeSelection *sel;
1620
1621         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, 
1622 #ifdef MODEST_TOOLKIT_HILDON2
1623                                        "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1624 #endif
1625                                        NULL));
1626         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1627
1628         if (query)
1629                 priv->query = g_object_ref (query);
1630
1631         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1632         priv->changed_signal = g_signal_connect (sel, "changed",
1633                                                  G_CALLBACK (on_selection_changed), self);
1634
1635         g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1636
1637         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1638
1639         return GTK_WIDGET(self);
1640 }
1641
1642 /* this feels dirty; any other way to expand all the root items? */
1643 static void
1644 expand_root_items (ModestFolderView *self)
1645 {
1646         GtkTreePath *path;
1647         GtkTreeModel *model;
1648         GtkTreeIter iter;
1649
1650         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1651         path = gtk_tree_path_new_first ();
1652
1653         /* all folders should have child items, so.. */
1654         do {
1655                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1656                 gtk_tree_path_next (path);
1657         } while (gtk_tree_model_get_iter (model, &iter, path));
1658
1659         gtk_tree_path_free (path);
1660 }
1661
1662 /*
1663  * We use this function to implement the
1664  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1665  * account in this case, and the local folders.
1666  */
1667 static gboolean
1668 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1669 {
1670         ModestFolderViewPrivate *priv;
1671         gboolean retval = TRUE;
1672         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1673         GObject *instance = NULL;
1674         const gchar *id = NULL;
1675         guint i;
1676         gboolean found = FALSE;
1677         gboolean cleared = FALSE;
1678         ModestTnyFolderRules rules = 0;
1679
1680         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1681         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1682
1683         gtk_tree_model_get (model, iter,
1684                             TYPE_COLUMN, &type,
1685                             INSTANCE_COLUMN, &instance,
1686                             -1);
1687
1688         /* Do not show if there is no instance, this could indeed
1689            happen when the model is being modified while it's being
1690            drawn. This could occur for example when moving folders
1691            using drag&drop */
1692         if (!instance)
1693                 return FALSE;
1694
1695         if (TNY_IS_ACCOUNT (instance)) {
1696                 TnyAccount *acc = TNY_ACCOUNT (instance);
1697                 const gchar *account_id = tny_account_get_id (acc);
1698
1699                 /* If it isn't a special folder,
1700                  * don't show it unless it is the visible account: */
1701                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1702                     !modest_tny_account_is_virtual_local_folders (acc) &&
1703                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1704
1705                         /* Show only the visible account id */
1706                         if (priv->visible_account_id) {
1707                                 if (strcmp (account_id, priv->visible_account_id))
1708                                         retval = FALSE;
1709                         } else {
1710                                 retval = FALSE;
1711                         }
1712                 }
1713
1714                 /* Never show these to the user. They are merged into one folder
1715                  * in the local-folders account instead: */
1716                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1717                         retval = FALSE;
1718         } else {
1719                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1720                         /* Only show special folders for current account if needed */
1721                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1722                                 TnyAccount *account;
1723
1724                                 account = tny_folder_get_account (TNY_FOLDER (instance));
1725
1726                                 if (TNY_IS_ACCOUNT (account)) {
1727                                         const gchar *account_id = tny_account_get_id (account);
1728
1729                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
1730                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1731                                                 /* Show only the visible account id */
1732                                                 if (priv->visible_account_id) {
1733                                                         if (strcmp (account_id, priv->visible_account_id))
1734                                                                 retval = FALSE;
1735                                                 }
1736                                         }
1737                                                 g_object_unref (account);
1738                                 }
1739                         }
1740
1741                 }
1742         }
1743
1744         /* Check hiding (if necessary) */
1745         cleared = modest_email_clipboard_cleared (priv->clipboard);
1746         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1747                 id = tny_folder_get_id (TNY_FOLDER(instance));
1748                 if (priv->hidding_ids != NULL)
1749                         for (i=0; i < priv->n_selected && !found; i++)
1750                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1751                                         found = (!strcmp (priv->hidding_ids[i], id));
1752
1753                 retval = !found;
1754         }
1755
1756         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1757         folder as no message can be move there according to UI specs */
1758         if (!priv->show_non_move) {
1759                 if (TNY_IS_FOLDER (instance) && 
1760                     modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1761                         switch (type) {
1762                         case TNY_FOLDER_TYPE_OUTBOX:
1763                         case TNY_FOLDER_TYPE_SENT:
1764                         case TNY_FOLDER_TYPE_DRAFTS:
1765                                 retval = FALSE;
1766                                 break;
1767                         case TNY_FOLDER_TYPE_UNKNOWN:
1768                         case TNY_FOLDER_TYPE_NORMAL:
1769                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1770                                 if (type == TNY_FOLDER_TYPE_INVALID)
1771                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1772                                 
1773                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1774                                     type == TNY_FOLDER_TYPE_SENT
1775                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1776                                         retval = FALSE;
1777                                 break;
1778                         default:
1779                                 break;
1780                         }
1781                 }
1782         }
1783
1784         /* apply special filters */
1785         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1786                 if (TNY_IS_ACCOUNT (instance))
1787                         return FALSE;
1788         }
1789
1790         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1791                 if (TNY_IS_FOLDER (instance)) {
1792                         /* Check folder rules */
1793                         ModestTnyFolderRules rules;
1794
1795                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1796                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
1797                 } else if (TNY_IS_ACCOUNT (instance)) {
1798                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1799                                 retval = FALSE;
1800                         } else {
1801                                 retval = TRUE;
1802                         }
1803                 }
1804         }
1805
1806         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1807                 if (TNY_IS_FOLDER (instance)) {
1808                         TnyFolderType guess_type;
1809
1810                         if (TNY_FOLDER_TYPE_NORMAL) {
1811                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1812                         } else {
1813                                 guess_type = type;
1814                         }
1815
1816                         switch (type) {
1817                         case TNY_FOLDER_TYPE_OUTBOX:
1818                         case TNY_FOLDER_TYPE_SENT:
1819                         case TNY_FOLDER_TYPE_DRAFTS:
1820                         case TNY_FOLDER_TYPE_ARCHIVE:
1821                         case TNY_FOLDER_TYPE_INBOX:
1822                                 retval = FALSE;
1823                                 break;
1824                         case TNY_FOLDER_TYPE_UNKNOWN:
1825                         case TNY_FOLDER_TYPE_NORMAL:
1826                                 break;
1827                         default:
1828                                 break;
1829                         }
1830
1831                 } else if (TNY_IS_ACCOUNT (instance)) {
1832                         retval = FALSE;
1833                 }
1834         }
1835
1836         if (retval && TNY_IS_FOLDER (instance)) {
1837                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1838         }
1839
1840         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1841                 if (TNY_IS_FOLDER (instance)) {
1842                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1843                 } else if (TNY_IS_ACCOUNT (instance)) {
1844                         retval = FALSE;
1845                 }
1846         }
1847
1848         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1849                 if (TNY_IS_FOLDER (instance)) {
1850                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1851                 } else if (TNY_IS_ACCOUNT (instance)) {
1852                         retval = FALSE;
1853                 }
1854         }
1855
1856         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1857                 if (TNY_IS_FOLDER (instance)) {
1858                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1859                 } else if (TNY_IS_ACCOUNT (instance)) {
1860                         retval = FALSE;
1861                 }
1862         }
1863
1864         /* Free */
1865         g_object_unref (instance);
1866
1867         return retval;
1868 }
1869
1870
1871 gboolean
1872 modest_folder_view_update_model (ModestFolderView *self,
1873                                  TnyAccountStore *account_store)
1874 {
1875         ModestFolderViewPrivate *priv;
1876         GtkTreeModel *model /* , *old_model */;
1877         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1878
1879         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1880         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1881                               FALSE);
1882
1883         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1884
1885         /* Notify that there is no folder selected */
1886         g_signal_emit (G_OBJECT(self),
1887                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1888                        NULL, FALSE);
1889         if (priv->cur_folder_store) {
1890                 g_object_unref (priv->cur_folder_store);
1891                 priv->cur_folder_store = NULL;
1892         }
1893
1894         /* FIXME: the local accounts are not shown when the query
1895            selects only the subscribed folders */
1896 #ifdef MODEST_TOOLKIT_HILDON2
1897         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
1898                                                           TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1899         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1900                                                       MODEST_FOLDER_PATH_SEPARATOR);
1901 #else
1902         model = tny_gtk_folder_store_tree_model_new (NULL);
1903 #endif
1904
1905         /* When the model is a list store (plain representation) the
1906            outbox is not a child of any account so we have to manually
1907            delete it because removing the local folders account won't
1908            delete it (because tny_folder_get_account() is not defined
1909            for a merge folder */
1910         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1911                 TnyAccount *account;
1912                 ModestTnyAccountStore *acc_store;
1913
1914                 acc_store = modest_runtime_get_account_store ();
1915                 account = modest_tny_account_store_get_local_folders_account (acc_store);
1916
1917                 if (g_signal_handler_is_connected (account,
1918                                                    priv->outbox_deleted_handler))
1919                         g_signal_handler_disconnect (account,
1920                                                      priv->outbox_deleted_handler);
1921
1922                 priv->outbox_deleted_handler =
1923                         g_signal_connect (account,
1924                                           "outbox-deleted",
1925                                           G_CALLBACK (on_outbox_deleted_cb),
1926                                           self);
1927                 g_object_unref (account);
1928         }
1929
1930         /* Get the accounts: */
1931         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1932                                         TNY_LIST (model),
1933                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1934
1935         sortable = gtk_tree_model_sort_new_with_model (model);
1936         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1937                                               NAME_COLUMN,
1938                                               GTK_SORT_ASCENDING);
1939         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1940                                          NAME_COLUMN,
1941                                          cmp_rows, NULL, NULL);
1942
1943         /* Create filter model */
1944         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1945         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1946                                                 filter_row,
1947                                                 self,
1948                                                 NULL);
1949
1950         /* Set new model */
1951         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1952 #ifndef MODEST_TOOLKIT_HILDON2
1953         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1954                           (GCallback) on_row_inserted_maybe_select_folder, self);
1955 #endif
1956
1957         g_object_unref (model);
1958         g_object_unref (filter_model);
1959         g_object_unref (sortable);
1960
1961         /* Force a reselection of the INBOX next time the widget is shown */
1962         priv->reselect = TRUE;
1963
1964         return TRUE;
1965 }
1966
1967
1968 static void
1969 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1970 {
1971         GtkTreeModel *model = NULL;
1972         TnyFolderStore *folder = NULL;
1973         GtkTreeIter iter;
1974         ModestFolderView *tree_view = NULL;
1975         ModestFolderViewPrivate *priv = NULL;
1976         gboolean selected = FALSE;
1977
1978         g_return_if_fail (sel);
1979         g_return_if_fail (user_data);
1980
1981         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1982
1983         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1984
1985         tree_view = MODEST_FOLDER_VIEW (user_data);
1986
1987         if (selected) {
1988                 gtk_tree_model_get (model, &iter,
1989                                     INSTANCE_COLUMN, &folder,
1990                                     -1);
1991
1992                 /* If the folder is the same do not notify */
1993                 if (folder && priv->cur_folder_store == folder) {
1994                         g_object_unref (folder);
1995                         return;
1996                 }
1997         }
1998
1999         /* Current folder was unselected */
2000         if (priv->cur_folder_store) {
2001                 /* We must do this firstly because a libtinymail-camel
2002                    implementation detail. If we issue the signal
2003                    before doing the sync_async, then that signal could
2004                    cause (and it actually does it) a free of the
2005                    summary of the folder (because the main window will
2006                    clear the headers view */
2007                 if (TNY_IS_FOLDER(priv->cur_folder_store))
2008                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2009                                                FALSE, NULL, NULL, NULL);
2010
2011                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2012                        priv->cur_folder_store, FALSE);
2013
2014                 g_object_unref (priv->cur_folder_store);
2015                 priv->cur_folder_store = NULL;
2016         }
2017
2018         /* New current references */
2019         priv->cur_folder_store = folder;
2020
2021         /* New folder has been selected. Do not notify if there is
2022            nothing new selected */
2023         if (selected) {
2024                 g_signal_emit (G_OBJECT(tree_view),
2025                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2026                                0, priv->cur_folder_store, TRUE);
2027         }
2028 }
2029
2030 static void
2031 on_row_activated (GtkTreeView *treeview,
2032                   GtkTreePath *treepath,
2033                   GtkTreeViewColumn *column,
2034                   gpointer user_data)
2035 {
2036         GtkTreeModel *model = NULL;
2037         TnyFolderStore *folder = NULL;
2038         GtkTreeIter iter;
2039         ModestFolderView *self = NULL;
2040         ModestFolderViewPrivate *priv = NULL;
2041
2042         g_return_if_fail (treeview);
2043         g_return_if_fail (user_data);
2044
2045         self = MODEST_FOLDER_VIEW (user_data);
2046         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2047
2048         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2049
2050         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2051                 return;
2052
2053         gtk_tree_model_get (model, &iter,
2054                             INSTANCE_COLUMN, &folder,
2055                             -1);
2056
2057         g_signal_emit (G_OBJECT(self),
2058                        signals[FOLDER_ACTIVATED_SIGNAL],
2059                        0, folder);
2060
2061 #ifdef MODEST_TOOLKIT_HILDON2
2062         HildonUIMode ui_mode;
2063         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2064         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2065                 if (priv->cur_folder_store)
2066                         g_object_unref (priv->cur_folder_store);
2067                 priv->cur_folder_store = g_object_ref (folder);
2068         }
2069 #endif
2070
2071         g_object_unref (folder);
2072 }
2073
2074 TnyFolderStore *
2075 modest_folder_view_get_selected (ModestFolderView *self)
2076 {
2077         ModestFolderViewPrivate *priv;
2078
2079         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2080
2081         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2082         if (priv->cur_folder_store)
2083                 g_object_ref (priv->cur_folder_store);
2084
2085         return priv->cur_folder_store;
2086 }
2087
2088 static gint
2089 get_cmp_rows_type_pos (GObject *folder)
2090 {
2091         /* Remote accounts -> Local account -> MMC account .*/
2092         /* 0, 1, 2 */
2093
2094         if (TNY_IS_ACCOUNT (folder) &&
2095                 modest_tny_account_is_virtual_local_folders (
2096                         TNY_ACCOUNT (folder))) {
2097                 return 1;
2098         } else if (TNY_IS_ACCOUNT (folder)) {
2099                 TnyAccount *account = TNY_ACCOUNT (folder);
2100                 const gchar *account_id = tny_account_get_id (account);
2101                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2102                         return 2;
2103                 else
2104                         return 0;
2105         }
2106         else {
2107                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2108                 return -1; /* Should never happen */
2109         }
2110 }
2111
2112 static gboolean
2113 inbox_is_special (TnyFolderStore *folder_store)
2114 {
2115         gboolean is_special = TRUE;
2116
2117         if (TNY_IS_FOLDER (folder_store)) {
2118                 const gchar *id;
2119                 gchar *downcase;
2120                 gchar *last_bar;
2121                 gchar *last_inbox_bar;
2122
2123                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2124                 downcase = g_utf8_strdown (id, -1);
2125                 last_bar = g_strrstr (downcase, "/");
2126                 if (last_bar) {
2127                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2128                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2129                                 is_special = FALSE;
2130                 }
2131                 g_free (downcase);
2132         }
2133         return is_special;
2134 }
2135
2136 static gint
2137 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2138 {
2139         TnyAccount *account;
2140         gboolean is_special;
2141         /* Inbox, Outbox, Drafts, Sent, User */
2142         /* 0, 1, 2, 3, 4 */
2143
2144         if (!TNY_IS_FOLDER (folder_store))
2145                 return 4;
2146         switch (t) {
2147         case TNY_FOLDER_TYPE_INBOX:
2148         {
2149                 account = tny_folder_get_account (folder_store);
2150                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2151
2152                 /* In inbox case we need to know if the inbox is really the top
2153                  * inbox of the account, or if it's a submailbox inbox. To do
2154                  * this we'll apply an heuristic rule: Find last "/" and check
2155                  * if it's preceeded by another Inbox */
2156                 is_special = is_special && inbox_is_special (TNY_FOLDER_STORE (folder_store));
2157                 g_object_unref (account);
2158                 return is_special?0:4;
2159         }
2160         break;
2161         case TNY_FOLDER_TYPE_OUTBOX:
2162                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2163                 break;
2164         case TNY_FOLDER_TYPE_DRAFTS:
2165         {
2166                 account = tny_folder_get_account (folder_store);
2167                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2168                 g_object_unref (account);
2169                 return is_special?1:4;
2170         }
2171         break;
2172         case TNY_FOLDER_TYPE_SENT:
2173         {
2174                 account = tny_folder_get_account (folder_store);
2175                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2176                 g_object_unref (account);
2177                 return is_special?3:4;
2178         }
2179         break;
2180         default:
2181                 return 4;
2182         }
2183 }
2184
2185 static gint
2186 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2187 {
2188         const gchar *a1_name, *a2_name;
2189
2190         a1_name = tny_account_get_name (a1);
2191         a2_name = tny_account_get_name (a2);
2192
2193         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2194 }
2195
2196 static gint
2197 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2198 {
2199         TnyAccount *a1 = NULL, *a2 = NULL;
2200         gint cmp;
2201
2202         if (TNY_IS_ACCOUNT (s1)) {
2203                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2204         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2205                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2206         }
2207
2208         if (TNY_IS_ACCOUNT (s2)) {
2209                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2210         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2211                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2212         }
2213
2214         if (!a1 || !a2) {
2215                 if (!a1 && !a2)
2216                         cmp = 0;
2217                 else if (!a1)
2218                         cmp = 1;
2219                 else
2220                         cmp = -1;
2221                 goto finish;
2222         }
2223
2224         if (a1 == a2) {
2225                 cmp = 0;
2226                 goto finish;
2227         }
2228         /* First we sort with the type of account */
2229         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2230         if (cmp != 0)
2231                 goto finish;
2232
2233         cmp = compare_account_names (a1, a2);
2234
2235 finish:
2236         if (a1)
2237                 g_object_unref (a1);
2238         if (a2)
2239                 g_object_unref (a2);
2240
2241         return cmp;
2242 }
2243
2244 static gint
2245 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2246 {
2247         gint is_account1, is_account2;
2248
2249         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2250         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2251
2252         return is_account2 - is_account1;
2253 }
2254
2255 /*
2256  * This function orders the mail accounts according to these rules:
2257  * 1st - remote accounts
2258  * 2nd - local account
2259  * 3rd - MMC account
2260  */
2261 static gint
2262 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2263           gpointer user_data)
2264 {
2265         gint cmp = 0;
2266         gchar *name1 = NULL;
2267         gchar *name2 = NULL;
2268         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2269         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2270         GObject *folder1 = NULL;
2271         GObject *folder2 = NULL;
2272
2273         gtk_tree_model_get (tree_model, iter1,
2274                             NAME_COLUMN, &name1,
2275                             TYPE_COLUMN, &type,
2276                             INSTANCE_COLUMN, &folder1,
2277                             -1);
2278         gtk_tree_model_get (tree_model, iter2,
2279                             NAME_COLUMN, &name2,
2280                             TYPE_COLUMN, &type2,
2281                             INSTANCE_COLUMN, &folder2,
2282                             -1);
2283
2284         /* Return if we get no folder. This could happen when folder
2285            operations are happening. The model is updated after the
2286            folder copy/move actually occurs, so there could be
2287            situations where the model to be drawn is not correct */
2288         if (!folder1 || !folder2)
2289                 goto finish;
2290
2291         /* Sort by type. First the special folders, then the archives */
2292         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2293         if (cmp != 0)
2294                 goto finish;
2295
2296         /* Now we sort using the account of each folder */
2297         if (TNY_IS_FOLDER_STORE (folder1) && 
2298             TNY_IS_FOLDER_STORE (folder2)) {
2299                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2300                 if (cmp != 0)
2301                         goto finish;
2302
2303                 /* Each group is preceeded by its account */
2304                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2305                 if (cmp != 0)
2306                         goto finish;
2307         }
2308
2309         /* Pure sort by name */
2310         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2311  finish:
2312         if (folder1)
2313                 g_object_unref(G_OBJECT(folder1));
2314         if (folder2)
2315                 g_object_unref(G_OBJECT(folder2));
2316
2317         g_free (name1);
2318         g_free (name2);
2319
2320         return cmp;
2321 }
2322
2323 /*****************************************************************************/
2324 /*                        DRAG and DROP stuff                                */
2325 /*****************************************************************************/
2326 /*
2327  * This function fills the #GtkSelectionData with the row and the
2328  * model that has been dragged. It's called when this widget is a
2329  * source for dnd after the event drop happened
2330  */
2331 static void
2332 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2333                   guint info, guint time, gpointer data)
2334 {
2335         GtkTreeSelection *selection;
2336         GtkTreeModel *model;
2337         GtkTreeIter iter;
2338         GtkTreePath *source_row;
2339
2340         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2341         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2342
2343                 source_row = gtk_tree_model_get_path (model, &iter);
2344                 gtk_tree_set_row_drag_data (selection_data,
2345                                             model,
2346                                             source_row);
2347
2348                 gtk_tree_path_free (source_row);
2349         }
2350 }
2351
2352 typedef struct _DndHelper {
2353         ModestFolderView *folder_view;
2354         gboolean delete_source;
2355         GtkTreePath *source_row;
2356 } DndHelper;
2357
2358 static void
2359 dnd_helper_destroyer (DndHelper *helper)
2360 {
2361         /* Free the helper */
2362         gtk_tree_path_free (helper->source_row);
2363         g_slice_free (DndHelper, helper);
2364 }
2365
2366 static void
2367 xfer_folder_cb (ModestMailOperation *mail_op,
2368                 TnyFolder *new_folder,
2369                 gpointer user_data)
2370 {
2371         if (new_folder) {
2372                 /* Select the folder */
2373                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2374                                                   new_folder, FALSE);
2375         }
2376 }
2377
2378
2379 /* get the folder for the row the treepath refers to. */
2380 /* folder must be unref'd */
2381 static TnyFolderStore *
2382 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2383 {
2384         GtkTreeIter iter;
2385         TnyFolderStore *folder = NULL;
2386
2387         if (gtk_tree_model_get_iter (model,&iter, path))
2388                 gtk_tree_model_get (model, &iter,
2389                                     INSTANCE_COLUMN, &folder,
2390                                     -1);
2391         return folder;
2392 }
2393
2394
2395 /*
2396  * This function is used by drag_data_received_cb to manage drag and
2397  * drop of a header, i.e, and drag from the header view to the folder
2398  * view.
2399  */
2400 static void
2401 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2402                                 GtkTreeModel *dest_model,
2403                                 GtkTreePath  *dest_row,
2404                                 GtkSelectionData *selection_data)
2405 {
2406         TnyList *headers = NULL;
2407         TnyFolder *folder = NULL, *src_folder = NULL;
2408         TnyFolderType folder_type;
2409         GtkTreeIter source_iter, dest_iter;
2410         ModestWindowMgr *mgr = NULL;
2411         ModestWindow *main_win = NULL;
2412         gchar **uris, **tmp;
2413
2414         /* Build the list of headers */
2415         mgr = modest_runtime_get_window_mgr ();
2416         headers = tny_simple_list_new ();
2417         uris = modest_dnd_selection_data_get_paths (selection_data);
2418         tmp = uris;
2419
2420         while (*tmp != NULL) {
2421                 TnyHeader *header;
2422                 GtkTreePath *path;
2423                 gboolean first = TRUE;
2424
2425                 /* Get header */
2426                 path = gtk_tree_path_new_from_string (*tmp);
2427                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2428                 gtk_tree_model_get (source_model, &source_iter,
2429                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2430                                     &header, -1);
2431
2432                 /* Do not enable d&d of headers already opened */
2433                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2434                         tny_list_append (headers, G_OBJECT (header));
2435
2436                 if (G_UNLIKELY (first)) {
2437                         src_folder = tny_header_get_folder (header);
2438                         first = FALSE;
2439                 }
2440
2441                 /* Free and go on */
2442                 gtk_tree_path_free (path);
2443                 g_object_unref (header);
2444                 tmp++;
2445         }
2446         g_strfreev (uris);
2447
2448         /* This could happen ig we perform a d&d very quickly over the
2449            same row that row could dissapear because message is
2450            transferred */
2451         if (!TNY_IS_FOLDER (src_folder))
2452                 goto cleanup;
2453
2454         /* Get the target folder */
2455         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2456         gtk_tree_model_get (dest_model, &dest_iter,
2457                             INSTANCE_COLUMN,
2458                             &folder, -1);
2459
2460         if (!folder || !TNY_IS_FOLDER(folder)) {
2461 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2462                 goto cleanup;
2463         }
2464
2465         folder_type = modest_tny_folder_guess_folder_type (folder);
2466         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2467 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2468                 goto cleanup;  /* cannot move messages there */
2469         }
2470
2471         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2472 /*              g_warning ("folder not writable"); */
2473                 goto cleanup; /* verboten! */
2474         }
2475
2476         /* Ask for confirmation to move */
2477         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2478         if (!main_win) {
2479                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2480                 goto cleanup;
2481         }
2482
2483         /* Transfer messages */
2484         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2485                                                     headers, folder);
2486
2487         /* Frees */
2488 cleanup:
2489         if (G_IS_OBJECT (src_folder))
2490                 g_object_unref (src_folder);
2491         if (G_IS_OBJECT(folder))
2492                 g_object_unref (G_OBJECT (folder));
2493         if (G_IS_OBJECT(headers))
2494                 g_object_unref (headers);
2495 }
2496
2497 typedef struct {
2498         TnyFolderStore *src_folder;
2499         TnyFolderStore *dst_folder;
2500         ModestFolderView *folder_view;
2501         DndHelper *helper;
2502 } DndFolderInfo;
2503
2504 static void
2505 dnd_folder_info_destroyer (DndFolderInfo *info)
2506 {
2507         if (info->src_folder)
2508                 g_object_unref (info->src_folder);
2509         if (info->dst_folder)
2510                 g_object_unref (info->dst_folder);
2511         g_slice_free (DndFolderInfo, info);
2512 }
2513
2514 static void
2515 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2516                                     GtkWindow *parent_window,
2517                                     TnyAccount *account)
2518 {
2519         /* Show error */
2520         modest_ui_actions_on_account_connection_error (parent_window, account);
2521
2522         /* Free the helper & info */
2523         dnd_helper_destroyer (info->helper);
2524         dnd_folder_info_destroyer (info);
2525 }
2526
2527 static void
2528 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2529                                                      GError *err,
2530                                                      GtkWindow *parent_window,
2531                                                      TnyAccount *account,
2532                                                      gpointer user_data)
2533 {
2534         DndFolderInfo *info = NULL;
2535         ModestMailOperation *mail_op;
2536
2537         info = (DndFolderInfo *) user_data;
2538
2539         if (err || canceled) {
2540                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2541                 return;
2542         }
2543
2544         /* Do the mail operation */
2545         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2546                                                                  modest_ui_actions_move_folder_error_handler,
2547                                                                  info->src_folder, NULL);
2548
2549         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2550                                          mail_op);
2551
2552         /* Transfer the folder */
2553         modest_mail_operation_xfer_folder (mail_op,
2554                                            TNY_FOLDER (info->src_folder),
2555                                            info->dst_folder,
2556                                            info->helper->delete_source,
2557                                            xfer_folder_cb,
2558                                            info->helper->folder_view);
2559
2560         /* Frees */
2561         g_object_unref (G_OBJECT (mail_op));
2562         dnd_helper_destroyer (info->helper);
2563         dnd_folder_info_destroyer (info);
2564 }
2565
2566
2567 static void
2568 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2569                                                      GError *err,
2570                                                      GtkWindow *parent_window,
2571                                                      TnyAccount *account,
2572                                                      gpointer user_data)
2573 {
2574         DndFolderInfo *info = NULL;
2575
2576         info = (DndFolderInfo *) user_data;
2577
2578         if (err || canceled) {
2579                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2580                 return;
2581         }
2582
2583         /* Connect to source folder and perform the copy/move */
2584         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2585                                                        info->src_folder,
2586                                                        drag_and_drop_from_folder_view_src_folder_performer,
2587                                                        info);
2588 }
2589
2590 /*
2591  * This function is used by drag_data_received_cb to manage drag and
2592  * drop of a folder, i.e, and drag from the folder view to the same
2593  * folder view.
2594  */
2595 static void
2596 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2597                                 GtkTreeModel     *dest_model,
2598                                 GtkTreePath      *dest_row,
2599                                 GtkSelectionData *selection_data,
2600                                 DndHelper        *helper)
2601 {
2602         GtkTreeIter dest_iter, iter;
2603         TnyFolderStore *dest_folder = NULL;
2604         TnyFolderStore *folder = NULL;
2605         gboolean forbidden = FALSE;
2606         ModestWindow *win;
2607         DndFolderInfo *info = NULL;
2608
2609         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2610         if (!win) {
2611                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2612                 dnd_helper_destroyer (helper);
2613                 return;
2614         }
2615
2616         if (!forbidden) {
2617                 /* check the folder rules for the destination */
2618                 folder = tree_path_to_folder (dest_model, dest_row);
2619                 if (TNY_IS_FOLDER(folder)) {
2620                         ModestTnyFolderRules rules =
2621                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2622                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2623                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2624                         /* enable local root as destination for folders */
2625                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2626                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2627                                 forbidden = TRUE;
2628                 }
2629                 g_object_unref (folder);
2630         }
2631         if (!forbidden) {
2632                 /* check the folder rules for the source */
2633                 folder = tree_path_to_folder (source_model, helper->source_row);
2634                 if (TNY_IS_FOLDER(folder)) {
2635                         ModestTnyFolderRules rules =
2636                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2637                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2638                 } else
2639                         forbidden = TRUE;
2640                 g_object_unref (folder);
2641         }
2642
2643
2644         /* Check if the drag is possible */
2645         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2646                 /* Show error */
2647                 modest_platform_run_information_dialog ((GtkWindow *) win, 
2648                                                         _("mail_in_ui_folder_move_target_error"), 
2649                                                         FALSE);
2650                 /* Restore the previous selection */
2651                 folder = tree_path_to_folder (source_model, helper->source_row);
2652                 if (folder) {
2653                         if (TNY_IS_FOLDER (folder))
2654                                 modest_folder_view_select_folder (helper->folder_view, 
2655                                                                   TNY_FOLDER (folder), FALSE);
2656                         g_object_unref (folder);
2657                 }
2658                 dnd_helper_destroyer (helper);
2659                 return;
2660         }
2661
2662         /* Get data */
2663         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2664         gtk_tree_model_get (dest_model, &dest_iter,
2665                             INSTANCE_COLUMN,
2666                             &dest_folder, -1);
2667         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2668         gtk_tree_model_get (source_model, &iter,
2669                             INSTANCE_COLUMN,
2670                             &folder, -1);
2671
2672         /* Create the info for the performer */
2673         info = g_slice_new0 (DndFolderInfo);
2674         info->src_folder = g_object_ref (folder);
2675         info->dst_folder = g_object_ref (dest_folder);
2676         info->helper = helper;
2677
2678         /* Connect to the destination folder and perform the copy/move */
2679         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2680                                                        dest_folder,
2681                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2682                                                        info);
2683
2684         /* Frees */
2685         g_object_unref (dest_folder);
2686         g_object_unref (folder);
2687 }
2688
2689 /*
2690  * This function receives the data set by the "drag-data-get" signal
2691  * handler. This information comes within the #GtkSelectionData. This
2692  * function will manage both the drags of folders of the treeview and
2693  * drags of headers of the header view widget.
2694  */
2695 static void
2696 on_drag_data_received (GtkWidget *widget,
2697                        GdkDragContext *context,
2698                        gint x,
2699                        gint y,
2700                        GtkSelectionData *selection_data,
2701                        guint target_type,
2702                        guint time,
2703                        gpointer data)
2704 {
2705         GtkWidget *source_widget;
2706         GtkTreeModel *dest_model, *source_model;
2707         GtkTreePath *source_row, *dest_row;
2708         GtkTreeViewDropPosition pos;
2709         gboolean delete_source = FALSE;
2710         gboolean success = FALSE;
2711
2712         /* Do not allow further process */
2713         g_signal_stop_emission_by_name (widget, "drag-data-received");
2714         source_widget = gtk_drag_get_source_widget (context);
2715
2716         /* Get the action */
2717         if (context->action == GDK_ACTION_MOVE) {
2718                 delete_source = TRUE;
2719
2720                 /* Notify that there is no folder selected. We need to
2721                    do this in order to update the headers view (and
2722                    its monitors, because when moving, the old folder
2723                    won't longer exist. We can not wait for the end of
2724                    the operation, because the operation won't start if
2725                    the folder is in use */
2726                 if (source_widget == widget) {
2727                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2728                         gtk_tree_selection_unselect_all (sel);
2729                 }
2730         }
2731
2732         /* Check if the get_data failed */
2733         if (selection_data == NULL || selection_data->length < 0)
2734                 goto end;
2735
2736         /* Select the destination model */
2737         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2738
2739         /* Get the path to the destination row. Can not call
2740            gtk_tree_view_get_drag_dest_row() because the source row
2741            is not selected anymore */
2742         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2743                                            &dest_row, &pos);
2744
2745         /* Only allow drops IN other rows */
2746         if (!dest_row ||
2747             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2748             pos == GTK_TREE_VIEW_DROP_AFTER)
2749                 goto end;
2750
2751         success = TRUE;
2752         /* Drags from the header view */
2753         if (source_widget != widget) {
2754                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2755
2756                 drag_and_drop_from_header_view (source_model,
2757                                                 dest_model,
2758                                                 dest_row,
2759                                                 selection_data);
2760         } else {
2761                 DndHelper *helper = NULL;
2762
2763                 /* Get the source model and row */
2764                 gtk_tree_get_row_drag_data (selection_data,
2765                                             &source_model,
2766                                             &source_row);
2767
2768                 /* Create the helper */
2769                 helper = g_slice_new0 (DndHelper);
2770                 helper->delete_source = delete_source;
2771                 helper->source_row = gtk_tree_path_copy (source_row);
2772                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2773
2774                 drag_and_drop_from_folder_view (source_model,
2775                                                 dest_model,
2776                                                 dest_row,
2777                                                 selection_data,
2778                                                 helper);
2779
2780                 gtk_tree_path_free (source_row);
2781         }
2782
2783         /* Frees */
2784         gtk_tree_path_free (dest_row);
2785
2786  end:
2787         /* Finish the drag and drop */
2788         gtk_drag_finish (context, success, FALSE, time);
2789 }
2790
2791 /*
2792  * We define a "drag-drop" signal handler because we do not want to
2793  * use the default one, because the default one always calls
2794  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2795  * signal handler, because there we have all the information available
2796  * to know if the dnd was a success or not.
2797  */
2798 static gboolean
2799 drag_drop_cb (GtkWidget      *widget,
2800               GdkDragContext *context,
2801               gint            x,
2802               gint            y,
2803               guint           time,
2804               gpointer        user_data)
2805 {
2806         gpointer target;
2807
2808         if (!context->targets)
2809                 return FALSE;
2810
2811         /* Check if we're dragging a folder row */
2812         target = gtk_drag_dest_find_target (widget, context, NULL);
2813
2814         /* Request the data from the source. */
2815         gtk_drag_get_data(widget, context, target, time);
2816
2817     return TRUE;
2818 }
2819
2820 /*
2821  * This function expands a node of a tree view if it's not expanded
2822  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2823  * does that, so that's why they're here.
2824  */
2825 static gint
2826 expand_row_timeout (gpointer data)
2827 {
2828         GtkTreeView *tree_view = data;
2829         GtkTreePath *dest_path = NULL;
2830         GtkTreeViewDropPosition pos;
2831         gboolean result = FALSE;
2832
2833         gdk_threads_enter ();
2834
2835         gtk_tree_view_get_drag_dest_row (tree_view,
2836                                          &dest_path,
2837                                          &pos);
2838
2839         if (dest_path &&
2840             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2841              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2842                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2843                 gtk_tree_path_free (dest_path);
2844         }
2845         else {
2846                 if (dest_path)
2847                         gtk_tree_path_free (dest_path);
2848
2849                 result = TRUE;
2850         }
2851
2852         gdk_threads_leave ();
2853
2854         return result;
2855 }
2856
2857 /*
2858  * This function is called whenever the pointer is moved over a widget
2859  * while dragging some data. It installs a timeout that will expand a
2860  * node of the treeview if not expanded yet. This function also calls
2861  * gdk_drag_status in order to set the suggested action that will be
2862  * used by the "drag-data-received" signal handler to know if we
2863  * should do a move or just a copy of the data.
2864  */
2865 static gboolean
2866 on_drag_motion (GtkWidget      *widget,
2867                 GdkDragContext *context,
2868                 gint            x,
2869                 gint            y,
2870                 guint           time,
2871                 gpointer        user_data)
2872 {
2873         GtkTreeViewDropPosition pos;
2874         GtkTreePath *dest_row;
2875         GtkTreeModel *dest_model;
2876         ModestFolderViewPrivate *priv;
2877         GdkDragAction suggested_action;
2878         gboolean valid_location = FALSE;
2879         TnyFolderStore *folder = NULL;
2880
2881         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2882
2883         if (priv->timer_expander != 0) {
2884                 g_source_remove (priv->timer_expander);
2885                 priv->timer_expander = 0;
2886         }
2887
2888         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2889                                            x, y,
2890                                            &dest_row,
2891                                            &pos);
2892
2893         /* Do not allow drops between folders */
2894         if (!dest_row ||
2895             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2896             pos == GTK_TREE_VIEW_DROP_AFTER) {
2897                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2898                 gdk_drag_status(context, 0, time);
2899                 valid_location = FALSE;
2900                 goto out;
2901         } else {
2902                 valid_location = TRUE;
2903         }
2904
2905         /* Check that the destination folder is writable */
2906         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2907         folder = tree_path_to_folder (dest_model, dest_row);
2908         if (folder && TNY_IS_FOLDER (folder)) {
2909                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2910
2911                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2912                         valid_location = FALSE;
2913                         goto out;
2914                 }
2915         }
2916
2917         /* Expand the selected row after 1/2 second */
2918         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2919                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2920         }
2921         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2922
2923         /* Select the desired action. By default we pick MOVE */
2924         suggested_action = GDK_ACTION_MOVE;
2925
2926         if (context->actions == GDK_ACTION_COPY)
2927             gdk_drag_status(context, GDK_ACTION_COPY, time);
2928         else if (context->actions == GDK_ACTION_MOVE)
2929             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2930         else if (context->actions & suggested_action)
2931             gdk_drag_status(context, suggested_action, time);
2932         else
2933             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2934
2935  out:
2936         if (folder)
2937                 g_object_unref (folder);
2938         if (dest_row) {
2939                 gtk_tree_path_free (dest_row);
2940         }
2941         g_signal_stop_emission_by_name (widget, "drag-motion");
2942
2943         return valid_location;
2944 }
2945
2946 /*
2947  * This function sets the treeview as a source and a target for dnd
2948  * events. It also connects all the requirede signals.
2949  */
2950 static void
2951 setup_drag_and_drop (GtkTreeView *self)
2952 {
2953         /* Set up the folder view as a dnd destination. Set only the
2954            highlight flag, otherwise gtk will have a different
2955            behaviour */
2956 #ifdef MODEST_TOOLKIT_HILDON2
2957         return;
2958 #endif
2959         gtk_drag_dest_set (GTK_WIDGET (self),
2960                            GTK_DEST_DEFAULT_HIGHLIGHT,
2961                            folder_view_drag_types,
2962                            G_N_ELEMENTS (folder_view_drag_types),
2963                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2964
2965         g_signal_connect (G_OBJECT (self),
2966                           "drag_data_received",
2967                           G_CALLBACK (on_drag_data_received),
2968                           NULL);
2969
2970
2971         /* Set up the treeview as a dnd source */
2972         gtk_drag_source_set (GTK_WIDGET (self),
2973                              GDK_BUTTON1_MASK,
2974                              folder_view_drag_types,
2975                              G_N_ELEMENTS (folder_view_drag_types),
2976                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2977
2978         g_signal_connect (G_OBJECT (self),
2979                           "drag_motion",
2980                           G_CALLBACK (on_drag_motion),
2981                           NULL);
2982
2983         g_signal_connect (G_OBJECT (self),
2984                           "drag_data_get",
2985                           G_CALLBACK (on_drag_data_get),
2986                           NULL);
2987
2988         g_signal_connect (G_OBJECT (self),
2989                           "drag_drop",
2990                           G_CALLBACK (drag_drop_cb),
2991                           NULL);
2992 }
2993
2994 /*
2995  * This function manages the navigation through the folders using the
2996  * keyboard or the hardware keys in the device
2997  */
2998 static gboolean
2999 on_key_pressed (GtkWidget *self,
3000                 GdkEventKey *event,
3001                 gpointer user_data)
3002 {
3003         GtkTreeSelection *selection;
3004         GtkTreeIter iter;
3005         GtkTreeModel *model;
3006         gboolean retval = FALSE;
3007
3008         /* Up and Down are automatically managed by the treeview */
3009         if (event->keyval == GDK_Return) {
3010                 /* Expand/Collapse the selected row */
3011                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3012                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3013                         GtkTreePath *path;
3014
3015                         path = gtk_tree_model_get_path (model, &iter);
3016
3017                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3018                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3019                         else
3020                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3021                         gtk_tree_path_free (path);
3022                 }
3023                 /* No further processing */
3024                 retval = TRUE;
3025         }
3026
3027         return retval;
3028 }
3029
3030 /*
3031  * We listen to the changes in the local folder account name key,
3032  * because we want to show the right name in the view. The local
3033  * folder account name corresponds to the device name in the Maemo
3034  * version. We do this because we do not want to query gconf on each
3035  * tree view refresh. It's better to cache it and change whenever
3036  * necessary.
3037  */
3038 static void
3039 on_configuration_key_changed (ModestConf* conf,
3040                               const gchar *key,
3041                               ModestConfEvent event,
3042                               ModestConfNotificationId id,
3043                               ModestFolderView *self)
3044 {
3045         ModestFolderViewPrivate *priv;
3046
3047
3048         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3049         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3050
3051         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3052                 g_free (priv->local_account_name);
3053
3054                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3055                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3056                 else
3057                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3058                                                                            MODEST_CONF_DEVICE_NAME, NULL);
3059
3060                 /* Force a redraw */
3061 #if GTK_CHECK_VERSION(2, 8, 0)
3062                 GtkTreeViewColumn * tree_column;
3063
3064                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3065                                                         NAME_COLUMN);
3066                 gtk_tree_view_column_queue_resize (tree_column);
3067 #else
3068                 gtk_widget_queue_draw (GTK_WIDGET (self));
3069 #endif
3070         }
3071 }
3072
3073 void
3074 modest_folder_view_set_style (ModestFolderView *self,
3075                               ModestFolderViewStyle style)
3076 {
3077         ModestFolderViewPrivate *priv;
3078
3079         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3080         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3081                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3082
3083         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3084
3085
3086         priv->style = style;
3087 }
3088
3089 void
3090 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3091                                                              const gchar *account_id)
3092 {
3093         ModestFolderViewPrivate *priv;
3094         GtkTreeModel *model;
3095
3096         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3097
3098         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3099
3100         /* This will be used by the filter_row callback,
3101          * to decided which rows to show: */
3102         if (priv->visible_account_id) {
3103                 g_free (priv->visible_account_id);
3104                 priv->visible_account_id = NULL;
3105         }
3106         if (account_id)
3107                 priv->visible_account_id = g_strdup (account_id);
3108
3109         /* Refilter */
3110         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3111         if (GTK_IS_TREE_MODEL_FILTER (model))
3112                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3113
3114         /* Save settings to gconf */
3115         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3116                                    MODEST_CONF_FOLDER_VIEW_KEY);
3117 }
3118
3119 const gchar *
3120 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3121 {
3122         ModestFolderViewPrivate *priv;
3123
3124         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3125
3126         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3127
3128         return (const gchar *) priv->visible_account_id;
3129 }
3130
3131 static gboolean
3132 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3133 {
3134         do {
3135                 GtkTreeIter child;
3136                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3137
3138                 gtk_tree_model_get (model, iter,
3139                                     TYPE_COLUMN,
3140                                     &type, -1);
3141
3142                 gboolean result = FALSE;
3143                 if (type == TNY_FOLDER_TYPE_INBOX) {
3144                         result = TRUE;
3145                 }
3146                 if (result) {
3147                         *inbox_iter = *iter;
3148                         return TRUE;
3149                 }
3150
3151                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3152                         if (find_inbox_iter (model, &child, inbox_iter))
3153                                 return TRUE;
3154                 }
3155
3156         } while (gtk_tree_model_iter_next (model, iter));
3157
3158         return FALSE;
3159 }
3160
3161
3162
3163
3164 void
3165 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3166 {
3167         GtkTreeModel *model;
3168         GtkTreeIter iter, inbox_iter;
3169         GtkTreeSelection *sel;
3170         GtkTreePath *path = NULL;
3171
3172         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3173
3174         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3175         if (!model)
3176                 return;
3177
3178         expand_root_items (self);
3179         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3180
3181         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3182                 g_warning ("%s: model is empty", __FUNCTION__);
3183                 return;
3184         }
3185
3186         if (find_inbox_iter (model, &iter, &inbox_iter))
3187                 path = gtk_tree_model_get_path (model, &inbox_iter);
3188         else
3189                 path = gtk_tree_path_new_first ();
3190
3191         /* Select the row and free */
3192         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3193         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3194         gtk_tree_path_free (path);
3195
3196         /* set focus */
3197         gtk_widget_grab_focus (GTK_WIDGET(self));
3198 }
3199
3200
3201 /* recursive */
3202 static gboolean
3203 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3204                   TnyFolder* folder)
3205 {
3206         do {
3207                 GtkTreeIter child;
3208                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3209                 TnyFolder* a_folder;
3210                 gchar *name = NULL;
3211
3212                 gtk_tree_model_get (model, iter,
3213                                     INSTANCE_COLUMN, &a_folder,
3214                                     NAME_COLUMN, &name,
3215                                     TYPE_COLUMN, &type,
3216                                     -1);
3217                 g_free (name);
3218
3219                 if (folder == a_folder) {
3220                         g_object_unref (a_folder);
3221                         *folder_iter = *iter;
3222                         return TRUE;
3223                 }
3224                 g_object_unref (a_folder);
3225
3226                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3227                         if (find_folder_iter (model, &child, folder_iter, folder))
3228                                 return TRUE;
3229                 }
3230
3231         } while (gtk_tree_model_iter_next (model, iter));
3232
3233         return FALSE;
3234 }
3235
3236 #ifndef MODEST_TOOLKIT_HILDON2
3237 static void
3238 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3239                                      GtkTreePath *path,
3240                                      GtkTreeIter *iter,
3241                                      ModestFolderView *self)
3242 {
3243         ModestFolderViewPrivate *priv = NULL;
3244         GtkTreeSelection *sel;
3245         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3246         GObject *instance = NULL;
3247
3248         if (!MODEST_IS_FOLDER_VIEW(self))
3249                 return;
3250
3251         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3252
3253         priv->reexpand = TRUE;
3254
3255         gtk_tree_model_get (tree_model, iter,
3256                             TYPE_COLUMN, &type,
3257                             INSTANCE_COLUMN, &instance,
3258                             -1);
3259
3260         if (!instance)
3261                 return;
3262
3263         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3264                 priv->folder_to_select = g_object_ref (instance);
3265         }
3266         g_object_unref (instance);
3267
3268         if (priv->folder_to_select) {
3269
3270                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3271                                                        FALSE)) {
3272                         GtkTreePath *path;
3273                         path = gtk_tree_model_get_path (tree_model, iter);
3274                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3275
3276                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3277
3278                         gtk_tree_selection_select_iter (sel, iter);
3279                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3280
3281                         gtk_tree_path_free (path);
3282                 }
3283
3284                 /* Disable next */
3285                 modest_folder_view_disable_next_folder_selection (self);
3286
3287                 /* Refilter the model */
3288                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3289         }
3290 }
3291 #endif
3292
3293 void
3294 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3295 {
3296         ModestFolderViewPrivate *priv;
3297
3298         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3299
3300         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3301
3302         if (priv->folder_to_select)
3303                 g_object_unref(priv->folder_to_select);
3304
3305         priv->folder_to_select = NULL;
3306 }
3307
3308 gboolean
3309 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3310                                   gboolean after_change)
3311 {
3312         GtkTreeModel *model;
3313         GtkTreeIter iter, folder_iter;
3314         GtkTreeSelection *sel;
3315         ModestFolderViewPrivate *priv = NULL;
3316
3317         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3318         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3319
3320         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3321
3322         if (after_change) {
3323                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3324                 gtk_tree_selection_unselect_all (sel);
3325
3326                 if (priv->folder_to_select)
3327                         g_object_unref(priv->folder_to_select);
3328                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3329                 return TRUE;
3330         }
3331
3332         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3333         if (!model)
3334                 return FALSE;
3335
3336
3337         /* Refilter the model, before selecting the folder */
3338         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3339
3340         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3341                 g_warning ("%s: model is empty", __FUNCTION__);
3342                 return FALSE;
3343         }
3344
3345         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3346                 GtkTreePath *path;
3347
3348                 path = gtk_tree_model_get_path (model, &folder_iter);
3349                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3350
3351                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3352                 gtk_tree_selection_select_iter (sel, &folder_iter);
3353                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3354
3355                 gtk_tree_path_free (path);
3356                 return TRUE;
3357         }
3358         return FALSE;
3359 }
3360
3361
3362 void
3363 modest_folder_view_copy_selection (ModestFolderView *self)
3364 {
3365         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3366
3367         /* Copy selection */
3368         _clipboard_set_selected_data (self, FALSE);
3369 }
3370
3371 void
3372 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3373 {
3374         ModestFolderViewPrivate *priv = NULL;
3375         GtkTreeModel *model = NULL;
3376         const gchar **hidding = NULL;
3377         guint i, n_selected;
3378
3379         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3380         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3381
3382         /* Copy selection */
3383         if (!_clipboard_set_selected_data (folder_view, TRUE))
3384                 return;
3385
3386         /* Get hidding ids */
3387         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3388
3389         /* Clear hidding array created by previous cut operation */
3390         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3391
3392         /* Copy hidding array */
3393         priv->n_selected = n_selected;
3394         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3395         for (i=0; i < n_selected; i++)
3396                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3397
3398         /* Hide cut folders */
3399         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3400         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3401 }
3402
3403 void
3404 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3405                                ModestFolderView *folder_view_dst)
3406 {
3407         GtkTreeModel *filter_model = NULL;
3408         GtkTreeModel *model = NULL;
3409         GtkTreeModel *new_filter_model = NULL;
3410
3411         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3412         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3413
3414         /* Get src model*/
3415         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3416         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3417
3418         /* Build new filter model */
3419         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3420         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3421                                                 filter_row,
3422                                                 folder_view_dst,
3423                                                 NULL);
3424         /* Set copied model */
3425         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3426 #ifndef MODEST_TOOLKIT_HILDON2
3427         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3428                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3429 #endif
3430
3431         /* Free */
3432         g_object_unref (new_filter_model);
3433 }
3434
3435 void
3436 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3437                                           gboolean show)
3438 {
3439         GtkTreeModel *model = NULL;
3440         ModestFolderViewPrivate* priv;
3441
3442         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3443
3444         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3445         priv->show_non_move = show;
3446 /*      modest_folder_view_update_model(folder_view, */
3447 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3448
3449         /* Hide special folders */
3450         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3451         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3452                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3453         }
3454 }
3455
3456 /* Returns FALSE if it did not selected anything */
3457 static gboolean
3458 _clipboard_set_selected_data (ModestFolderView *folder_view,
3459                               gboolean delete)
3460 {
3461         ModestFolderViewPrivate *priv = NULL;
3462         TnyFolderStore *folder = NULL;
3463         gboolean retval = FALSE;
3464
3465         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3466         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3467
3468         /* Set selected data on clipboard   */
3469         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3470         folder = modest_folder_view_get_selected (folder_view);
3471
3472         /* Do not allow to select an account */
3473         if (TNY_IS_FOLDER (folder)) {
3474                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3475                 retval = TRUE;
3476         }
3477
3478         /* Free */
3479         g_object_unref (folder);
3480
3481         return retval;
3482 }
3483
3484 static void
3485 _clear_hidding_filter (ModestFolderView *folder_view)
3486 {
3487         ModestFolderViewPrivate *priv;
3488         guint i;
3489
3490         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3491         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3492
3493         if (priv->hidding_ids != NULL) {
3494                 for (i=0; i < priv->n_selected; i++)
3495                         g_free (priv->hidding_ids[i]);
3496                 g_free(priv->hidding_ids);
3497         }
3498 }
3499
3500
3501 static void
3502 on_display_name_changed (ModestAccountMgr *mgr,
3503                          const gchar *account,
3504                          gpointer user_data)
3505 {
3506         ModestFolderView *self;
3507
3508         self = MODEST_FOLDER_VIEW (user_data);
3509
3510         /* Force a redraw */
3511 #if GTK_CHECK_VERSION(2, 8, 0)
3512         GtkTreeViewColumn * tree_column;
3513
3514         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3515                                                 NAME_COLUMN);
3516         gtk_tree_view_column_queue_resize (tree_column);
3517 #else
3518         gtk_widget_queue_draw (GTK_WIDGET (self));
3519 #endif
3520 }
3521
3522 void 
3523 modest_folder_view_set_cell_style (ModestFolderView *self,
3524                                    ModestFolderViewCellStyle cell_style)
3525 {
3526         ModestFolderViewPrivate *priv = NULL;
3527
3528         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3529         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3530
3531         priv->cell_style = cell_style;
3532
3533         g_object_set (G_OBJECT (priv->messages_renderer),
3534                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3535                       NULL);
3536         
3537         gtk_widget_queue_draw (GTK_WIDGET (self));
3538 }
3539
3540 static void
3541 update_style (ModestFolderView *self)
3542 {
3543         ModestFolderViewPrivate *priv;
3544         GdkColor style_color;
3545         PangoAttrList *attr_list;
3546         GtkStyle *style;
3547         PangoAttribute *attr;
3548
3549         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3550         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3551
3552         /* Set color */
3553
3554         attr_list = pango_attr_list_new ();
3555         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3556                 gdk_color_parse ("grey", &style_color);
3557         }
3558         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3559         pango_attr_list_insert (attr_list, attr);
3560         
3561         /* set font */
3562         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3563                                            (GTK_WIDGET(self)),
3564                                            "SmallSystemFont", NULL,
3565                                            G_TYPE_NONE);  
3566         attr = pango_attr_font_desc_new (pango_font_description_copy
3567                                          (style->font_desc));
3568         pango_attr_list_insert (attr_list, attr);
3569
3570         g_object_set (G_OBJECT (priv->messages_renderer),
3571                       "foreground-gdk", &style_color,
3572                       "foreground-set", TRUE,
3573                       "attributes", attr_list,
3574                       NULL);
3575         pango_attr_list_unref (attr_list);
3576 }
3577
3578 static void 
3579 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3580 {
3581         if (strcmp ("style", spec->name) == 0) {
3582                 update_style (MODEST_FOLDER_VIEW (obj));
3583                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3584         } 
3585 }
3586
3587 void 
3588 modest_folder_view_set_filter (ModestFolderView *self,
3589                                ModestFolderViewFilter filter)
3590 {
3591         ModestFolderViewPrivate *priv;
3592         GtkTreeModel *filter_model;
3593
3594         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3595         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3596
3597         priv->filter |= filter;
3598
3599         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3600         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3601                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3602         }
3603 }
3604
3605 void 
3606 modest_folder_view_unset_filter (ModestFolderView *self,
3607                                  ModestFolderViewFilter filter)
3608 {
3609         ModestFolderViewPrivate *priv;
3610         GtkTreeModel *filter_model;
3611
3612         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3613         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3614
3615         priv->filter &= ~filter;
3616
3617         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3618         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3619                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3620         }
3621 }