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