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