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