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