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