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