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