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