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