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