64e9281d8eedadc46b88e90be3650d094f9ff689
[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_warning ("BUG: %s: not a valid 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 #else
1116                       "ellipsize", PANGO_ELLIPSIZE_END,
1117 #endif
1118                       "ellipsize-set", TRUE, NULL);
1119         gtk_tree_view_column_pack_start (column, renderer, TRUE);
1120         gtk_tree_view_column_set_cell_data_func(column, renderer,
1121                                                 text_cell_data, treeview, NULL);
1122
1123         priv->messages_renderer = gtk_cell_renderer_text_new ();
1124         g_object_set (priv->messages_renderer, 
1125 #ifdef MODEST_TOOLKIT_HILDON2
1126                       "yalign", 0.0,
1127                       "ypad", MODEST_MARGIN_DEFAULT,
1128                       "xpad", MODEST_MARGIN_DOUBLE,
1129 #else
1130                       "scale", PANGO_SCALE_X_SMALL,
1131                       "scale-set", TRUE,
1132 #endif
1133                       "alignment", PANGO_ALIGN_RIGHT,
1134                       "align-set", TRUE,
1135                       "xalign", 1.0,
1136                       NULL);
1137         gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1138         gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1139                                                 messages_cell_data, treeview, NULL);
1140
1141         /* Set selection mode */
1142         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1143         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1144
1145         /* Set treeview appearance */
1146         gtk_tree_view_column_set_spacing (column, 2);
1147         gtk_tree_view_column_set_resizable (column, TRUE);
1148         gtk_tree_view_column_set_fixed_width (column, TRUE);
1149         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1150         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1151
1152         /* Add column */
1153         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1154 }
1155
1156 static void
1157 modest_folder_view_init (ModestFolderView *obj)
1158 {
1159         ModestFolderViewPrivate *priv;
1160         ModestConf *conf;
1161
1162         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1163
1164         priv->timer_expander = 0;
1165         priv->account_store  = NULL;
1166         priv->query          = NULL;
1167         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1168         priv->cur_folder_store   = NULL;
1169         priv->visible_account_id = NULL;
1170         priv->mailbox = NULL;
1171         priv->folder_to_select = NULL;
1172         priv->outbox_deleted_handler = 0;
1173         priv->reexpand = TRUE;
1174
1175         /* Initialize the local account name */
1176         conf = modest_runtime_get_conf();
1177         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1178
1179         /* Init email clipboard */
1180         priv->clipboard = modest_runtime_get_email_clipboard ();
1181         priv->hidding_ids = NULL;
1182         priv->n_selected = 0;
1183         priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1184         priv->reselect = FALSE;
1185         priv->show_non_move = TRUE;
1186         priv->list_to_move = NULL;
1187
1188         /* Build treeview */
1189         add_columns (GTK_WIDGET (obj));
1190
1191         /* Setup drag and drop */
1192         setup_drag_and_drop (GTK_TREE_VIEW(obj));
1193
1194         /* Connect signals */
1195         g_signal_connect (G_OBJECT (obj),
1196                           "key-press-event",
1197                           G_CALLBACK (on_key_pressed), NULL);
1198
1199         priv->display_name_changed_signal =
1200                 g_signal_connect (modest_runtime_get_account_mgr (),
1201                                   "display_name_changed",
1202                                   G_CALLBACK (on_display_name_changed),
1203                                   obj);
1204
1205         /*
1206          * Track changes in the local account name (in the device it
1207          * will be the device name)
1208          */
1209         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1210                                                   "key_changed",
1211                                                   G_CALLBACK(on_configuration_key_changed),
1212                                                   obj);
1213
1214         update_style (obj);
1215         g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1216
1217
1218 }
1219
1220 static void
1221 tny_account_store_view_init (gpointer g, gpointer iface_data)
1222 {
1223         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1224
1225         klass->set_account_store = modest_folder_view_set_account_store;
1226 }
1227
1228 static void
1229 modest_folder_view_finalize (GObject *obj)
1230 {
1231         ModestFolderViewPrivate *priv;
1232         GtkTreeSelection    *sel;
1233         TnyAccount *local_account;
1234
1235         g_return_if_fail (obj);
1236
1237         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1238
1239         if (priv->timer_expander != 0) {
1240                 g_source_remove (priv->timer_expander);
1241                 priv->timer_expander = 0;
1242         }
1243
1244         local_account = (TnyAccount *)
1245                 modest_tny_account_store_get_local_folders_account (modest_runtime_get_account_store ());
1246         if (local_account) {
1247                 if (g_signal_handler_is_connected (local_account,
1248                                                    priv->outbox_deleted_handler))
1249                         g_signal_handler_disconnect (local_account,
1250                                                      priv->outbox_deleted_handler);
1251                 g_object_unref (local_account);
1252         }
1253
1254         if (priv->account_store) {
1255                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1256                                              priv->account_inserted_signal);
1257                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1258                                              priv->account_removed_signal);
1259                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1260                                              priv->account_changed_signal);
1261                 g_object_unref (G_OBJECT(priv->account_store));
1262                 priv->account_store = NULL;
1263         }
1264
1265         if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (), 
1266                                            priv->display_name_changed_signal)) {
1267                 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1268                                              priv->display_name_changed_signal);
1269                 priv->display_name_changed_signal = 0;
1270         }
1271
1272         if (priv->query) {
1273                 g_object_unref (G_OBJECT (priv->query));
1274                 priv->query = NULL;
1275         }
1276
1277         if (priv->folder_to_select) {
1278                 g_object_unref (G_OBJECT(priv->folder_to_select));
1279                 priv->folder_to_select = NULL;
1280         }
1281
1282         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1283         if (sel)
1284                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1285
1286         g_free (priv->local_account_name);
1287         g_free (priv->visible_account_id);
1288         g_free (priv->mailbox);
1289
1290         if (priv->conf_key_signal) {
1291                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1292                                              priv->conf_key_signal);
1293                 priv->conf_key_signal = 0;
1294         }
1295
1296         if (priv->cur_folder_store) {
1297                 g_object_unref (priv->cur_folder_store);
1298                 priv->cur_folder_store = NULL;
1299         }
1300
1301         if (priv->list_to_move) {
1302                 g_object_unref (priv->list_to_move);
1303                 priv->list_to_move = NULL;
1304         }
1305
1306         /* Clear hidding array created by cut operation */
1307         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1308
1309         G_OBJECT_CLASS(parent_class)->finalize (obj);
1310 }
1311
1312
1313 static void
1314 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1315 {
1316         ModestFolderViewPrivate *priv;
1317         TnyDevice *device;
1318
1319         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1320         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1321
1322         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1323         device = tny_account_store_get_device (account_store);
1324
1325         if (G_UNLIKELY (priv->account_store)) {
1326
1327                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1328                                                    priv->account_inserted_signal))
1329                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1330                                                      priv->account_inserted_signal);
1331                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1332                                                    priv->account_removed_signal))
1333                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1334                                                      priv->account_removed_signal);
1335                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1336                                                    priv->account_changed_signal))
1337                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1338                                                      priv->account_changed_signal);
1339                 g_object_unref (G_OBJECT (priv->account_store));
1340         }
1341
1342         priv->account_store = g_object_ref (G_OBJECT (account_store));
1343
1344         priv->account_removed_signal =
1345                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1346                                   G_CALLBACK (on_account_removed), self);
1347
1348         priv->account_inserted_signal =
1349                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1350                                   G_CALLBACK (on_account_inserted), self);
1351
1352         priv->account_changed_signal =
1353                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1354                                   G_CALLBACK (on_account_changed), self);
1355
1356         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1357         priv->reselect = FALSE;
1358         modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1359
1360         g_object_unref (G_OBJECT (device));
1361 }
1362
1363 static void
1364 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1365                       gpointer user_data)
1366 {
1367         ModestFolderView *self;
1368         GtkTreeModel *model, *filter_model;
1369         TnyFolder *outbox;
1370
1371         self = MODEST_FOLDER_VIEW (user_data);
1372
1373         if (!get_inner_models (self, &filter_model, NULL, &model))
1374                 return;
1375
1376         /* Remove outbox from model */
1377         outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1378         tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1379         g_object_unref (outbox);
1380
1381         /* Refilter view */
1382         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1383 }
1384
1385 static void
1386 on_account_inserted (TnyAccountStore *account_store,
1387                      TnyAccount *account,
1388                      gpointer user_data)
1389 {
1390         ModestFolderViewPrivate *priv;
1391         GtkTreeModel *model, *filter_model;
1392
1393         /* Ignore transport account insertions, we're not showing them
1394            in the folder view */
1395         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1396                 return;
1397
1398         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1399
1400
1401         /* If we're adding a new account, and there is no previous
1402            one, we need to select the visible server account */
1403         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1404             !priv->visible_account_id)
1405                 modest_widget_memory_restore (modest_runtime_get_conf(),
1406                                               G_OBJECT (user_data),
1407                                               MODEST_CONF_FOLDER_VIEW_KEY);
1408
1409
1410         /* Get models */
1411         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1412                                &filter_model, NULL, &model))
1413                 return;
1414
1415         /* Insert the account in the model */
1416         tny_list_append (TNY_LIST (model), G_OBJECT (account));
1417
1418         /* When the model is a list store (plain representation) the
1419            outbox is not a child of any account so we have to manually
1420            delete it because removing the local folders account won't
1421            delete it (because tny_folder_get_account() is not defined
1422            for a merge folder */
1423         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1424             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1425
1426                 priv->outbox_deleted_handler =
1427                         g_signal_connect (account,
1428                                           "outbox-deleted",
1429                                           G_CALLBACK (on_outbox_deleted_cb),
1430                                           user_data);
1431         }
1432
1433         /* Refilter the model */
1434         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1435 }
1436
1437
1438 static gboolean
1439 same_account_selected (ModestFolderView *self,
1440                        TnyAccount *account)
1441 {
1442         ModestFolderViewPrivate *priv;
1443         gboolean same_account = FALSE;
1444
1445         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1446
1447         if (priv->cur_folder_store) {
1448                 TnyAccount *selected_folder_account = NULL;
1449
1450                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1451                         selected_folder_account =
1452                                 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1453                 } else {
1454                         selected_folder_account =
1455                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1456                 }
1457
1458                 if (selected_folder_account == account)
1459                         same_account = TRUE;
1460
1461                 g_object_unref (selected_folder_account);
1462         }
1463         return same_account;
1464 }
1465
1466 /**
1467  *
1468  * Selects the first inbox or the local account in an idle
1469  */
1470 static gboolean
1471 on_idle_select_first_inbox_or_local (gpointer user_data)
1472 {
1473         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1474
1475         gdk_threads_enter ();
1476         modest_folder_view_select_first_inbox_or_local (self);
1477         gdk_threads_leave ();
1478
1479         return FALSE;
1480 }
1481
1482 static void
1483 on_account_changed (TnyAccountStore *account_store,
1484                     TnyAccount *tny_account,
1485                     gpointer user_data)
1486 {
1487         ModestFolderView *self;
1488         ModestFolderViewPrivate *priv;
1489         GtkTreeModel *model, *filter_model;
1490         GtkTreeSelection *sel;
1491         gboolean same_account;
1492
1493         /* Ignore transport account insertions, we're not showing them
1494            in the folder view */
1495         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1496                 return;
1497
1498         self = MODEST_FOLDER_VIEW (user_data);
1499         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1500
1501         /* Get the inner model */
1502         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1503                                &filter_model, NULL, &model))
1504                 return;
1505
1506         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1507
1508         /* Invalidate the cur_folder_store only if the selected folder
1509            belongs to the account that is being removed */
1510         same_account = same_account_selected (self, tny_account);
1511         if (same_account) {
1512                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1513                 gtk_tree_selection_unselect_all (sel);
1514         }
1515
1516         /* Remove the account from the model */
1517         tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1518
1519         /* Insert the account in the model */
1520         tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1521
1522         /* Refilter the model */
1523         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1524
1525         /* Select the first INBOX if the currently selected folder
1526            belongs to the account that is being deleted */
1527         if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1528                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1529 }
1530
1531 static void
1532 on_account_removed (TnyAccountStore *account_store,
1533                     TnyAccount *account,
1534                     gpointer user_data)
1535 {
1536         ModestFolderView *self = NULL;
1537         ModestFolderViewPrivate *priv;
1538         GtkTreeModel *model, *filter_model;
1539         GtkTreeSelection *sel = NULL;
1540         gboolean same_account = FALSE;
1541
1542         /* Ignore transport account removals, we're not showing them
1543            in the folder view */
1544         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1545                 return;
1546
1547         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1548                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1549                 return;
1550         }
1551
1552         self = MODEST_FOLDER_VIEW (user_data);
1553         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1554
1555         /* Invalidate the cur_folder_store only if the selected folder
1556            belongs to the account that is being removed */
1557         same_account = same_account_selected (self, account);
1558         if (same_account) {
1559                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1560                 gtk_tree_selection_unselect_all (sel);
1561         }
1562
1563         /* Invalidate row to select only if the folder to select
1564            belongs to the account that is being removed*/
1565         if (priv->folder_to_select) {
1566                 TnyAccount *folder_to_select_account = NULL;
1567
1568                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1569                 if (folder_to_select_account == account) {
1570                         modest_folder_view_disable_next_folder_selection (self);
1571                         g_object_unref (priv->folder_to_select);
1572                         priv->folder_to_select = NULL;
1573                 }
1574                 g_object_unref (folder_to_select_account);
1575         }
1576
1577         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1578                                &filter_model, NULL, &model))
1579                 return;
1580
1581         /* Disconnect the signal handler */
1582         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1583             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1584                 if (g_signal_handler_is_connected (account,
1585                                                    priv->outbox_deleted_handler))
1586                         g_signal_handler_disconnect (account,
1587                                                      priv->outbox_deleted_handler);
1588         }
1589
1590         /* Remove the account from the model */
1591         tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1592
1593         /* If the removed account is the currently viewed one then
1594            clear the configuration value. The new visible account will be the default account */
1595         if (priv->visible_account_id &&
1596             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1597
1598                 /* Clear the current visible account_id */
1599                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1600                 modest_folder_view_set_mailbox (self, NULL);
1601
1602                 /* Call the restore method, this will set the new visible account */
1603                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1604                                               MODEST_CONF_FOLDER_VIEW_KEY);
1605         }
1606
1607         /* Refilter the model */
1608         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1609
1610         /* Select the first INBOX if the currently selected folder
1611            belongs to the account that is being deleted */
1612         if (same_account)
1613                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1614 }
1615
1616 void
1617 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1618 {
1619         GtkTreeViewColumn *col;
1620
1621         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1622
1623         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1624         if (!col) {
1625                 g_printerr ("modest: failed get column for title\n");
1626                 return;
1627         }
1628
1629         gtk_tree_view_column_set_title (col, title);
1630         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1631                                            title != NULL);
1632 }
1633
1634 static gboolean
1635 modest_folder_view_on_map (ModestFolderView *self,
1636                            GdkEventExpose *event,
1637                            gpointer data)
1638 {
1639         ModestFolderViewPrivate *priv;
1640
1641         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1642
1643         /* This won't happen often */
1644         if (G_UNLIKELY (priv->reselect)) {
1645                 /* Select the first inbox or the local account if not found */
1646
1647                 /* TODO: this could cause a lock at startup, so we
1648                    comment it for the moment. We know that this will
1649                    be a bug, because the INBOX is not selected, but we
1650                    need to rewrite some parts of Modest to avoid the
1651                    deathlock situation */
1652                 /* TODO: check if this is still the case */
1653                 priv->reselect = FALSE;
1654                 modest_folder_view_select_first_inbox_or_local (self);
1655                 /* Notify the display name observers */
1656                 g_signal_emit (G_OBJECT(self),
1657                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1658                                NULL);
1659         }
1660
1661         if (priv->reexpand) {
1662                 expand_root_items (self);
1663                 priv->reexpand = FALSE;
1664         }
1665
1666         return FALSE;
1667 }
1668
1669 GtkWidget*
1670 modest_folder_view_new (TnyFolderStoreQuery *query)
1671 {
1672         GObject *self;
1673         ModestFolderViewPrivate *priv;
1674         GtkTreeSelection *sel;
1675
1676         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, 
1677 #ifdef MODEST_TOOLKIT_HILDON2
1678                                        "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1679 #endif
1680                                        NULL));
1681         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1682
1683         if (query)
1684                 priv->query = g_object_ref (query);
1685
1686         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1687         priv->changed_signal = g_signal_connect (sel, "changed",
1688                                                  G_CALLBACK (on_selection_changed), self);
1689
1690         g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1691
1692         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1693
1694         return GTK_WIDGET(self);
1695 }
1696
1697 /* this feels dirty; any other way to expand all the root items? */
1698 static void
1699 expand_root_items (ModestFolderView *self)
1700 {
1701         GtkTreePath *path;
1702         GtkTreeModel *model;
1703         GtkTreeIter iter;
1704
1705         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1706         path = gtk_tree_path_new_first ();
1707
1708         /* all folders should have child items, so.. */
1709         do {
1710                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1711                 gtk_tree_path_next (path);
1712         } while (gtk_tree_model_get_iter (model, &iter, path));
1713
1714         gtk_tree_path_free (path);
1715 }
1716
1717 static gboolean
1718 is_parent_of (TnyFolder *a, TnyFolder *b)
1719 {
1720         const gchar *a_id;
1721         gboolean retval = FALSE;
1722
1723         a_id = tny_folder_get_id (a);
1724         if (a_id) {
1725                 gchar *string_to_match;
1726                 const gchar *b_id;
1727
1728                 string_to_match = g_strconcat (a_id, "/", NULL);
1729                 b_id = tny_folder_get_id (b);
1730                 retval = g_str_has_prefix (b_id, string_to_match);
1731                 g_free (string_to_match);
1732         }
1733         
1734         return retval;
1735 }
1736
1737 typedef struct _ForeachFolderInfo {
1738         gchar *needle;
1739         gboolean found;
1740 } ForeachFolderInfo;
1741
1742 static gboolean 
1743 foreach_folder_with_id (GtkTreeModel *model,
1744                         GtkTreePath *path,
1745                         GtkTreeIter *iter,
1746                         gpointer data)
1747 {
1748         ForeachFolderInfo *info;
1749         GObject *instance;
1750
1751         info = (ForeachFolderInfo *) data;
1752         gtk_tree_model_get (model, iter,
1753                             INSTANCE_COLUMN, &instance,
1754                             -1);
1755
1756         if (TNY_IS_FOLDER (instance)) {
1757                 const gchar *id;
1758                 gchar *collate;
1759                 id = tny_folder_get_id (TNY_FOLDER (instance));
1760                 if (id) {
1761                         collate = g_utf8_collate_key (id, -1);
1762                         info->found = !strcmp (info->needle, collate);
1763                         g_free (collate);
1764                 }
1765         }
1766
1767         return info->found;
1768         
1769 }
1770
1771
1772 static gboolean
1773 has_folder_with_id (ModestFolderView *self, const gchar *id)
1774 {
1775         GtkTreeModel *model;
1776         ForeachFolderInfo info = {NULL, FALSE};
1777
1778         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1779         info.needle = g_utf8_collate_key (id, -1);
1780         
1781         gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1782         g_free (info.needle);
1783
1784         return info.found;
1785 }
1786
1787 static gboolean
1788 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1789 {
1790         const gchar *a_id;
1791         gboolean retval = FALSE;
1792
1793         a_id = tny_folder_get_id (a);
1794         if (a_id) {
1795                 const gchar *b_id;
1796                 b_id = tny_folder_get_id (b);
1797                 
1798                 if (b_id) {
1799                         const gchar *last_bar;
1800                         gchar *string_to_match;
1801                         last_bar = g_strrstr (b_id, "/");
1802                         if (last_bar)
1803                                 last_bar++;
1804                         else
1805                                 last_bar = b_id;
1806                         string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1807                         retval = has_folder_with_id (self, string_to_match);
1808                         g_free (string_to_match);
1809                 }
1810         }
1811
1812         return retval;
1813 }
1814
1815 static gboolean
1816 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1817 {
1818         ModestFolderViewPrivate *priv;
1819         TnyIterator *iterator;
1820         gboolean retval = TRUE;
1821
1822         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1823         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1824
1825         for (iterator = tny_list_create_iterator (priv->list_to_move);
1826              retval && !tny_iterator_is_done (iterator);
1827              tny_iterator_next (iterator)) {
1828                 GObject *instance;
1829                 instance = tny_iterator_get_current (iterator);
1830                 if (instance == (GObject *) folder) {
1831                         retval = FALSE;
1832                 } else if (TNY_IS_FOLDER (instance)) {
1833                         retval = !is_parent_of (TNY_FOLDER (instance), folder);
1834                         if (retval) {
1835                                 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1836                         }
1837                 }
1838                 g_object_unref (instance);
1839         }
1840         g_object_unref (iterator);
1841
1842         return retval;
1843 }
1844
1845
1846 /*
1847  * We use this function to implement the
1848  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1849  * account in this case, and the local folders.
1850  */
1851 static gboolean
1852 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1853 {
1854         ModestFolderViewPrivate *priv;
1855         gboolean retval = TRUE;
1856         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1857         GObject *instance = NULL;
1858         const gchar *id = NULL;
1859         guint i;
1860         gboolean found = FALSE;
1861         gboolean cleared = FALSE;
1862         ModestTnyFolderRules rules = 0;
1863         gchar *fname;
1864
1865         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1866         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1867
1868         gtk_tree_model_get (model, iter,
1869                             NAME_COLUMN, &fname,
1870                             TYPE_COLUMN, &type,
1871                             INSTANCE_COLUMN, &instance,
1872                             -1);
1873
1874         /* Do not show if there is no instance, this could indeed
1875            happen when the model is being modified while it's being
1876            drawn. This could occur for example when moving folders
1877            using drag&drop */
1878         if (!instance) {
1879                 g_free (fname);
1880                 return FALSE;
1881         }
1882
1883         if (TNY_IS_ACCOUNT (instance)) {
1884                 TnyAccount *acc = TNY_ACCOUNT (instance);
1885                 const gchar *account_id = tny_account_get_id (acc);
1886
1887                 /* If it isn't a special folder,
1888                  * don't show it unless it is the visible account: */
1889                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1890                     !modest_tny_account_is_virtual_local_folders (acc) &&
1891                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1892
1893                         /* Show only the visible account id */
1894                         if (priv->visible_account_id) {
1895                                 if (strcmp (account_id, priv->visible_account_id))
1896                                         retval = FALSE;
1897                         } else {
1898                                 retval = FALSE;
1899                         }
1900                 }
1901
1902                 /* Never show these to the user. They are merged into one folder
1903                  * in the local-folders account instead: */
1904                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1905                         retval = FALSE;
1906         } else {
1907                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1908                         /* Only show special folders for current account if needed */
1909                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1910                                 TnyAccount *account;
1911
1912                                 account = tny_folder_get_account (TNY_FOLDER (instance));
1913
1914                                 if (TNY_IS_ACCOUNT (account)) {
1915                                         const gchar *account_id = tny_account_get_id (account);
1916
1917                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
1918                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1919                                                 /* Show only the visible account id */
1920                                                 if (priv->visible_account_id) {
1921                                                   if (strcmp (account_id, priv->visible_account_id)) {
1922                                                           retval = FALSE;
1923                                                   } else if (priv->mailbox) {
1924                                                           /* Filter mailboxes */
1925                                                           if (!g_str_has_prefix (fname, priv->mailbox)) {
1926                                                                   retval = FALSE;
1927                                                           } else if (!strcmp (fname, priv->mailbox)) {
1928                                                                   /* Hide mailbox parent */
1929                                                                   retval = FALSE;
1930                                                           }
1931                                                   }
1932                                                 }
1933                                         }
1934                                                 g_object_unref (account);
1935                                 }
1936                         }
1937
1938                 }
1939         }
1940
1941         /* Check hiding (if necessary) */
1942         cleared = modest_email_clipboard_cleared (priv->clipboard);
1943         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1944                 id = tny_folder_get_id (TNY_FOLDER(instance));
1945                 if (priv->hidding_ids != NULL)
1946                         for (i=0; i < priv->n_selected && !found; i++)
1947                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1948                                         found = (!strcmp (priv->hidding_ids[i], id));
1949
1950                 retval = !found;
1951         }
1952
1953         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1954         folder as no message can be move there according to UI specs */
1955         if (retval && !priv->show_non_move) {
1956                 if (priv->list_to_move && 
1957                     tny_list_get_length (priv->list_to_move) > 0 &&
1958                     TNY_IS_FOLDER (instance)) {
1959                         retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
1960                 }
1961                 if (retval && TNY_IS_FOLDER (instance) && 
1962                     modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
1963                         switch (type) {
1964                         case TNY_FOLDER_TYPE_OUTBOX:
1965                         case TNY_FOLDER_TYPE_SENT:
1966                         case TNY_FOLDER_TYPE_DRAFTS:
1967                                 retval = FALSE;
1968                                 break;
1969                         case TNY_FOLDER_TYPE_UNKNOWN:
1970                         case TNY_FOLDER_TYPE_NORMAL:
1971                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1972                                 if (type == TNY_FOLDER_TYPE_INVALID)
1973                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1974                                 
1975                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1976                                     type == TNY_FOLDER_TYPE_SENT
1977                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1978                                         retval = FALSE;
1979                                 break;
1980                         default:
1981                                 break;
1982                         }
1983                 }
1984         }
1985
1986         /* apply special filters */
1987         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
1988                 if (TNY_IS_ACCOUNT (instance))
1989                         return FALSE;
1990         }
1991
1992         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
1993                 if (TNY_IS_FOLDER (instance))
1994                         return FALSE;
1995         }
1996
1997         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
1998                 if (TNY_IS_ACCOUNT (instance)) {
1999                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2000                                 return FALSE;
2001                 } else if (TNY_IS_FOLDER (instance)) {
2002                         if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2003                                 return FALSE;
2004                 }
2005         }
2006
2007         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2008                 if (TNY_IS_ACCOUNT (instance)) {
2009                         if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2010                                 return FALSE;
2011                 } else if (TNY_IS_FOLDER (instance)) {
2012                         if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2013                                 return FALSE;
2014                 }
2015         }
2016
2017         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2018                 /* A mailbox is a fake folder with an @ in the middle of the name */
2019                 if (!TNY_IS_FOLDER (instance) ||
2020                     !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2021                         return FALSE;
2022                 } else {
2023                         const gchar *folder_name;
2024                         folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2025                         if (!folder_name || strchr (folder_name, '@') == NULL)
2026                                 return FALSE;
2027                 }
2028                 
2029         }
2030
2031         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2032                 if (TNY_IS_FOLDER (instance)) {
2033                         /* Check folder rules */
2034                         ModestTnyFolderRules rules;
2035
2036                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2037                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2038                 } else if (TNY_IS_ACCOUNT (instance)) {
2039                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2040                                 retval = FALSE;
2041                         } else {
2042                                 retval = TRUE;
2043                         }
2044                 }
2045         }
2046
2047         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2048                 if (TNY_IS_FOLDER (instance)) {
2049                         TnyFolderType guess_type;
2050
2051                         if (TNY_FOLDER_TYPE_NORMAL) {
2052                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2053                         } else {
2054                                 guess_type = type;
2055                         }
2056
2057                         switch (type) {
2058                         case TNY_FOLDER_TYPE_OUTBOX:
2059                         case TNY_FOLDER_TYPE_SENT:
2060                         case TNY_FOLDER_TYPE_DRAFTS:
2061                         case TNY_FOLDER_TYPE_ARCHIVE:
2062                         case TNY_FOLDER_TYPE_INBOX:
2063                                 retval = FALSE;
2064                                 break;
2065                         case TNY_FOLDER_TYPE_UNKNOWN:
2066                         case TNY_FOLDER_TYPE_NORMAL:
2067                                 break;
2068                         default:
2069                                 break;
2070                         }
2071
2072                 } else if (TNY_IS_ACCOUNT (instance)) {
2073                         retval = FALSE;
2074                 }
2075         }
2076
2077         if (retval && TNY_IS_FOLDER (instance)) {
2078                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2079         }
2080
2081         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2082                 if (TNY_IS_FOLDER (instance)) {
2083                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2084                 } else if (TNY_IS_ACCOUNT (instance)) {
2085                         retval = FALSE;
2086                 }
2087         }
2088
2089         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2090                 if (TNY_IS_FOLDER (instance)) {
2091                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2092                 } else if (TNY_IS_ACCOUNT (instance)) {
2093                         retval = FALSE;
2094                 }
2095         }
2096
2097         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2098                 if (TNY_IS_FOLDER (instance)) {
2099                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2100                 } else if (TNY_IS_ACCOUNT (instance)) {
2101                         retval = FALSE;
2102                 }
2103         }
2104
2105         /* Free */
2106         g_object_unref (instance);
2107         g_free (fname);
2108
2109         return retval;
2110 }
2111
2112
2113 gboolean
2114 modest_folder_view_update_model (ModestFolderView *self,
2115                                  TnyAccountStore *account_store)
2116 {
2117         ModestFolderViewPrivate *priv;
2118         GtkTreeModel *model /* , *old_model */;
2119         GtkTreeModel *filter_model = NULL, *sortable = NULL;
2120
2121         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2122         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2123                               FALSE);
2124
2125         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2126
2127         /* Notify that there is no folder selected */
2128         g_signal_emit (G_OBJECT(self),
2129                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2130                        NULL, FALSE);
2131         if (priv->cur_folder_store) {
2132                 g_object_unref (priv->cur_folder_store);
2133                 priv->cur_folder_store = NULL;
2134         }
2135
2136         /* FIXME: the local accounts are not shown when the query
2137            selects only the subscribed folders */
2138 #ifdef MODEST_TOOLKIT_HILDON2
2139         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
2140                                                           TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2141         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2142                                                       MODEST_FOLDER_PATH_SEPARATOR);
2143 #else
2144         model = tny_gtk_folder_store_tree_model_new (NULL);
2145 #endif
2146
2147         /* When the model is a list store (plain representation) the
2148            outbox is not a child of any account so we have to manually
2149            delete it because removing the local folders account won't
2150            delete it (because tny_folder_get_account() is not defined
2151            for a merge folder */
2152         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2153                 TnyAccount *account;
2154                 ModestTnyAccountStore *acc_store;
2155
2156                 acc_store = modest_runtime_get_account_store ();
2157                 account = modest_tny_account_store_get_local_folders_account (acc_store);
2158
2159                 if (g_signal_handler_is_connected (account,
2160                                                    priv->outbox_deleted_handler))
2161                         g_signal_handler_disconnect (account,
2162                                                      priv->outbox_deleted_handler);
2163
2164                 priv->outbox_deleted_handler =
2165                         g_signal_connect (account,
2166                                           "outbox-deleted",
2167                                           G_CALLBACK (on_outbox_deleted_cb),
2168                                           self);
2169                 g_object_unref (account);
2170         }
2171
2172         /* Get the accounts: */
2173         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2174                                         TNY_LIST (model),
2175                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2176
2177         sortable = gtk_tree_model_sort_new_with_model (model);
2178         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2179                                               NAME_COLUMN,
2180                                               GTK_SORT_ASCENDING);
2181         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2182                                          NAME_COLUMN,
2183                                          cmp_rows, NULL, NULL);
2184
2185         /* Create filter model */
2186         filter_model = gtk_tree_model_filter_new (sortable, NULL);
2187         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2188                                                 filter_row,
2189                                                 self,
2190                                                 NULL);
2191
2192         /* Set new model */
2193         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2194 #ifndef MODEST_TOOLKIT_HILDON2
2195         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2196                           (GCallback) on_row_inserted_maybe_select_folder, self);
2197 #endif
2198
2199         g_object_unref (model);
2200         g_object_unref (filter_model);
2201         g_object_unref (sortable);
2202
2203         /* Force a reselection of the INBOX next time the widget is shown */
2204         priv->reselect = TRUE;
2205
2206         return TRUE;
2207 }
2208
2209
2210 static void
2211 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2212 {
2213         GtkTreeModel *model = NULL;
2214         TnyFolderStore *folder = NULL;
2215         GtkTreeIter iter;
2216         ModestFolderView *tree_view = NULL;
2217         ModestFolderViewPrivate *priv = NULL;
2218         gboolean selected = FALSE;
2219
2220         g_return_if_fail (sel);
2221         g_return_if_fail (user_data);
2222
2223         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2224
2225         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2226
2227         tree_view = MODEST_FOLDER_VIEW (user_data);
2228
2229         if (selected) {
2230                 gtk_tree_model_get (model, &iter,
2231                                     INSTANCE_COLUMN, &folder,
2232                                     -1);
2233
2234                 /* If the folder is the same do not notify */
2235                 if (folder && priv->cur_folder_store == folder) {
2236                         g_object_unref (folder);
2237                         return;
2238                 }
2239         }
2240
2241         /* Current folder was unselected */
2242         if (priv->cur_folder_store) {
2243                 /* We must do this firstly because a libtinymail-camel
2244                    implementation detail. If we issue the signal
2245                    before doing the sync_async, then that signal could
2246                    cause (and it actually does it) a free of the
2247                    summary of the folder (because the main window will
2248                    clear the headers view */
2249                 if (TNY_IS_FOLDER(priv->cur_folder_store))
2250                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2251                                                FALSE, NULL, NULL, NULL);
2252
2253                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2254                        priv->cur_folder_store, FALSE);
2255
2256                 g_object_unref (priv->cur_folder_store);
2257                 priv->cur_folder_store = NULL;
2258         }
2259
2260         /* New current references */
2261         priv->cur_folder_store = folder;
2262
2263         /* New folder has been selected. Do not notify if there is
2264            nothing new selected */
2265         if (selected) {
2266                 g_signal_emit (G_OBJECT(tree_view),
2267                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2268                                0, priv->cur_folder_store, TRUE);
2269         }
2270 }
2271
2272 static void
2273 on_row_activated (GtkTreeView *treeview,
2274                   GtkTreePath *treepath,
2275                   GtkTreeViewColumn *column,
2276                   gpointer user_data)
2277 {
2278         GtkTreeModel *model = NULL;
2279         TnyFolderStore *folder = NULL;
2280         GtkTreeIter iter;
2281         ModestFolderView *self = NULL;
2282         ModestFolderViewPrivate *priv = NULL;
2283
2284         g_return_if_fail (treeview);
2285         g_return_if_fail (user_data);
2286
2287         self = MODEST_FOLDER_VIEW (user_data);
2288         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2289
2290         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2291
2292         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2293                 return;
2294
2295         gtk_tree_model_get (model, &iter,
2296                             INSTANCE_COLUMN, &folder,
2297                             -1);
2298
2299         g_signal_emit (G_OBJECT(self),
2300                        signals[FOLDER_ACTIVATED_SIGNAL],
2301                        0, folder);
2302
2303 #ifdef MODEST_TOOLKIT_HILDON2
2304         HildonUIMode ui_mode;
2305         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2306         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2307                 if (priv->cur_folder_store)
2308                         g_object_unref (priv->cur_folder_store);
2309                 priv->cur_folder_store = g_object_ref (folder);
2310         }
2311 #endif
2312
2313         g_object_unref (folder);
2314 }
2315
2316 TnyFolderStore *
2317 modest_folder_view_get_selected (ModestFolderView *self)
2318 {
2319         ModestFolderViewPrivate *priv;
2320
2321         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2322
2323         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2324         if (priv->cur_folder_store)
2325                 g_object_ref (priv->cur_folder_store);
2326
2327         return priv->cur_folder_store;
2328 }
2329
2330 static gint
2331 get_cmp_rows_type_pos (GObject *folder)
2332 {
2333         /* Remote accounts -> Local account -> MMC account .*/
2334         /* 0, 1, 2 */
2335
2336         if (TNY_IS_ACCOUNT (folder) &&
2337                 modest_tny_account_is_virtual_local_folders (
2338                         TNY_ACCOUNT (folder))) {
2339                 return 1;
2340         } else if (TNY_IS_ACCOUNT (folder)) {
2341                 TnyAccount *account = TNY_ACCOUNT (folder);
2342                 const gchar *account_id = tny_account_get_id (account);
2343                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2344                         return 2;
2345                 else
2346                         return 0;
2347         }
2348         else {
2349                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2350                 return -1; /* Should never happen */
2351         }
2352 }
2353
2354 static gboolean
2355 inbox_is_special (TnyFolderStore *folder_store)
2356 {
2357         gboolean is_special = TRUE;
2358
2359         if (TNY_IS_FOLDER (folder_store)) {
2360                 const gchar *id;
2361                 gchar *downcase;
2362                 gchar *last_bar;
2363                 gchar *last_inbox_bar;
2364
2365                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2366                 downcase = g_utf8_strdown (id, -1);
2367                 last_bar = g_strrstr (downcase, "/");
2368                 if (last_bar) {
2369                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2370                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2371                                 is_special = FALSE;
2372                 } else {
2373                         is_special = FALSE;
2374                 }
2375                 g_free (downcase);
2376         }
2377         return is_special;
2378 }
2379
2380 static gint
2381 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2382 {
2383         TnyAccount *account;
2384         gboolean is_special;
2385         /* Inbox, Outbox, Drafts, Sent, User */
2386         /* 0, 1, 2, 3, 4 */
2387
2388         if (!TNY_IS_FOLDER (folder_store))
2389                 return 4;
2390         switch (t) {
2391         case TNY_FOLDER_TYPE_INBOX:
2392         {
2393                 account = tny_folder_get_account (folder_store);
2394                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2395
2396                 /* In inbox case we need to know if the inbox is really the top
2397                  * inbox of the account, or if it's a submailbox inbox. To do
2398                  * this we'll apply an heuristic rule: Find last "/" and check
2399                  * if it's preceeded by another Inbox */
2400                 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2401                 g_object_unref (account);
2402                 return is_special?0:4;
2403         }
2404         break;
2405         case TNY_FOLDER_TYPE_OUTBOX:
2406                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2407                 break;
2408         case TNY_FOLDER_TYPE_DRAFTS:
2409         {
2410                 account = tny_folder_get_account (folder_store);
2411                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2412                 g_object_unref (account);
2413                 return is_special?1:4;
2414         }
2415         break;
2416         case TNY_FOLDER_TYPE_SENT:
2417         {
2418                 account = tny_folder_get_account (folder_store);
2419                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2420                 g_object_unref (account);
2421                 return is_special?3:4;
2422         }
2423         break;
2424         default:
2425                 return 4;
2426         }
2427 }
2428
2429 static gint
2430 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2431 {
2432         const gchar *a1_name, *a2_name;
2433
2434         a1_name = tny_account_get_name (a1);
2435         a2_name = tny_account_get_name (a2);
2436
2437         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2438 }
2439
2440 static gint
2441 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2442 {
2443         TnyAccount *a1 = NULL, *a2 = NULL;
2444         gint cmp;
2445
2446         if (TNY_IS_ACCOUNT (s1)) {
2447                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2448         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2449                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2450         }
2451
2452         if (TNY_IS_ACCOUNT (s2)) {
2453                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2454         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2455                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2456         }
2457
2458         if (!a1 || !a2) {
2459                 if (!a1 && !a2)
2460                         cmp = 0;
2461                 else if (!a1)
2462                         cmp = 1;
2463                 else
2464                         cmp = -1;
2465                 goto finish;
2466         }
2467
2468         if (a1 == a2) {
2469                 cmp = 0;
2470                 goto finish;
2471         }
2472         /* First we sort with the type of account */
2473         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2474         if (cmp != 0)
2475                 goto finish;
2476
2477         cmp = compare_account_names (a1, a2);
2478
2479 finish:
2480         if (a1)
2481                 g_object_unref (a1);
2482         if (a2)
2483                 g_object_unref (a2);
2484
2485         return cmp;
2486 }
2487
2488 static gint
2489 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2490 {
2491         gint is_account1, is_account2;
2492
2493         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2494         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2495
2496         return is_account2 - is_account1;
2497 }
2498
2499 /*
2500  * This function orders the mail accounts according to these rules:
2501  * 1st - remote accounts
2502  * 2nd - local account
2503  * 3rd - MMC account
2504  */
2505 static gint
2506 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2507           gpointer user_data)
2508 {
2509         gint cmp = 0;
2510         gchar *name1 = NULL;
2511         gchar *name2 = NULL;
2512         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2513         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2514         GObject *folder1 = NULL;
2515         GObject *folder2 = NULL;
2516
2517         gtk_tree_model_get (tree_model, iter1,
2518                             NAME_COLUMN, &name1,
2519                             TYPE_COLUMN, &type,
2520                             INSTANCE_COLUMN, &folder1,
2521                             -1);
2522         gtk_tree_model_get (tree_model, iter2,
2523                             NAME_COLUMN, &name2,
2524                             TYPE_COLUMN, &type2,
2525                             INSTANCE_COLUMN, &folder2,
2526                             -1);
2527
2528         /* Return if we get no folder. This could happen when folder
2529            operations are happening. The model is updated after the
2530            folder copy/move actually occurs, so there could be
2531            situations where the model to be drawn is not correct */
2532         if (!folder1 || !folder2)
2533                 goto finish;
2534
2535         /* Sort by type. First the special folders, then the archives */
2536         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2537         if (cmp != 0)
2538                 goto finish;
2539
2540         /* Now we sort using the account of each folder */
2541         if (TNY_IS_FOLDER_STORE (folder1) && 
2542             TNY_IS_FOLDER_STORE (folder2)) {
2543                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2544                 if (cmp != 0)
2545                         goto finish;
2546
2547                 /* Each group is preceeded by its account */
2548                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2549                 if (cmp != 0)
2550                         goto finish;
2551         }
2552
2553         /* Pure sort by name */
2554         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2555  finish:
2556         if (folder1)
2557                 g_object_unref(G_OBJECT(folder1));
2558         if (folder2)
2559                 g_object_unref(G_OBJECT(folder2));
2560
2561         g_free (name1);
2562         g_free (name2);
2563
2564         return cmp;
2565 }
2566
2567 /*****************************************************************************/
2568 /*                        DRAG and DROP stuff                                */
2569 /*****************************************************************************/
2570 /*
2571  * This function fills the #GtkSelectionData with the row and the
2572  * model that has been dragged. It's called when this widget is a
2573  * source for dnd after the event drop happened
2574  */
2575 static void
2576 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2577                   guint info, guint time, gpointer data)
2578 {
2579         GtkTreeSelection *selection;
2580         GtkTreeModel *model;
2581         GtkTreeIter iter;
2582         GtkTreePath *source_row;
2583
2584         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2585         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2586
2587                 source_row = gtk_tree_model_get_path (model, &iter);
2588                 gtk_tree_set_row_drag_data (selection_data,
2589                                             model,
2590                                             source_row);
2591
2592                 gtk_tree_path_free (source_row);
2593         }
2594 }
2595
2596 typedef struct _DndHelper {
2597         ModestFolderView *folder_view;
2598         gboolean delete_source;
2599         GtkTreePath *source_row;
2600 } DndHelper;
2601
2602 static void
2603 dnd_helper_destroyer (DndHelper *helper)
2604 {
2605         /* Free the helper */
2606         gtk_tree_path_free (helper->source_row);
2607         g_slice_free (DndHelper, helper);
2608 }
2609
2610 static void
2611 xfer_folder_cb (ModestMailOperation *mail_op,
2612                 TnyFolder *new_folder,
2613                 gpointer user_data)
2614 {
2615         if (new_folder) {
2616                 /* Select the folder */
2617                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2618                                                   new_folder, FALSE);
2619         }
2620 }
2621
2622
2623 /* get the folder for the row the treepath refers to. */
2624 /* folder must be unref'd */
2625 static TnyFolderStore *
2626 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2627 {
2628         GtkTreeIter iter;
2629         TnyFolderStore *folder = NULL;
2630
2631         if (gtk_tree_model_get_iter (model,&iter, path))
2632                 gtk_tree_model_get (model, &iter,
2633                                     INSTANCE_COLUMN, &folder,
2634                                     -1);
2635         return folder;
2636 }
2637
2638
2639 /*
2640  * This function is used by drag_data_received_cb to manage drag and
2641  * drop of a header, i.e, and drag from the header view to the folder
2642  * view.
2643  */
2644 static void
2645 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2646                                 GtkTreeModel *dest_model,
2647                                 GtkTreePath  *dest_row,
2648                                 GtkSelectionData *selection_data)
2649 {
2650         TnyList *headers = NULL;
2651         TnyFolder *folder = NULL, *src_folder = NULL;
2652         TnyFolderType folder_type;
2653         GtkTreeIter source_iter, dest_iter;
2654         ModestWindowMgr *mgr = NULL;
2655         ModestWindow *main_win = NULL;
2656         gchar **uris, **tmp;
2657
2658         /* Build the list of headers */
2659         mgr = modest_runtime_get_window_mgr ();
2660         headers = tny_simple_list_new ();
2661         uris = modest_dnd_selection_data_get_paths (selection_data);
2662         tmp = uris;
2663
2664         while (*tmp != NULL) {
2665                 TnyHeader *header;
2666                 GtkTreePath *path;
2667                 gboolean first = TRUE;
2668
2669                 /* Get header */
2670                 path = gtk_tree_path_new_from_string (*tmp);
2671                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2672                 gtk_tree_model_get (source_model, &source_iter,
2673                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2674                                     &header, -1);
2675
2676                 /* Do not enable d&d of headers already opened */
2677                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2678                         tny_list_append (headers, G_OBJECT (header));
2679
2680                 if (G_UNLIKELY (first)) {
2681                         src_folder = tny_header_get_folder (header);
2682                         first = FALSE;
2683                 }
2684
2685                 /* Free and go on */
2686                 gtk_tree_path_free (path);
2687                 g_object_unref (header);
2688                 tmp++;
2689         }
2690         g_strfreev (uris);
2691
2692         /* This could happen ig we perform a d&d very quickly over the
2693            same row that row could dissapear because message is
2694            transferred */
2695         if (!TNY_IS_FOLDER (src_folder))
2696                 goto cleanup;
2697
2698         /* Get the target folder */
2699         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2700         gtk_tree_model_get (dest_model, &dest_iter,
2701                             INSTANCE_COLUMN,
2702                             &folder, -1);
2703
2704         if (!folder || !TNY_IS_FOLDER(folder)) {
2705 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2706                 goto cleanup;
2707         }
2708
2709         folder_type = modest_tny_folder_guess_folder_type (folder);
2710         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2711 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2712                 goto cleanup;  /* cannot move messages there */
2713         }
2714
2715         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2716 /*              g_warning ("folder not writable"); */
2717                 goto cleanup; /* verboten! */
2718         }
2719
2720         /* Ask for confirmation to move */
2721         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2722         if (!main_win) {
2723                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2724                 goto cleanup;
2725         }
2726
2727         /* Transfer messages */
2728         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2729                                                     headers, folder);
2730
2731         /* Frees */
2732 cleanup:
2733         if (G_IS_OBJECT (src_folder))
2734                 g_object_unref (src_folder);
2735         if (G_IS_OBJECT(folder))
2736                 g_object_unref (G_OBJECT (folder));
2737         if (G_IS_OBJECT(headers))
2738                 g_object_unref (headers);
2739 }
2740
2741 typedef struct {
2742         TnyFolderStore *src_folder;
2743         TnyFolderStore *dst_folder;
2744         ModestFolderView *folder_view;
2745         DndHelper *helper;
2746 } DndFolderInfo;
2747
2748 static void
2749 dnd_folder_info_destroyer (DndFolderInfo *info)
2750 {
2751         if (info->src_folder)
2752                 g_object_unref (info->src_folder);
2753         if (info->dst_folder)
2754                 g_object_unref (info->dst_folder);
2755         g_slice_free (DndFolderInfo, info);
2756 }
2757
2758 static void
2759 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2760                                     GtkWindow *parent_window,
2761                                     TnyAccount *account)
2762 {
2763         /* Show error */
2764         modest_ui_actions_on_account_connection_error (parent_window, account);
2765
2766         /* Free the helper & info */
2767         dnd_helper_destroyer (info->helper);
2768         dnd_folder_info_destroyer (info);
2769 }
2770
2771 static void
2772 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2773                                                      GError *err,
2774                                                      GtkWindow *parent_window,
2775                                                      TnyAccount *account,
2776                                                      gpointer user_data)
2777 {
2778         DndFolderInfo *info = NULL;
2779         ModestMailOperation *mail_op;
2780
2781         info = (DndFolderInfo *) user_data;
2782
2783         if (err || canceled) {
2784                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2785                 return;
2786         }
2787
2788         /* Do the mail operation */
2789         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2790                                                                  modest_ui_actions_move_folder_error_handler,
2791                                                                  info->src_folder, NULL);
2792
2793         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2794                                          mail_op);
2795
2796         /* Transfer the folder */
2797         modest_mail_operation_xfer_folder (mail_op,
2798                                            TNY_FOLDER (info->src_folder),
2799                                            info->dst_folder,
2800                                            info->helper->delete_source,
2801                                            xfer_folder_cb,
2802                                            info->helper->folder_view);
2803
2804         /* Frees */
2805         g_object_unref (G_OBJECT (mail_op));
2806         dnd_helper_destroyer (info->helper);
2807         dnd_folder_info_destroyer (info);
2808 }
2809
2810
2811 static void
2812 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2813                                                      GError *err,
2814                                                      GtkWindow *parent_window,
2815                                                      TnyAccount *account,
2816                                                      gpointer user_data)
2817 {
2818         DndFolderInfo *info = NULL;
2819
2820         info = (DndFolderInfo *) user_data;
2821
2822         if (err || canceled) {
2823                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2824                 return;
2825         }
2826
2827         /* Connect to source folder and perform the copy/move */
2828         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2829                                                        info->src_folder,
2830                                                        drag_and_drop_from_folder_view_src_folder_performer,
2831                                                        info);
2832 }
2833
2834 /*
2835  * This function is used by drag_data_received_cb to manage drag and
2836  * drop of a folder, i.e, and drag from the folder view to the same
2837  * folder view.
2838  */
2839 static void
2840 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2841                                 GtkTreeModel     *dest_model,
2842                                 GtkTreePath      *dest_row,
2843                                 GtkSelectionData *selection_data,
2844                                 DndHelper        *helper)
2845 {
2846         GtkTreeIter dest_iter, iter;
2847         TnyFolderStore *dest_folder = NULL;
2848         TnyFolderStore *folder = NULL;
2849         gboolean forbidden = FALSE;
2850         ModestWindow *win;
2851         DndFolderInfo *info = NULL;
2852
2853         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2854         if (!win) {
2855                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2856                 dnd_helper_destroyer (helper);
2857                 return;
2858         }
2859
2860         if (!forbidden) {
2861                 /* check the folder rules for the destination */
2862                 folder = tree_path_to_folder (dest_model, dest_row);
2863                 if (TNY_IS_FOLDER(folder)) {
2864                         ModestTnyFolderRules rules =
2865                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2866                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2867                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2868                         /* enable local root as destination for folders */
2869                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2870                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2871                                 forbidden = TRUE;
2872                 }
2873                 g_object_unref (folder);
2874         }
2875         if (!forbidden) {
2876                 /* check the folder rules for the source */
2877                 folder = tree_path_to_folder (source_model, helper->source_row);
2878                 if (TNY_IS_FOLDER(folder)) {
2879                         ModestTnyFolderRules rules =
2880                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2881                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2882                 } else
2883                         forbidden = TRUE;
2884                 g_object_unref (folder);
2885         }
2886
2887
2888         /* Check if the drag is possible */
2889         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2890                 /* Show error */
2891                 modest_platform_run_information_dialog ((GtkWindow *) win, 
2892                                                         _("mail_in_ui_folder_move_target_error"), 
2893                                                         FALSE);
2894                 /* Restore the previous selection */
2895                 folder = tree_path_to_folder (source_model, helper->source_row);
2896                 if (folder) {
2897                         if (TNY_IS_FOLDER (folder))
2898                                 modest_folder_view_select_folder (helper->folder_view, 
2899                                                                   TNY_FOLDER (folder), FALSE);
2900                         g_object_unref (folder);
2901                 }
2902                 dnd_helper_destroyer (helper);
2903                 return;
2904         }
2905
2906         /* Get data */
2907         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2908         gtk_tree_model_get (dest_model, &dest_iter,
2909                             INSTANCE_COLUMN,
2910                             &dest_folder, -1);
2911         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2912         gtk_tree_model_get (source_model, &iter,
2913                             INSTANCE_COLUMN,
2914                             &folder, -1);
2915
2916         /* Create the info for the performer */
2917         info = g_slice_new0 (DndFolderInfo);
2918         info->src_folder = g_object_ref (folder);
2919         info->dst_folder = g_object_ref (dest_folder);
2920         info->helper = helper;
2921
2922         /* Connect to the destination folder and perform the copy/move */
2923         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2924                                                        dest_folder,
2925                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2926                                                        info);
2927
2928         /* Frees */
2929         g_object_unref (dest_folder);
2930         g_object_unref (folder);
2931 }
2932
2933 /*
2934  * This function receives the data set by the "drag-data-get" signal
2935  * handler. This information comes within the #GtkSelectionData. This
2936  * function will manage both the drags of folders of the treeview and
2937  * drags of headers of the header view widget.
2938  */
2939 static void
2940 on_drag_data_received (GtkWidget *widget,
2941                        GdkDragContext *context,
2942                        gint x,
2943                        gint y,
2944                        GtkSelectionData *selection_data,
2945                        guint target_type,
2946                        guint time,
2947                        gpointer data)
2948 {
2949         GtkWidget *source_widget;
2950         GtkTreeModel *dest_model, *source_model;
2951         GtkTreePath *source_row, *dest_row;
2952         GtkTreeViewDropPosition pos;
2953         gboolean delete_source = FALSE;
2954         gboolean success = FALSE;
2955
2956         /* Do not allow further process */
2957         g_signal_stop_emission_by_name (widget, "drag-data-received");
2958         source_widget = gtk_drag_get_source_widget (context);
2959
2960         /* Get the action */
2961         if (context->action == GDK_ACTION_MOVE) {
2962                 delete_source = TRUE;
2963
2964                 /* Notify that there is no folder selected. We need to
2965                    do this in order to update the headers view (and
2966                    its monitors, because when moving, the old folder
2967                    won't longer exist. We can not wait for the end of
2968                    the operation, because the operation won't start if
2969                    the folder is in use */
2970                 if (source_widget == widget) {
2971                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2972                         gtk_tree_selection_unselect_all (sel);
2973                 }
2974         }
2975
2976         /* Check if the get_data failed */
2977         if (selection_data == NULL || selection_data->length < 0)
2978                 goto end;
2979
2980         /* Select the destination model */
2981         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2982
2983         /* Get the path to the destination row. Can not call
2984            gtk_tree_view_get_drag_dest_row() because the source row
2985            is not selected anymore */
2986         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2987                                            &dest_row, &pos);
2988
2989         /* Only allow drops IN other rows */
2990         if (!dest_row ||
2991             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2992             pos == GTK_TREE_VIEW_DROP_AFTER)
2993                 goto end;
2994
2995         success = TRUE;
2996         /* Drags from the header view */
2997         if (source_widget != widget) {
2998                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2999
3000                 drag_and_drop_from_header_view (source_model,
3001                                                 dest_model,
3002                                                 dest_row,
3003                                                 selection_data);
3004         } else {
3005                 DndHelper *helper = NULL;
3006
3007                 /* Get the source model and row */
3008                 gtk_tree_get_row_drag_data (selection_data,
3009                                             &source_model,
3010                                             &source_row);
3011
3012                 /* Create the helper */
3013                 helper = g_slice_new0 (DndHelper);
3014                 helper->delete_source = delete_source;
3015                 helper->source_row = gtk_tree_path_copy (source_row);
3016                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3017
3018                 drag_and_drop_from_folder_view (source_model,
3019                                                 dest_model,
3020                                                 dest_row,
3021                                                 selection_data,
3022                                                 helper);
3023
3024                 gtk_tree_path_free (source_row);
3025         }
3026
3027         /* Frees */
3028         gtk_tree_path_free (dest_row);
3029
3030  end:
3031         /* Finish the drag and drop */
3032         gtk_drag_finish (context, success, FALSE, time);
3033 }
3034
3035 /*
3036  * We define a "drag-drop" signal handler because we do not want to
3037  * use the default one, because the default one always calls
3038  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3039  * signal handler, because there we have all the information available
3040  * to know if the dnd was a success or not.
3041  */
3042 static gboolean
3043 drag_drop_cb (GtkWidget      *widget,
3044               GdkDragContext *context,
3045               gint            x,
3046               gint            y,
3047               guint           time,
3048               gpointer        user_data)
3049 {
3050         gpointer target;
3051
3052         if (!context->targets)
3053                 return FALSE;
3054
3055         /* Check if we're dragging a folder row */
3056         target = gtk_drag_dest_find_target (widget, context, NULL);
3057
3058         /* Request the data from the source. */
3059         gtk_drag_get_data(widget, context, target, time);
3060
3061     return TRUE;
3062 }
3063
3064 /*
3065  * This function expands a node of a tree view if it's not expanded
3066  * yet. Not sure why it needs the threads stuff, but gtk+`example code
3067  * does that, so that's why they're here.
3068  */
3069 static gint
3070 expand_row_timeout (gpointer data)
3071 {
3072         GtkTreeView *tree_view = data;
3073         GtkTreePath *dest_path = NULL;
3074         GtkTreeViewDropPosition pos;
3075         gboolean result = FALSE;
3076
3077         gdk_threads_enter ();
3078
3079         gtk_tree_view_get_drag_dest_row (tree_view,
3080                                          &dest_path,
3081                                          &pos);
3082
3083         if (dest_path &&
3084             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3085              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3086                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3087                 gtk_tree_path_free (dest_path);
3088         }
3089         else {
3090                 if (dest_path)
3091                         gtk_tree_path_free (dest_path);
3092
3093                 result = TRUE;
3094         }
3095
3096         gdk_threads_leave ();
3097
3098         return result;
3099 }
3100
3101 /*
3102  * This function is called whenever the pointer is moved over a widget
3103  * while dragging some data. It installs a timeout that will expand a
3104  * node of the treeview if not expanded yet. This function also calls
3105  * gdk_drag_status in order to set the suggested action that will be
3106  * used by the "drag-data-received" signal handler to know if we
3107  * should do a move or just a copy of the data.
3108  */
3109 static gboolean
3110 on_drag_motion (GtkWidget      *widget,
3111                 GdkDragContext *context,
3112                 gint            x,
3113                 gint            y,
3114                 guint           time,
3115                 gpointer        user_data)
3116 {
3117         GtkTreeViewDropPosition pos;
3118         GtkTreePath *dest_row;
3119         GtkTreeModel *dest_model;
3120         ModestFolderViewPrivate *priv;
3121         GdkDragAction suggested_action;
3122         gboolean valid_location = FALSE;
3123         TnyFolderStore *folder = NULL;
3124
3125         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3126
3127         if (priv->timer_expander != 0) {
3128                 g_source_remove (priv->timer_expander);
3129                 priv->timer_expander = 0;
3130         }
3131
3132         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3133                                            x, y,
3134                                            &dest_row,
3135                                            &pos);
3136
3137         /* Do not allow drops between folders */
3138         if (!dest_row ||
3139             pos == GTK_TREE_VIEW_DROP_BEFORE ||
3140             pos == GTK_TREE_VIEW_DROP_AFTER) {
3141                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3142                 gdk_drag_status(context, 0, time);
3143                 valid_location = FALSE;
3144                 goto out;
3145         } else {
3146                 valid_location = TRUE;
3147         }
3148
3149         /* Check that the destination folder is writable */
3150         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3151         folder = tree_path_to_folder (dest_model, dest_row);
3152         if (folder && TNY_IS_FOLDER (folder)) {
3153                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3154
3155                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3156                         valid_location = FALSE;
3157                         goto out;
3158                 }
3159         }
3160
3161         /* Expand the selected row after 1/2 second */
3162         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3163                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3164         }
3165         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3166
3167         /* Select the desired action. By default we pick MOVE */
3168         suggested_action = GDK_ACTION_MOVE;
3169
3170         if (context->actions == GDK_ACTION_COPY)
3171             gdk_drag_status(context, GDK_ACTION_COPY, time);
3172         else if (context->actions == GDK_ACTION_MOVE)
3173             gdk_drag_status(context, GDK_ACTION_MOVE, time);
3174         else if (context->actions & suggested_action)
3175             gdk_drag_status(context, suggested_action, time);
3176         else
3177             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3178
3179  out:
3180         if (folder)
3181                 g_object_unref (folder);
3182         if (dest_row) {
3183                 gtk_tree_path_free (dest_row);
3184         }
3185         g_signal_stop_emission_by_name (widget, "drag-motion");
3186
3187         return valid_location;
3188 }
3189
3190 /*
3191  * This function sets the treeview as a source and a target for dnd
3192  * events. It also connects all the requirede signals.
3193  */
3194 static void
3195 setup_drag_and_drop (GtkTreeView *self)
3196 {
3197         /* Set up the folder view as a dnd destination. Set only the
3198            highlight flag, otherwise gtk will have a different
3199            behaviour */
3200 #ifdef MODEST_TOOLKIT_HILDON2
3201         return;
3202 #endif
3203         gtk_drag_dest_set (GTK_WIDGET (self),
3204                            GTK_DEST_DEFAULT_HIGHLIGHT,
3205                            folder_view_drag_types,
3206                            G_N_ELEMENTS (folder_view_drag_types),
3207                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
3208
3209         g_signal_connect (G_OBJECT (self),
3210                           "drag_data_received",
3211                           G_CALLBACK (on_drag_data_received),
3212                           NULL);
3213
3214
3215         /* Set up the treeview as a dnd source */
3216         gtk_drag_source_set (GTK_WIDGET (self),
3217                              GDK_BUTTON1_MASK,
3218                              folder_view_drag_types,
3219                              G_N_ELEMENTS (folder_view_drag_types),
3220                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
3221
3222         g_signal_connect (G_OBJECT (self),
3223                           "drag_motion",
3224                           G_CALLBACK (on_drag_motion),
3225                           NULL);
3226
3227         g_signal_connect (G_OBJECT (self),
3228                           "drag_data_get",
3229                           G_CALLBACK (on_drag_data_get),
3230                           NULL);
3231
3232         g_signal_connect (G_OBJECT (self),
3233                           "drag_drop",
3234                           G_CALLBACK (drag_drop_cb),
3235                           NULL);
3236 }
3237
3238 /*
3239  * This function manages the navigation through the folders using the
3240  * keyboard or the hardware keys in the device
3241  */
3242 static gboolean
3243 on_key_pressed (GtkWidget *self,
3244                 GdkEventKey *event,
3245                 gpointer user_data)
3246 {
3247         GtkTreeSelection *selection;
3248         GtkTreeIter iter;
3249         GtkTreeModel *model;
3250         gboolean retval = FALSE;
3251
3252         /* Up and Down are automatically managed by the treeview */
3253         if (event->keyval == GDK_Return) {
3254                 /* Expand/Collapse the selected row */
3255                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3256                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3257                         GtkTreePath *path;
3258
3259                         path = gtk_tree_model_get_path (model, &iter);
3260
3261                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3262                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3263                         else
3264                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3265                         gtk_tree_path_free (path);
3266                 }
3267                 /* No further processing */
3268                 retval = TRUE;
3269         }
3270
3271         return retval;
3272 }
3273
3274 /*
3275  * We listen to the changes in the local folder account name key,
3276  * because we want to show the right name in the view. The local
3277  * folder account name corresponds to the device name in the Maemo
3278  * version. We do this because we do not want to query gconf on each
3279  * tree view refresh. It's better to cache it and change whenever
3280  * necessary.
3281  */
3282 static void
3283 on_configuration_key_changed (ModestConf* conf,
3284                               const gchar *key,
3285                               ModestConfEvent event,
3286                               ModestConfNotificationId id,
3287                               ModestFolderView *self)
3288 {
3289         ModestFolderViewPrivate *priv;
3290
3291
3292         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3293         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3294
3295         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3296                 g_free (priv->local_account_name);
3297
3298                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3299                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3300                 else
3301                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3302                                                                            MODEST_CONF_DEVICE_NAME, NULL);
3303
3304                 /* Force a redraw */
3305 #if GTK_CHECK_VERSION(2, 8, 0)
3306                 GtkTreeViewColumn * tree_column;
3307
3308                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3309                                                         NAME_COLUMN);
3310                 gtk_tree_view_column_queue_resize (tree_column);
3311 #else
3312                 gtk_widget_queue_draw (GTK_WIDGET (self));
3313 #endif
3314         }
3315 }
3316
3317 void
3318 modest_folder_view_set_style (ModestFolderView *self,
3319                               ModestFolderViewStyle style)
3320 {
3321         ModestFolderViewPrivate *priv;
3322
3323         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3324         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3325                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3326
3327         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3328
3329
3330         priv->style = style;
3331 }
3332
3333 void
3334 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3335                                                              const gchar *account_id)
3336 {
3337         ModestFolderViewPrivate *priv;
3338         GtkTreeModel *model;
3339
3340         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3341
3342         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3343
3344         /* This will be used by the filter_row callback,
3345          * to decided which rows to show: */
3346         if (priv->visible_account_id) {
3347                 g_free (priv->visible_account_id);
3348                 priv->visible_account_id = NULL;
3349         }
3350         if (account_id)
3351                 priv->visible_account_id = g_strdup (account_id);
3352
3353         /* Refilter */
3354         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3355         if (GTK_IS_TREE_MODEL_FILTER (model))
3356                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3357
3358         /* Save settings to gconf */
3359         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3360                                    MODEST_CONF_FOLDER_VIEW_KEY);
3361
3362         /* Notify observers */
3363         g_signal_emit (G_OBJECT(self),
3364                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3365                        account_id);
3366 }
3367
3368 const gchar *
3369 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3370 {
3371         ModestFolderViewPrivate *priv;
3372
3373         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3374
3375         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3376
3377         return (const gchar *) priv->visible_account_id;
3378 }
3379
3380 static gboolean
3381 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3382 {
3383         do {
3384                 GtkTreeIter child;
3385                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3386
3387                 gtk_tree_model_get (model, iter,
3388                                     TYPE_COLUMN,
3389                                     &type, -1);
3390
3391                 gboolean result = FALSE;
3392                 if (type == TNY_FOLDER_TYPE_INBOX) {
3393                         result = TRUE;
3394                 }
3395                 if (result) {
3396                         *inbox_iter = *iter;
3397                         return TRUE;
3398                 }
3399
3400                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3401                         if (find_inbox_iter (model, &child, inbox_iter))
3402                                 return TRUE;
3403                 }
3404
3405         } while (gtk_tree_model_iter_next (model, iter));
3406
3407         return FALSE;
3408 }
3409
3410
3411
3412
3413 void
3414 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3415 {
3416         GtkTreeModel *model;
3417         GtkTreeIter iter, inbox_iter;
3418         GtkTreeSelection *sel;
3419         GtkTreePath *path = NULL;
3420
3421         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3422
3423         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3424         if (!model)
3425                 return;
3426
3427         expand_root_items (self);
3428         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3429
3430         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3431                 g_warning ("%s: model is empty", __FUNCTION__);
3432                 return;
3433         }
3434
3435         if (find_inbox_iter (model, &iter, &inbox_iter))
3436                 path = gtk_tree_model_get_path (model, &inbox_iter);
3437         else
3438                 path = gtk_tree_path_new_first ();
3439
3440         /* Select the row and free */
3441         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3442         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3443         gtk_tree_path_free (path);
3444
3445         /* set focus */
3446         gtk_widget_grab_focus (GTK_WIDGET(self));
3447 }
3448
3449
3450 /* recursive */
3451 static gboolean
3452 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3453                   TnyFolder* folder)
3454 {
3455         do {
3456                 GtkTreeIter child;
3457                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3458                 TnyFolder* a_folder;
3459                 gchar *name = NULL;
3460
3461                 gtk_tree_model_get (model, iter,
3462                                     INSTANCE_COLUMN, &a_folder,
3463                                     NAME_COLUMN, &name,
3464                                     TYPE_COLUMN, &type,
3465                                     -1);
3466                 g_free (name);
3467
3468                 if (folder == a_folder) {
3469                         g_object_unref (a_folder);
3470                         *folder_iter = *iter;
3471                         return TRUE;
3472                 }
3473                 g_object_unref (a_folder);
3474
3475                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3476                         if (find_folder_iter (model, &child, folder_iter, folder))
3477                                 return TRUE;
3478                 }
3479
3480         } while (gtk_tree_model_iter_next (model, iter));
3481
3482         return FALSE;
3483 }
3484
3485 #ifndef MODEST_TOOLKIT_HILDON2
3486 static void
3487 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3488                                      GtkTreePath *path,
3489                                      GtkTreeIter *iter,
3490                                      ModestFolderView *self)
3491 {
3492         ModestFolderViewPrivate *priv = NULL;
3493         GtkTreeSelection *sel;
3494         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3495         GObject *instance = NULL;
3496
3497         if (!MODEST_IS_FOLDER_VIEW(self))
3498                 return;
3499
3500         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3501
3502         priv->reexpand = TRUE;
3503
3504         gtk_tree_model_get (tree_model, iter,
3505                             TYPE_COLUMN, &type,
3506                             INSTANCE_COLUMN, &instance,
3507                             -1);
3508
3509         if (!instance)
3510                 return;
3511
3512         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3513                 priv->folder_to_select = g_object_ref (instance);
3514         }
3515         g_object_unref (instance);
3516
3517         if (priv->folder_to_select) {
3518
3519                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3520                                                        FALSE)) {
3521                         GtkTreePath *path;
3522                         path = gtk_tree_model_get_path (tree_model, iter);
3523                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3524
3525                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3526
3527                         gtk_tree_selection_select_iter (sel, iter);
3528                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3529
3530                         gtk_tree_path_free (path);
3531                 }
3532
3533                 /* Disable next */
3534                 modest_folder_view_disable_next_folder_selection (self);
3535
3536                 /* Refilter the model */
3537                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3538         }
3539 }
3540 #endif
3541
3542 void
3543 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3544 {
3545         ModestFolderViewPrivate *priv;
3546
3547         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3548
3549         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3550
3551         if (priv->folder_to_select)
3552                 g_object_unref(priv->folder_to_select);
3553
3554         priv->folder_to_select = NULL;
3555 }
3556
3557 gboolean
3558 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3559                                   gboolean after_change)
3560 {
3561         GtkTreeModel *model;
3562         GtkTreeIter iter, folder_iter;
3563         GtkTreeSelection *sel;
3564         ModestFolderViewPrivate *priv = NULL;
3565
3566         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3567         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3568
3569         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3570
3571         if (after_change) {
3572                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3573                 gtk_tree_selection_unselect_all (sel);
3574
3575                 if (priv->folder_to_select)
3576                         g_object_unref(priv->folder_to_select);
3577                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3578                 return TRUE;
3579         }
3580
3581         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3582         if (!model)
3583                 return FALSE;
3584
3585
3586         /* Refilter the model, before selecting the folder */
3587         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3588
3589         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3590                 g_warning ("%s: model is empty", __FUNCTION__);
3591                 return FALSE;
3592         }
3593
3594         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3595                 GtkTreePath *path;
3596
3597                 path = gtk_tree_model_get_path (model, &folder_iter);
3598                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3599
3600                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3601                 gtk_tree_selection_select_iter (sel, &folder_iter);
3602                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3603
3604                 gtk_tree_path_free (path);
3605                 return TRUE;
3606         }
3607         return FALSE;
3608 }
3609
3610
3611 void
3612 modest_folder_view_copy_selection (ModestFolderView *self)
3613 {
3614         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3615
3616         /* Copy selection */
3617         _clipboard_set_selected_data (self, FALSE);
3618 }
3619
3620 void
3621 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3622 {
3623         ModestFolderViewPrivate *priv = NULL;
3624         GtkTreeModel *model = NULL;
3625         const gchar **hidding = NULL;
3626         guint i, n_selected;
3627
3628         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3629         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3630
3631         /* Copy selection */
3632         if (!_clipboard_set_selected_data (folder_view, TRUE))
3633                 return;
3634
3635         /* Get hidding ids */
3636         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3637
3638         /* Clear hidding array created by previous cut operation */
3639         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3640
3641         /* Copy hidding array */
3642         priv->n_selected = n_selected;
3643         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3644         for (i=0; i < n_selected; i++)
3645                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3646
3647         /* Hide cut folders */
3648         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3649         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3650 }
3651
3652 void
3653 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3654                                ModestFolderView *folder_view_dst)
3655 {
3656         GtkTreeModel *filter_model = NULL;
3657         GtkTreeModel *model = NULL;
3658         GtkTreeModel *new_filter_model = NULL;
3659
3660         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3661         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3662
3663         /* Get src model*/
3664         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3665         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3666
3667         /* Build new filter model */
3668         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3669         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3670                                                 filter_row,
3671                                                 folder_view_dst,
3672                                                 NULL);
3673         /* Set copied model */
3674         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3675 #ifndef MODEST_TOOLKIT_HILDON2
3676         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3677                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3678 #endif
3679
3680         /* Free */
3681         g_object_unref (new_filter_model);
3682 }
3683
3684 void
3685 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3686                                           gboolean show)
3687 {
3688         GtkTreeModel *model = NULL;
3689         ModestFolderViewPrivate* priv;
3690
3691         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3692
3693         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3694         priv->show_non_move = show;
3695 /*      modest_folder_view_update_model(folder_view, */
3696 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3697
3698         /* Hide special folders */
3699         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3700         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3701                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3702         }
3703 }
3704
3705 /* Returns FALSE if it did not selected anything */
3706 static gboolean
3707 _clipboard_set_selected_data (ModestFolderView *folder_view,
3708                               gboolean delete)
3709 {
3710         ModestFolderViewPrivate *priv = NULL;
3711         TnyFolderStore *folder = NULL;
3712         gboolean retval = FALSE;
3713
3714         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3715         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3716
3717         /* Set selected data on clipboard   */
3718         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3719         folder = modest_folder_view_get_selected (folder_view);
3720
3721         /* Do not allow to select an account */
3722         if (TNY_IS_FOLDER (folder)) {
3723                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3724                 retval = TRUE;
3725         }
3726
3727         /* Free */
3728         g_object_unref (folder);
3729
3730         return retval;
3731 }
3732
3733 static void
3734 _clear_hidding_filter (ModestFolderView *folder_view)
3735 {
3736         ModestFolderViewPrivate *priv;
3737         guint i;
3738
3739         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3740         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3741
3742         if (priv->hidding_ids != NULL) {
3743                 for (i=0; i < priv->n_selected; i++)
3744                         g_free (priv->hidding_ids[i]);
3745                 g_free(priv->hidding_ids);
3746         }
3747 }
3748
3749
3750 static void
3751 on_display_name_changed (ModestAccountMgr *mgr,
3752                          const gchar *account,
3753                          gpointer user_data)
3754 {
3755         ModestFolderView *self;
3756
3757         self = MODEST_FOLDER_VIEW (user_data);
3758
3759         /* Force a redraw */
3760 #if GTK_CHECK_VERSION(2, 8, 0)
3761         GtkTreeViewColumn * tree_column;
3762
3763         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3764                                                 NAME_COLUMN);
3765         gtk_tree_view_column_queue_resize (tree_column);
3766 #else
3767         gtk_widget_queue_draw (GTK_WIDGET (self));
3768 #endif
3769 }
3770
3771 void 
3772 modest_folder_view_set_cell_style (ModestFolderView *self,
3773                                    ModestFolderViewCellStyle cell_style)
3774 {
3775         ModestFolderViewPrivate *priv = NULL;
3776
3777         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3778         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3779
3780         priv->cell_style = cell_style;
3781
3782         g_object_set (G_OBJECT (priv->messages_renderer),
3783                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3784                       NULL);
3785         
3786         gtk_widget_queue_draw (GTK_WIDGET (self));
3787 }
3788
3789 static void
3790 update_style (ModestFolderView *self)
3791 {
3792         ModestFolderViewPrivate *priv;
3793         GdkColor style_color;
3794         PangoAttrList *attr_list;
3795         GtkStyle *style;
3796         PangoAttribute *attr;
3797
3798         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3799         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3800
3801         /* Set color */
3802
3803         attr_list = pango_attr_list_new ();
3804         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3805                 gdk_color_parse ("grey", &style_color);
3806         }
3807         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3808         pango_attr_list_insert (attr_list, attr);
3809         
3810         /* set font */
3811         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3812                                            (GTK_WIDGET(self)),
3813                                            "SmallSystemFont", NULL,
3814                                            G_TYPE_NONE);
3815         if (style) {
3816                 attr = pango_attr_font_desc_new (pango_font_description_copy
3817                                                  (style->font_desc));
3818                 pango_attr_list_insert (attr_list, attr);
3819
3820                 g_object_set (G_OBJECT (priv->messages_renderer),
3821                               "foreground-gdk", &style_color,
3822                               "foreground-set", TRUE,
3823                               "attributes", attr_list,
3824                               NULL);
3825                 pango_attr_list_unref (attr_list);
3826         }
3827 }
3828
3829 static void 
3830 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3831 {
3832         if (strcmp ("style", spec->name) == 0) {
3833                 update_style (MODEST_FOLDER_VIEW (obj));
3834                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3835         } 
3836 }
3837
3838 void 
3839 modest_folder_view_set_filter (ModestFolderView *self,
3840                                ModestFolderViewFilter filter)
3841 {
3842         ModestFolderViewPrivate *priv;
3843         GtkTreeModel *filter_model;
3844
3845         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3846         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3847
3848         priv->filter |= filter;
3849
3850         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3851         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3852                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3853         }
3854 }
3855
3856 void 
3857 modest_folder_view_unset_filter (ModestFolderView *self,
3858                                  ModestFolderViewFilter filter)
3859 {
3860         ModestFolderViewPrivate *priv;
3861         GtkTreeModel *filter_model;
3862
3863         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3864         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3865
3866         priv->filter &= ~filter;
3867
3868         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3869         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3870                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3871         }
3872 }
3873
3874 gboolean
3875 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3876                                              ModestTnyFolderRules rules)
3877 {
3878         GtkTreeModel *filter_model;
3879         GtkTreeIter iter;
3880         gboolean fulfil = FALSE;
3881
3882         if (!get_inner_models (self, &filter_model, NULL, NULL))
3883                 return FALSE;
3884
3885         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3886                 return FALSE;
3887
3888         do {
3889                 TnyFolderStore *folder;
3890
3891                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3892                 if (folder) {
3893                         if (TNY_IS_FOLDER (folder)) {
3894                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3895                                 /* Folder rules are negative: non_writable, non_deletable... */
3896                                 if (!(folder_rules & rules))
3897                                         fulfil = TRUE;
3898                         }
3899                         g_object_unref (folder);
3900                 }
3901
3902         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3903
3904         return fulfil;
3905 }
3906
3907 void 
3908 modest_folder_view_set_list_to_move (ModestFolderView *self,
3909                                      TnyList *list)
3910 {
3911         ModestFolderViewPrivate *priv;
3912
3913         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3914         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3915
3916         if (priv->list_to_move)
3917                 g_object_unref (priv->list_to_move);
3918
3919         if (list)
3920                 g_object_ref (list);
3921
3922         priv->list_to_move = list;
3923 }
3924
3925 void
3926 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
3927 {
3928         ModestFolderViewPrivate *priv;
3929
3930         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3931         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3932
3933         if (priv->mailbox)
3934                 g_free (priv->mailbox);
3935
3936         priv->mailbox = g_strdup (mailbox);
3937
3938         /* Notify observers */
3939         g_signal_emit (G_OBJECT(self),
3940                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3941                        priv->visible_account_id);
3942 }
3943
3944 const gchar *
3945 modest_folder_view_get_mailbox (ModestFolderView *self)
3946 {
3947         ModestFolderViewPrivate *priv;
3948
3949         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
3950         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3951
3952         return (const gchar *) priv->mailbox;
3953 }