Search if folder with same child name as list to mov (fixes NB#102359).
[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 typedef struct _ForeachFolderInfo {
1702         gchar *needle;
1703         gboolean found;
1704 } ForeachFolderInfo;
1705
1706 static gboolean 
1707 foreach_folder_with_id (GtkTreeModel *model,
1708                         GtkTreePath *path,
1709                         GtkTreeIter *iter,
1710                         gpointer data)
1711 {
1712         ForeachFolderInfo *info;
1713         GObject *instance;
1714
1715         info = (ForeachFolderInfo *) data;
1716         gtk_tree_model_get (model, iter,
1717                             INSTANCE_COLUMN, &instance,
1718                             -1);
1719
1720         if (TNY_IS_FOLDER (instance)) {
1721                 const gchar *id;
1722                 gchar *collate;
1723                 id = tny_folder_get_id (TNY_FOLDER (instance));
1724                 if (id) {
1725                         collate = g_utf8_collate_key (id, -1);
1726                         info->found = !strcmp (info->needle, collate);
1727                         g_free (collate);
1728                 }
1729         }
1730
1731         return info->found;
1732         
1733 }
1734
1735
1736 static gboolean
1737 has_folder_with_id (ModestFolderView *self, const gchar *id)
1738 {
1739         GtkTreeModel *model;
1740         ForeachFolderInfo info = {NULL, FALSE};
1741
1742         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1743         info.needle = g_utf8_collate_key (id, -1);
1744         
1745         gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1746         g_free (info.needle);
1747
1748         return info.found;
1749 }
1750
1751 static gboolean
1752 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1753 {
1754         const gchar *a_id;
1755         gboolean retval = FALSE;
1756
1757         a_id = tny_folder_get_id (a);
1758         if (a_id) {
1759                 const gchar *b_id;
1760                 b_id = tny_folder_get_id (b);
1761                 
1762                 if (b_id) {
1763                         const gchar *last_bar;
1764                         gchar *string_to_match;
1765                         last_bar = g_strrstr (b_id, "/");
1766                         if (last_bar)
1767                                 last_bar++;
1768                         else
1769                                 last_bar = b_id;
1770                         string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1771                         retval = has_folder_with_id (self, string_to_match);
1772                         g_free (string_to_match);
1773                 }
1774         }
1775
1776         return retval;
1777 }
1778
1779 static gboolean
1780 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1781 {
1782         ModestFolderViewPrivate *priv;
1783         TnyIterator *iterator;
1784         gboolean retval = TRUE;
1785
1786         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1787         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1788
1789         for (iterator = tny_list_create_iterator (priv->list_to_move);
1790              retval && !tny_iterator_is_done (iterator);
1791              tny_iterator_next (iterator)) {
1792                 GObject *instance;
1793                 instance = tny_iterator_get_current (iterator);
1794                 if (instance == (GObject *) folder) {
1795                         retval = FALSE;
1796                 } else if (TNY_IS_FOLDER (instance)) {
1797                         retval = !is_parent_of (TNY_FOLDER (instance), folder);
1798                         if (retval) {
1799                                 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1800                         }
1801                 }
1802                 g_object_unref (instance);
1803         }
1804         g_object_unref (iterator);
1805
1806         return retval;
1807 }
1808
1809
1810 /*
1811  * We use this function to implement the
1812  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1813  * account in this case, and the local folders.
1814  */
1815 static gboolean
1816 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1817 {
1818         ModestFolderViewPrivate *priv;
1819         gboolean retval = TRUE;
1820         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1821         GObject *instance = NULL;
1822         const gchar *id = NULL;
1823         guint i;
1824         gboolean found = FALSE;
1825         gboolean cleared = FALSE;
1826         ModestTnyFolderRules rules = 0;
1827
1828         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1829         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1830
1831         gtk_tree_model_get (model, iter,
1832                             TYPE_COLUMN, &type,
1833                             INSTANCE_COLUMN, &instance,
1834                             -1);
1835
1836         /* Do not show if there is no instance, this could indeed
1837            happen when the model is being modified while it's being
1838            drawn. This could occur for example when moving folders
1839            using drag&drop */
1840         if (!instance)
1841                 return FALSE;
1842
1843         if (TNY_IS_ACCOUNT (instance)) {
1844                 TnyAccount *acc = TNY_ACCOUNT (instance);
1845                 const gchar *account_id = tny_account_get_id (acc);
1846
1847                 /* If it isn't a special folder,
1848                  * don't show it unless it is the visible account: */
1849                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1850                     !modest_tny_account_is_virtual_local_folders (acc) &&
1851                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1852
1853                         /* Show only the visible account id */
1854                         if (priv->visible_account_id) {
1855                                 if (strcmp (account_id, priv->visible_account_id))
1856                                         retval = FALSE;
1857                         } else {
1858                                 retval = FALSE;
1859                         }
1860                 }
1861
1862                 /* Never show these to the user. They are merged into one folder
1863                  * in the local-folders account instead: */
1864                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1865                         retval = FALSE;
1866         } else {
1867                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1868                         /* Only show special folders for current account if needed */
1869                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1870                                 TnyAccount *account;
1871
1872                                 account = tny_folder_get_account (TNY_FOLDER (instance));
1873
1874                                 if (TNY_IS_ACCOUNT (account)) {
1875                                         const gchar *account_id = tny_account_get_id (account);
1876
1877                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
1878                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1879                                                 /* Show only the visible account id */
1880                                                 if (priv->visible_account_id) {
1881                                                         if (strcmp (account_id, priv->visible_account_id))
1882                                                                 retval = FALSE;
1883                                                 }
1884                                         }
1885                                                 g_object_unref (account);
1886                                 }
1887                         }
1888
1889                 }
1890         }
1891
1892         /* Check hiding (if necessary) */
1893         cleared = modest_email_clipboard_cleared (priv->clipboard);
1894         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1895                 id = tny_folder_get_id (TNY_FOLDER(instance));
1896                 if (priv->hidding_ids != NULL)
1897                         for (i=0; i < priv->n_selected && !found; i++)
1898                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1899                                         found = (!strcmp (priv->hidding_ids[i], id));
1900
1901                 retval = !found;
1902         }
1903
1904         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1905         folder as no message can be move there according to UI specs */
1906         if (!priv->show_non_move) {
1907                 if (priv->list_to_move && 
1908                     tny_list_get_length (priv->list_to_move) > 0 &&
1909                     TNY_IS_FOLDER (instance)) {
1910                         retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1911                 }
1912                 if (retval && TNY_IS_FOLDER (instance) && 
1913                     modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1914                         switch (type) {
1915                         case TNY_FOLDER_TYPE_OUTBOX:
1916                         case TNY_FOLDER_TYPE_SENT:
1917                         case TNY_FOLDER_TYPE_DRAFTS:
1918                                 retval = FALSE;
1919                                 break;
1920                         case TNY_FOLDER_TYPE_UNKNOWN:
1921                         case TNY_FOLDER_TYPE_NORMAL:
1922                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1923                                 if (type == TNY_FOLDER_TYPE_INVALID)
1924                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1925                                 
1926                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1927                                     type == TNY_FOLDER_TYPE_SENT
1928                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1929                                         retval = FALSE;
1930                                 break;
1931                         default:
1932                                 break;
1933                         }
1934                 }
1935         }
1936
1937         /* apply special filters */
1938         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1939                 if (TNY_IS_ACCOUNT (instance))
1940                         return FALSE;
1941         }
1942
1943         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1944                 if (TNY_IS_FOLDER (instance)) {
1945                         /* Check folder rules */
1946                         ModestTnyFolderRules rules;
1947
1948                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1949                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
1950                 } else if (TNY_IS_ACCOUNT (instance)) {
1951                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1952                                 retval = FALSE;
1953                         } else {
1954                                 retval = TRUE;
1955                         }
1956                 }
1957         }
1958
1959         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1960                 if (TNY_IS_FOLDER (instance)) {
1961                         TnyFolderType guess_type;
1962
1963                         if (TNY_FOLDER_TYPE_NORMAL) {
1964                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1965                         } else {
1966                                 guess_type = type;
1967                         }
1968
1969                         switch (type) {
1970                         case TNY_FOLDER_TYPE_OUTBOX:
1971                         case TNY_FOLDER_TYPE_SENT:
1972                         case TNY_FOLDER_TYPE_DRAFTS:
1973                         case TNY_FOLDER_TYPE_ARCHIVE:
1974                         case TNY_FOLDER_TYPE_INBOX:
1975                                 retval = FALSE;
1976                                 break;
1977                         case TNY_FOLDER_TYPE_UNKNOWN:
1978                         case TNY_FOLDER_TYPE_NORMAL:
1979                                 break;
1980                         default:
1981                                 break;
1982                         }
1983
1984                 } else if (TNY_IS_ACCOUNT (instance)) {
1985                         retval = FALSE;
1986                 }
1987         }
1988
1989         if (retval && TNY_IS_FOLDER (instance)) {
1990                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1991         }
1992
1993         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1994                 if (TNY_IS_FOLDER (instance)) {
1995                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1996                 } else if (TNY_IS_ACCOUNT (instance)) {
1997                         retval = FALSE;
1998                 }
1999         }
2000
2001         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2002                 if (TNY_IS_FOLDER (instance)) {
2003                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2004                 } else if (TNY_IS_ACCOUNT (instance)) {
2005                         retval = FALSE;
2006                 }
2007         }
2008
2009         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2010                 if (TNY_IS_FOLDER (instance)) {
2011                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2012                 } else if (TNY_IS_ACCOUNT (instance)) {
2013                         retval = FALSE;
2014                 }
2015         }
2016
2017         /* Free */
2018         g_object_unref (instance);
2019
2020         return retval;
2021 }
2022
2023
2024 gboolean
2025 modest_folder_view_update_model (ModestFolderView *self,
2026                                  TnyAccountStore *account_store)
2027 {
2028         ModestFolderViewPrivate *priv;
2029         GtkTreeModel *model /* , *old_model */;
2030         GtkTreeModel *filter_model = NULL, *sortable = NULL;
2031
2032         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2033         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2034                               FALSE);
2035
2036         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2037
2038         /* Notify that there is no folder selected */
2039         g_signal_emit (G_OBJECT(self),
2040                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2041                        NULL, FALSE);
2042         if (priv->cur_folder_store) {
2043                 g_object_unref (priv->cur_folder_store);
2044                 priv->cur_folder_store = NULL;
2045         }
2046
2047         /* FIXME: the local accounts are not shown when the query
2048            selects only the subscribed folders */
2049 #ifdef MODEST_TOOLKIT_HILDON2
2050         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
2051                                                           TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2052         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2053                                                       MODEST_FOLDER_PATH_SEPARATOR);
2054 #else
2055         model = tny_gtk_folder_store_tree_model_new (NULL);
2056 #endif
2057
2058         /* When the model is a list store (plain representation) the
2059            outbox is not a child of any account so we have to manually
2060            delete it because removing the local folders account won't
2061            delete it (because tny_folder_get_account() is not defined
2062            for a merge folder */
2063         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2064                 TnyAccount *account;
2065                 ModestTnyAccountStore *acc_store;
2066
2067                 acc_store = modest_runtime_get_account_store ();
2068                 account = modest_tny_account_store_get_local_folders_account (acc_store);
2069
2070                 if (g_signal_handler_is_connected (account,
2071                                                    priv->outbox_deleted_handler))
2072                         g_signal_handler_disconnect (account,
2073                                                      priv->outbox_deleted_handler);
2074
2075                 priv->outbox_deleted_handler =
2076                         g_signal_connect (account,
2077                                           "outbox-deleted",
2078                                           G_CALLBACK (on_outbox_deleted_cb),
2079                                           self);
2080                 g_object_unref (account);
2081         }
2082
2083         /* Get the accounts: */
2084         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2085                                         TNY_LIST (model),
2086                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2087
2088         sortable = gtk_tree_model_sort_new_with_model (model);
2089         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2090                                               NAME_COLUMN,
2091                                               GTK_SORT_ASCENDING);
2092         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2093                                          NAME_COLUMN,
2094                                          cmp_rows, NULL, NULL);
2095
2096         /* Create filter model */
2097         filter_model = gtk_tree_model_filter_new (sortable, NULL);
2098         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2099                                                 filter_row,
2100                                                 self,
2101                                                 NULL);
2102
2103         /* Set new model */
2104         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2105 #ifndef MODEST_TOOLKIT_HILDON2
2106         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2107                           (GCallback) on_row_inserted_maybe_select_folder, self);
2108 #endif
2109
2110         g_object_unref (model);
2111         g_object_unref (filter_model);
2112         g_object_unref (sortable);
2113
2114         /* Force a reselection of the INBOX next time the widget is shown */
2115         priv->reselect = TRUE;
2116
2117         return TRUE;
2118 }
2119
2120
2121 static void
2122 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2123 {
2124         GtkTreeModel *model = NULL;
2125         TnyFolderStore *folder = NULL;
2126         GtkTreeIter iter;
2127         ModestFolderView *tree_view = NULL;
2128         ModestFolderViewPrivate *priv = NULL;
2129         gboolean selected = FALSE;
2130
2131         g_return_if_fail (sel);
2132         g_return_if_fail (user_data);
2133
2134         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2135
2136         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2137
2138         tree_view = MODEST_FOLDER_VIEW (user_data);
2139
2140         if (selected) {
2141                 gtk_tree_model_get (model, &iter,
2142                                     INSTANCE_COLUMN, &folder,
2143                                     -1);
2144
2145                 /* If the folder is the same do not notify */
2146                 if (folder && priv->cur_folder_store == folder) {
2147                         g_object_unref (folder);
2148                         return;
2149                 }
2150         }
2151
2152         /* Current folder was unselected */
2153         if (priv->cur_folder_store) {
2154                 /* We must do this firstly because a libtinymail-camel
2155                    implementation detail. If we issue the signal
2156                    before doing the sync_async, then that signal could
2157                    cause (and it actually does it) a free of the
2158                    summary of the folder (because the main window will
2159                    clear the headers view */
2160                 if (TNY_IS_FOLDER(priv->cur_folder_store))
2161                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2162                                                FALSE, NULL, NULL, NULL);
2163
2164                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2165                        priv->cur_folder_store, FALSE);
2166
2167                 g_object_unref (priv->cur_folder_store);
2168                 priv->cur_folder_store = NULL;
2169         }
2170
2171         /* New current references */
2172         priv->cur_folder_store = folder;
2173
2174         /* New folder has been selected. Do not notify if there is
2175            nothing new selected */
2176         if (selected) {
2177                 g_signal_emit (G_OBJECT(tree_view),
2178                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2179                                0, priv->cur_folder_store, TRUE);
2180         }
2181 }
2182
2183 static void
2184 on_row_activated (GtkTreeView *treeview,
2185                   GtkTreePath *treepath,
2186                   GtkTreeViewColumn *column,
2187                   gpointer user_data)
2188 {
2189         GtkTreeModel *model = NULL;
2190         TnyFolderStore *folder = NULL;
2191         GtkTreeIter iter;
2192         ModestFolderView *self = NULL;
2193         ModestFolderViewPrivate *priv = NULL;
2194
2195         g_return_if_fail (treeview);
2196         g_return_if_fail (user_data);
2197
2198         self = MODEST_FOLDER_VIEW (user_data);
2199         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2200
2201         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2202
2203         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2204                 return;
2205
2206         gtk_tree_model_get (model, &iter,
2207                             INSTANCE_COLUMN, &folder,
2208                             -1);
2209
2210         g_signal_emit (G_OBJECT(self),
2211                        signals[FOLDER_ACTIVATED_SIGNAL],
2212                        0, folder);
2213
2214 #ifdef MODEST_TOOLKIT_HILDON2
2215         HildonUIMode ui_mode;
2216         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2217         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2218                 if (priv->cur_folder_store)
2219                         g_object_unref (priv->cur_folder_store);
2220                 priv->cur_folder_store = g_object_ref (folder);
2221         }
2222 #endif
2223
2224         g_object_unref (folder);
2225 }
2226
2227 TnyFolderStore *
2228 modest_folder_view_get_selected (ModestFolderView *self)
2229 {
2230         ModestFolderViewPrivate *priv;
2231
2232         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2233
2234         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2235         if (priv->cur_folder_store)
2236                 g_object_ref (priv->cur_folder_store);
2237
2238         return priv->cur_folder_store;
2239 }
2240
2241 static gint
2242 get_cmp_rows_type_pos (GObject *folder)
2243 {
2244         /* Remote accounts -> Local account -> MMC account .*/
2245         /* 0, 1, 2 */
2246
2247         if (TNY_IS_ACCOUNT (folder) &&
2248                 modest_tny_account_is_virtual_local_folders (
2249                         TNY_ACCOUNT (folder))) {
2250                 return 1;
2251         } else if (TNY_IS_ACCOUNT (folder)) {
2252                 TnyAccount *account = TNY_ACCOUNT (folder);
2253                 const gchar *account_id = tny_account_get_id (account);
2254                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2255                         return 2;
2256                 else
2257                         return 0;
2258         }
2259         else {
2260                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2261                 return -1; /* Should never happen */
2262         }
2263 }
2264
2265 static gboolean
2266 inbox_is_special (TnyFolderStore *folder_store)
2267 {
2268         gboolean is_special = TRUE;
2269
2270         if (TNY_IS_FOLDER (folder_store)) {
2271                 const gchar *id;
2272                 gchar *downcase;
2273                 gchar *last_bar;
2274                 gchar *last_inbox_bar;
2275
2276                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2277                 downcase = g_utf8_strdown (id, -1);
2278                 last_bar = g_strrstr (downcase, "/");
2279                 if (last_bar) {
2280                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2281                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2282                                 is_special = FALSE;
2283                 } else {
2284                         is_special = FALSE;
2285                 }
2286                 g_free (downcase);
2287         }
2288         return is_special;
2289 }
2290
2291 static gint
2292 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2293 {
2294         TnyAccount *account;
2295         gboolean is_special;
2296         /* Inbox, Outbox, Drafts, Sent, User */
2297         /* 0, 1, 2, 3, 4 */
2298
2299         if (!TNY_IS_FOLDER (folder_store))
2300                 return 4;
2301         switch (t) {
2302         case TNY_FOLDER_TYPE_INBOX:
2303         {
2304                 account = tny_folder_get_account (folder_store);
2305                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2306
2307                 /* In inbox case we need to know if the inbox is really the top
2308                  * inbox of the account, or if it's a submailbox inbox. To do
2309                  * this we'll apply an heuristic rule: Find last "/" and check
2310                  * if it's preceeded by another Inbox */
2311                 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2312                 g_object_unref (account);
2313                 return is_special?0:4;
2314         }
2315         break;
2316         case TNY_FOLDER_TYPE_OUTBOX:
2317                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2318                 break;
2319         case TNY_FOLDER_TYPE_DRAFTS:
2320         {
2321                 account = tny_folder_get_account (folder_store);
2322                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2323                 g_object_unref (account);
2324                 return is_special?1:4;
2325         }
2326         break;
2327         case TNY_FOLDER_TYPE_SENT:
2328         {
2329                 account = tny_folder_get_account (folder_store);
2330                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2331                 g_object_unref (account);
2332                 return is_special?3:4;
2333         }
2334         break;
2335         default:
2336                 return 4;
2337         }
2338 }
2339
2340 static gint
2341 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2342 {
2343         const gchar *a1_name, *a2_name;
2344
2345         a1_name = tny_account_get_name (a1);
2346         a2_name = tny_account_get_name (a2);
2347
2348         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2349 }
2350
2351 static gint
2352 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2353 {
2354         TnyAccount *a1 = NULL, *a2 = NULL;
2355         gint cmp;
2356
2357         if (TNY_IS_ACCOUNT (s1)) {
2358                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2359         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2360                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2361         }
2362
2363         if (TNY_IS_ACCOUNT (s2)) {
2364                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2365         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2366                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2367         }
2368
2369         if (!a1 || !a2) {
2370                 if (!a1 && !a2)
2371                         cmp = 0;
2372                 else if (!a1)
2373                         cmp = 1;
2374                 else
2375                         cmp = -1;
2376                 goto finish;
2377         }
2378
2379         if (a1 == a2) {
2380                 cmp = 0;
2381                 goto finish;
2382         }
2383         /* First we sort with the type of account */
2384         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2385         if (cmp != 0)
2386                 goto finish;
2387
2388         cmp = compare_account_names (a1, a2);
2389
2390 finish:
2391         if (a1)
2392                 g_object_unref (a1);
2393         if (a2)
2394                 g_object_unref (a2);
2395
2396         return cmp;
2397 }
2398
2399 static gint
2400 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2401 {
2402         gint is_account1, is_account2;
2403
2404         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2405         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2406
2407         return is_account2 - is_account1;
2408 }
2409
2410 /*
2411  * This function orders the mail accounts according to these rules:
2412  * 1st - remote accounts
2413  * 2nd - local account
2414  * 3rd - MMC account
2415  */
2416 static gint
2417 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2418           gpointer user_data)
2419 {
2420         gint cmp = 0;
2421         gchar *name1 = NULL;
2422         gchar *name2 = NULL;
2423         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2424         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2425         GObject *folder1 = NULL;
2426         GObject *folder2 = NULL;
2427
2428         gtk_tree_model_get (tree_model, iter1,
2429                             NAME_COLUMN, &name1,
2430                             TYPE_COLUMN, &type,
2431                             INSTANCE_COLUMN, &folder1,
2432                             -1);
2433         gtk_tree_model_get (tree_model, iter2,
2434                             NAME_COLUMN, &name2,
2435                             TYPE_COLUMN, &type2,
2436                             INSTANCE_COLUMN, &folder2,
2437                             -1);
2438
2439         /* Return if we get no folder. This could happen when folder
2440            operations are happening. The model is updated after the
2441            folder copy/move actually occurs, so there could be
2442            situations where the model to be drawn is not correct */
2443         if (!folder1 || !folder2)
2444                 goto finish;
2445
2446         /* Sort by type. First the special folders, then the archives */
2447         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2448         if (cmp != 0)
2449                 goto finish;
2450
2451         /* Now we sort using the account of each folder */
2452         if (TNY_IS_FOLDER_STORE (folder1) && 
2453             TNY_IS_FOLDER_STORE (folder2)) {
2454                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2455                 if (cmp != 0)
2456                         goto finish;
2457
2458                 /* Each group is preceeded by its account */
2459                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2460                 if (cmp != 0)
2461                         goto finish;
2462         }
2463
2464         /* Pure sort by name */
2465         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2466  finish:
2467         if (folder1)
2468                 g_object_unref(G_OBJECT(folder1));
2469         if (folder2)
2470                 g_object_unref(G_OBJECT(folder2));
2471
2472         g_free (name1);
2473         g_free (name2);
2474
2475         return cmp;
2476 }
2477
2478 /*****************************************************************************/
2479 /*                        DRAG and DROP stuff                                */
2480 /*****************************************************************************/
2481 /*
2482  * This function fills the #GtkSelectionData with the row and the
2483  * model that has been dragged. It's called when this widget is a
2484  * source for dnd after the event drop happened
2485  */
2486 static void
2487 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2488                   guint info, guint time, gpointer data)
2489 {
2490         GtkTreeSelection *selection;
2491         GtkTreeModel *model;
2492         GtkTreeIter iter;
2493         GtkTreePath *source_row;
2494
2495         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2496         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2497
2498                 source_row = gtk_tree_model_get_path (model, &iter);
2499                 gtk_tree_set_row_drag_data (selection_data,
2500                                             model,
2501                                             source_row);
2502
2503                 gtk_tree_path_free (source_row);
2504         }
2505 }
2506
2507 typedef struct _DndHelper {
2508         ModestFolderView *folder_view;
2509         gboolean delete_source;
2510         GtkTreePath *source_row;
2511 } DndHelper;
2512
2513 static void
2514 dnd_helper_destroyer (DndHelper *helper)
2515 {
2516         /* Free the helper */
2517         gtk_tree_path_free (helper->source_row);
2518         g_slice_free (DndHelper, helper);
2519 }
2520
2521 static void
2522 xfer_folder_cb (ModestMailOperation *mail_op,
2523                 TnyFolder *new_folder,
2524                 gpointer user_data)
2525 {
2526         if (new_folder) {
2527                 /* Select the folder */
2528                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2529                                                   new_folder, FALSE);
2530         }
2531 }
2532
2533
2534 /* get the folder for the row the treepath refers to. */
2535 /* folder must be unref'd */
2536 static TnyFolderStore *
2537 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2538 {
2539         GtkTreeIter iter;
2540         TnyFolderStore *folder = NULL;
2541
2542         if (gtk_tree_model_get_iter (model,&iter, path))
2543                 gtk_tree_model_get (model, &iter,
2544                                     INSTANCE_COLUMN, &folder,
2545                                     -1);
2546         return folder;
2547 }
2548
2549
2550 /*
2551  * This function is used by drag_data_received_cb to manage drag and
2552  * drop of a header, i.e, and drag from the header view to the folder
2553  * view.
2554  */
2555 static void
2556 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2557                                 GtkTreeModel *dest_model,
2558                                 GtkTreePath  *dest_row,
2559                                 GtkSelectionData *selection_data)
2560 {
2561         TnyList *headers = NULL;
2562         TnyFolder *folder = NULL, *src_folder = NULL;
2563         TnyFolderType folder_type;
2564         GtkTreeIter source_iter, dest_iter;
2565         ModestWindowMgr *mgr = NULL;
2566         ModestWindow *main_win = NULL;
2567         gchar **uris, **tmp;
2568
2569         /* Build the list of headers */
2570         mgr = modest_runtime_get_window_mgr ();
2571         headers = tny_simple_list_new ();
2572         uris = modest_dnd_selection_data_get_paths (selection_data);
2573         tmp = uris;
2574
2575         while (*tmp != NULL) {
2576                 TnyHeader *header;
2577                 GtkTreePath *path;
2578                 gboolean first = TRUE;
2579
2580                 /* Get header */
2581                 path = gtk_tree_path_new_from_string (*tmp);
2582                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2583                 gtk_tree_model_get (source_model, &source_iter,
2584                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2585                                     &header, -1);
2586
2587                 /* Do not enable d&d of headers already opened */
2588                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2589                         tny_list_append (headers, G_OBJECT (header));
2590
2591                 if (G_UNLIKELY (first)) {
2592                         src_folder = tny_header_get_folder (header);
2593                         first = FALSE;
2594                 }
2595
2596                 /* Free and go on */
2597                 gtk_tree_path_free (path);
2598                 g_object_unref (header);
2599                 tmp++;
2600         }
2601         g_strfreev (uris);
2602
2603         /* This could happen ig we perform a d&d very quickly over the
2604            same row that row could dissapear because message is
2605            transferred */
2606         if (!TNY_IS_FOLDER (src_folder))
2607                 goto cleanup;
2608
2609         /* Get the target folder */
2610         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2611         gtk_tree_model_get (dest_model, &dest_iter,
2612                             INSTANCE_COLUMN,
2613                             &folder, -1);
2614
2615         if (!folder || !TNY_IS_FOLDER(folder)) {
2616 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2617                 goto cleanup;
2618         }
2619
2620         folder_type = modest_tny_folder_guess_folder_type (folder);
2621         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2622 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2623                 goto cleanup;  /* cannot move messages there */
2624         }
2625
2626         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2627 /*              g_warning ("folder not writable"); */
2628                 goto cleanup; /* verboten! */
2629         }
2630
2631         /* Ask for confirmation to move */
2632         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2633         if (!main_win) {
2634                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2635                 goto cleanup;
2636         }
2637
2638         /* Transfer messages */
2639         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2640                                                     headers, folder);
2641
2642         /* Frees */
2643 cleanup:
2644         if (G_IS_OBJECT (src_folder))
2645                 g_object_unref (src_folder);
2646         if (G_IS_OBJECT(folder))
2647                 g_object_unref (G_OBJECT (folder));
2648         if (G_IS_OBJECT(headers))
2649                 g_object_unref (headers);
2650 }
2651
2652 typedef struct {
2653         TnyFolderStore *src_folder;
2654         TnyFolderStore *dst_folder;
2655         ModestFolderView *folder_view;
2656         DndHelper *helper;
2657 } DndFolderInfo;
2658
2659 static void
2660 dnd_folder_info_destroyer (DndFolderInfo *info)
2661 {
2662         if (info->src_folder)
2663                 g_object_unref (info->src_folder);
2664         if (info->dst_folder)
2665                 g_object_unref (info->dst_folder);
2666         g_slice_free (DndFolderInfo, info);
2667 }
2668
2669 static void
2670 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2671                                     GtkWindow *parent_window,
2672                                     TnyAccount *account)
2673 {
2674         /* Show error */
2675         modest_ui_actions_on_account_connection_error (parent_window, account);
2676
2677         /* Free the helper & info */
2678         dnd_helper_destroyer (info->helper);
2679         dnd_folder_info_destroyer (info);
2680 }
2681
2682 static void
2683 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2684                                                      GError *err,
2685                                                      GtkWindow *parent_window,
2686                                                      TnyAccount *account,
2687                                                      gpointer user_data)
2688 {
2689         DndFolderInfo *info = NULL;
2690         ModestMailOperation *mail_op;
2691
2692         info = (DndFolderInfo *) user_data;
2693
2694         if (err || canceled) {
2695                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2696                 return;
2697         }
2698
2699         /* Do the mail operation */
2700         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2701                                                                  modest_ui_actions_move_folder_error_handler,
2702                                                                  info->src_folder, NULL);
2703
2704         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2705                                          mail_op);
2706
2707         /* Transfer the folder */
2708         modest_mail_operation_xfer_folder (mail_op,
2709                                            TNY_FOLDER (info->src_folder),
2710                                            info->dst_folder,
2711                                            info->helper->delete_source,
2712                                            xfer_folder_cb,
2713                                            info->helper->folder_view);
2714
2715         /* Frees */
2716         g_object_unref (G_OBJECT (mail_op));
2717         dnd_helper_destroyer (info->helper);
2718         dnd_folder_info_destroyer (info);
2719 }
2720
2721
2722 static void
2723 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2724                                                      GError *err,
2725                                                      GtkWindow *parent_window,
2726                                                      TnyAccount *account,
2727                                                      gpointer user_data)
2728 {
2729         DndFolderInfo *info = NULL;
2730
2731         info = (DndFolderInfo *) user_data;
2732
2733         if (err || canceled) {
2734                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2735                 return;
2736         }
2737
2738         /* Connect to source folder and perform the copy/move */
2739         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2740                                                        info->src_folder,
2741                                                        drag_and_drop_from_folder_view_src_folder_performer,
2742                                                        info);
2743 }
2744
2745 /*
2746  * This function is used by drag_data_received_cb to manage drag and
2747  * drop of a folder, i.e, and drag from the folder view to the same
2748  * folder view.
2749  */
2750 static void
2751 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2752                                 GtkTreeModel     *dest_model,
2753                                 GtkTreePath      *dest_row,
2754                                 GtkSelectionData *selection_data,
2755                                 DndHelper        *helper)
2756 {
2757         GtkTreeIter dest_iter, iter;
2758         TnyFolderStore *dest_folder = NULL;
2759         TnyFolderStore *folder = NULL;
2760         gboolean forbidden = FALSE;
2761         ModestWindow *win;
2762         DndFolderInfo *info = NULL;
2763
2764         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2765         if (!win) {
2766                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2767                 dnd_helper_destroyer (helper);
2768                 return;
2769         }
2770
2771         if (!forbidden) {
2772                 /* check the folder rules for the destination */
2773                 folder = tree_path_to_folder (dest_model, dest_row);
2774                 if (TNY_IS_FOLDER(folder)) {
2775                         ModestTnyFolderRules rules =
2776                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2777                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2778                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2779                         /* enable local root as destination for folders */
2780                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2781                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2782                                 forbidden = TRUE;
2783                 }
2784                 g_object_unref (folder);
2785         }
2786         if (!forbidden) {
2787                 /* check the folder rules for the source */
2788                 folder = tree_path_to_folder (source_model, helper->source_row);
2789                 if (TNY_IS_FOLDER(folder)) {
2790                         ModestTnyFolderRules rules =
2791                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2792                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2793                 } else
2794                         forbidden = TRUE;
2795                 g_object_unref (folder);
2796         }
2797
2798
2799         /* Check if the drag is possible */
2800         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2801                 /* Show error */
2802                 modest_platform_run_information_dialog ((GtkWindow *) win, 
2803                                                         _("mail_in_ui_folder_move_target_error"), 
2804                                                         FALSE);
2805                 /* Restore the previous selection */
2806                 folder = tree_path_to_folder (source_model, helper->source_row);
2807                 if (folder) {
2808                         if (TNY_IS_FOLDER (folder))
2809                                 modest_folder_view_select_folder (helper->folder_view, 
2810                                                                   TNY_FOLDER (folder), FALSE);
2811                         g_object_unref (folder);
2812                 }
2813                 dnd_helper_destroyer (helper);
2814                 return;
2815         }
2816
2817         /* Get data */
2818         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2819         gtk_tree_model_get (dest_model, &dest_iter,
2820                             INSTANCE_COLUMN,
2821                             &dest_folder, -1);
2822         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2823         gtk_tree_model_get (source_model, &iter,
2824                             INSTANCE_COLUMN,
2825                             &folder, -1);
2826
2827         /* Create the info for the performer */
2828         info = g_slice_new0 (DndFolderInfo);
2829         info->src_folder = g_object_ref (folder);
2830         info->dst_folder = g_object_ref (dest_folder);
2831         info->helper = helper;
2832
2833         /* Connect to the destination folder and perform the copy/move */
2834         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2835                                                        dest_folder,
2836                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2837                                                        info);
2838
2839         /* Frees */
2840         g_object_unref (dest_folder);
2841         g_object_unref (folder);
2842 }
2843
2844 /*
2845  * This function receives the data set by the "drag-data-get" signal
2846  * handler. This information comes within the #GtkSelectionData. This
2847  * function will manage both the drags of folders of the treeview and
2848  * drags of headers of the header view widget.
2849  */
2850 static void
2851 on_drag_data_received (GtkWidget *widget,
2852                        GdkDragContext *context,
2853                        gint x,
2854                        gint y,
2855                        GtkSelectionData *selection_data,
2856                        guint target_type,
2857                        guint time,
2858                        gpointer data)
2859 {
2860         GtkWidget *source_widget;
2861         GtkTreeModel *dest_model, *source_model;
2862         GtkTreePath *source_row, *dest_row;
2863         GtkTreeViewDropPosition pos;
2864         gboolean delete_source = FALSE;
2865         gboolean success = FALSE;
2866
2867         /* Do not allow further process */
2868         g_signal_stop_emission_by_name (widget, "drag-data-received");
2869         source_widget = gtk_drag_get_source_widget (context);
2870
2871         /* Get the action */
2872         if (context->action == GDK_ACTION_MOVE) {
2873                 delete_source = TRUE;
2874
2875                 /* Notify that there is no folder selected. We need to
2876                    do this in order to update the headers view (and
2877                    its monitors, because when moving, the old folder
2878                    won't longer exist. We can not wait for the end of
2879                    the operation, because the operation won't start if
2880                    the folder is in use */
2881                 if (source_widget == widget) {
2882                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2883                         gtk_tree_selection_unselect_all (sel);
2884                 }
2885         }
2886
2887         /* Check if the get_data failed */
2888         if (selection_data == NULL || selection_data->length < 0)
2889                 goto end;
2890
2891         /* Select the destination model */
2892         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2893
2894         /* Get the path to the destination row. Can not call
2895            gtk_tree_view_get_drag_dest_row() because the source row
2896            is not selected anymore */
2897         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2898                                            &dest_row, &pos);
2899
2900         /* Only allow drops IN other rows */
2901         if (!dest_row ||
2902             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2903             pos == GTK_TREE_VIEW_DROP_AFTER)
2904                 goto end;
2905
2906         success = TRUE;
2907         /* Drags from the header view */
2908         if (source_widget != widget) {
2909                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2910
2911                 drag_and_drop_from_header_view (source_model,
2912                                                 dest_model,
2913                                                 dest_row,
2914                                                 selection_data);
2915         } else {
2916                 DndHelper *helper = NULL;
2917
2918                 /* Get the source model and row */
2919                 gtk_tree_get_row_drag_data (selection_data,
2920                                             &source_model,
2921                                             &source_row);
2922
2923                 /* Create the helper */
2924                 helper = g_slice_new0 (DndHelper);
2925                 helper->delete_source = delete_source;
2926                 helper->source_row = gtk_tree_path_copy (source_row);
2927                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2928
2929                 drag_and_drop_from_folder_view (source_model,
2930                                                 dest_model,
2931                                                 dest_row,
2932                                                 selection_data,
2933                                                 helper);
2934
2935                 gtk_tree_path_free (source_row);
2936         }
2937
2938         /* Frees */
2939         gtk_tree_path_free (dest_row);
2940
2941  end:
2942         /* Finish the drag and drop */
2943         gtk_drag_finish (context, success, FALSE, time);
2944 }
2945
2946 /*
2947  * We define a "drag-drop" signal handler because we do not want to
2948  * use the default one, because the default one always calls
2949  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2950  * signal handler, because there we have all the information available
2951  * to know if the dnd was a success or not.
2952  */
2953 static gboolean
2954 drag_drop_cb (GtkWidget      *widget,
2955               GdkDragContext *context,
2956               gint            x,
2957               gint            y,
2958               guint           time,
2959               gpointer        user_data)
2960 {
2961         gpointer target;
2962
2963         if (!context->targets)
2964                 return FALSE;
2965
2966         /* Check if we're dragging a folder row */
2967         target = gtk_drag_dest_find_target (widget, context, NULL);
2968
2969         /* Request the data from the source. */
2970         gtk_drag_get_data(widget, context, target, time);
2971
2972     return TRUE;
2973 }
2974
2975 /*
2976  * This function expands a node of a tree view if it's not expanded
2977  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2978  * does that, so that's why they're here.
2979  */
2980 static gint
2981 expand_row_timeout (gpointer data)
2982 {
2983         GtkTreeView *tree_view = data;
2984         GtkTreePath *dest_path = NULL;
2985         GtkTreeViewDropPosition pos;
2986         gboolean result = FALSE;
2987
2988         gdk_threads_enter ();
2989
2990         gtk_tree_view_get_drag_dest_row (tree_view,
2991                                          &dest_path,
2992                                          &pos);
2993
2994         if (dest_path &&
2995             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2996              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2997                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2998                 gtk_tree_path_free (dest_path);
2999         }
3000         else {
3001                 if (dest_path)
3002                         gtk_tree_path_free (dest_path);
3003
3004                 result = TRUE;
3005         }
3006
3007         gdk_threads_leave ();
3008
3009         return result;
3010 }
3011
3012 /*
3013  * This function is called whenever the pointer is moved over a widget
3014  * while dragging some data. It installs a timeout that will expand a
3015  * node of the treeview if not expanded yet. This function also calls
3016  * gdk_drag_status in order to set the suggested action that will be
3017  * used by the "drag-data-received" signal handler to know if we
3018  * should do a move or just a copy of the data.
3019  */
3020 static gboolean
3021 on_drag_motion (GtkWidget      *widget,
3022                 GdkDragContext *context,
3023                 gint            x,
3024                 gint            y,
3025                 guint           time,
3026                 gpointer        user_data)
3027 {
3028         GtkTreeViewDropPosition pos;
3029         GtkTreePath *dest_row;
3030         GtkTreeModel *dest_model;
3031         ModestFolderViewPrivate *priv;
3032         GdkDragAction suggested_action;
3033         gboolean valid_location = FALSE;
3034         TnyFolderStore *folder = NULL;
3035
3036         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3037
3038         if (priv->timer_expander != 0) {
3039                 g_source_remove (priv->timer_expander);
3040                 priv->timer_expander = 0;
3041         }
3042
3043         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3044                                            x, y,
3045                                            &dest_row,
3046                                            &pos);
3047
3048         /* Do not allow drops between folders */
3049         if (!dest_row ||
3050             pos == GTK_TREE_VIEW_DROP_BEFORE ||
3051             pos == GTK_TREE_VIEW_DROP_AFTER) {
3052                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3053                 gdk_drag_status(context, 0, time);
3054                 valid_location = FALSE;
3055                 goto out;
3056         } else {
3057                 valid_location = TRUE;
3058         }
3059
3060         /* Check that the destination folder is writable */
3061         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3062         folder = tree_path_to_folder (dest_model, dest_row);
3063         if (folder && TNY_IS_FOLDER (folder)) {
3064                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3065
3066                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3067                         valid_location = FALSE;
3068                         goto out;
3069                 }
3070         }
3071
3072         /* Expand the selected row after 1/2 second */
3073         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3074                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3075         }
3076         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3077
3078         /* Select the desired action. By default we pick MOVE */
3079         suggested_action = GDK_ACTION_MOVE;
3080
3081         if (context->actions == GDK_ACTION_COPY)
3082             gdk_drag_status(context, GDK_ACTION_COPY, time);
3083         else if (context->actions == GDK_ACTION_MOVE)
3084             gdk_drag_status(context, GDK_ACTION_MOVE, time);
3085         else if (context->actions & suggested_action)
3086             gdk_drag_status(context, suggested_action, time);
3087         else
3088             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3089
3090  out:
3091         if (folder)
3092                 g_object_unref (folder);
3093         if (dest_row) {
3094                 gtk_tree_path_free (dest_row);
3095         }
3096         g_signal_stop_emission_by_name (widget, "drag-motion");
3097
3098         return valid_location;
3099 }
3100
3101 /*
3102  * This function sets the treeview as a source and a target for dnd
3103  * events. It also connects all the requirede signals.
3104  */
3105 static void
3106 setup_drag_and_drop (GtkTreeView *self)
3107 {
3108         /* Set up the folder view as a dnd destination. Set only the
3109            highlight flag, otherwise gtk will have a different
3110            behaviour */
3111 #ifdef MODEST_TOOLKIT_HILDON2
3112         return;
3113 #endif
3114         gtk_drag_dest_set (GTK_WIDGET (self),
3115                            GTK_DEST_DEFAULT_HIGHLIGHT,
3116                            folder_view_drag_types,
3117                            G_N_ELEMENTS (folder_view_drag_types),
3118                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
3119
3120         g_signal_connect (G_OBJECT (self),
3121                           "drag_data_received",
3122                           G_CALLBACK (on_drag_data_received),
3123                           NULL);
3124
3125
3126         /* Set up the treeview as a dnd source */
3127         gtk_drag_source_set (GTK_WIDGET (self),
3128                              GDK_BUTTON1_MASK,
3129                              folder_view_drag_types,
3130                              G_N_ELEMENTS (folder_view_drag_types),
3131                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
3132
3133         g_signal_connect (G_OBJECT (self),
3134                           "drag_motion",
3135                           G_CALLBACK (on_drag_motion),
3136                           NULL);
3137
3138         g_signal_connect (G_OBJECT (self),
3139                           "drag_data_get",
3140                           G_CALLBACK (on_drag_data_get),
3141                           NULL);
3142
3143         g_signal_connect (G_OBJECT (self),
3144                           "drag_drop",
3145                           G_CALLBACK (drag_drop_cb),
3146                           NULL);
3147 }
3148
3149 /*
3150  * This function manages the navigation through the folders using the
3151  * keyboard or the hardware keys in the device
3152  */
3153 static gboolean
3154 on_key_pressed (GtkWidget *self,
3155                 GdkEventKey *event,
3156                 gpointer user_data)
3157 {
3158         GtkTreeSelection *selection;
3159         GtkTreeIter iter;
3160         GtkTreeModel *model;
3161         gboolean retval = FALSE;
3162
3163         /* Up and Down are automatically managed by the treeview */
3164         if (event->keyval == GDK_Return) {
3165                 /* Expand/Collapse the selected row */
3166                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3167                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3168                         GtkTreePath *path;
3169
3170                         path = gtk_tree_model_get_path (model, &iter);
3171
3172                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3173                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3174                         else
3175                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3176                         gtk_tree_path_free (path);
3177                 }
3178                 /* No further processing */
3179                 retval = TRUE;
3180         }
3181
3182         return retval;
3183 }
3184
3185 /*
3186  * We listen to the changes in the local folder account name key,
3187  * because we want to show the right name in the view. The local
3188  * folder account name corresponds to the device name in the Maemo
3189  * version. We do this because we do not want to query gconf on each
3190  * tree view refresh. It's better to cache it and change whenever
3191  * necessary.
3192  */
3193 static void
3194 on_configuration_key_changed (ModestConf* conf,
3195                               const gchar *key,
3196                               ModestConfEvent event,
3197                               ModestConfNotificationId id,
3198                               ModestFolderView *self)
3199 {
3200         ModestFolderViewPrivate *priv;
3201
3202
3203         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3204         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3205
3206         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3207                 g_free (priv->local_account_name);
3208
3209                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3210                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3211                 else
3212                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3213                                                                            MODEST_CONF_DEVICE_NAME, NULL);
3214
3215                 /* Force a redraw */
3216 #if GTK_CHECK_VERSION(2, 8, 0)
3217                 GtkTreeViewColumn * tree_column;
3218
3219                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3220                                                         NAME_COLUMN);
3221                 gtk_tree_view_column_queue_resize (tree_column);
3222 #else
3223                 gtk_widget_queue_draw (GTK_WIDGET (self));
3224 #endif
3225         }
3226 }
3227
3228 void
3229 modest_folder_view_set_style (ModestFolderView *self,
3230                               ModestFolderViewStyle style)
3231 {
3232         ModestFolderViewPrivate *priv;
3233
3234         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3235         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3236                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3237
3238         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3239
3240
3241         priv->style = style;
3242 }
3243
3244 void
3245 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3246                                                              const gchar *account_id)
3247 {
3248         ModestFolderViewPrivate *priv;
3249         GtkTreeModel *model;
3250
3251         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3252
3253         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3254
3255         /* This will be used by the filter_row callback,
3256          * to decided which rows to show: */
3257         if (priv->visible_account_id) {
3258                 g_free (priv->visible_account_id);
3259                 priv->visible_account_id = NULL;
3260         }
3261         if (account_id)
3262                 priv->visible_account_id = g_strdup (account_id);
3263
3264         /* Refilter */
3265         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3266         if (GTK_IS_TREE_MODEL_FILTER (model))
3267                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3268
3269         /* Save settings to gconf */
3270         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3271                                    MODEST_CONF_FOLDER_VIEW_KEY);
3272 }
3273
3274 const gchar *
3275 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3276 {
3277         ModestFolderViewPrivate *priv;
3278
3279         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3280
3281         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3282
3283         return (const gchar *) priv->visible_account_id;
3284 }
3285
3286 static gboolean
3287 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3288 {
3289         do {
3290                 GtkTreeIter child;
3291                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3292
3293                 gtk_tree_model_get (model, iter,
3294                                     TYPE_COLUMN,
3295                                     &type, -1);
3296
3297                 gboolean result = FALSE;
3298                 if (type == TNY_FOLDER_TYPE_INBOX) {
3299                         result = TRUE;
3300                 }
3301                 if (result) {
3302                         *inbox_iter = *iter;
3303                         return TRUE;
3304                 }
3305
3306                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3307                         if (find_inbox_iter (model, &child, inbox_iter))
3308                                 return TRUE;
3309                 }
3310
3311         } while (gtk_tree_model_iter_next (model, iter));
3312
3313         return FALSE;
3314 }
3315
3316
3317
3318
3319 void
3320 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3321 {
3322         GtkTreeModel *model;
3323         GtkTreeIter iter, inbox_iter;
3324         GtkTreeSelection *sel;
3325         GtkTreePath *path = NULL;
3326
3327         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3328
3329         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3330         if (!model)
3331                 return;
3332
3333         expand_root_items (self);
3334         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3335
3336         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3337                 g_warning ("%s: model is empty", __FUNCTION__);
3338                 return;
3339         }
3340
3341         if (find_inbox_iter (model, &iter, &inbox_iter))
3342                 path = gtk_tree_model_get_path (model, &inbox_iter);
3343         else
3344                 path = gtk_tree_path_new_first ();
3345
3346         /* Select the row and free */
3347         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3348         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3349         gtk_tree_path_free (path);
3350
3351         /* set focus */
3352         gtk_widget_grab_focus (GTK_WIDGET(self));
3353 }
3354
3355
3356 /* recursive */
3357 static gboolean
3358 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3359                   TnyFolder* folder)
3360 {
3361         do {
3362                 GtkTreeIter child;
3363                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3364                 TnyFolder* a_folder;
3365                 gchar *name = NULL;
3366
3367                 gtk_tree_model_get (model, iter,
3368                                     INSTANCE_COLUMN, &a_folder,
3369                                     NAME_COLUMN, &name,
3370                                     TYPE_COLUMN, &type,
3371                                     -1);
3372                 g_free (name);
3373
3374                 if (folder == a_folder) {
3375                         g_object_unref (a_folder);
3376                         *folder_iter = *iter;
3377                         return TRUE;
3378                 }
3379                 g_object_unref (a_folder);
3380
3381                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3382                         if (find_folder_iter (model, &child, folder_iter, folder))
3383                                 return TRUE;
3384                 }
3385
3386         } while (gtk_tree_model_iter_next (model, iter));
3387
3388         return FALSE;
3389 }
3390
3391 #ifndef MODEST_TOOLKIT_HILDON2
3392 static void
3393 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3394                                      GtkTreePath *path,
3395                                      GtkTreeIter *iter,
3396                                      ModestFolderView *self)
3397 {
3398         ModestFolderViewPrivate *priv = NULL;
3399         GtkTreeSelection *sel;
3400         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3401         GObject *instance = NULL;
3402
3403         if (!MODEST_IS_FOLDER_VIEW(self))
3404                 return;
3405
3406         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3407
3408         priv->reexpand = TRUE;
3409
3410         gtk_tree_model_get (tree_model, iter,
3411                             TYPE_COLUMN, &type,
3412                             INSTANCE_COLUMN, &instance,
3413                             -1);
3414
3415         if (!instance)
3416                 return;
3417
3418         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3419                 priv->folder_to_select = g_object_ref (instance);
3420         }
3421         g_object_unref (instance);
3422
3423         if (priv->folder_to_select) {
3424
3425                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3426                                                        FALSE)) {
3427                         GtkTreePath *path;
3428                         path = gtk_tree_model_get_path (tree_model, iter);
3429                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3430
3431                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3432
3433                         gtk_tree_selection_select_iter (sel, iter);
3434                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3435
3436                         gtk_tree_path_free (path);
3437                 }
3438
3439                 /* Disable next */
3440                 modest_folder_view_disable_next_folder_selection (self);
3441
3442                 /* Refilter the model */
3443                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3444         }
3445 }
3446 #endif
3447
3448 void
3449 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3450 {
3451         ModestFolderViewPrivate *priv;
3452
3453         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3454
3455         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3456
3457         if (priv->folder_to_select)
3458                 g_object_unref(priv->folder_to_select);
3459
3460         priv->folder_to_select = NULL;
3461 }
3462
3463 gboolean
3464 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3465                                   gboolean after_change)
3466 {
3467         GtkTreeModel *model;
3468         GtkTreeIter iter, folder_iter;
3469         GtkTreeSelection *sel;
3470         ModestFolderViewPrivate *priv = NULL;
3471
3472         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3473         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3474
3475         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3476
3477         if (after_change) {
3478                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3479                 gtk_tree_selection_unselect_all (sel);
3480
3481                 if (priv->folder_to_select)
3482                         g_object_unref(priv->folder_to_select);
3483                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3484                 return TRUE;
3485         }
3486
3487         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3488         if (!model)
3489                 return FALSE;
3490
3491
3492         /* Refilter the model, before selecting the folder */
3493         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3494
3495         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3496                 g_warning ("%s: model is empty", __FUNCTION__);
3497                 return FALSE;
3498         }
3499
3500         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3501                 GtkTreePath *path;
3502
3503                 path = gtk_tree_model_get_path (model, &folder_iter);
3504                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3505
3506                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3507                 gtk_tree_selection_select_iter (sel, &folder_iter);
3508                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3509
3510                 gtk_tree_path_free (path);
3511                 return TRUE;
3512         }
3513         return FALSE;
3514 }
3515
3516
3517 void
3518 modest_folder_view_copy_selection (ModestFolderView *self)
3519 {
3520         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3521
3522         /* Copy selection */
3523         _clipboard_set_selected_data (self, FALSE);
3524 }
3525
3526 void
3527 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3528 {
3529         ModestFolderViewPrivate *priv = NULL;
3530         GtkTreeModel *model = NULL;
3531         const gchar **hidding = NULL;
3532         guint i, n_selected;
3533
3534         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3535         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3536
3537         /* Copy selection */
3538         if (!_clipboard_set_selected_data (folder_view, TRUE))
3539                 return;
3540
3541         /* Get hidding ids */
3542         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3543
3544         /* Clear hidding array created by previous cut operation */
3545         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3546
3547         /* Copy hidding array */
3548         priv->n_selected = n_selected;
3549         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3550         for (i=0; i < n_selected; i++)
3551                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3552
3553         /* Hide cut folders */
3554         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3555         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3556 }
3557
3558 void
3559 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3560                                ModestFolderView *folder_view_dst)
3561 {
3562         GtkTreeModel *filter_model = NULL;
3563         GtkTreeModel *model = NULL;
3564         GtkTreeModel *new_filter_model = NULL;
3565
3566         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3567         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3568
3569         /* Get src model*/
3570         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3571         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3572
3573         /* Build new filter model */
3574         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3575         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3576                                                 filter_row,
3577                                                 folder_view_dst,
3578                                                 NULL);
3579         /* Set copied model */
3580         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3581 #ifndef MODEST_TOOLKIT_HILDON2
3582         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3583                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3584 #endif
3585
3586         /* Free */
3587         g_object_unref (new_filter_model);
3588 }
3589
3590 void
3591 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3592                                           gboolean show)
3593 {
3594         GtkTreeModel *model = NULL;
3595         ModestFolderViewPrivate* priv;
3596
3597         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3598
3599         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3600         priv->show_non_move = show;
3601 /*      modest_folder_view_update_model(folder_view, */
3602 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3603
3604         /* Hide special folders */
3605         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3606         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3607                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3608         }
3609 }
3610
3611 /* Returns FALSE if it did not selected anything */
3612 static gboolean
3613 _clipboard_set_selected_data (ModestFolderView *folder_view,
3614                               gboolean delete)
3615 {
3616         ModestFolderViewPrivate *priv = NULL;
3617         TnyFolderStore *folder = NULL;
3618         gboolean retval = FALSE;
3619
3620         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3621         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3622
3623         /* Set selected data on clipboard   */
3624         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3625         folder = modest_folder_view_get_selected (folder_view);
3626
3627         /* Do not allow to select an account */
3628         if (TNY_IS_FOLDER (folder)) {
3629                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3630                 retval = TRUE;
3631         }
3632
3633         /* Free */
3634         g_object_unref (folder);
3635
3636         return retval;
3637 }
3638
3639 static void
3640 _clear_hidding_filter (ModestFolderView *folder_view)
3641 {
3642         ModestFolderViewPrivate *priv;
3643         guint i;
3644
3645         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3646         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3647
3648         if (priv->hidding_ids != NULL) {
3649                 for (i=0; i < priv->n_selected; i++)
3650                         g_free (priv->hidding_ids[i]);
3651                 g_free(priv->hidding_ids);
3652         }
3653 }
3654
3655
3656 static void
3657 on_display_name_changed (ModestAccountMgr *mgr,
3658                          const gchar *account,
3659                          gpointer user_data)
3660 {
3661         ModestFolderView *self;
3662
3663         self = MODEST_FOLDER_VIEW (user_data);
3664
3665         /* Force a redraw */
3666 #if GTK_CHECK_VERSION(2, 8, 0)
3667         GtkTreeViewColumn * tree_column;
3668
3669         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3670                                                 NAME_COLUMN);
3671         gtk_tree_view_column_queue_resize (tree_column);
3672 #else
3673         gtk_widget_queue_draw (GTK_WIDGET (self));
3674 #endif
3675 }
3676
3677 void 
3678 modest_folder_view_set_cell_style (ModestFolderView *self,
3679                                    ModestFolderViewCellStyle cell_style)
3680 {
3681         ModestFolderViewPrivate *priv = NULL;
3682
3683         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3684         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3685
3686         priv->cell_style = cell_style;
3687
3688         g_object_set (G_OBJECT (priv->messages_renderer),
3689                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3690                       NULL);
3691         
3692         gtk_widget_queue_draw (GTK_WIDGET (self));
3693 }
3694
3695 static void
3696 update_style (ModestFolderView *self)
3697 {
3698         ModestFolderViewPrivate *priv;
3699         GdkColor style_color;
3700         PangoAttrList *attr_list;
3701         GtkStyle *style;
3702         PangoAttribute *attr;
3703
3704         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3705         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3706
3707         /* Set color */
3708
3709         attr_list = pango_attr_list_new ();
3710         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3711                 gdk_color_parse ("grey", &style_color);
3712         }
3713         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3714         pango_attr_list_insert (attr_list, attr);
3715         
3716         /* set font */
3717         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3718                                            (GTK_WIDGET(self)),
3719                                            "SmallSystemFont", NULL,
3720                                            G_TYPE_NONE);
3721         if (style) {
3722                 attr = pango_attr_font_desc_new (pango_font_description_copy
3723                                                  (style->font_desc));
3724                 pango_attr_list_insert (attr_list, attr);
3725
3726                 g_object_set (G_OBJECT (priv->messages_renderer),
3727                               "foreground-gdk", &style_color,
3728                               "foreground-set", TRUE,
3729                               "attributes", attr_list,
3730                               NULL);
3731                 pango_attr_list_unref (attr_list);
3732         }
3733 }
3734
3735 static void 
3736 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3737 {
3738         if (strcmp ("style", spec->name) == 0) {
3739                 update_style (MODEST_FOLDER_VIEW (obj));
3740                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3741         } 
3742 }
3743
3744 void 
3745 modest_folder_view_set_filter (ModestFolderView *self,
3746                                ModestFolderViewFilter filter)
3747 {
3748         ModestFolderViewPrivate *priv;
3749         GtkTreeModel *filter_model;
3750
3751         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3752         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3753
3754         priv->filter |= filter;
3755
3756         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3757         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3758                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3759         }
3760 }
3761
3762 void 
3763 modest_folder_view_unset_filter (ModestFolderView *self,
3764                                  ModestFolderViewFilter filter)
3765 {
3766         ModestFolderViewPrivate *priv;
3767         GtkTreeModel *filter_model;
3768
3769         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3770         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3771
3772         priv->filter &= ~filter;
3773
3774         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3775         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3776                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3777         }
3778 }
3779
3780 gboolean
3781 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3782                                              ModestTnyFolderRules rules)
3783 {
3784         GtkTreeModel *filter_model;
3785         GtkTreeIter iter;
3786         gboolean fulfil = FALSE;
3787
3788         if (!get_inner_models (self, &filter_model, NULL, NULL))
3789                 return FALSE;
3790
3791         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3792                 return FALSE;
3793
3794         do {
3795                 TnyFolderStore *folder;
3796
3797                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3798                 if (folder) {
3799                         if (TNY_IS_FOLDER (folder)) {
3800                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3801                                 /* Folder rules are negative: non_writable, non_deletable... */
3802                                 if (!(folder_rules & rules))
3803                                         fulfil = TRUE;
3804                         }
3805                         g_object_unref (folder);
3806                 }
3807
3808         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3809
3810         return fulfil;
3811 }
3812
3813 void 
3814 modest_folder_view_set_list_to_move (ModestFolderView *self,
3815                                      TnyList *list)
3816 {
3817         ModestFolderViewPrivate *priv;
3818
3819         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3820         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3821
3822         if (priv->list_to_move)
3823                 g_object_unref (priv->list_to_move);
3824
3825         if (list)
3826                 g_object_ref (list);
3827
3828         priv->list_to_move = list;
3829 }