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