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