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