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