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