Now folder paths are shown as a set of dots (fixes part of NB#97144).
[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 #ifdef MODEST_TOOLKIT_HILDON2
36 #include <tny-gtk-folder-list-store.h>
37 #else
38 #include <tny-gtk-folder-store-tree-model.h>
39 #endif
40 #include <tny-gtk-header-list-model.h>
41 #include <tny-merge-folder.h>
42 #include <tny-folder.h>
43 #include <tny-folder-store-observer.h>
44 #include <tny-account-store.h>
45 #include <tny-account.h>
46 #include <tny-folder.h>
47 #include <tny-camel-folder.h>
48 #include <tny-simple-list.h>
49 #include <tny-camel-account.h>
50 #include <modest-tny-account.h>
51 #include <modest-tny-folder.h>
52 #include <modest-tny-local-folders-account.h>
53 #include <modest-tny-outbox-account.h>
54 #include <modest-marshal.h>
55 #include <modest-icon-names.h>
56 #include <modest-tny-account-store.h>
57 #include <modest-tny-local-folders-account.h>
58 #include <modest-text-utils.h>
59 #include <modest-runtime.h>
60 #include "modest-folder-view.h"
61 #include <modest-platform.h>
62 #include <modest-widget-memory.h>
63 #include <modest-ui-actions.h>
64 #include "modest-dnd.h"
65 #include "modest-ui-constants.h"
66 #include "widgets/modest-window.h"
67
68 /* Folder view drag types */
69 const GtkTargetEntry folder_view_drag_types[] =
70 {
71         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
72         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
73 };
74
75 /* Default icon sizes for Fremantle style are different */
76 #ifdef MODEST_TOOLKIT_HILDON2
77 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_BIG
78 #else
79 #define FOLDER_ICON_SIZE MODEST_ICON_SIZE_SMALL
80 #endif
81
82 /* Column names depending on we use list store or tree store */
83 #ifdef MODEST_TOOLKIT_HILDON2
84 #define NAME_COLUMN TNY_GTK_FOLDER_LIST_STORE_NAME_COLUMN
85 #define UNREAD_COLUMN TNY_GTK_FOLDER_LIST_STORE_UNREAD_COLUMN
86 #define ALL_COLUMN TNY_GTK_FOLDER_LIST_STORE_ALL_COLUMN
87 #define TYPE_COLUMN TNY_GTK_FOLDER_LIST_STORE_TYPE_COLUMN
88 #define INSTANCE_COLUMN TNY_GTK_FOLDER_LIST_STORE_INSTANCE_COLUMN
89 #else
90 #define NAME_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN
91 #define UNREAD_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN
92 #define ALL_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN
93 #define TYPE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN
94 #define INSTANCE_COLUMN TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN
95 #endif
96
97 /* 'private'/'protected' functions */
98 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
99 static void modest_folder_view_init        (ModestFolderView *obj);
100 static void modest_folder_view_finalize    (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
196 enum {
197         FOLDER_SELECTION_CHANGED_SIGNAL,
198         FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
199         FOLDER_ACTIVATED_SIGNAL,
200         LAST_SIGNAL
201 };
202
203 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
204 struct _ModestFolderViewPrivate {
205         TnyAccountStore      *account_store;
206         TnyFolderStore       *cur_folder_store;
207
208         TnyFolder            *folder_to_select; /* folder to select after the next update */
209
210         gulong                changed_signal;
211         gulong                account_inserted_signal;
212         gulong                account_removed_signal;
213         gulong                account_changed_signal;
214         gulong                conf_key_signal;
215         gulong                display_name_changed_signal;
216
217         /* not unref this object, its a singlenton */
218         ModestEmailClipboard *clipboard;
219
220         /* Filter tree model */
221         gchar **hidding_ids;
222         guint n_selected;
223         ModestFolderViewFilter filter;
224
225         TnyFolderStoreQuery  *query;
226         guint                 timer_expander;
227
228         gchar                *local_account_name;
229         gchar                *visible_account_id;
230         ModestFolderViewStyle style;
231         ModestFolderViewCellStyle cell_style;
232
233         gboolean  reselect; /* we use this to force a reselection of the INBOX */
234         gboolean  show_non_move;
235         gboolean  reexpand; /* next time we expose, we'll expand all root folders */
236
237         GtkCellRenderer *messages_renderer;
238 };
239 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                       \
240         (G_TYPE_INSTANCE_GET_PRIVATE((o),                       \
241                                      MODEST_TYPE_FOLDER_VIEW,   \
242                                      ModestFolderViewPrivate))
243 /* globals */
244 static GObjectClass *parent_class = NULL;
245
246 static guint signals[LAST_SIGNAL] = {0};
247
248 GType
249 modest_folder_view_get_type (void)
250 {
251         static GType my_type = 0;
252         if (!my_type) {
253                 static const GTypeInfo my_info = {
254                         sizeof(ModestFolderViewClass),
255                         NULL,           /* base init */
256                         NULL,           /* base finalize */
257                         (GClassInitFunc) modest_folder_view_class_init,
258                         NULL,           /* class finalize */
259                         NULL,           /* class data */
260                         sizeof(ModestFolderView),
261                         1,              /* n_preallocs */
262                         (GInstanceInitFunc) modest_folder_view_init,
263                         NULL
264                 };
265
266                 static const GInterfaceInfo tny_account_store_view_info = {
267                         (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
268                         NULL,         /* interface_finalize */
269                         NULL          /* interface_data */
270                 };
271
272
273                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
274                                                   "ModestFolderView",
275                                                   &my_info, 0);
276
277                 g_type_add_interface_static (my_type,
278                                              TNY_TYPE_ACCOUNT_STORE_VIEW,
279                                              &tny_account_store_view_info);
280         }
281         return my_type;
282 }
283
284 static void
285 modest_folder_view_class_init (ModestFolderViewClass *klass)
286 {
287         GObjectClass *gobject_class;
288         GtkTreeViewClass *treeview_class;
289         gobject_class = (GObjectClass*) klass;
290         treeview_class = (GtkTreeViewClass*) klass;
291
292         parent_class            = g_type_class_peek_parent (klass);
293         gobject_class->finalize = modest_folder_view_finalize;
294
295         g_type_class_add_private (gobject_class,
296                                   sizeof(ModestFolderViewPrivate));
297
298         signals[FOLDER_SELECTION_CHANGED_SIGNAL] =
299                 g_signal_new ("folder_selection_changed",
300                               G_TYPE_FROM_CLASS (gobject_class),
301                               G_SIGNAL_RUN_FIRST,
302                               G_STRUCT_OFFSET (ModestFolderViewClass,
303                                                folder_selection_changed),
304                               NULL, NULL,
305                               modest_marshal_VOID__POINTER_BOOLEAN,
306                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
307
308         /*
309          * This signal is emitted whenever the currently selected
310          * folder display name is computed. Note that the name could
311          * be different to the folder name, because we could append
312          * the unread messages count to the folder name to build the
313          * folder display name
314          */
315         signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] =
316                 g_signal_new ("folder-display-name-changed",
317                               G_TYPE_FROM_CLASS (gobject_class),
318                               G_SIGNAL_RUN_FIRST,
319                               G_STRUCT_OFFSET (ModestFolderViewClass,
320                                                folder_display_name_changed),
321                               NULL, NULL,
322                               g_cclosure_marshal_VOID__STRING,
323                               G_TYPE_NONE, 1, G_TYPE_STRING);
324
325         signals[FOLDER_ACTIVATED_SIGNAL] =
326                 g_signal_new ("folder_activated",
327                               G_TYPE_FROM_CLASS (gobject_class),
328                               G_SIGNAL_RUN_FIRST,
329                               G_STRUCT_OFFSET (ModestFolderViewClass,
330                                                folder_activated),
331                               NULL, NULL,
332                               g_cclosure_marshal_VOID__POINTER,
333                               G_TYPE_NONE, 1, G_TYPE_POINTER);
334
335         treeview_class->select_cursor_parent = NULL;
336
337 #ifdef MODEST_TOOLKIT_HILDON2
338         gtk_rc_parse_string ("class \"ModestFolderView\" style \"fremantle-touchlist\"");
339         
340 #endif
341
342 }
343
344 /* Simplify checks for NULLs: */
345 static gboolean
346 strings_are_equal (const gchar *a, const gchar *b)
347 {
348         if (!a && !b)
349                 return TRUE;
350         if (a && b)
351         {
352                 return (strcmp (a, b) == 0);
353         }
354         else
355                 return FALSE;
356 }
357
358 static gboolean
359 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path,  GtkTreeIter *iter, gpointer data)
360 {
361         GObject *instance = NULL;
362
363         gtk_tree_model_get (model, iter,
364                             INSTANCE_COLUMN, &instance,
365                             -1);
366
367         if (!instance)
368                 return FALSE; /* keep walking */
369
370         if (!TNY_IS_ACCOUNT (instance)) {
371                 g_object_unref (instance);
372                 return FALSE; /* keep walking */
373         }
374
375         /* Check if this is the looked-for account: */
376         TnyAccount *this_account = TNY_ACCOUNT (instance);
377         TnyAccount *account = TNY_ACCOUNT (data);
378
379         const gchar *this_account_id = tny_account_get_id(this_account);
380         const gchar *account_id = tny_account_get_id(account);
381         g_object_unref (instance);
382         instance = NULL;
383
384         /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
385         if (strings_are_equal(this_account_id, account_id)) {
386                 /* Tell the model that the data has changed, so that
387                  * it calls the cell_data_func callbacks again: */
388                 /* TODO: This does not seem to actually cause the new string to be shown: */
389                 gtk_tree_model_row_changed (model, path, iter);
390
391                 return TRUE; /* stop walking */
392         }
393
394         return FALSE; /* keep walking */
395 }
396
397 typedef struct
398 {
399         ModestFolderView *self;
400         gchar *previous_name;
401 } GetMmcAccountNameData;
402
403 static void
404 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
405 {
406         /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
407
408         GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
409
410         if (!strings_are_equal (
411                 tny_account_get_name(TNY_ACCOUNT(account)),
412                 data->previous_name)) {
413
414                 /* Tell the model that the data has changed, so that
415                  * it calls the cell_data_func callbacks again: */
416                 ModestFolderView *self = data->self;
417                 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
418                 if (model)
419                         gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
420         }
421
422         g_free (data->previous_name);
423         g_slice_free (GetMmcAccountNameData, data);
424 }
425
426 static void
427 convert_parent_folders_to_dots (gchar **item_name)
428 {
429         gint n_parents = 0;
430         gchar *c;
431         gchar *last_separator;
432
433         if (item_name == NULL)
434                 return;
435
436         for (c = *item_name; *c != '\0'; c++) {
437                 if (g_str_has_prefix (c, MODEST_FOLDER_PATH_SEPARATOR)) {
438                         n_parents++;
439                 }
440         }
441
442         last_separator = g_strrstr (*item_name, MODEST_FOLDER_PATH_SEPARATOR);
443         if (last_separator != NULL) {
444                 last_separator = last_separator + strlen (MODEST_FOLDER_PATH_SEPARATOR);
445         }
446
447         if (n_parents > 0) {
448                 GString *buffer;
449                 gint i;
450
451                 buffer = g_string_new ("");
452                 for (i = 0; i < n_parents; i++) {
453                         buffer = g_string_append (buffer, MODEST_FOLDER_DOT);
454                 }
455                 buffer = g_string_append (buffer, last_separator);
456                 g_free (*item_name);
457                 *item_name = g_string_free (buffer, FALSE);
458         }
459
460 }
461
462 static void
463 format_compact_style (gchar **item_name, 
464                       GObject *instance,
465                       gboolean bold,
466                       gboolean multiaccount,
467                       gboolean *use_markup)
468 {
469         TnyFolder *folder;
470         gboolean is_special;
471         TnyFolderType folder_type;
472
473         if (!TNY_IS_FOLDER (instance))
474                 return;
475
476         folder = (TnyFolder *) instance;
477
478         folder_type = tny_folder_get_folder_type (folder);
479         is_special = (get_cmp_pos (folder_type, folder)!= 4);
480
481         if (!is_special || multiaccount) {
482                 TnyAccount *account = tny_folder_get_account (folder);
483                 const gchar *folder_name;
484                 gboolean concat_folder_name = FALSE;
485                 GString *buffer;
486
487                 /* Should not happen */
488                 if (account == NULL)
489                         return;
490
491                 /* convert parent folders to dots */
492                 convert_parent_folders_to_dots  (item_name);
493
494                 folder_name = tny_folder_get_name (folder);
495                 if (g_str_has_suffix (*item_name, folder_name)) {
496                         gchar *offset = g_strrstr (*item_name, folder_name);
497                         *offset = '\0';
498                         concat_folder_name = TRUE;
499                 }
500
501                 buffer = g_string_new ("");
502
503                 buffer = g_string_append (buffer, *item_name);
504                 if (concat_folder_name) {
505                         if (bold) buffer = g_string_append (buffer, "<span weight='bold'>");
506                         buffer = g_string_append (buffer, folder_name);
507                         if (bold) buffer = g_string_append (buffer, "</span>");
508                 }
509                 g_free (*item_name);
510                 g_object_unref (account);
511
512                 *item_name = g_string_free (buffer, FALSE);
513                 *use_markup = bold;
514         } else {
515                 *use_markup = FALSE;
516         }
517 }
518
519 static void
520 text_cell_data  (GtkTreeViewColumn *column,
521                  GtkCellRenderer *renderer,
522                  GtkTreeModel *tree_model,
523                  GtkTreeIter *iter,
524                  gpointer data)
525 {
526         ModestFolderViewPrivate *priv;
527         GObject *rendobj = (GObject *) renderer;
528         gchar *fname = NULL;
529         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
530         GObject *instance = NULL;
531         gboolean use_markup = FALSE;
532
533         gtk_tree_model_get (tree_model, iter,
534                             NAME_COLUMN, &fname,
535                             TYPE_COLUMN, &type,
536                             INSTANCE_COLUMN, &instance,
537                             -1);
538         if (!fname || !instance)
539                 goto end;
540
541         ModestFolderView *self = MODEST_FOLDER_VIEW (data);
542         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
543
544         gchar *item_name = NULL;
545         gint item_weight = 400;
546
547         if (type != TNY_FOLDER_TYPE_ROOT) {
548                 gint number = 0;
549                 gboolean drafts;
550
551                 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
552                     modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
553                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
554                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
555                                 g_free (fname);
556                                 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
557                         }
558                 } else {
559                         /* Sometimes an special folder is reported by the server as
560                            NORMAL, like some versions of Dovecot */
561                         if (type == TNY_FOLDER_TYPE_NORMAL ||
562                             type == TNY_FOLDER_TYPE_UNKNOWN) {
563                                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
564                         }
565                 }
566
567                 if (type == TNY_FOLDER_TYPE_INBOX) {
568                         g_free (fname);
569                         fname = g_strdup (_("mcen_me_folder_inbox"));
570                 }
571
572                 /* note: we cannot reliably get the counts from the tree model, we need
573                  * to use explicit calls on tny_folder for some reason.
574                  */
575                 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
576                 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
577                     (type == TNY_FOLDER_TYPE_OUTBOX) ||
578                     (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
579                         number = tny_folder_get_all_count (TNY_FOLDER(instance));
580                         drafts = TRUE;
581                 } else {
582                         number = tny_folder_get_unread_count (TNY_FOLDER(instance));
583                         drafts = FALSE;
584                 }
585
586                 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
587                         item_name = g_strdup (fname);
588                         if (number > 0) {
589                                 item_weight = 800;
590                         } else {
591                                 item_weight = 400;
592                         }
593                 } else {
594                         /* Use bold font style if there are unread or unset messages */
595                         if (number > 0) {
596                                 item_name = g_strdup_printf ("%s (%d)", fname, number);
597                                 item_weight = 800;
598                         } else {
599                                 item_name = g_strdup (fname);
600                                 item_weight = 400;
601                         }
602                 }
603
604         } else if (TNY_IS_ACCOUNT (instance)) {
605                 /* If it's a server account */
606                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
607                         item_name = g_strdup (priv->local_account_name);
608                         item_weight = 800;
609                 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
610                         /* fname is only correct when the items are first
611                          * added to the model, not when the account is
612                          * changed later, so get the name from the account
613                          * instance: */
614                         item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
615                         item_weight = 800;
616                 } else {
617                         item_name = g_strdup (fname);
618                         item_weight = 800;
619                 }
620         }
621
622         if (!item_name)
623                 item_name = g_strdup ("unknown");
624
625         if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
626                 gboolean multiaccount;
627
628                 multiaccount = (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL);
629                 /* Convert item_name to markup */
630                 format_compact_style (&item_name, instance, 
631                                       item_weight == 800, 
632                                       multiaccount, &use_markup);
633         }
634
635         if (item_name && item_weight) {
636                 /* Set the name in the treeview cell: */
637                 if (use_markup)
638                         g_object_set (rendobj, "markup", item_name, NULL);
639                 else
640                         g_object_set (rendobj, "text", item_name, "weight", item_weight, NULL);
641
642                 /* Notify display name observers */
643                 /* TODO: What listens for this signal, and how can it use only the new name? */
644                 if (((GObject *) priv->cur_folder_store) == instance) {
645                         g_signal_emit (G_OBJECT(self),
646                                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
647                                                item_name);
648                 }
649                 g_free (item_name);
650
651         }
652
653         /* If it is a Memory card account, make sure that we have the correct name.
654          * This function will be trigerred again when the name has been retrieved: */
655         if (TNY_IS_STORE_ACCOUNT (instance) &&
656                 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
657
658                 /* Get the account name asynchronously: */
659                 GetMmcAccountNameData *callback_data =
660                         g_slice_new0(GetMmcAccountNameData);
661                 callback_data->self = self;
662
663                 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
664                 if (name)
665                         callback_data->previous_name = g_strdup (name);
666
667                 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance),
668                                                          on_get_mmc_account_name, callback_data);
669         }
670  end:
671         if (instance)
672                 g_object_unref (G_OBJECT (instance));
673         if (fname)
674                 g_free (fname);
675 }
676
677 static void
678 messages_cell_data  (GtkTreeViewColumn *column,
679                  GtkCellRenderer *renderer,
680                  GtkTreeModel *tree_model,
681                  GtkTreeIter *iter,
682                  gpointer data)
683 {
684         ModestFolderView *self; 
685         ModestFolderViewPrivate *priv;
686         GObject *rendobj = (GObject *) renderer;
687         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
688         GObject *instance = NULL;
689         gchar *item_name = NULL;
690
691         gtk_tree_model_get (tree_model, iter,
692                             TYPE_COLUMN, &type,
693                             INSTANCE_COLUMN, &instance,
694                             -1);
695         if (!instance)
696                 goto end;
697
698         self = MODEST_FOLDER_VIEW (data);
699         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
700
701
702         if (type != TNY_FOLDER_TYPE_ROOT) {
703                 gint number = 0;
704                 gboolean drafts;
705
706                 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
707                     modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
708                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
709                 } else {
710                         /* Sometimes an special folder is reported by the server as
711                            NORMAL, like some versions of Dovecot */
712                         if (type == TNY_FOLDER_TYPE_NORMAL ||
713                             type == TNY_FOLDER_TYPE_UNKNOWN) {
714                                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
715                         }
716                 }
717
718                 /* note: we cannot reliably get the counts from the tree model, we need
719                  * to use explicit calls on tny_folder for some reason.
720                  */
721                 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
722                 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
723                     (type == TNY_FOLDER_TYPE_OUTBOX) ||
724                     (type == TNY_FOLDER_TYPE_MERGE)) { /* _OUTBOX actually returns _MERGE... */
725                         number = tny_folder_get_all_count (TNY_FOLDER(instance));
726                         drafts = TRUE;
727                 } else {
728                         number = tny_folder_get_unread_count (TNY_FOLDER(instance));
729                         drafts = FALSE;
730                 }
731
732                 if (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT) {
733                         if (number > 0) {
734                                 item_name = g_strdup_printf (drafts?_("mcen_ti_messages"):_("mcen_ti_new_messages"), 
735                                                              number);
736                         } 
737                 } 
738
739         } 
740
741         if (!item_name)
742                 item_name = g_strdup ("");
743
744         if (item_name) {
745                 /* Set the name in the treeview cell: */
746                 g_object_set (rendobj,"text", item_name, NULL);
747
748                 g_free (item_name);
749
750         }
751
752  end:
753         if (instance)
754                 g_object_unref (G_OBJECT (instance));
755 }
756
757
758 typedef struct {
759         GdkPixbuf *pixbuf;
760         GdkPixbuf *pixbuf_open;
761         GdkPixbuf *pixbuf_close;
762 } ThreePixbufs;
763
764
765 static inline GdkPixbuf *
766 get_composite_pixbuf (const gchar *icon_name,
767                       const gint size,
768                       GdkPixbuf *base_pixbuf)
769 {
770         GdkPixbuf *emblem, *retval = NULL;
771
772         emblem = modest_platform_get_icon (icon_name, size);
773         if (emblem) {
774                 retval = gdk_pixbuf_copy (base_pixbuf);
775                 gdk_pixbuf_composite (emblem, retval, 0, 0,
776                                       MIN (gdk_pixbuf_get_width (emblem),
777                                            gdk_pixbuf_get_width (retval)),
778                                       MIN (gdk_pixbuf_get_height (emblem),
779                                            gdk_pixbuf_get_height (retval)),
780                                                   0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
781                 g_object_unref (emblem);
782         }
783         return retval;
784 }
785
786 static inline ThreePixbufs *
787 get_composite_icons (const gchar *icon_code,
788                      GdkPixbuf **pixbuf,
789                      GdkPixbuf **pixbuf_open,
790                      GdkPixbuf **pixbuf_close)
791 {
792         ThreePixbufs *retval;
793
794         if (!*pixbuf)
795                 *pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (icon_code, FOLDER_ICON_SIZE));
796
797         if (!*pixbuf_open)
798                 *pixbuf_open = get_composite_pixbuf ("qgn_list_gene_fldr_exp",
799                                                      FOLDER_ICON_SIZE,
800                                                      *pixbuf);
801
802         if (!*pixbuf_close)
803                 *pixbuf_close = get_composite_pixbuf ("qgn_list_gene_fldr_clp",
804                                                       FOLDER_ICON_SIZE,
805                                                       *pixbuf);
806
807         retval = g_slice_new0 (ThreePixbufs);
808         if (*pixbuf)
809                 retval->pixbuf = g_object_ref (*pixbuf);
810         if (*pixbuf_open)
811                 retval->pixbuf_open = g_object_ref (*pixbuf_open);
812         if (*pixbuf_close)
813                 retval->pixbuf_close = g_object_ref (*pixbuf_close);
814
815         return retval;
816 }
817
818 static inline ThreePixbufs*
819 get_folder_icons (TnyFolderType type, GObject *instance)
820 {
821         static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
822                 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
823                 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
824                 *normal_pixbuf = NULL, *anorm_pixbuf = NULL,
825                 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
826
827         static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
828                 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
829                 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
830                 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL,
831                 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
832
833         static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
834                 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
835                 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
836                 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL,
837                 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
838
839         ThreePixbufs *retval = NULL;
840
841         /* Sometimes an special folder is reported by the server as
842            NORMAL, like some versions of Dovecot */
843         if (type == TNY_FOLDER_TYPE_NORMAL ||
844             type == TNY_FOLDER_TYPE_UNKNOWN) {
845                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
846         }
847
848         /* Remote folders should not be treated as special folders */
849         if (TNY_IS_FOLDER_STORE (instance) &&
850             !TNY_IS_ACCOUNT (instance) &&
851             type != TNY_FOLDER_TYPE_INBOX &&
852             modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
853                 return get_composite_icons (MODEST_FOLDER_ICON_NORMAL,
854                                             &normal_pixbuf,
855                                             &normal_pixbuf_open,
856                                             &normal_pixbuf_close);
857         }
858
859         switch (type) {
860                 const gchar *icon_code;
861
862         case TNY_FOLDER_TYPE_INVALID:
863                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
864                 break;
865
866         case TNY_FOLDER_TYPE_ROOT:
867                 if (TNY_IS_ACCOUNT (instance)) {
868
869                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
870                                 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
871                                                               &avirt_pixbuf,
872                                                               &avirt_pixbuf_open,
873                                                               &avirt_pixbuf_close);
874                         } else {
875                                 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
876
877                                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
878                                         retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
879                                                                       &ammc_pixbuf,
880                                                                       &ammc_pixbuf_open,
881                                                                       &ammc_pixbuf_close);
882                                 } else {
883                                         retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
884                                                                       &anorm_pixbuf,
885                                                                       &anorm_pixbuf_open,
886                                                                       &anorm_pixbuf_close);
887                                 }
888                         }
889                 }
890                 break;
891         case TNY_FOLDER_TYPE_INBOX:
892                 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
893                                               &inbox_pixbuf,
894                                               &inbox_pixbuf_open,
895                                               &inbox_pixbuf_close);
896                 break;
897         case TNY_FOLDER_TYPE_OUTBOX:
898                 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
899                                               &outbox_pixbuf,
900                                               &outbox_pixbuf_open,
901                                               &outbox_pixbuf_close);
902                 break;
903         case TNY_FOLDER_TYPE_JUNK:
904                 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
905                                               &junk_pixbuf,
906                                               &junk_pixbuf_open,
907                                               &junk_pixbuf_close);
908                 break;
909         case TNY_FOLDER_TYPE_SENT:
910                 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
911                                               &sent_pixbuf,
912                                               &sent_pixbuf_open,
913                                               &sent_pixbuf_close);
914                 break;
915         case TNY_FOLDER_TYPE_TRASH:
916                 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
917                                               &trash_pixbuf,
918                                               &trash_pixbuf_open,
919                                               &trash_pixbuf_close);
920                 break;
921         case TNY_FOLDER_TYPE_DRAFTS:
922                 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
923                                               &draft_pixbuf,
924                                               &draft_pixbuf_open,
925                                               &draft_pixbuf_close);
926                 break;
927         case TNY_FOLDER_TYPE_NORMAL:
928         default:
929                 /* Memory card folders could have an special icon */
930                 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
931                         icon_code = MODEST_FOLDER_ICON_MMC_FOLDER;
932                 else
933                         icon_code = MODEST_FOLDER_ICON_NORMAL;
934
935                 retval = get_composite_icons (icon_code,
936                                               &normal_pixbuf,
937                                               &normal_pixbuf_open,
938                                               &normal_pixbuf_close);
939                 break;
940         }
941
942         return retval;
943 }
944
945 static void
946 free_pixbufs (ThreePixbufs *pixbufs)
947 {
948         if (pixbufs->pixbuf)
949                 g_object_unref (pixbufs->pixbuf);
950         if (pixbufs->pixbuf_open)
951                 g_object_unref (pixbufs->pixbuf_open);
952         if (pixbufs->pixbuf_close)
953                 g_object_unref (pixbufs->pixbuf_close);
954         g_slice_free (ThreePixbufs, pixbufs);
955 }
956
957 static void
958 icon_cell_data  (GtkTreeViewColumn *column,
959                  GtkCellRenderer *renderer,
960                  GtkTreeModel *tree_model,
961                  GtkTreeIter *iter,
962                  gpointer data)
963 {
964         GObject *rendobj = NULL, *instance = NULL;
965         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
966         gboolean has_children;
967         ThreePixbufs *pixbufs;
968
969         rendobj = (GObject *) renderer;
970
971         gtk_tree_model_get (tree_model, iter,
972                             TYPE_COLUMN, &type,
973                             INSTANCE_COLUMN, &instance,
974                             -1);
975
976         if (!instance)
977                 return;
978
979         has_children = gtk_tree_model_iter_has_child (tree_model, iter);
980         pixbufs = get_folder_icons (type, instance);
981         g_object_unref (instance);
982
983         /* Set pixbuf */
984         g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
985
986         if (has_children) {
987                 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
988                 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
989         }
990
991         free_pixbufs (pixbufs);
992 }
993
994 static void
995 add_columns (GtkWidget *treeview)
996 {
997         GtkTreeViewColumn *column;
998         GtkCellRenderer *renderer;
999         GtkTreeSelection *sel;
1000         ModestFolderViewPrivate *priv;
1001
1002         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
1003
1004         /* Create column */
1005         column = gtk_tree_view_column_new ();
1006
1007         /* Set icon and text render function */
1008         renderer = gtk_cell_renderer_pixbuf_new();
1009         gtk_tree_view_column_pack_start (column, renderer, FALSE);
1010         gtk_tree_view_column_set_cell_data_func(column, renderer,
1011                                                 icon_cell_data, treeview, NULL);
1012
1013         renderer = gtk_cell_renderer_text_new();
1014         g_object_set (renderer, 
1015 #ifdef MODEST_TOOLKIT_HILDON2
1016                       "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1017 #else
1018                       "ellipsize", PANGO_ELLIPSIZE_END,
1019 #endif
1020                       "ellipsize-set", TRUE, NULL);
1021         gtk_tree_view_column_pack_start (column, renderer, TRUE);
1022         gtk_tree_view_column_set_cell_data_func(column, renderer,
1023                                                 text_cell_data, treeview, NULL);
1024
1025         priv->messages_renderer = gtk_cell_renderer_text_new ();
1026         g_object_set (priv->messages_renderer, 
1027                       "scale", PANGO_SCALE_X_SMALL,
1028                       "scale-set", TRUE,
1029                       "alignment", PANGO_ALIGN_RIGHT,
1030                       "align-set", TRUE,
1031                       "xalign", 1.0,
1032                       NULL);
1033         gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
1034         gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
1035                                                 messages_cell_data, treeview, NULL);
1036
1037         /* Set selection mode */
1038         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1039         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1040
1041         /* Set treeview appearance */
1042         gtk_tree_view_column_set_spacing (column, 2);
1043         gtk_tree_view_column_set_resizable (column, TRUE);
1044         gtk_tree_view_column_set_fixed_width (column, TRUE);
1045         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1046         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1047
1048         /* Add column */
1049         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1050 }
1051
1052 static void
1053 modest_folder_view_init (ModestFolderView *obj)
1054 {
1055         ModestFolderViewPrivate *priv;
1056         ModestConf *conf;
1057
1058         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1059
1060         priv->timer_expander = 0;
1061         priv->account_store  = NULL;
1062         priv->query          = NULL;
1063         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1064         priv->cur_folder_store   = NULL;
1065         priv->visible_account_id = NULL;
1066         priv->folder_to_select = NULL;
1067
1068         priv->reexpand = TRUE;
1069
1070         /* Initialize the local account name */
1071         conf = modest_runtime_get_conf();
1072         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1073
1074         /* Init email clipboard */
1075         priv->clipboard = modest_runtime_get_email_clipboard ();
1076         priv->hidding_ids = NULL;
1077         priv->n_selected = 0;
1078         priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1079         priv->reselect = FALSE;
1080         priv->show_non_move = TRUE;
1081
1082         /* Build treeview */
1083         add_columns (GTK_WIDGET (obj));
1084
1085         /* Setup drag and drop */
1086         setup_drag_and_drop (GTK_TREE_VIEW(obj));
1087
1088         /* Connect signals */
1089         g_signal_connect (G_OBJECT (obj),
1090                           "key-press-event",
1091                           G_CALLBACK (on_key_pressed), NULL);
1092
1093         priv->display_name_changed_signal =
1094                 g_signal_connect (modest_runtime_get_account_mgr (),
1095                                   "display_name_changed",
1096                                   G_CALLBACK (on_display_name_changed),
1097                                   obj);
1098
1099         /*
1100          * Track changes in the local account name (in the device it
1101          * will be the device name)
1102          */
1103         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1104                                                   "key_changed",
1105                                                   G_CALLBACK(on_configuration_key_changed),
1106                                                   obj);
1107
1108         update_style (obj);
1109         g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1110
1111
1112 }
1113
1114 static void
1115 tny_account_store_view_init (gpointer g, gpointer iface_data)
1116 {
1117         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1118
1119         klass->set_account_store = modest_folder_view_set_account_store;
1120 }
1121
1122 static void
1123 modest_folder_view_finalize (GObject *obj)
1124 {
1125         ModestFolderViewPrivate *priv;
1126         GtkTreeSelection    *sel;
1127
1128         g_return_if_fail (obj);
1129
1130         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1131
1132         if (priv->timer_expander != 0) {
1133                 g_source_remove (priv->timer_expander);
1134                 priv->timer_expander = 0;
1135         }
1136
1137         if (priv->account_store) {
1138                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1139                                              priv->account_inserted_signal);
1140                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1141                                              priv->account_removed_signal);
1142                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1143                                              priv->account_changed_signal);
1144                 g_object_unref (G_OBJECT(priv->account_store));
1145                 priv->account_store = NULL;
1146         }
1147
1148         if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (), 
1149                                            priv->display_name_changed_signal)) {
1150                 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1151                                              priv->display_name_changed_signal);
1152                 priv->display_name_changed_signal = 0;
1153         }
1154
1155         if (priv->query) {
1156                 g_object_unref (G_OBJECT (priv->query));
1157                 priv->query = NULL;
1158         }
1159
1160         if (priv->folder_to_select) {
1161                 g_object_unref (G_OBJECT(priv->folder_to_select));
1162                 priv->folder_to_select = NULL;
1163         }
1164
1165         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1166         if (sel)
1167                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1168
1169         g_free (priv->local_account_name);
1170         g_free (priv->visible_account_id);
1171
1172         if (priv->conf_key_signal) {
1173                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1174                                              priv->conf_key_signal);
1175                 priv->conf_key_signal = 0;
1176         }
1177
1178         if (priv->cur_folder_store) {
1179                 g_object_unref (priv->cur_folder_store);
1180                 priv->cur_folder_store = NULL;
1181         }
1182
1183         /* Clear hidding array created by cut operation */
1184         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1185
1186         G_OBJECT_CLASS(parent_class)->finalize (obj);
1187 }
1188
1189
1190 static void
1191 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1192 {
1193         ModestFolderViewPrivate *priv;
1194         TnyDevice *device;
1195
1196         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1197         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1198
1199         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1200         device = tny_account_store_get_device (account_store);
1201
1202         if (G_UNLIKELY (priv->account_store)) {
1203
1204                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1205                                                    priv->account_inserted_signal))
1206                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1207                                                      priv->account_inserted_signal);
1208                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1209                                                    priv->account_removed_signal))
1210                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1211                                                      priv->account_removed_signal);
1212                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1213                                                    priv->account_changed_signal))
1214                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1215                                                      priv->account_changed_signal);
1216                 g_object_unref (G_OBJECT (priv->account_store));
1217         }
1218
1219         priv->account_store = g_object_ref (G_OBJECT (account_store));
1220
1221         priv->account_removed_signal =
1222                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1223                                   G_CALLBACK (on_account_removed), self);
1224
1225         priv->account_inserted_signal =
1226                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1227                                   G_CALLBACK (on_account_inserted), self);
1228
1229         priv->account_changed_signal =
1230                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1231                                   G_CALLBACK (on_account_changed), self);
1232
1233         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1234         priv->reselect = FALSE;
1235         modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1236
1237         g_object_unref (G_OBJECT (device));
1238 }
1239
1240 static void
1241 on_account_inserted (TnyAccountStore *account_store,
1242                      TnyAccount *account,
1243                      gpointer user_data)
1244 {
1245         ModestFolderViewPrivate *priv;
1246         GtkTreeModel *sort_model, *filter_model;
1247
1248         /* Ignore transport account insertions, we're not showing them
1249            in the folder view */
1250         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1251                 return;
1252
1253         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1254
1255
1256         /* If we're adding a new account, and there is no previous
1257            one, we need to select the visible server account */
1258         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1259             !priv->visible_account_id)
1260                 modest_widget_memory_restore (modest_runtime_get_conf(),
1261                                               G_OBJECT (user_data),
1262                                               MODEST_CONF_FOLDER_VIEW_KEY);
1263
1264         if (!GTK_IS_TREE_VIEW(user_data)) {
1265                 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1266                 return;
1267         }
1268
1269         /* Get the inner model */
1270         /* check, is some rare cases, we did not get the right thing here,
1271          * NB#84097 */
1272         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1273         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1274                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1275                 return;
1276         }
1277
1278         /* check, is some rare cases, we did not get the right thing here,
1279          * NB#84097 */
1280         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1281         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1282                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1283                 return;
1284         }
1285
1286         /* Insert the account in the model */
1287         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1288                          G_OBJECT (account));
1289
1290         /* Refilter the model */
1291         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1292 }
1293
1294
1295 static gboolean
1296 same_account_selected (ModestFolderView *self,
1297                        TnyAccount *account)
1298 {
1299         ModestFolderViewPrivate *priv;
1300         gboolean same_account = FALSE;
1301
1302         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1303
1304         if (priv->cur_folder_store) {
1305                 TnyAccount *selected_folder_account = NULL;
1306
1307                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1308                         selected_folder_account =
1309                                 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1310                 } else {
1311                         selected_folder_account =
1312                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1313                 }
1314
1315                 if (selected_folder_account == account)
1316                         same_account = TRUE;
1317
1318                 g_object_unref (selected_folder_account);
1319         }
1320         return same_account;
1321 }
1322
1323 /**
1324  *
1325  * Selects the first inbox or the local account in an idle
1326  */
1327 static gboolean
1328 on_idle_select_first_inbox_or_local (gpointer user_data)
1329 {
1330         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1331
1332         gdk_threads_enter ();
1333         modest_folder_view_select_first_inbox_or_local (self);
1334         gdk_threads_leave ();
1335
1336         return FALSE;
1337 }
1338
1339 static void
1340 on_account_changed (TnyAccountStore *account_store,
1341                     TnyAccount *tny_account,
1342                     gpointer user_data)
1343 {
1344         ModestFolderView *self;
1345         ModestFolderViewPrivate *priv;
1346         GtkTreeModel *sort_model, *filter_model;
1347         GtkTreeSelection *sel;
1348         gboolean same_account;
1349
1350         /* Ignore transport account insertions, we're not showing them
1351            in the folder view */
1352         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1353                 return;
1354
1355         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1356                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1357                 return;
1358         }
1359
1360         self = MODEST_FOLDER_VIEW (user_data);
1361         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1362
1363         /* Get the inner model */
1364         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1365         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1366                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1367                 return;
1368         }
1369
1370         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1371         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1372                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1373                 return;
1374         }
1375
1376         /* Invalidate the cur_folder_store only if the selected folder
1377            belongs to the account that is being removed */
1378         same_account = same_account_selected (self, tny_account);
1379         if (same_account) {
1380                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1381                 gtk_tree_selection_unselect_all (sel);
1382         }
1383
1384         /* Remove the account from the model */
1385         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1386                          G_OBJECT (tny_account));
1387
1388         /* Insert the account in the model */
1389         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1390                          G_OBJECT (tny_account));
1391
1392         /* Refilter the model */
1393         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1394
1395         /* Select the first INBOX if the currently selected folder
1396            belongs to the account that is being deleted */
1397         if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1398                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1399 }
1400
1401 static void
1402 on_account_removed (TnyAccountStore *account_store,
1403                     TnyAccount *account,
1404                     gpointer user_data)
1405 {
1406         ModestFolderView *self = NULL;
1407         ModestFolderViewPrivate *priv;
1408         GtkTreeModel *sort_model, *filter_model;
1409         GtkTreeSelection *sel = NULL;
1410         gboolean same_account = FALSE;
1411
1412         /* Ignore transport account removals, we're not showing them
1413            in the folder view */
1414         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1415                 return;
1416
1417         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1418                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1419                 return;
1420         }
1421
1422         self = MODEST_FOLDER_VIEW (user_data);
1423         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1424
1425         /* Invalidate the cur_folder_store only if the selected folder
1426            belongs to the account that is being removed */
1427         same_account = same_account_selected (self, account);
1428         if (same_account) {
1429                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1430                 gtk_tree_selection_unselect_all (sel);
1431         }
1432
1433         /* Invalidate row to select only if the folder to select
1434            belongs to the account that is being removed*/
1435         if (priv->folder_to_select) {
1436                 TnyAccount *folder_to_select_account = NULL;
1437
1438                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1439                 if (folder_to_select_account == account) {
1440                         modest_folder_view_disable_next_folder_selection (self);
1441                         g_object_unref (priv->folder_to_select);
1442                         priv->folder_to_select = NULL;
1443                 }
1444                 g_object_unref (folder_to_select_account);
1445         }
1446
1447         /* Remove the account from the model */
1448         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1449         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1450                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1451                 return;
1452         }
1453
1454         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1455         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1456                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1457                 return;
1458         }
1459
1460         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1461                          G_OBJECT (account));
1462
1463         /* If the removed account is the currently viewed one then
1464            clear the configuration value. The new visible account will be the default account */
1465         if (priv->visible_account_id &&
1466             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1467
1468                 /* Clear the current visible account_id */
1469                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1470
1471                 /* Call the restore method, this will set the new visible account */
1472                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1473                                               MODEST_CONF_FOLDER_VIEW_KEY);
1474         }
1475
1476         /* Refilter the model */
1477         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1478
1479         /* Select the first INBOX if the currently selected folder
1480            belongs to the account that is being deleted */
1481         if (same_account)
1482                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1483 }
1484
1485 void
1486 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1487 {
1488         GtkTreeViewColumn *col;
1489
1490         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1491
1492         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1493         if (!col) {
1494                 g_printerr ("modest: failed get column for title\n");
1495                 return;
1496         }
1497
1498         gtk_tree_view_column_set_title (col, title);
1499         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1500                                            title != NULL);
1501 }
1502
1503 static gboolean
1504 modest_folder_view_on_map (ModestFolderView *self,
1505                            GdkEventExpose *event,
1506                            gpointer data)
1507 {
1508         ModestFolderViewPrivate *priv;
1509
1510         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1511
1512         /* This won't happen often */
1513         if (G_UNLIKELY (priv->reselect)) {
1514                 /* Select the first inbox or the local account if not found */
1515
1516                 /* TODO: this could cause a lock at startup, so we
1517                    comment it for the moment. We know that this will
1518                    be a bug, because the INBOX is not selected, but we
1519                    need to rewrite some parts of Modest to avoid the
1520                    deathlock situation */
1521                 /* TODO: check if this is still the case */
1522                 priv->reselect = FALSE;
1523                 modest_folder_view_select_first_inbox_or_local (self);
1524                 /* Notify the display name observers */
1525                 g_signal_emit (G_OBJECT(self),
1526                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1527                                NULL);
1528         }
1529
1530         if (priv->reexpand) {
1531                 expand_root_items (self);
1532                 priv->reexpand = FALSE;
1533         }
1534
1535         return FALSE;
1536 }
1537
1538 GtkWidget*
1539 modest_folder_view_new (TnyFolderStoreQuery *query)
1540 {
1541         GObject *self;
1542         ModestFolderViewPrivate *priv;
1543         GtkTreeSelection *sel;
1544
1545         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, 
1546 #ifdef MODEST_TOOLKIT_HILDON2
1547                                        "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1548 #endif
1549                                        NULL));
1550         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1551
1552         if (query)
1553                 priv->query = g_object_ref (query);
1554
1555         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1556         priv->changed_signal = g_signal_connect (sel, "changed",
1557                                                  G_CALLBACK (on_selection_changed), self);
1558
1559         g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1560
1561         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1562
1563         return GTK_WIDGET(self);
1564 }
1565
1566 /* this feels dirty; any other way to expand all the root items? */
1567 static void
1568 expand_root_items (ModestFolderView *self)
1569 {
1570         GtkTreePath *path;
1571         GtkTreeModel *model;
1572         GtkTreeIter iter;
1573
1574         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1575         path = gtk_tree_path_new_first ();
1576
1577         /* all folders should have child items, so.. */
1578         do {
1579                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1580                 gtk_tree_path_next (path);
1581         } while (gtk_tree_model_get_iter (model, &iter, path));
1582
1583         gtk_tree_path_free (path);
1584 }
1585
1586 /*
1587  * We use this function to implement the
1588  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1589  * account in this case, and the local folders.
1590  */
1591 static gboolean
1592 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1593 {
1594         ModestFolderViewPrivate *priv;
1595         gboolean retval = TRUE;
1596         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1597         GObject *instance = NULL;
1598         const gchar *id = NULL;
1599         guint i;
1600         gboolean found = FALSE;
1601         gboolean cleared = FALSE;
1602         ModestTnyFolderRules rules = 0;
1603
1604         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1605         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1606
1607         gtk_tree_model_get (model, iter,
1608                             TYPE_COLUMN, &type,
1609                             INSTANCE_COLUMN, &instance,
1610                             -1);
1611
1612         /* Do not show if there is no instance, this could indeed
1613            happen when the model is being modified while it's being
1614            drawn. This could occur for example when moving folders
1615            using drag&drop */
1616         if (!instance)
1617                 return FALSE;
1618
1619         if (TNY_IS_ACCOUNT (instance)) {
1620 #ifdef MODEST_TOOLKIT_HILDON2
1621                 /* In hildon 2.2 we don't show the account rows */
1622                 return FALSE;
1623 #else
1624                 TnyAccount *acc = TNY_ACCOUNT (instance);
1625                 const gchar *account_id = tny_account_get_id (acc);
1626
1627                 /* If it isn't a special folder,
1628                  * don't show it unless it is the visible account: */
1629                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1630                     !modest_tny_account_is_virtual_local_folders (acc) &&
1631                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1632
1633                         /* Show only the visible account id */
1634                         if (priv->visible_account_id) {
1635                                 if (strcmp (account_id, priv->visible_account_id))
1636                                         retval = FALSE;
1637                         } else {
1638                                 retval = FALSE;
1639                         }
1640                 }
1641
1642                 /* Never show these to the user. They are merged into one folder
1643                  * in the local-folders account instead: */
1644                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1645                         retval = FALSE;
1646 #endif
1647         } else {
1648                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1649                         /* Only show special folders for current account if needed */
1650                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1651                                 TnyAccount *account;
1652
1653                                 account = tny_folder_get_account (TNY_FOLDER (instance));
1654
1655                                 if (TNY_IS_ACCOUNT (account)) {
1656                                         const gchar *account_id = tny_account_get_id (account);
1657
1658                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
1659                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1660                                                 /* Show only the visible account id */
1661                                                 if (priv->visible_account_id) {
1662                                                         if (strcmp (account_id, priv->visible_account_id))
1663                                                                 retval = FALSE;
1664                                                 }
1665                                         }
1666                                                 g_object_unref (account);
1667                                 }
1668                         }
1669
1670                 }
1671         }
1672
1673         /* Check hiding (if necessary) */
1674         cleared = modest_email_clipboard_cleared (priv->clipboard);
1675         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1676                 id = tny_folder_get_id (TNY_FOLDER(instance));
1677                 if (priv->hidding_ids != NULL)
1678                         for (i=0; i < priv->n_selected && !found; i++)
1679                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1680                                         found = (!strcmp (priv->hidding_ids[i], id));
1681
1682                 retval = !found;
1683         }
1684
1685         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1686         folder as no message can be move there according to UI specs */
1687         if (!priv->show_non_move) {
1688                 switch (type) {
1689                         case TNY_FOLDER_TYPE_OUTBOX:
1690                         case TNY_FOLDER_TYPE_SENT:
1691                         case TNY_FOLDER_TYPE_DRAFTS:
1692                                 retval = FALSE;
1693                                 break;
1694                         case TNY_FOLDER_TYPE_UNKNOWN:
1695                         case TNY_FOLDER_TYPE_NORMAL:
1696                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1697                                 if (type == TNY_FOLDER_TYPE_INVALID)
1698                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1699
1700                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1701                                     type == TNY_FOLDER_TYPE_SENT
1702                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1703                                         retval = FALSE;
1704                                 break;
1705                         default:
1706                                 break;
1707                 }
1708         }
1709
1710         /* apply special filters */
1711         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1712                 if (TNY_IS_FOLDER (instance)) {
1713                         TnyFolderCaps capabilities;
1714
1715                         capabilities = tny_folder_get_caps (TNY_FOLDER (instance));
1716                         retval = !(capabilities & TNY_FOLDER_CAPS_NOCHILDREN);
1717
1718                         if (retval) {
1719                                 retval = ((type != TNY_FOLDER_TYPE_DRAFTS) &&
1720                                           (type != TNY_FOLDER_TYPE_OUTBOX) &&
1721                                           (type != TNY_FOLDER_TYPE_SENT));
1722                         }
1723                 } else if (TNY_IS_ACCOUNT (instance)) {
1724                         retval = FALSE;
1725                 }
1726         }
1727
1728         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1729                 if (TNY_IS_FOLDER (instance)) {
1730                         TnyFolderType guess_type;
1731                         
1732                         if (TNY_FOLDER_TYPE_NORMAL) {
1733                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1734                         } else {
1735                                 guess_type = type;
1736                         }
1737
1738                         switch (type) {
1739                         case TNY_FOLDER_TYPE_OUTBOX:
1740                         case TNY_FOLDER_TYPE_SENT:
1741                         case TNY_FOLDER_TYPE_DRAFTS:
1742                         case TNY_FOLDER_TYPE_ARCHIVE:
1743                         case TNY_FOLDER_TYPE_INBOX:
1744                                 retval = FALSE;
1745                                 break;
1746                         case TNY_FOLDER_TYPE_UNKNOWN:
1747                         case TNY_FOLDER_TYPE_NORMAL:
1748                                 break;
1749                         default:
1750                                 break;
1751                         }
1752                         
1753                 } else if (TNY_IS_ACCOUNT (instance)) {
1754                         retval = FALSE;
1755                 }
1756         }
1757
1758         if (retval && TNY_IS_FOLDER (instance)) {
1759                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1760         }
1761
1762         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1763                 if (TNY_IS_FOLDER (instance)) {
1764                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1765                 } else if (TNY_IS_ACCOUNT (instance)) {
1766                         retval = FALSE;
1767                 }
1768         }
1769
1770         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1771                 if (TNY_IS_FOLDER (instance)) {
1772                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1773                 } else if (TNY_IS_ACCOUNT (instance)) {
1774                         retval = FALSE;
1775                 }
1776         }
1777
1778         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1779                 if (TNY_IS_FOLDER (instance)) {
1780                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1781                 } else if (TNY_IS_ACCOUNT (instance)) {
1782                         retval = FALSE;
1783                 }
1784         }
1785
1786         /* Free */
1787         g_object_unref (instance);
1788
1789         return retval;
1790 }
1791
1792
1793 gboolean
1794 modest_folder_view_update_model (ModestFolderView *self,
1795                                  TnyAccountStore *account_store)
1796 {
1797         ModestFolderViewPrivate *priv;
1798         GtkTreeModel *model /* , *old_model */;
1799         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1800
1801         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1802         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1803                               FALSE);
1804
1805         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1806
1807         /* Notify that there is no folder selected */
1808         g_signal_emit (G_OBJECT(self),
1809                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1810                        NULL, FALSE);
1811         if (priv->cur_folder_store) {
1812                 g_object_unref (priv->cur_folder_store);
1813                 priv->cur_folder_store = NULL;
1814         }
1815
1816         /* FIXME: the local accounts are not shown when the query
1817            selects only the subscribed folders */
1818 #ifdef MODEST_TOOLKIT_HILDON2
1819         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
1820                                                           TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1821         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1822                                                       MODEST_FOLDER_PATH_SEPARATOR);
1823 #else
1824         model = tny_gtk_folder_store_tree_model_new (NULL);
1825 #endif
1826
1827         /* Get the accounts: */
1828         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1829                                         TNY_LIST (model),
1830                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1831
1832         sortable = gtk_tree_model_sort_new_with_model (model);
1833         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1834                                               NAME_COLUMN,
1835                                               GTK_SORT_ASCENDING);
1836         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1837                                          NAME_COLUMN,
1838                                          cmp_rows, NULL, NULL);
1839
1840         /* Create filter model */
1841         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1842         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1843                                                 filter_row,
1844                                                 self,
1845                                                 NULL);
1846
1847         /* Set new model */
1848         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1849 #ifndef MODEST_TOOLKIT_HILDON2
1850         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1851                           (GCallback) on_row_inserted_maybe_select_folder, self);
1852 #endif
1853
1854         g_object_unref (model);
1855         g_object_unref (filter_model);
1856         g_object_unref (sortable);
1857
1858         /* Force a reselection of the INBOX next time the widget is shown */
1859         priv->reselect = TRUE;
1860
1861         return TRUE;
1862 }
1863
1864
1865 static void
1866 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1867 {
1868         GtkTreeModel *model = NULL;
1869         TnyFolderStore *folder = NULL;
1870         GtkTreeIter iter;
1871         ModestFolderView *tree_view = NULL;
1872         ModestFolderViewPrivate *priv = NULL;
1873         gboolean selected = FALSE;
1874
1875         g_return_if_fail (sel);
1876         g_return_if_fail (user_data);
1877
1878         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1879
1880         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1881
1882         tree_view = MODEST_FOLDER_VIEW (user_data);
1883
1884         if (selected) {
1885                 gtk_tree_model_get (model, &iter,
1886                                     INSTANCE_COLUMN, &folder,
1887                                     -1);
1888
1889                 /* If the folder is the same do not notify */
1890                 if (folder && priv->cur_folder_store == folder) {
1891                         g_object_unref (folder);
1892                         return;
1893                 }
1894         }
1895
1896         /* Current folder was unselected */
1897         if (priv->cur_folder_store) {
1898                 /* We must do this firstly because a libtinymail-camel
1899                    implementation detail. If we issue the signal
1900                    before doing the sync_async, then that signal could
1901                    cause (and it actually does it) a free of the
1902                    summary of the folder (because the main window will
1903                    clear the headers view */
1904                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1905                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1906                                                FALSE, NULL, NULL, NULL);
1907
1908                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1909                        priv->cur_folder_store, FALSE);
1910
1911                 g_object_unref (priv->cur_folder_store);
1912                 priv->cur_folder_store = NULL;
1913         }
1914
1915         /* New current references */
1916         priv->cur_folder_store = folder;
1917
1918         /* New folder has been selected. Do not notify if there is
1919            nothing new selected */
1920         if (selected) {
1921                 g_signal_emit (G_OBJECT(tree_view),
1922                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1923                                0, priv->cur_folder_store, TRUE);
1924         }
1925 }
1926
1927 static void
1928 on_row_activated (GtkTreeView *treeview,
1929                   GtkTreePath *treepath,
1930                   GtkTreeViewColumn *column,
1931                   gpointer user_data)
1932 {
1933         GtkTreeModel *model = NULL;
1934         TnyFolderStore *folder = NULL;
1935         GtkTreeIter iter;
1936         ModestFolderView *self = NULL;
1937         ModestFolderViewPrivate *priv = NULL;
1938
1939         g_return_if_fail (treeview);
1940         g_return_if_fail (user_data);
1941
1942         self = MODEST_FOLDER_VIEW (user_data);
1943         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1944
1945         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1946
1947         if (!gtk_tree_model_get_iter (model, &iter, treepath))
1948                 return;
1949
1950         gtk_tree_model_get (model, &iter,
1951                             INSTANCE_COLUMN, &folder,
1952                             -1);
1953
1954         g_signal_emit (G_OBJECT(self),
1955                        signals[FOLDER_ACTIVATED_SIGNAL],
1956                        0, folder);
1957
1958 #ifdef MODEST_TOOLKIT_HILDON2
1959         HildonUIMode ui_mode;
1960         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
1961         if (ui_mode == HILDON_UI_MODE_NORMAL) {
1962                 if (priv->cur_folder_store)
1963                         g_object_unref (priv->cur_folder_store);
1964                 priv->cur_folder_store = g_object_ref (folder);
1965         }
1966 #endif
1967
1968         g_object_unref (folder);
1969 }
1970
1971 TnyFolderStore *
1972 modest_folder_view_get_selected (ModestFolderView *self)
1973 {
1974         ModestFolderViewPrivate *priv;
1975
1976         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1977
1978         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1979         if (priv->cur_folder_store)
1980                 g_object_ref (priv->cur_folder_store);
1981
1982         return priv->cur_folder_store;
1983 }
1984
1985 static gint
1986 get_cmp_rows_type_pos (GObject *folder)
1987 {
1988         /* Remote accounts -> Local account -> MMC account .*/
1989         /* 0, 1, 2 */
1990
1991         if (TNY_IS_ACCOUNT (folder) &&
1992                 modest_tny_account_is_virtual_local_folders (
1993                         TNY_ACCOUNT (folder))) {
1994                 return 1;
1995         } else if (TNY_IS_ACCOUNT (folder)) {
1996                 TnyAccount *account = TNY_ACCOUNT (folder);
1997                 const gchar *account_id = tny_account_get_id (account);
1998                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1999                         return 2;
2000                 else
2001                         return 0;
2002         }
2003         else {
2004                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2005                 return -1; /* Should never happen */
2006         }
2007 }
2008
2009 static gint
2010 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2011 {
2012         TnyAccount *account;
2013         gboolean is_special;
2014         /* Inbox, Outbox, Drafts, Sent, User */
2015         /* 0, 1, 2, 3, 4 */
2016
2017         if (!TNY_IS_FOLDER (folder_store))
2018                 return 4;
2019         switch (t) {
2020         case TNY_FOLDER_TYPE_INBOX:
2021         {
2022                 account = tny_folder_get_account (folder_store);
2023                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2024                 g_object_unref (account);
2025                 return is_special?0:4;
2026         }
2027         break;
2028         case TNY_FOLDER_TYPE_OUTBOX:
2029                 return 2;
2030                 break;
2031         case TNY_FOLDER_TYPE_DRAFTS:
2032         {
2033                 account = tny_folder_get_account (folder_store);
2034                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2035                 g_object_unref (account);
2036                 return is_special?1:4;
2037         }
2038         break;
2039         case TNY_FOLDER_TYPE_SENT:
2040         {
2041                 account = tny_folder_get_account (folder_store);
2042                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2043                 g_object_unref (account);
2044                 return is_special?3:4;
2045         }
2046         break;
2047         default:
2048                 return 4;
2049         }
2050 }
2051
2052 static gint
2053 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2054 {
2055         const gchar *a1_name, *a2_name;
2056
2057         a1_name = tny_account_get_name (a1);
2058         a2_name = tny_account_get_name (a2);
2059
2060         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2061 }
2062
2063 static gint
2064 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2065 {
2066         TnyAccount *a1, *a2;
2067         gint cmp;
2068
2069         if (TNY_IS_ACCOUNT (s1)) {
2070                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2071         } else {
2072                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2073         }
2074
2075         if (TNY_IS_ACCOUNT (s2)) {
2076                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2077         } else {
2078                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2079         }
2080
2081         if (a1 == a2) {
2082                 cmp = 0;
2083                 goto finish;
2084         }
2085         /* First we sort with the type of account */
2086         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2087         if (cmp != 0)
2088                 goto finish;
2089
2090         cmp = compare_account_names (a1, a2);
2091
2092 finish:
2093         g_object_unref (a1);
2094         g_object_unref (a2);
2095
2096         return cmp;
2097 }
2098
2099 static gint
2100 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2101 {
2102         gint is_account1, is_account2;
2103
2104         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2105         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2106
2107         return is_account2 - is_account1;
2108 }
2109
2110 /*
2111  * This function orders the mail accounts according to these rules:
2112  * 1st - remote accounts
2113  * 2nd - local account
2114  * 3rd - MMC account
2115  */
2116 static gint
2117 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2118           gpointer user_data)
2119 {
2120         gint cmp = 0;
2121         gchar *name1 = NULL;
2122         gchar *name2 = NULL;
2123         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2124         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2125         GObject *folder1 = NULL;
2126         GObject *folder2 = NULL;
2127
2128         gtk_tree_model_get (tree_model, iter1,
2129                             NAME_COLUMN, &name1,
2130                             TYPE_COLUMN, &type,
2131                             INSTANCE_COLUMN, &folder1,
2132                             -1);
2133         gtk_tree_model_get (tree_model, iter2,
2134                             NAME_COLUMN, &name2,
2135                             TYPE_COLUMN, &type2,
2136                             INSTANCE_COLUMN, &folder2,
2137                             -1);
2138
2139         /* Return if we get no folder. This could happen when folder
2140            operations are happening. The model is updated after the
2141            folder copy/move actually occurs, so there could be
2142            situations where the model to be drawn is not correct */
2143         if (!folder1 || !folder2)
2144                 goto finish;
2145
2146         /* Sort by type. First the special folders, then the archives */
2147         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2148         if (cmp != 0)
2149                 goto finish;
2150
2151         /* Now we sort using the account of each folder */
2152         cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2153         if (cmp != 0)
2154                 goto finish;
2155
2156         /* Each group is preceeded by its account */
2157         cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2158         if (cmp != 0)
2159                 goto finish;
2160
2161         /* Pure sort by name */
2162         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2163  finish:
2164         if (folder1)
2165                 g_object_unref(G_OBJECT(folder1));
2166         if (folder2)
2167                 g_object_unref(G_OBJECT(folder2));
2168
2169         g_free (name1);
2170         g_free (name2);
2171
2172         return cmp;
2173 }
2174
2175 /*****************************************************************************/
2176 /*                        DRAG and DROP stuff                                */
2177 /*****************************************************************************/
2178 /*
2179  * This function fills the #GtkSelectionData with the row and the
2180  * model that has been dragged. It's called when this widget is a
2181  * source for dnd after the event drop happened
2182  */
2183 static void
2184 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2185                   guint info, guint time, gpointer data)
2186 {
2187         GtkTreeSelection *selection;
2188         GtkTreeModel *model;
2189         GtkTreeIter iter;
2190         GtkTreePath *source_row;
2191
2192         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2193         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2194
2195                 source_row = gtk_tree_model_get_path (model, &iter);
2196                 gtk_tree_set_row_drag_data (selection_data,
2197                                             model,
2198                                             source_row);
2199
2200                 gtk_tree_path_free (source_row);
2201         }
2202 }
2203
2204 typedef struct _DndHelper {
2205         ModestFolderView *folder_view;
2206         gboolean delete_source;
2207         GtkTreePath *source_row;
2208 } DndHelper;
2209
2210 static void
2211 dnd_helper_destroyer (DndHelper *helper)
2212 {
2213         /* Free the helper */
2214         gtk_tree_path_free (helper->source_row);
2215         g_slice_free (DndHelper, helper);
2216 }
2217
2218 static void
2219 xfer_folder_cb (ModestMailOperation *mail_op,
2220                 TnyFolder *new_folder,
2221                 gpointer user_data)
2222 {
2223         if (new_folder) {
2224                 /* Select the folder */
2225                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2226                                                   new_folder, FALSE);
2227         }
2228 }
2229
2230
2231 /* get the folder for the row the treepath refers to. */
2232 /* folder must be unref'd */
2233 static TnyFolderStore *
2234 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2235 {
2236         GtkTreeIter iter;
2237         TnyFolderStore *folder = NULL;
2238
2239         if (gtk_tree_model_get_iter (model,&iter, path))
2240                 gtk_tree_model_get (model, &iter,
2241                                     INSTANCE_COLUMN, &folder,
2242                                     -1);
2243         return folder;
2244 }
2245
2246
2247 /*
2248  * This function is used by drag_data_received_cb to manage drag and
2249  * drop of a header, i.e, and drag from the header view to the folder
2250  * view.
2251  */
2252 static void
2253 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2254                                 GtkTreeModel *dest_model,
2255                                 GtkTreePath  *dest_row,
2256                                 GtkSelectionData *selection_data)
2257 {
2258         TnyList *headers = NULL;
2259         TnyFolder *folder = NULL, *src_folder = NULL;
2260         TnyFolderType folder_type;
2261         GtkTreeIter source_iter, dest_iter;
2262         ModestWindowMgr *mgr = NULL;
2263         ModestWindow *main_win = NULL;
2264         gchar **uris, **tmp;
2265
2266         /* Build the list of headers */
2267         mgr = modest_runtime_get_window_mgr ();
2268         headers = tny_simple_list_new ();
2269         uris = modest_dnd_selection_data_get_paths (selection_data);
2270         tmp = uris;
2271
2272         while (*tmp != NULL) {
2273                 TnyHeader *header;
2274                 GtkTreePath *path;
2275                 gboolean first = TRUE;
2276
2277                 /* Get header */
2278                 path = gtk_tree_path_new_from_string (*tmp);
2279                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2280                 gtk_tree_model_get (source_model, &source_iter,
2281                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2282                                     &header, -1);
2283
2284                 /* Do not enable d&d of headers already opened */
2285                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2286                         tny_list_append (headers, G_OBJECT (header));
2287
2288                 if (G_UNLIKELY (first)) {
2289                         src_folder = tny_header_get_folder (header);
2290                         first = FALSE;
2291                 }
2292
2293                 /* Free and go on */
2294                 gtk_tree_path_free (path);
2295                 g_object_unref (header);
2296                 tmp++;
2297         }
2298         g_strfreev (uris);
2299
2300         /* This could happen ig we perform a d&d very quickly over the
2301            same row that row could dissapear because message is
2302            transferred */
2303         if (!TNY_IS_FOLDER (src_folder))
2304                 goto cleanup;
2305
2306         /* Get the target folder */
2307         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2308         gtk_tree_model_get (dest_model, &dest_iter,
2309                             INSTANCE_COLUMN,
2310                             &folder, -1);
2311
2312         if (!folder || !TNY_IS_FOLDER(folder)) {
2313 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2314                 goto cleanup;
2315         }
2316
2317         folder_type = modest_tny_folder_guess_folder_type (folder);
2318         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2319 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2320                 goto cleanup;  /* cannot move messages there */
2321         }
2322
2323         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2324 /*              g_warning ("folder not writable"); */
2325                 goto cleanup; /* verboten! */
2326         }
2327
2328         /* Ask for confirmation to move */
2329         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2330         if (!main_win) {
2331                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2332                 goto cleanup;
2333         }
2334
2335         /* Transfer messages */
2336         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2337                                                     headers, folder);
2338
2339         /* Frees */
2340 cleanup:
2341         if (G_IS_OBJECT (src_folder))
2342                 g_object_unref (src_folder);
2343         if (G_IS_OBJECT(folder))
2344                 g_object_unref (G_OBJECT (folder));
2345         if (G_IS_OBJECT(headers))
2346                 g_object_unref (headers);
2347 }
2348
2349 typedef struct {
2350         TnyFolderStore *src_folder;
2351         TnyFolderStore *dst_folder;
2352         ModestFolderView *folder_view;
2353         DndHelper *helper;
2354 } DndFolderInfo;
2355
2356 static void
2357 dnd_folder_info_destroyer (DndFolderInfo *info)
2358 {
2359         if (info->src_folder)
2360                 g_object_unref (info->src_folder);
2361         if (info->dst_folder)
2362                 g_object_unref (info->dst_folder);
2363         g_slice_free (DndFolderInfo, info);
2364 }
2365
2366 static void
2367 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2368                                     GtkWindow *parent_window,
2369                                     TnyAccount *account)
2370 {
2371         /* Show error */
2372         modest_ui_actions_on_account_connection_error (parent_window, account);
2373
2374         /* Free the helper & info */
2375         dnd_helper_destroyer (info->helper);
2376         dnd_folder_info_destroyer (info);
2377 }
2378
2379 static void
2380 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2381                                                      GError *err,
2382                                                      GtkWindow *parent_window,
2383                                                      TnyAccount *account,
2384                                                      gpointer user_data)
2385 {
2386         DndFolderInfo *info = NULL;
2387         ModestMailOperation *mail_op;
2388
2389         info = (DndFolderInfo *) user_data;
2390
2391         if (err || canceled) {
2392                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2393                 return;
2394         }
2395
2396         /* Do the mail operation */
2397         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2398                                                                  modest_ui_actions_move_folder_error_handler,
2399                                                                  info->src_folder, NULL);
2400
2401         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2402                                          mail_op);
2403
2404         /* Transfer the folder */
2405         modest_mail_operation_xfer_folder (mail_op,
2406                                            TNY_FOLDER (info->src_folder),
2407                                            info->dst_folder,
2408                                            info->helper->delete_source,
2409                                            xfer_folder_cb,
2410                                            info->helper->folder_view);
2411
2412         /* Frees */
2413         g_object_unref (G_OBJECT (mail_op));
2414         dnd_helper_destroyer (info->helper);
2415         dnd_folder_info_destroyer (info);
2416 }
2417
2418
2419 static void
2420 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2421                                                      GError *err,
2422                                                      GtkWindow *parent_window,
2423                                                      TnyAccount *account,
2424                                                      gpointer user_data)
2425 {
2426         DndFolderInfo *info = NULL;
2427
2428         info = (DndFolderInfo *) user_data;
2429
2430         if (err || canceled) {
2431                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2432                 return;
2433         }
2434
2435         /* Connect to source folder and perform the copy/move */
2436         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2437                                                        info->src_folder,
2438                                                        drag_and_drop_from_folder_view_src_folder_performer,
2439                                                        info);
2440 }
2441
2442 /*
2443  * This function is used by drag_data_received_cb to manage drag and
2444  * drop of a folder, i.e, and drag from the folder view to the same
2445  * folder view.
2446  */
2447 static void
2448 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2449                                 GtkTreeModel     *dest_model,
2450                                 GtkTreePath      *dest_row,
2451                                 GtkSelectionData *selection_data,
2452                                 DndHelper        *helper)
2453 {
2454         GtkTreeIter dest_iter, iter;
2455         TnyFolderStore *dest_folder = NULL;
2456         TnyFolderStore *folder = NULL;
2457         gboolean forbidden = FALSE;
2458         ModestWindow *win;
2459         DndFolderInfo *info = NULL;
2460
2461         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2462         if (!win) {
2463                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2464                 dnd_helper_destroyer (helper);
2465                 return;
2466         }
2467
2468         if (!forbidden) {
2469                 /* check the folder rules for the destination */
2470                 folder = tree_path_to_folder (dest_model, dest_row);
2471                 if (TNY_IS_FOLDER(folder)) {
2472                         ModestTnyFolderRules rules =
2473                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2474                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2475                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2476                         /* enable local root as destination for folders */
2477                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2478                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2479                                 forbidden = TRUE;
2480                 }
2481                 g_object_unref (folder);
2482         }
2483         if (!forbidden) {
2484                 /* check the folder rules for the source */
2485                 folder = tree_path_to_folder (source_model, helper->source_row);
2486                 if (TNY_IS_FOLDER(folder)) {
2487                         ModestTnyFolderRules rules =
2488                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2489                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2490                 } else
2491                         forbidden = TRUE;
2492                 g_object_unref (folder);
2493         }
2494
2495
2496         /* Check if the drag is possible */
2497         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2498                 /* Show error */
2499                 modest_platform_run_information_dialog ((GtkWindow *) win, 
2500                                                         _("mail_in_ui_folder_move_target_error"), 
2501                                                         FALSE);
2502                 /* Restore the previous selection */
2503                 folder = tree_path_to_folder (source_model, helper->source_row);
2504                 if (folder) {
2505                         if (TNY_IS_FOLDER (folder))
2506                                 modest_folder_view_select_folder (helper->folder_view, 
2507                                                                   TNY_FOLDER (folder), FALSE);
2508                         g_object_unref (folder);
2509                 }
2510                 dnd_helper_destroyer (helper);
2511                 return;
2512         }
2513
2514         /* Get data */
2515         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2516         gtk_tree_model_get (dest_model, &dest_iter,
2517                             INSTANCE_COLUMN,
2518                             &dest_folder, -1);
2519         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2520         gtk_tree_model_get (source_model, &iter,
2521                             INSTANCE_COLUMN,
2522                             &folder, -1);
2523
2524         /* Create the info for the performer */
2525         info = g_slice_new0 (DndFolderInfo);
2526         info->src_folder = g_object_ref (folder);
2527         info->dst_folder = g_object_ref (dest_folder);
2528         info->helper = helper;
2529
2530         /* Connect to the destination folder and perform the copy/move */
2531         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2532                                                        dest_folder,
2533                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2534                                                        info);
2535
2536         /* Frees */
2537         g_object_unref (dest_folder);
2538         g_object_unref (folder);
2539 }
2540
2541 /*
2542  * This function receives the data set by the "drag-data-get" signal
2543  * handler. This information comes within the #GtkSelectionData. This
2544  * function will manage both the drags of folders of the treeview and
2545  * drags of headers of the header view widget.
2546  */
2547 static void
2548 on_drag_data_received (GtkWidget *widget,
2549                        GdkDragContext *context,
2550                        gint x,
2551                        gint y,
2552                        GtkSelectionData *selection_data,
2553                        guint target_type,
2554                        guint time,
2555                        gpointer data)
2556 {
2557         GtkWidget *source_widget;
2558         GtkTreeModel *dest_model, *source_model;
2559         GtkTreePath *source_row, *dest_row;
2560         GtkTreeViewDropPosition pos;
2561         gboolean delete_source = FALSE;
2562         gboolean success = FALSE;
2563
2564         /* Do not allow further process */
2565         g_signal_stop_emission_by_name (widget, "drag-data-received");
2566         source_widget = gtk_drag_get_source_widget (context);
2567
2568         /* Get the action */
2569         if (context->action == GDK_ACTION_MOVE) {
2570                 delete_source = TRUE;
2571
2572                 /* Notify that there is no folder selected. We need to
2573                    do this in order to update the headers view (and
2574                    its monitors, because when moving, the old folder
2575                    won't longer exist. We can not wait for the end of
2576                    the operation, because the operation won't start if
2577                    the folder is in use */
2578                 if (source_widget == widget) {
2579                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2580                         gtk_tree_selection_unselect_all (sel);
2581                 }
2582         }
2583
2584         /* Check if the get_data failed */
2585         if (selection_data == NULL || selection_data->length < 0)
2586                 goto end;
2587
2588         /* Select the destination model */
2589         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2590
2591         /* Get the path to the destination row. Can not call
2592            gtk_tree_view_get_drag_dest_row() because the source row
2593            is not selected anymore */
2594         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2595                                            &dest_row, &pos);
2596
2597         /* Only allow drops IN other rows */
2598         if (!dest_row ||
2599             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2600             pos == GTK_TREE_VIEW_DROP_AFTER)
2601                 goto end;
2602
2603         success = TRUE;
2604         /* Drags from the header view */
2605         if (source_widget != widget) {
2606                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2607
2608                 drag_and_drop_from_header_view (source_model,
2609                                                 dest_model,
2610                                                 dest_row,
2611                                                 selection_data);
2612         } else {
2613                 DndHelper *helper = NULL;
2614
2615                 /* Get the source model and row */
2616                 gtk_tree_get_row_drag_data (selection_data,
2617                                             &source_model,
2618                                             &source_row);
2619
2620                 /* Create the helper */
2621                 helper = g_slice_new0 (DndHelper);
2622                 helper->delete_source = delete_source;
2623                 helper->source_row = gtk_tree_path_copy (source_row);
2624                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2625
2626                 drag_and_drop_from_folder_view (source_model,
2627                                                 dest_model,
2628                                                 dest_row,
2629                                                 selection_data,
2630                                                 helper);
2631
2632                 gtk_tree_path_free (source_row);
2633         }
2634
2635         /* Frees */
2636         gtk_tree_path_free (dest_row);
2637
2638  end:
2639         /* Finish the drag and drop */
2640         gtk_drag_finish (context, success, FALSE, time);
2641 }
2642
2643 /*
2644  * We define a "drag-drop" signal handler because we do not want to
2645  * use the default one, because the default one always calls
2646  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2647  * signal handler, because there we have all the information available
2648  * to know if the dnd was a success or not.
2649  */
2650 static gboolean
2651 drag_drop_cb (GtkWidget      *widget,
2652               GdkDragContext *context,
2653               gint            x,
2654               gint            y,
2655               guint           time,
2656               gpointer        user_data)
2657 {
2658         gpointer target;
2659
2660         if (!context->targets)
2661                 return FALSE;
2662
2663         /* Check if we're dragging a folder row */
2664         target = gtk_drag_dest_find_target (widget, context, NULL);
2665
2666         /* Request the data from the source. */
2667         gtk_drag_get_data(widget, context, target, time);
2668
2669     return TRUE;
2670 }
2671
2672 /*
2673  * This function expands a node of a tree view if it's not expanded
2674  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2675  * does that, so that's why they're here.
2676  */
2677 static gint
2678 expand_row_timeout (gpointer data)
2679 {
2680         GtkTreeView *tree_view = data;
2681         GtkTreePath *dest_path = NULL;
2682         GtkTreeViewDropPosition pos;
2683         gboolean result = FALSE;
2684
2685         gdk_threads_enter ();
2686
2687         gtk_tree_view_get_drag_dest_row (tree_view,
2688                                          &dest_path,
2689                                          &pos);
2690
2691         if (dest_path &&
2692             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2693              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2694                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2695                 gtk_tree_path_free (dest_path);
2696         }
2697         else {
2698                 if (dest_path)
2699                         gtk_tree_path_free (dest_path);
2700
2701                 result = TRUE;
2702         }
2703
2704         gdk_threads_leave ();
2705
2706         return result;
2707 }
2708
2709 /*
2710  * This function is called whenever the pointer is moved over a widget
2711  * while dragging some data. It installs a timeout that will expand a
2712  * node of the treeview if not expanded yet. This function also calls
2713  * gdk_drag_status in order to set the suggested action that will be
2714  * used by the "drag-data-received" signal handler to know if we
2715  * should do a move or just a copy of the data.
2716  */
2717 static gboolean
2718 on_drag_motion (GtkWidget      *widget,
2719                 GdkDragContext *context,
2720                 gint            x,
2721                 gint            y,
2722                 guint           time,
2723                 gpointer        user_data)
2724 {
2725         GtkTreeViewDropPosition pos;
2726         GtkTreePath *dest_row;
2727         GtkTreeModel *dest_model;
2728         ModestFolderViewPrivate *priv;
2729         GdkDragAction suggested_action;
2730         gboolean valid_location = FALSE;
2731         TnyFolderStore *folder = NULL;
2732
2733         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2734
2735         if (priv->timer_expander != 0) {
2736                 g_source_remove (priv->timer_expander);
2737                 priv->timer_expander = 0;
2738         }
2739
2740         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2741                                            x, y,
2742                                            &dest_row,
2743                                            &pos);
2744
2745         /* Do not allow drops between folders */
2746         if (!dest_row ||
2747             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2748             pos == GTK_TREE_VIEW_DROP_AFTER) {
2749                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2750                 gdk_drag_status(context, 0, time);
2751                 valid_location = FALSE;
2752                 goto out;
2753         } else {
2754                 valid_location = TRUE;
2755         }
2756
2757         /* Check that the destination folder is writable */
2758         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2759         folder = tree_path_to_folder (dest_model, dest_row);
2760         if (folder && TNY_IS_FOLDER (folder)) {
2761                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2762
2763                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2764                         valid_location = FALSE;
2765                         goto out;
2766                 }
2767         }
2768
2769         /* Expand the selected row after 1/2 second */
2770         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2771                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2772         }
2773         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2774
2775         /* Select the desired action. By default we pick MOVE */
2776         suggested_action = GDK_ACTION_MOVE;
2777
2778         if (context->actions == GDK_ACTION_COPY)
2779             gdk_drag_status(context, GDK_ACTION_COPY, time);
2780         else if (context->actions == GDK_ACTION_MOVE)
2781             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2782         else if (context->actions & suggested_action)
2783             gdk_drag_status(context, suggested_action, time);
2784         else
2785             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2786
2787  out:
2788         if (folder)
2789                 g_object_unref (folder);
2790         if (dest_row) {
2791                 gtk_tree_path_free (dest_row);
2792         }
2793         g_signal_stop_emission_by_name (widget, "drag-motion");
2794
2795         return valid_location;
2796 }
2797
2798 /*
2799  * This function sets the treeview as a source and a target for dnd
2800  * events. It also connects all the requirede signals.
2801  */
2802 static void
2803 setup_drag_and_drop (GtkTreeView *self)
2804 {
2805         /* Set up the folder view as a dnd destination. Set only the
2806            highlight flag, otherwise gtk will have a different
2807            behaviour */
2808 #ifdef MODEST_TOOLKIT_HILDON2
2809         return;
2810 #endif
2811         gtk_drag_dest_set (GTK_WIDGET (self),
2812                            GTK_DEST_DEFAULT_HIGHLIGHT,
2813                            folder_view_drag_types,
2814                            G_N_ELEMENTS (folder_view_drag_types),
2815                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2816
2817         g_signal_connect (G_OBJECT (self),
2818                           "drag_data_received",
2819                           G_CALLBACK (on_drag_data_received),
2820                           NULL);
2821
2822
2823         /* Set up the treeview as a dnd source */
2824         gtk_drag_source_set (GTK_WIDGET (self),
2825                              GDK_BUTTON1_MASK,
2826                              folder_view_drag_types,
2827                              G_N_ELEMENTS (folder_view_drag_types),
2828                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2829
2830         g_signal_connect (G_OBJECT (self),
2831                           "drag_motion",
2832                           G_CALLBACK (on_drag_motion),
2833                           NULL);
2834
2835         g_signal_connect (G_OBJECT (self),
2836                           "drag_data_get",
2837                           G_CALLBACK (on_drag_data_get),
2838                           NULL);
2839
2840         g_signal_connect (G_OBJECT (self),
2841                           "drag_drop",
2842                           G_CALLBACK (drag_drop_cb),
2843                           NULL);
2844 }
2845
2846 /*
2847  * This function manages the navigation through the folders using the
2848  * keyboard or the hardware keys in the device
2849  */
2850 static gboolean
2851 on_key_pressed (GtkWidget *self,
2852                 GdkEventKey *event,
2853                 gpointer user_data)
2854 {
2855         GtkTreeSelection *selection;
2856         GtkTreeIter iter;
2857         GtkTreeModel *model;
2858         gboolean retval = FALSE;
2859
2860         /* Up and Down are automatically managed by the treeview */
2861         if (event->keyval == GDK_Return) {
2862                 /* Expand/Collapse the selected row */
2863                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2864                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2865                         GtkTreePath *path;
2866
2867                         path = gtk_tree_model_get_path (model, &iter);
2868
2869                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2870                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2871                         else
2872                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2873                         gtk_tree_path_free (path);
2874                 }
2875                 /* No further processing */
2876                 retval = TRUE;
2877         }
2878
2879         return retval;
2880 }
2881
2882 /*
2883  * We listen to the changes in the local folder account name key,
2884  * because we want to show the right name in the view. The local
2885  * folder account name corresponds to the device name in the Maemo
2886  * version. We do this because we do not want to query gconf on each
2887  * tree view refresh. It's better to cache it and change whenever
2888  * necessary.
2889  */
2890 static void
2891 on_configuration_key_changed (ModestConf* conf,
2892                               const gchar *key,
2893                               ModestConfEvent event,
2894                               ModestConfNotificationId id,
2895                               ModestFolderView *self)
2896 {
2897         ModestFolderViewPrivate *priv;
2898
2899
2900         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2901         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2902
2903         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2904                 g_free (priv->local_account_name);
2905
2906                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2907                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2908                 else
2909                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2910                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2911
2912                 /* Force a redraw */
2913 #if GTK_CHECK_VERSION(2, 8, 0)
2914                 GtkTreeViewColumn * tree_column;
2915
2916                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2917                                                         NAME_COLUMN);
2918                 gtk_tree_view_column_queue_resize (tree_column);
2919 #else
2920                 gtk_widget_queue_draw (GTK_WIDGET (self));
2921 #endif
2922         }
2923 }
2924
2925 void
2926 modest_folder_view_set_style (ModestFolderView *self,
2927                               ModestFolderViewStyle style)
2928 {
2929         ModestFolderViewPrivate *priv;
2930
2931         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2932         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2933                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2934
2935         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2936
2937
2938         priv->style = style;
2939 }
2940
2941 void
2942 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2943                                                              const gchar *account_id)
2944 {
2945         ModestFolderViewPrivate *priv;
2946         GtkTreeModel *model;
2947
2948         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2949
2950         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2951
2952         /* This will be used by the filter_row callback,
2953          * to decided which rows to show: */
2954         if (priv->visible_account_id) {
2955                 g_free (priv->visible_account_id);
2956                 priv->visible_account_id = NULL;
2957         }
2958         if (account_id)
2959                 priv->visible_account_id = g_strdup (account_id);
2960
2961         /* Refilter */
2962         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2963         if (GTK_IS_TREE_MODEL_FILTER (model))
2964                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2965
2966         /* Save settings to gconf */
2967         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2968                                    MODEST_CONF_FOLDER_VIEW_KEY);
2969 }
2970
2971 const gchar *
2972 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2973 {
2974         ModestFolderViewPrivate *priv;
2975
2976         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2977
2978         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2979
2980         return (const gchar *) priv->visible_account_id;
2981 }
2982
2983 static gboolean
2984 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2985 {
2986         do {
2987                 GtkTreeIter child;
2988                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2989
2990                 gtk_tree_model_get (model, iter,
2991                                     TYPE_COLUMN,
2992                                     &type, -1);
2993
2994                 gboolean result = FALSE;
2995                 if (type == TNY_FOLDER_TYPE_INBOX) {
2996                         result = TRUE;
2997                 }
2998                 if (result) {
2999                         *inbox_iter = *iter;
3000                         return TRUE;
3001                 }
3002
3003                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3004                         if (find_inbox_iter (model, &child, inbox_iter))
3005                                 return TRUE;
3006                 }
3007
3008         } while (gtk_tree_model_iter_next (model, iter));
3009
3010         return FALSE;
3011 }
3012
3013
3014
3015
3016 void
3017 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3018 {
3019         GtkTreeModel *model;
3020         GtkTreeIter iter, inbox_iter;
3021         GtkTreeSelection *sel;
3022         GtkTreePath *path = NULL;
3023
3024         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3025
3026         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3027         if (!model)
3028                 return;
3029
3030         expand_root_items (self);
3031         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3032
3033         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3034                 g_warning ("%s: model is empty", __FUNCTION__);
3035                 return;
3036         }
3037
3038         if (find_inbox_iter (model, &iter, &inbox_iter))
3039                 path = gtk_tree_model_get_path (model, &inbox_iter);
3040         else
3041                 path = gtk_tree_path_new_first ();
3042
3043         /* Select the row and free */
3044         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3045         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3046         gtk_tree_path_free (path);
3047
3048         /* set focus */
3049         gtk_widget_grab_focus (GTK_WIDGET(self));
3050 }
3051
3052
3053 /* recursive */
3054 static gboolean
3055 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3056                   TnyFolder* folder)
3057 {
3058         do {
3059                 GtkTreeIter child;
3060                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3061                 TnyFolder* a_folder;
3062                 gchar *name = NULL;
3063
3064                 gtk_tree_model_get (model, iter,
3065                                     INSTANCE_COLUMN, &a_folder,
3066                                     NAME_COLUMN, &name,
3067                                     TYPE_COLUMN, &type,
3068                                     -1);
3069                 g_free (name);
3070
3071                 if (folder == a_folder) {
3072                         g_object_unref (a_folder);
3073                         *folder_iter = *iter;
3074                         return TRUE;
3075                 }
3076                 g_object_unref (a_folder);
3077
3078                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3079                         if (find_folder_iter (model, &child, folder_iter, folder))
3080                                 return TRUE;
3081                 }
3082
3083         } while (gtk_tree_model_iter_next (model, iter));
3084
3085         return FALSE;
3086 }
3087
3088 #ifndef MODEST_TOOLKIT_HILDON2
3089 static void
3090 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3091                                      GtkTreePath *path,
3092                                      GtkTreeIter *iter,
3093                                      ModestFolderView *self)
3094 {
3095         ModestFolderViewPrivate *priv = NULL;
3096         GtkTreeSelection *sel;
3097         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3098         GObject *instance = NULL;
3099
3100         if (!MODEST_IS_FOLDER_VIEW(self))
3101                 return;
3102
3103         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3104
3105         priv->reexpand = TRUE;
3106
3107         gtk_tree_model_get (tree_model, iter,
3108                             TYPE_COLUMN, &type,
3109                             INSTANCE_COLUMN, &instance,
3110                             -1);
3111
3112         if (!instance)
3113                 return;
3114
3115         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3116                 priv->folder_to_select = g_object_ref (instance);
3117         }
3118         g_object_unref (instance);
3119
3120         if (priv->folder_to_select) {
3121
3122                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3123                                                        FALSE)) {
3124                         GtkTreePath *path;
3125                         path = gtk_tree_model_get_path (tree_model, iter);
3126                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3127
3128                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3129
3130                         gtk_tree_selection_select_iter (sel, iter);
3131                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3132
3133                         gtk_tree_path_free (path);
3134                 }
3135
3136                 /* Disable next */
3137                 modest_folder_view_disable_next_folder_selection (self);
3138
3139                 /* Refilter the model */
3140                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3141         }
3142 }
3143 #endif
3144
3145 void
3146 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3147 {
3148         ModestFolderViewPrivate *priv;
3149
3150         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3151
3152         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3153
3154         if (priv->folder_to_select)
3155                 g_object_unref(priv->folder_to_select);
3156
3157         priv->folder_to_select = NULL;
3158 }
3159
3160 gboolean
3161 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3162                                   gboolean after_change)
3163 {
3164         GtkTreeModel *model;
3165         GtkTreeIter iter, folder_iter;
3166         GtkTreeSelection *sel;
3167         ModestFolderViewPrivate *priv = NULL;
3168
3169         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3170         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3171
3172         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3173
3174         if (after_change) {
3175                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3176                 gtk_tree_selection_unselect_all (sel);
3177
3178                 if (priv->folder_to_select)
3179                         g_object_unref(priv->folder_to_select);
3180                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3181                 return TRUE;
3182         }
3183
3184         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3185         if (!model)
3186                 return FALSE;
3187
3188
3189         /* Refilter the model, before selecting the folder */
3190         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3191
3192         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3193                 g_warning ("%s: model is empty", __FUNCTION__);
3194                 return FALSE;
3195         }
3196
3197         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3198                 GtkTreePath *path;
3199
3200                 path = gtk_tree_model_get_path (model, &folder_iter);
3201                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3202
3203                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3204                 gtk_tree_selection_select_iter (sel, &folder_iter);
3205                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3206
3207                 gtk_tree_path_free (path);
3208                 return TRUE;
3209         }
3210         return FALSE;
3211 }
3212
3213
3214 void
3215 modest_folder_view_copy_selection (ModestFolderView *self)
3216 {
3217         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3218
3219         /* Copy selection */
3220         _clipboard_set_selected_data (self, FALSE);
3221 }
3222
3223 void
3224 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3225 {
3226         ModestFolderViewPrivate *priv = NULL;
3227         GtkTreeModel *model = NULL;
3228         const gchar **hidding = NULL;
3229         guint i, n_selected;
3230
3231         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3232         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3233
3234         /* Copy selection */
3235         if (!_clipboard_set_selected_data (folder_view, TRUE))
3236                 return;
3237
3238         /* Get hidding ids */
3239         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3240
3241         /* Clear hidding array created by previous cut operation */
3242         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3243
3244         /* Copy hidding array */
3245         priv->n_selected = n_selected;
3246         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3247         for (i=0; i < n_selected; i++)
3248                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3249
3250         /* Hide cut folders */
3251         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3252         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3253 }
3254
3255 void
3256 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3257                                ModestFolderView *folder_view_dst)
3258 {
3259         GtkTreeModel *filter_model = NULL;
3260         GtkTreeModel *model = NULL;
3261         GtkTreeModel *new_filter_model = NULL;
3262
3263         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3264         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3265
3266         /* Get src model*/
3267         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3268         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3269
3270         /* Build new filter model */
3271         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3272         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3273                                                 filter_row,
3274                                                 folder_view_dst,
3275                                                 NULL);
3276         /* Set copied model */
3277         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3278 #ifndef MODEST_TOOLKIT_HILDON2
3279         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3280                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3281 #endif
3282
3283         /* Free */
3284         g_object_unref (new_filter_model);
3285 }
3286
3287 void
3288 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3289                                           gboolean show)
3290 {
3291         GtkTreeModel *model = NULL;
3292         ModestFolderViewPrivate* priv;
3293
3294         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3295
3296         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3297         priv->show_non_move = show;
3298 /*      modest_folder_view_update_model(folder_view, */
3299 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3300
3301         /* Hide special folders */
3302         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3303         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3304                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3305         }
3306 }
3307
3308 /* Returns FALSE if it did not selected anything */
3309 static gboolean
3310 _clipboard_set_selected_data (ModestFolderView *folder_view,
3311                               gboolean delete)
3312 {
3313         ModestFolderViewPrivate *priv = NULL;
3314         TnyFolderStore *folder = NULL;
3315         gboolean retval = FALSE;
3316
3317         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3318         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3319
3320         /* Set selected data on clipboard   */
3321         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3322         folder = modest_folder_view_get_selected (folder_view);
3323
3324         /* Do not allow to select an account */
3325         if (TNY_IS_FOLDER (folder)) {
3326                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3327                 retval = TRUE;
3328         }
3329
3330         /* Free */
3331         g_object_unref (folder);
3332
3333         return retval;
3334 }
3335
3336 static void
3337 _clear_hidding_filter (ModestFolderView *folder_view)
3338 {
3339         ModestFolderViewPrivate *priv;
3340         guint i;
3341
3342         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3343         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3344
3345         if (priv->hidding_ids != NULL) {
3346                 for (i=0; i < priv->n_selected; i++)
3347                         g_free (priv->hidding_ids[i]);
3348                 g_free(priv->hidding_ids);
3349         }
3350 }
3351
3352
3353 static void
3354 on_display_name_changed (ModestAccountMgr *mgr,
3355                          const gchar *account,
3356                          gpointer user_data)
3357 {
3358         ModestFolderView *self;
3359
3360         self = MODEST_FOLDER_VIEW (user_data);
3361
3362         /* Force a redraw */
3363 #if GTK_CHECK_VERSION(2, 8, 0)
3364         GtkTreeViewColumn * tree_column;
3365
3366         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3367                                                 NAME_COLUMN);
3368         gtk_tree_view_column_queue_resize (tree_column);
3369 #else
3370         gtk_widget_queue_draw (GTK_WIDGET (self));
3371 #endif
3372 }
3373
3374 void 
3375 modest_folder_view_set_cell_style (ModestFolderView *self,
3376                                    ModestFolderViewCellStyle cell_style)
3377 {
3378         ModestFolderViewPrivate *priv = NULL;
3379
3380         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3381         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3382
3383         priv->cell_style = cell_style;
3384
3385         g_object_set (G_OBJECT (priv->messages_renderer),
3386                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3387                       NULL);
3388         
3389         gtk_widget_queue_draw (GTK_WIDGET (self));
3390 }
3391
3392 static void
3393 update_style (ModestFolderView *self)
3394 {
3395         ModestFolderViewPrivate *priv;
3396         GdkColor style_color;
3397
3398         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3399         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3400
3401         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3402                 gdk_color_parse ("grey", &style_color);
3403         }
3404
3405         g_object_set (G_OBJECT (priv->messages_renderer),
3406                       "foreground-gdk", &style_color,
3407                       "foreground-set", TRUE,
3408                       NULL);
3409 }
3410
3411 static void 
3412 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3413 {
3414         if (strcmp ("style", spec->name) == 0) {
3415                 update_style (MODEST_FOLDER_VIEW (obj));
3416                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3417         } 
3418 }
3419
3420 void 
3421 modest_folder_view_set_filter (ModestFolderView *self,
3422                                ModestFolderViewFilter filter)
3423 {
3424         ModestFolderViewPrivate *priv;
3425         GtkTreeModel *filter_model;
3426
3427         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3428         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3429
3430         priv->filter = filter;
3431
3432         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3433         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3434                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3435         }
3436 }