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