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