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