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