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