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