More Diablo warnings
[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 ((ModestTnyAccountStore *)priv->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 #ifdef MODEST_TOOLKIT_HILDON2
1692                 /* In hildon 2.2 we don't show the account rows */
1693                 return FALSE;
1694 #else
1695                 TnyAccount *acc = TNY_ACCOUNT (instance);
1696                 const gchar *account_id = tny_account_get_id (acc);
1697
1698                 /* If it isn't a special folder,
1699                  * don't show it unless it is the visible account: */
1700                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1701                     !modest_tny_account_is_virtual_local_folders (acc) &&
1702                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1703
1704                         /* Show only the visible account id */
1705                         if (priv->visible_account_id) {
1706                                 if (strcmp (account_id, priv->visible_account_id))
1707                                         retval = FALSE;
1708                         } else {
1709                                 retval = FALSE;
1710                         }
1711                 }
1712
1713                 /* Never show these to the user. They are merged into one folder
1714                  * in the local-folders account instead: */
1715                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1716                         retval = FALSE;
1717 #endif
1718         } else {
1719                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1720                         /* Only show special folders for current account if needed */
1721                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1722                                 TnyAccount *account;
1723
1724                                 account = tny_folder_get_account (TNY_FOLDER (instance));
1725
1726                                 if (TNY_IS_ACCOUNT (account)) {
1727                                         const gchar *account_id = tny_account_get_id (account);
1728
1729                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
1730                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1731                                                 /* Show only the visible account id */
1732                                                 if (priv->visible_account_id) {
1733                                                         if (strcmp (account_id, priv->visible_account_id))
1734                                                                 retval = FALSE;
1735                                                 }
1736                                         }
1737                                                 g_object_unref (account);
1738                                 }
1739                         }
1740
1741                 }
1742         }
1743
1744         /* Check hiding (if necessary) */
1745         cleared = modest_email_clipboard_cleared (priv->clipboard);
1746         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1747                 id = tny_folder_get_id (TNY_FOLDER(instance));
1748                 if (priv->hidding_ids != NULL)
1749                         for (i=0; i < priv->n_selected && !found; i++)
1750                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1751                                         found = (!strcmp (priv->hidding_ids[i], id));
1752
1753                 retval = !found;
1754         }
1755
1756         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1757         folder as no message can be move there according to UI specs */
1758         if (!priv->show_non_move) {
1759                 switch (type) {
1760                         case TNY_FOLDER_TYPE_OUTBOX:
1761                         case TNY_FOLDER_TYPE_SENT:
1762                         case TNY_FOLDER_TYPE_DRAFTS:
1763                                 retval = FALSE;
1764                                 break;
1765                         case TNY_FOLDER_TYPE_UNKNOWN:
1766                         case TNY_FOLDER_TYPE_NORMAL:
1767                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1768                                 if (type == TNY_FOLDER_TYPE_INVALID)
1769                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1770
1771                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1772                                     type == TNY_FOLDER_TYPE_SENT
1773                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1774                                         retval = FALSE;
1775                                 break;
1776                         default:
1777                                 break;
1778                 }
1779         }
1780
1781         /* apply special filters */
1782         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1783                 if (TNY_IS_FOLDER (instance)) {
1784                         TnyFolderCaps capabilities;
1785
1786                         capabilities = tny_folder_get_caps (TNY_FOLDER (instance));
1787                         retval = !(capabilities & TNY_FOLDER_CAPS_NOCHILDREN);
1788
1789                         if (retval) {
1790                                 retval = ((type != TNY_FOLDER_TYPE_DRAFTS) &&
1791                                           (type != TNY_FOLDER_TYPE_OUTBOX) &&
1792                                           (type != TNY_FOLDER_TYPE_SENT));
1793                         }
1794                 } else if (TNY_IS_ACCOUNT (instance)) {
1795                         retval = FALSE;
1796                 }
1797         }
1798
1799         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1800                 if (TNY_IS_FOLDER (instance)) {
1801                         TnyFolderType guess_type;
1802                         
1803                         if (TNY_FOLDER_TYPE_NORMAL) {
1804                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1805                         } else {
1806                                 guess_type = type;
1807                         }
1808
1809                         switch (type) {
1810                         case TNY_FOLDER_TYPE_OUTBOX:
1811                         case TNY_FOLDER_TYPE_SENT:
1812                         case TNY_FOLDER_TYPE_DRAFTS:
1813                         case TNY_FOLDER_TYPE_ARCHIVE:
1814                         case TNY_FOLDER_TYPE_INBOX:
1815                                 retval = FALSE;
1816                                 break;
1817                         case TNY_FOLDER_TYPE_UNKNOWN:
1818                         case TNY_FOLDER_TYPE_NORMAL:
1819                                 break;
1820                         default:
1821                                 break;
1822                         }
1823                         
1824                 } else if (TNY_IS_ACCOUNT (instance)) {
1825                         retval = FALSE;
1826                 }
1827         }
1828
1829         if (retval && TNY_IS_FOLDER (instance)) {
1830                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1831         }
1832
1833         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
1834                 if (TNY_IS_FOLDER (instance)) {
1835                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
1836                 } else if (TNY_IS_ACCOUNT (instance)) {
1837                         retval = FALSE;
1838                 }
1839         }
1840
1841         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
1842                 if (TNY_IS_FOLDER (instance)) {
1843                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
1844                 } else if (TNY_IS_ACCOUNT (instance)) {
1845                         retval = FALSE;
1846                 }
1847         }
1848
1849         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
1850                 if (TNY_IS_FOLDER (instance)) {
1851                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
1852                 } else if (TNY_IS_ACCOUNT (instance)) {
1853                         retval = FALSE;
1854                 }
1855         }
1856
1857         /* Free */
1858         g_object_unref (instance);
1859
1860         return retval;
1861 }
1862
1863
1864 gboolean
1865 modest_folder_view_update_model (ModestFolderView *self,
1866                                  TnyAccountStore *account_store)
1867 {
1868         ModestFolderViewPrivate *priv;
1869         GtkTreeModel *model /* , *old_model */;
1870         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1871
1872         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1873         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1874                               FALSE);
1875
1876         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1877
1878         /* Notify that there is no folder selected */
1879         g_signal_emit (G_OBJECT(self),
1880                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1881                        NULL, FALSE);
1882         if (priv->cur_folder_store) {
1883                 g_object_unref (priv->cur_folder_store);
1884                 priv->cur_folder_store = NULL;
1885         }
1886
1887         /* FIXME: the local accounts are not shown when the query
1888            selects only the subscribed folders */
1889 #ifdef MODEST_TOOLKIT_HILDON2
1890         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
1891                                                           TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1892         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1893                                                       MODEST_FOLDER_PATH_SEPARATOR);
1894 #else
1895         model = tny_gtk_folder_store_tree_model_new (NULL);
1896 #endif
1897
1898         /* When the model is a list store (plain representation) the
1899            outbox is not a child of any account so we have to manually
1900            delete it because removing the local folders account won't
1901            delete it (because tny_folder_get_account() is not defined
1902            for a merge folder */
1903         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
1904                 TnyAccount *account;
1905                 ModestTnyAccountStore *acc_store;
1906
1907                 acc_store = modest_runtime_get_account_store ();
1908                 account = modest_tny_account_store_get_local_folders_account (acc_store);
1909
1910                 if (g_signal_handler_is_connected (account,
1911                                                    priv->outbox_deleted_handler))
1912                         g_signal_handler_disconnect (account,
1913                                                      priv->outbox_deleted_handler);
1914
1915                 priv->outbox_deleted_handler =
1916                         g_signal_connect (account,
1917                                           "outbox-deleted",
1918                                           G_CALLBACK (on_outbox_deleted_cb),
1919                                           self);
1920                 g_object_unref (account);
1921         }
1922
1923         /* Get the accounts: */
1924         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1925                                         TNY_LIST (model),
1926                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1927
1928         sortable = gtk_tree_model_sort_new_with_model (model);
1929         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1930                                               NAME_COLUMN,
1931                                               GTK_SORT_ASCENDING);
1932         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1933                                          NAME_COLUMN,
1934                                          cmp_rows, NULL, NULL);
1935
1936         /* Create filter model */
1937         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1938         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1939                                                 filter_row,
1940                                                 self,
1941                                                 NULL);
1942
1943         /* Set new model */
1944         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1945 #ifndef MODEST_TOOLKIT_HILDON2
1946         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1947                           (GCallback) on_row_inserted_maybe_select_folder, self);
1948 #endif
1949
1950         g_object_unref (model);
1951         g_object_unref (filter_model);
1952         g_object_unref (sortable);
1953
1954         /* Force a reselection of the INBOX next time the widget is shown */
1955         priv->reselect = TRUE;
1956
1957         return TRUE;
1958 }
1959
1960
1961 static void
1962 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1963 {
1964         GtkTreeModel *model = NULL;
1965         TnyFolderStore *folder = NULL;
1966         GtkTreeIter iter;
1967         ModestFolderView *tree_view = NULL;
1968         ModestFolderViewPrivate *priv = NULL;
1969         gboolean selected = FALSE;
1970
1971         g_return_if_fail (sel);
1972         g_return_if_fail (user_data);
1973
1974         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1975
1976         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1977
1978         tree_view = MODEST_FOLDER_VIEW (user_data);
1979
1980         if (selected) {
1981                 gtk_tree_model_get (model, &iter,
1982                                     INSTANCE_COLUMN, &folder,
1983                                     -1);
1984
1985                 /* If the folder is the same do not notify */
1986                 if (folder && priv->cur_folder_store == folder) {
1987                         g_object_unref (folder);
1988                         return;
1989                 }
1990         }
1991
1992         /* Current folder was unselected */
1993         if (priv->cur_folder_store) {
1994                 /* We must do this firstly because a libtinymail-camel
1995                    implementation detail. If we issue the signal
1996                    before doing the sync_async, then that signal could
1997                    cause (and it actually does it) a free of the
1998                    summary of the folder (because the main window will
1999                    clear the headers view */
2000                 if (TNY_IS_FOLDER(priv->cur_folder_store))
2001                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2002                                                FALSE, NULL, NULL, NULL);
2003
2004                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2005                        priv->cur_folder_store, FALSE);
2006
2007                 g_object_unref (priv->cur_folder_store);
2008                 priv->cur_folder_store = NULL;
2009         }
2010
2011         /* New current references */
2012         priv->cur_folder_store = folder;
2013
2014         /* New folder has been selected. Do not notify if there is
2015            nothing new selected */
2016         if (selected) {
2017                 g_signal_emit (G_OBJECT(tree_view),
2018                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2019                                0, priv->cur_folder_store, TRUE);
2020         }
2021 }
2022
2023 static void
2024 on_row_activated (GtkTreeView *treeview,
2025                   GtkTreePath *treepath,
2026                   GtkTreeViewColumn *column,
2027                   gpointer user_data)
2028 {
2029         GtkTreeModel *model = NULL;
2030         TnyFolderStore *folder = NULL;
2031         GtkTreeIter iter;
2032         ModestFolderView *self = NULL;
2033         ModestFolderViewPrivate *priv = NULL;
2034
2035         g_return_if_fail (treeview);
2036         g_return_if_fail (user_data);
2037
2038         self = MODEST_FOLDER_VIEW (user_data);
2039         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2040
2041         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2042
2043         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2044                 return;
2045
2046         gtk_tree_model_get (model, &iter,
2047                             INSTANCE_COLUMN, &folder,
2048                             -1);
2049
2050         g_signal_emit (G_OBJECT(self),
2051                        signals[FOLDER_ACTIVATED_SIGNAL],
2052                        0, folder);
2053
2054 #ifdef MODEST_TOOLKIT_HILDON2
2055         HildonUIMode ui_mode;
2056         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2057         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2058                 if (priv->cur_folder_store)
2059                         g_object_unref (priv->cur_folder_store);
2060                 priv->cur_folder_store = g_object_ref (folder);
2061         }
2062 #endif
2063
2064         g_object_unref (folder);
2065 }
2066
2067 TnyFolderStore *
2068 modest_folder_view_get_selected (ModestFolderView *self)
2069 {
2070         ModestFolderViewPrivate *priv;
2071
2072         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2073
2074         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2075         if (priv->cur_folder_store)
2076                 g_object_ref (priv->cur_folder_store);
2077
2078         return priv->cur_folder_store;
2079 }
2080
2081 static gint
2082 get_cmp_rows_type_pos (GObject *folder)
2083 {
2084         /* Remote accounts -> Local account -> MMC account .*/
2085         /* 0, 1, 2 */
2086
2087         if (TNY_IS_ACCOUNT (folder) &&
2088                 modest_tny_account_is_virtual_local_folders (
2089                         TNY_ACCOUNT (folder))) {
2090                 return 1;
2091         } else if (TNY_IS_ACCOUNT (folder)) {
2092                 TnyAccount *account = TNY_ACCOUNT (folder);
2093                 const gchar *account_id = tny_account_get_id (account);
2094                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2095                         return 2;
2096                 else
2097                         return 0;
2098         }
2099         else {
2100                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2101                 return -1; /* Should never happen */
2102         }
2103 }
2104
2105 static gint
2106 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2107 {
2108         TnyAccount *account;
2109         gboolean is_special;
2110         /* Inbox, Outbox, Drafts, Sent, User */
2111         /* 0, 1, 2, 3, 4 */
2112
2113         if (!TNY_IS_FOLDER (folder_store))
2114                 return 4;
2115         switch (t) {
2116         case TNY_FOLDER_TYPE_INBOX:
2117         {
2118                 account = tny_folder_get_account (folder_store);
2119                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2120                 g_object_unref (account);
2121                 return is_special?0:4;
2122         }
2123         break;
2124         case TNY_FOLDER_TYPE_OUTBOX:
2125                 return 2;
2126                 break;
2127         case TNY_FOLDER_TYPE_DRAFTS:
2128         {
2129                 account = tny_folder_get_account (folder_store);
2130                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2131                 g_object_unref (account);
2132                 return is_special?1:4;
2133         }
2134         break;
2135         case TNY_FOLDER_TYPE_SENT:
2136         {
2137                 account = tny_folder_get_account (folder_store);
2138                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2139                 g_object_unref (account);
2140                 return is_special?3:4;
2141         }
2142         break;
2143         default:
2144                 return 4;
2145         }
2146 }
2147
2148 static gint
2149 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2150 {
2151         const gchar *a1_name, *a2_name;
2152
2153         a1_name = tny_account_get_name (a1);
2154         a2_name = tny_account_get_name (a2);
2155
2156         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2157 }
2158
2159 static gint
2160 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2161 {
2162         TnyAccount *a1, *a2;
2163         gint cmp;
2164
2165         if (TNY_IS_ACCOUNT (s1)) {
2166                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2167         } else {
2168                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2169         }
2170
2171         if (TNY_IS_ACCOUNT (s2)) {
2172                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2173         } else {
2174                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2175         }
2176
2177         if (a1 == a2) {
2178                 cmp = 0;
2179                 goto finish;
2180         }
2181         /* First we sort with the type of account */
2182         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2183         if (cmp != 0)
2184                 goto finish;
2185
2186         cmp = compare_account_names (a1, a2);
2187
2188 finish:
2189         g_object_unref (a1);
2190         g_object_unref (a2);
2191
2192         return cmp;
2193 }
2194
2195 static gint
2196 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2197 {
2198         gint is_account1, is_account2;
2199
2200         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2201         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2202
2203         return is_account2 - is_account1;
2204 }
2205
2206 /*
2207  * This function orders the mail accounts according to these rules:
2208  * 1st - remote accounts
2209  * 2nd - local account
2210  * 3rd - MMC account
2211  */
2212 static gint
2213 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2214           gpointer user_data)
2215 {
2216         gint cmp = 0;
2217         gchar *name1 = NULL;
2218         gchar *name2 = NULL;
2219         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2220         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2221         GObject *folder1 = NULL;
2222         GObject *folder2 = NULL;
2223
2224         gtk_tree_model_get (tree_model, iter1,
2225                             NAME_COLUMN, &name1,
2226                             TYPE_COLUMN, &type,
2227                             INSTANCE_COLUMN, &folder1,
2228                             -1);
2229         gtk_tree_model_get (tree_model, iter2,
2230                             NAME_COLUMN, &name2,
2231                             TYPE_COLUMN, &type2,
2232                             INSTANCE_COLUMN, &folder2,
2233                             -1);
2234
2235         /* Return if we get no folder. This could happen when folder
2236            operations are happening. The model is updated after the
2237            folder copy/move actually occurs, so there could be
2238            situations where the model to be drawn is not correct */
2239         if (!folder1 || !folder2)
2240                 goto finish;
2241
2242         /* Sort by type. First the special folders, then the archives */
2243         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2244         if (cmp != 0)
2245                 goto finish;
2246
2247         /* Now we sort using the account of each folder */
2248         cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2249         if (cmp != 0)
2250                 goto finish;
2251
2252         /* Each group is preceeded by its account */
2253         cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2254         if (cmp != 0)
2255                 goto finish;
2256
2257         /* Pure sort by name */
2258         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2259  finish:
2260         if (folder1)
2261                 g_object_unref(G_OBJECT(folder1));
2262         if (folder2)
2263                 g_object_unref(G_OBJECT(folder2));
2264
2265         g_free (name1);
2266         g_free (name2);
2267
2268         return cmp;
2269 }
2270
2271 /*****************************************************************************/
2272 /*                        DRAG and DROP stuff                                */
2273 /*****************************************************************************/
2274 /*
2275  * This function fills the #GtkSelectionData with the row and the
2276  * model that has been dragged. It's called when this widget is a
2277  * source for dnd after the event drop happened
2278  */
2279 static void
2280 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2281                   guint info, guint time, gpointer data)
2282 {
2283         GtkTreeSelection *selection;
2284         GtkTreeModel *model;
2285         GtkTreeIter iter;
2286         GtkTreePath *source_row;
2287
2288         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2289         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2290
2291                 source_row = gtk_tree_model_get_path (model, &iter);
2292                 gtk_tree_set_row_drag_data (selection_data,
2293                                             model,
2294                                             source_row);
2295
2296                 gtk_tree_path_free (source_row);
2297         }
2298 }
2299
2300 typedef struct _DndHelper {
2301         ModestFolderView *folder_view;
2302         gboolean delete_source;
2303         GtkTreePath *source_row;
2304 } DndHelper;
2305
2306 static void
2307 dnd_helper_destroyer (DndHelper *helper)
2308 {
2309         /* Free the helper */
2310         gtk_tree_path_free (helper->source_row);
2311         g_slice_free (DndHelper, helper);
2312 }
2313
2314 static void
2315 xfer_folder_cb (ModestMailOperation *mail_op,
2316                 TnyFolder *new_folder,
2317                 gpointer user_data)
2318 {
2319         if (new_folder) {
2320                 /* Select the folder */
2321                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2322                                                   new_folder, FALSE);
2323         }
2324 }
2325
2326
2327 /* get the folder for the row the treepath refers to. */
2328 /* folder must be unref'd */
2329 static TnyFolderStore *
2330 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2331 {
2332         GtkTreeIter iter;
2333         TnyFolderStore *folder = NULL;
2334
2335         if (gtk_tree_model_get_iter (model,&iter, path))
2336                 gtk_tree_model_get (model, &iter,
2337                                     INSTANCE_COLUMN, &folder,
2338                                     -1);
2339         return folder;
2340 }
2341
2342
2343 /*
2344  * This function is used by drag_data_received_cb to manage drag and
2345  * drop of a header, i.e, and drag from the header view to the folder
2346  * view.
2347  */
2348 static void
2349 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2350                                 GtkTreeModel *dest_model,
2351                                 GtkTreePath  *dest_row,
2352                                 GtkSelectionData *selection_data)
2353 {
2354         TnyList *headers = NULL;
2355         TnyFolder *folder = NULL, *src_folder = NULL;
2356         TnyFolderType folder_type;
2357         GtkTreeIter source_iter, dest_iter;
2358         ModestWindowMgr *mgr = NULL;
2359         ModestWindow *main_win = NULL;
2360         gchar **uris, **tmp;
2361
2362         /* Build the list of headers */
2363         mgr = modest_runtime_get_window_mgr ();
2364         headers = tny_simple_list_new ();
2365         uris = modest_dnd_selection_data_get_paths (selection_data);
2366         tmp = uris;
2367
2368         while (*tmp != NULL) {
2369                 TnyHeader *header;
2370                 GtkTreePath *path;
2371                 gboolean first = TRUE;
2372
2373                 /* Get header */
2374                 path = gtk_tree_path_new_from_string (*tmp);
2375                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2376                 gtk_tree_model_get (source_model, &source_iter,
2377                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2378                                     &header, -1);
2379
2380                 /* Do not enable d&d of headers already opened */
2381                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2382                         tny_list_append (headers, G_OBJECT (header));
2383
2384                 if (G_UNLIKELY (first)) {
2385                         src_folder = tny_header_get_folder (header);
2386                         first = FALSE;
2387                 }
2388
2389                 /* Free and go on */
2390                 gtk_tree_path_free (path);
2391                 g_object_unref (header);
2392                 tmp++;
2393         }
2394         g_strfreev (uris);
2395
2396         /* This could happen ig we perform a d&d very quickly over the
2397            same row that row could dissapear because message is
2398            transferred */
2399         if (!TNY_IS_FOLDER (src_folder))
2400                 goto cleanup;
2401
2402         /* Get the target folder */
2403         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2404         gtk_tree_model_get (dest_model, &dest_iter,
2405                             INSTANCE_COLUMN,
2406                             &folder, -1);
2407
2408         if (!folder || !TNY_IS_FOLDER(folder)) {
2409 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2410                 goto cleanup;
2411         }
2412
2413         folder_type = modest_tny_folder_guess_folder_type (folder);
2414         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2415 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2416                 goto cleanup;  /* cannot move messages there */
2417         }
2418
2419         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2420 /*              g_warning ("folder not writable"); */
2421                 goto cleanup; /* verboten! */
2422         }
2423
2424         /* Ask for confirmation to move */
2425         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2426         if (!main_win) {
2427                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2428                 goto cleanup;
2429         }
2430
2431         /* Transfer messages */
2432         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2433                                                     headers, folder);
2434
2435         /* Frees */
2436 cleanup:
2437         if (G_IS_OBJECT (src_folder))
2438                 g_object_unref (src_folder);
2439         if (G_IS_OBJECT(folder))
2440                 g_object_unref (G_OBJECT (folder));
2441         if (G_IS_OBJECT(headers))
2442                 g_object_unref (headers);
2443 }
2444
2445 typedef struct {
2446         TnyFolderStore *src_folder;
2447         TnyFolderStore *dst_folder;
2448         ModestFolderView *folder_view;
2449         DndHelper *helper;
2450 } DndFolderInfo;
2451
2452 static void
2453 dnd_folder_info_destroyer (DndFolderInfo *info)
2454 {
2455         if (info->src_folder)
2456                 g_object_unref (info->src_folder);
2457         if (info->dst_folder)
2458                 g_object_unref (info->dst_folder);
2459         g_slice_free (DndFolderInfo, info);
2460 }
2461
2462 static void
2463 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2464                                     GtkWindow *parent_window,
2465                                     TnyAccount *account)
2466 {
2467         /* Show error */
2468         modest_ui_actions_on_account_connection_error (parent_window, account);
2469
2470         /* Free the helper & info */
2471         dnd_helper_destroyer (info->helper);
2472         dnd_folder_info_destroyer (info);
2473 }
2474
2475 static void
2476 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2477                                                      GError *err,
2478                                                      GtkWindow *parent_window,
2479                                                      TnyAccount *account,
2480                                                      gpointer user_data)
2481 {
2482         DndFolderInfo *info = NULL;
2483         ModestMailOperation *mail_op;
2484
2485         info = (DndFolderInfo *) user_data;
2486
2487         if (err || canceled) {
2488                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2489                 return;
2490         }
2491
2492         /* Do the mail operation */
2493         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2494                                                                  modest_ui_actions_move_folder_error_handler,
2495                                                                  info->src_folder, NULL);
2496
2497         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2498                                          mail_op);
2499
2500         /* Transfer the folder */
2501         modest_mail_operation_xfer_folder (mail_op,
2502                                            TNY_FOLDER (info->src_folder),
2503                                            info->dst_folder,
2504                                            info->helper->delete_source,
2505                                            xfer_folder_cb,
2506                                            info->helper->folder_view);
2507
2508         /* Frees */
2509         g_object_unref (G_OBJECT (mail_op));
2510         dnd_helper_destroyer (info->helper);
2511         dnd_folder_info_destroyer (info);
2512 }
2513
2514
2515 static void
2516 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2517                                                      GError *err,
2518                                                      GtkWindow *parent_window,
2519                                                      TnyAccount *account,
2520                                                      gpointer user_data)
2521 {
2522         DndFolderInfo *info = NULL;
2523
2524         info = (DndFolderInfo *) user_data;
2525
2526         if (err || canceled) {
2527                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2528                 return;
2529         }
2530
2531         /* Connect to source folder and perform the copy/move */
2532         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2533                                                        info->src_folder,
2534                                                        drag_and_drop_from_folder_view_src_folder_performer,
2535                                                        info);
2536 }
2537
2538 /*
2539  * This function is used by drag_data_received_cb to manage drag and
2540  * drop of a folder, i.e, and drag from the folder view to the same
2541  * folder view.
2542  */
2543 static void
2544 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2545                                 GtkTreeModel     *dest_model,
2546                                 GtkTreePath      *dest_row,
2547                                 GtkSelectionData *selection_data,
2548                                 DndHelper        *helper)
2549 {
2550         GtkTreeIter dest_iter, iter;
2551         TnyFolderStore *dest_folder = NULL;
2552         TnyFolderStore *folder = NULL;
2553         gboolean forbidden = FALSE;
2554         ModestWindow *win;
2555         DndFolderInfo *info = NULL;
2556
2557         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2558         if (!win) {
2559                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2560                 dnd_helper_destroyer (helper);
2561                 return;
2562         }
2563
2564         if (!forbidden) {
2565                 /* check the folder rules for the destination */
2566                 folder = tree_path_to_folder (dest_model, dest_row);
2567                 if (TNY_IS_FOLDER(folder)) {
2568                         ModestTnyFolderRules rules =
2569                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2570                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2571                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2572                         /* enable local root as destination for folders */
2573                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2574                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2575                                 forbidden = TRUE;
2576                 }
2577                 g_object_unref (folder);
2578         }
2579         if (!forbidden) {
2580                 /* check the folder rules for the source */
2581                 folder = tree_path_to_folder (source_model, helper->source_row);
2582                 if (TNY_IS_FOLDER(folder)) {
2583                         ModestTnyFolderRules rules =
2584                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2585                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2586                 } else
2587                         forbidden = TRUE;
2588                 g_object_unref (folder);
2589         }
2590
2591
2592         /* Check if the drag is possible */
2593         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2594                 /* Show error */
2595                 modest_platform_run_information_dialog ((GtkWindow *) win, 
2596                                                         _("mail_in_ui_folder_move_target_error"), 
2597                                                         FALSE);
2598                 /* Restore the previous selection */
2599                 folder = tree_path_to_folder (source_model, helper->source_row);
2600                 if (folder) {
2601                         if (TNY_IS_FOLDER (folder))
2602                                 modest_folder_view_select_folder (helper->folder_view, 
2603                                                                   TNY_FOLDER (folder), FALSE);
2604                         g_object_unref (folder);
2605                 }
2606                 dnd_helper_destroyer (helper);
2607                 return;
2608         }
2609
2610         /* Get data */
2611         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2612         gtk_tree_model_get (dest_model, &dest_iter,
2613                             INSTANCE_COLUMN,
2614                             &dest_folder, -1);
2615         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2616         gtk_tree_model_get (source_model, &iter,
2617                             INSTANCE_COLUMN,
2618                             &folder, -1);
2619
2620         /* Create the info for the performer */
2621         info = g_slice_new0 (DndFolderInfo);
2622         info->src_folder = g_object_ref (folder);
2623         info->dst_folder = g_object_ref (dest_folder);
2624         info->helper = helper;
2625
2626         /* Connect to the destination folder and perform the copy/move */
2627         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2628                                                        dest_folder,
2629                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2630                                                        info);
2631
2632         /* Frees */
2633         g_object_unref (dest_folder);
2634         g_object_unref (folder);
2635 }
2636
2637 /*
2638  * This function receives the data set by the "drag-data-get" signal
2639  * handler. This information comes within the #GtkSelectionData. This
2640  * function will manage both the drags of folders of the treeview and
2641  * drags of headers of the header view widget.
2642  */
2643 static void
2644 on_drag_data_received (GtkWidget *widget,
2645                        GdkDragContext *context,
2646                        gint x,
2647                        gint y,
2648                        GtkSelectionData *selection_data,
2649                        guint target_type,
2650                        guint time,
2651                        gpointer data)
2652 {
2653         GtkWidget *source_widget;
2654         GtkTreeModel *dest_model, *source_model;
2655         GtkTreePath *source_row, *dest_row;
2656         GtkTreeViewDropPosition pos;
2657         gboolean delete_source = FALSE;
2658         gboolean success = FALSE;
2659
2660         /* Do not allow further process */
2661         g_signal_stop_emission_by_name (widget, "drag-data-received");
2662         source_widget = gtk_drag_get_source_widget (context);
2663
2664         /* Get the action */
2665         if (context->action == GDK_ACTION_MOVE) {
2666                 delete_source = TRUE;
2667
2668                 /* Notify that there is no folder selected. We need to
2669                    do this in order to update the headers view (and
2670                    its monitors, because when moving, the old folder
2671                    won't longer exist. We can not wait for the end of
2672                    the operation, because the operation won't start if
2673                    the folder is in use */
2674                 if (source_widget == widget) {
2675                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2676                         gtk_tree_selection_unselect_all (sel);
2677                 }
2678         }
2679
2680         /* Check if the get_data failed */
2681         if (selection_data == NULL || selection_data->length < 0)
2682                 goto end;
2683
2684         /* Select the destination model */
2685         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2686
2687         /* Get the path to the destination row. Can not call
2688            gtk_tree_view_get_drag_dest_row() because the source row
2689            is not selected anymore */
2690         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2691                                            &dest_row, &pos);
2692
2693         /* Only allow drops IN other rows */
2694         if (!dest_row ||
2695             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2696             pos == GTK_TREE_VIEW_DROP_AFTER)
2697                 goto end;
2698
2699         success = TRUE;
2700         /* Drags from the header view */
2701         if (source_widget != widget) {
2702                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2703
2704                 drag_and_drop_from_header_view (source_model,
2705                                                 dest_model,
2706                                                 dest_row,
2707                                                 selection_data);
2708         } else {
2709                 DndHelper *helper = NULL;
2710
2711                 /* Get the source model and row */
2712                 gtk_tree_get_row_drag_data (selection_data,
2713                                             &source_model,
2714                                             &source_row);
2715
2716                 /* Create the helper */
2717                 helper = g_slice_new0 (DndHelper);
2718                 helper->delete_source = delete_source;
2719                 helper->source_row = gtk_tree_path_copy (source_row);
2720                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2721
2722                 drag_and_drop_from_folder_view (source_model,
2723                                                 dest_model,
2724                                                 dest_row,
2725                                                 selection_data,
2726                                                 helper);
2727
2728                 gtk_tree_path_free (source_row);
2729         }
2730
2731         /* Frees */
2732         gtk_tree_path_free (dest_row);
2733
2734  end:
2735         /* Finish the drag and drop */
2736         gtk_drag_finish (context, success, FALSE, time);
2737 }
2738
2739 /*
2740  * We define a "drag-drop" signal handler because we do not want to
2741  * use the default one, because the default one always calls
2742  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2743  * signal handler, because there we have all the information available
2744  * to know if the dnd was a success or not.
2745  */
2746 static gboolean
2747 drag_drop_cb (GtkWidget      *widget,
2748               GdkDragContext *context,
2749               gint            x,
2750               gint            y,
2751               guint           time,
2752               gpointer        user_data)
2753 {
2754         gpointer target;
2755
2756         if (!context->targets)
2757                 return FALSE;
2758
2759         /* Check if we're dragging a folder row */
2760         target = gtk_drag_dest_find_target (widget, context, NULL);
2761
2762         /* Request the data from the source. */
2763         gtk_drag_get_data(widget, context, target, time);
2764
2765     return TRUE;
2766 }
2767
2768 /*
2769  * This function expands a node of a tree view if it's not expanded
2770  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2771  * does that, so that's why they're here.
2772  */
2773 static gint
2774 expand_row_timeout (gpointer data)
2775 {
2776         GtkTreeView *tree_view = data;
2777         GtkTreePath *dest_path = NULL;
2778         GtkTreeViewDropPosition pos;
2779         gboolean result = FALSE;
2780
2781         gdk_threads_enter ();
2782
2783         gtk_tree_view_get_drag_dest_row (tree_view,
2784                                          &dest_path,
2785                                          &pos);
2786
2787         if (dest_path &&
2788             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2789              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2790                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2791                 gtk_tree_path_free (dest_path);
2792         }
2793         else {
2794                 if (dest_path)
2795                         gtk_tree_path_free (dest_path);
2796
2797                 result = TRUE;
2798         }
2799
2800         gdk_threads_leave ();
2801
2802         return result;
2803 }
2804
2805 /*
2806  * This function is called whenever the pointer is moved over a widget
2807  * while dragging some data. It installs a timeout that will expand a
2808  * node of the treeview if not expanded yet. This function also calls
2809  * gdk_drag_status in order to set the suggested action that will be
2810  * used by the "drag-data-received" signal handler to know if we
2811  * should do a move or just a copy of the data.
2812  */
2813 static gboolean
2814 on_drag_motion (GtkWidget      *widget,
2815                 GdkDragContext *context,
2816                 gint            x,
2817                 gint            y,
2818                 guint           time,
2819                 gpointer        user_data)
2820 {
2821         GtkTreeViewDropPosition pos;
2822         GtkTreePath *dest_row;
2823         GtkTreeModel *dest_model;
2824         ModestFolderViewPrivate *priv;
2825         GdkDragAction suggested_action;
2826         gboolean valid_location = FALSE;
2827         TnyFolderStore *folder = NULL;
2828
2829         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2830
2831         if (priv->timer_expander != 0) {
2832                 g_source_remove (priv->timer_expander);
2833                 priv->timer_expander = 0;
2834         }
2835
2836         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2837                                            x, y,
2838                                            &dest_row,
2839                                            &pos);
2840
2841         /* Do not allow drops between folders */
2842         if (!dest_row ||
2843             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2844             pos == GTK_TREE_VIEW_DROP_AFTER) {
2845                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2846                 gdk_drag_status(context, 0, time);
2847                 valid_location = FALSE;
2848                 goto out;
2849         } else {
2850                 valid_location = TRUE;
2851         }
2852
2853         /* Check that the destination folder is writable */
2854         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2855         folder = tree_path_to_folder (dest_model, dest_row);
2856         if (folder && TNY_IS_FOLDER (folder)) {
2857                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2858
2859                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2860                         valid_location = FALSE;
2861                         goto out;
2862                 }
2863         }
2864
2865         /* Expand the selected row after 1/2 second */
2866         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2867                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2868         }
2869         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2870
2871         /* Select the desired action. By default we pick MOVE */
2872         suggested_action = GDK_ACTION_MOVE;
2873
2874         if (context->actions == GDK_ACTION_COPY)
2875             gdk_drag_status(context, GDK_ACTION_COPY, time);
2876         else if (context->actions == GDK_ACTION_MOVE)
2877             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2878         else if (context->actions & suggested_action)
2879             gdk_drag_status(context, suggested_action, time);
2880         else
2881             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2882
2883  out:
2884         if (folder)
2885                 g_object_unref (folder);
2886         if (dest_row) {
2887                 gtk_tree_path_free (dest_row);
2888         }
2889         g_signal_stop_emission_by_name (widget, "drag-motion");
2890
2891         return valid_location;
2892 }
2893
2894 /*
2895  * This function sets the treeview as a source and a target for dnd
2896  * events. It also connects all the requirede signals.
2897  */
2898 static void
2899 setup_drag_and_drop (GtkTreeView *self)
2900 {
2901         /* Set up the folder view as a dnd destination. Set only the
2902            highlight flag, otherwise gtk will have a different
2903            behaviour */
2904 #ifdef MODEST_TOOLKIT_HILDON2
2905         return;
2906 #endif
2907         gtk_drag_dest_set (GTK_WIDGET (self),
2908                            GTK_DEST_DEFAULT_HIGHLIGHT,
2909                            folder_view_drag_types,
2910                            G_N_ELEMENTS (folder_view_drag_types),
2911                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2912
2913         g_signal_connect (G_OBJECT (self),
2914                           "drag_data_received",
2915                           G_CALLBACK (on_drag_data_received),
2916                           NULL);
2917
2918
2919         /* Set up the treeview as a dnd source */
2920         gtk_drag_source_set (GTK_WIDGET (self),
2921                              GDK_BUTTON1_MASK,
2922                              folder_view_drag_types,
2923                              G_N_ELEMENTS (folder_view_drag_types),
2924                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2925
2926         g_signal_connect (G_OBJECT (self),
2927                           "drag_motion",
2928                           G_CALLBACK (on_drag_motion),
2929                           NULL);
2930
2931         g_signal_connect (G_OBJECT (self),
2932                           "drag_data_get",
2933                           G_CALLBACK (on_drag_data_get),
2934                           NULL);
2935
2936         g_signal_connect (G_OBJECT (self),
2937                           "drag_drop",
2938                           G_CALLBACK (drag_drop_cb),
2939                           NULL);
2940 }
2941
2942 /*
2943  * This function manages the navigation through the folders using the
2944  * keyboard or the hardware keys in the device
2945  */
2946 static gboolean
2947 on_key_pressed (GtkWidget *self,
2948                 GdkEventKey *event,
2949                 gpointer user_data)
2950 {
2951         GtkTreeSelection *selection;
2952         GtkTreeIter iter;
2953         GtkTreeModel *model;
2954         gboolean retval = FALSE;
2955
2956         /* Up and Down are automatically managed by the treeview */
2957         if (event->keyval == GDK_Return) {
2958                 /* Expand/Collapse the selected row */
2959                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2960                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2961                         GtkTreePath *path;
2962
2963                         path = gtk_tree_model_get_path (model, &iter);
2964
2965                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2966                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2967                         else
2968                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2969                         gtk_tree_path_free (path);
2970                 }
2971                 /* No further processing */
2972                 retval = TRUE;
2973         }
2974
2975         return retval;
2976 }
2977
2978 /*
2979  * We listen to the changes in the local folder account name key,
2980  * because we want to show the right name in the view. The local
2981  * folder account name corresponds to the device name in the Maemo
2982  * version. We do this because we do not want to query gconf on each
2983  * tree view refresh. It's better to cache it and change whenever
2984  * necessary.
2985  */
2986 static void
2987 on_configuration_key_changed (ModestConf* conf,
2988                               const gchar *key,
2989                               ModestConfEvent event,
2990                               ModestConfNotificationId id,
2991                               ModestFolderView *self)
2992 {
2993         ModestFolderViewPrivate *priv;
2994
2995
2996         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2997         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2998
2999         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3000                 g_free (priv->local_account_name);
3001
3002                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3003                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3004                 else
3005                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3006                                                                            MODEST_CONF_DEVICE_NAME, NULL);
3007
3008                 /* Force a redraw */
3009 #if GTK_CHECK_VERSION(2, 8, 0)
3010                 GtkTreeViewColumn * tree_column;
3011
3012                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3013                                                         NAME_COLUMN);
3014                 gtk_tree_view_column_queue_resize (tree_column);
3015 #else
3016                 gtk_widget_queue_draw (GTK_WIDGET (self));
3017 #endif
3018         }
3019 }
3020
3021 void
3022 modest_folder_view_set_style (ModestFolderView *self,
3023                               ModestFolderViewStyle style)
3024 {
3025         ModestFolderViewPrivate *priv;
3026
3027         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3028         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3029                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3030
3031         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3032
3033
3034         priv->style = style;
3035 }
3036
3037 void
3038 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3039                                                              const gchar *account_id)
3040 {
3041         ModestFolderViewPrivate *priv;
3042         GtkTreeModel *model;
3043
3044         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3045
3046         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3047
3048         /* This will be used by the filter_row callback,
3049          * to decided which rows to show: */
3050         if (priv->visible_account_id) {
3051                 g_free (priv->visible_account_id);
3052                 priv->visible_account_id = NULL;
3053         }
3054         if (account_id)
3055                 priv->visible_account_id = g_strdup (account_id);
3056
3057         /* Refilter */
3058         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3059         if (GTK_IS_TREE_MODEL_FILTER (model))
3060                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3061
3062         /* Save settings to gconf */
3063         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3064                                    MODEST_CONF_FOLDER_VIEW_KEY);
3065 }
3066
3067 const gchar *
3068 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3069 {
3070         ModestFolderViewPrivate *priv;
3071
3072         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3073
3074         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3075
3076         return (const gchar *) priv->visible_account_id;
3077 }
3078
3079 static gboolean
3080 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3081 {
3082         do {
3083                 GtkTreeIter child;
3084                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3085
3086                 gtk_tree_model_get (model, iter,
3087                                     TYPE_COLUMN,
3088                                     &type, -1);
3089
3090                 gboolean result = FALSE;
3091                 if (type == TNY_FOLDER_TYPE_INBOX) {
3092                         result = TRUE;
3093                 }
3094                 if (result) {
3095                         *inbox_iter = *iter;
3096                         return TRUE;
3097                 }
3098
3099                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3100                         if (find_inbox_iter (model, &child, inbox_iter))
3101                                 return TRUE;
3102                 }
3103
3104         } while (gtk_tree_model_iter_next (model, iter));
3105
3106         return FALSE;
3107 }
3108
3109
3110
3111
3112 void
3113 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3114 {
3115         GtkTreeModel *model;
3116         GtkTreeIter iter, inbox_iter;
3117         GtkTreeSelection *sel;
3118         GtkTreePath *path = NULL;
3119
3120         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3121
3122         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3123         if (!model)
3124                 return;
3125
3126         expand_root_items (self);
3127         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3128
3129         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3130                 g_warning ("%s: model is empty", __FUNCTION__);
3131                 return;
3132         }
3133
3134         if (find_inbox_iter (model, &iter, &inbox_iter))
3135                 path = gtk_tree_model_get_path (model, &inbox_iter);
3136         else
3137                 path = gtk_tree_path_new_first ();
3138
3139         /* Select the row and free */
3140         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3141         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3142         gtk_tree_path_free (path);
3143
3144         /* set focus */
3145         gtk_widget_grab_focus (GTK_WIDGET(self));
3146 }
3147
3148
3149 /* recursive */
3150 static gboolean
3151 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3152                   TnyFolder* folder)
3153 {
3154         do {
3155                 GtkTreeIter child;
3156                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3157                 TnyFolder* a_folder;
3158                 gchar *name = NULL;
3159
3160                 gtk_tree_model_get (model, iter,
3161                                     INSTANCE_COLUMN, &a_folder,
3162                                     NAME_COLUMN, &name,
3163                                     TYPE_COLUMN, &type,
3164                                     -1);
3165                 g_free (name);
3166
3167                 if (folder == a_folder) {
3168                         g_object_unref (a_folder);
3169                         *folder_iter = *iter;
3170                         return TRUE;
3171                 }
3172                 g_object_unref (a_folder);
3173
3174                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3175                         if (find_folder_iter (model, &child, folder_iter, folder))
3176                                 return TRUE;
3177                 }
3178
3179         } while (gtk_tree_model_iter_next (model, iter));
3180
3181         return FALSE;
3182 }
3183
3184 #ifndef MODEST_TOOLKIT_HILDON2
3185 static void
3186 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3187                                      GtkTreePath *path,
3188                                      GtkTreeIter *iter,
3189                                      ModestFolderView *self)
3190 {
3191         ModestFolderViewPrivate *priv = NULL;
3192         GtkTreeSelection *sel;
3193         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3194         GObject *instance = NULL;
3195
3196         if (!MODEST_IS_FOLDER_VIEW(self))
3197                 return;
3198
3199         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3200
3201         priv->reexpand = TRUE;
3202
3203         gtk_tree_model_get (tree_model, iter,
3204                             TYPE_COLUMN, &type,
3205                             INSTANCE_COLUMN, &instance,
3206                             -1);
3207
3208         if (!instance)
3209                 return;
3210
3211         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3212                 priv->folder_to_select = g_object_ref (instance);
3213         }
3214         g_object_unref (instance);
3215
3216         if (priv->folder_to_select) {
3217
3218                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3219                                                        FALSE)) {
3220                         GtkTreePath *path;
3221                         path = gtk_tree_model_get_path (tree_model, iter);
3222                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3223
3224                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3225
3226                         gtk_tree_selection_select_iter (sel, iter);
3227                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3228
3229                         gtk_tree_path_free (path);
3230                 }
3231
3232                 /* Disable next */
3233                 modest_folder_view_disable_next_folder_selection (self);
3234
3235                 /* Refilter the model */
3236                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3237         }
3238 }
3239 #endif
3240
3241 void
3242 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3243 {
3244         ModestFolderViewPrivate *priv;
3245
3246         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3247
3248         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3249
3250         if (priv->folder_to_select)
3251                 g_object_unref(priv->folder_to_select);
3252
3253         priv->folder_to_select = NULL;
3254 }
3255
3256 gboolean
3257 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3258                                   gboolean after_change)
3259 {
3260         GtkTreeModel *model;
3261         GtkTreeIter iter, folder_iter;
3262         GtkTreeSelection *sel;
3263         ModestFolderViewPrivate *priv = NULL;
3264
3265         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3266         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3267
3268         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3269
3270         if (after_change) {
3271                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3272                 gtk_tree_selection_unselect_all (sel);
3273
3274                 if (priv->folder_to_select)
3275                         g_object_unref(priv->folder_to_select);
3276                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3277                 return TRUE;
3278         }
3279
3280         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3281         if (!model)
3282                 return FALSE;
3283
3284
3285         /* Refilter the model, before selecting the folder */
3286         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3287
3288         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3289                 g_warning ("%s: model is empty", __FUNCTION__);
3290                 return FALSE;
3291         }
3292
3293         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3294                 GtkTreePath *path;
3295
3296                 path = gtk_tree_model_get_path (model, &folder_iter);
3297                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3298
3299                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3300                 gtk_tree_selection_select_iter (sel, &folder_iter);
3301                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3302
3303                 gtk_tree_path_free (path);
3304                 return TRUE;
3305         }
3306         return FALSE;
3307 }
3308
3309
3310 void
3311 modest_folder_view_copy_selection (ModestFolderView *self)
3312 {
3313         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3314
3315         /* Copy selection */
3316         _clipboard_set_selected_data (self, FALSE);
3317 }
3318
3319 void
3320 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3321 {
3322         ModestFolderViewPrivate *priv = NULL;
3323         GtkTreeModel *model = NULL;
3324         const gchar **hidding = NULL;
3325         guint i, n_selected;
3326
3327         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3328         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3329
3330         /* Copy selection */
3331         if (!_clipboard_set_selected_data (folder_view, TRUE))
3332                 return;
3333
3334         /* Get hidding ids */
3335         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3336
3337         /* Clear hidding array created by previous cut operation */
3338         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3339
3340         /* Copy hidding array */
3341         priv->n_selected = n_selected;
3342         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3343         for (i=0; i < n_selected; i++)
3344                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3345
3346         /* Hide cut folders */
3347         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3348         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3349 }
3350
3351 void
3352 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3353                                ModestFolderView *folder_view_dst)
3354 {
3355         GtkTreeModel *filter_model = NULL;
3356         GtkTreeModel *model = NULL;
3357         GtkTreeModel *new_filter_model = NULL;
3358
3359         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3360         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3361
3362         /* Get src model*/
3363         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3364         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3365
3366         /* Build new filter model */
3367         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3368         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3369                                                 filter_row,
3370                                                 folder_view_dst,
3371                                                 NULL);
3372         /* Set copied model */
3373         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3374 #ifndef MODEST_TOOLKIT_HILDON2
3375         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3376                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3377 #endif
3378
3379         /* Free */
3380         g_object_unref (new_filter_model);
3381 }
3382
3383 void
3384 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3385                                           gboolean show)
3386 {
3387         GtkTreeModel *model = NULL;
3388         ModestFolderViewPrivate* priv;
3389
3390         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3391
3392         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3393         priv->show_non_move = show;
3394 /*      modest_folder_view_update_model(folder_view, */
3395 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3396
3397         /* Hide special folders */
3398         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3399         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3400                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3401         }
3402 }
3403
3404 /* Returns FALSE if it did not selected anything */
3405 static gboolean
3406 _clipboard_set_selected_data (ModestFolderView *folder_view,
3407                               gboolean delete)
3408 {
3409         ModestFolderViewPrivate *priv = NULL;
3410         TnyFolderStore *folder = NULL;
3411         gboolean retval = FALSE;
3412
3413         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3414         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3415
3416         /* Set selected data on clipboard   */
3417         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3418         folder = modest_folder_view_get_selected (folder_view);
3419
3420         /* Do not allow to select an account */
3421         if (TNY_IS_FOLDER (folder)) {
3422                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3423                 retval = TRUE;
3424         }
3425
3426         /* Free */
3427         g_object_unref (folder);
3428
3429         return retval;
3430 }
3431
3432 static void
3433 _clear_hidding_filter (ModestFolderView *folder_view)
3434 {
3435         ModestFolderViewPrivate *priv;
3436         guint i;
3437
3438         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3439         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3440
3441         if (priv->hidding_ids != NULL) {
3442                 for (i=0; i < priv->n_selected; i++)
3443                         g_free (priv->hidding_ids[i]);
3444                 g_free(priv->hidding_ids);
3445         }
3446 }
3447
3448
3449 static void
3450 on_display_name_changed (ModestAccountMgr *mgr,
3451                          const gchar *account,
3452                          gpointer user_data)
3453 {
3454         ModestFolderView *self;
3455
3456         self = MODEST_FOLDER_VIEW (user_data);
3457
3458         /* Force a redraw */
3459 #if GTK_CHECK_VERSION(2, 8, 0)
3460         GtkTreeViewColumn * tree_column;
3461
3462         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3463                                                 NAME_COLUMN);
3464         gtk_tree_view_column_queue_resize (tree_column);
3465 #else
3466         gtk_widget_queue_draw (GTK_WIDGET (self));
3467 #endif
3468 }
3469
3470 void 
3471 modest_folder_view_set_cell_style (ModestFolderView *self,
3472                                    ModestFolderViewCellStyle cell_style)
3473 {
3474         ModestFolderViewPrivate *priv = NULL;
3475
3476         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3477         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3478
3479         priv->cell_style = cell_style;
3480
3481         g_object_set (G_OBJECT (priv->messages_renderer),
3482                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3483                       NULL);
3484         
3485         gtk_widget_queue_draw (GTK_WIDGET (self));
3486 }
3487
3488 static void
3489 update_style (ModestFolderView *self)
3490 {
3491         ModestFolderViewPrivate *priv;
3492         GdkColor style_color;
3493
3494         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3495         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3496
3497         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3498                 gdk_color_parse ("grey", &style_color);
3499         }
3500
3501         g_object_set (G_OBJECT (priv->messages_renderer),
3502                       "foreground-gdk", &style_color,
3503                       "foreground-set", TRUE,
3504                       NULL);
3505 }
3506
3507 static void 
3508 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3509 {
3510         if (strcmp ("style", spec->name) == 0) {
3511                 update_style (MODEST_FOLDER_VIEW (obj));
3512                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3513         } 
3514 }
3515
3516 void 
3517 modest_folder_view_set_filter (ModestFolderView *self,
3518                                ModestFolderViewFilter filter)
3519 {
3520         ModestFolderViewPrivate *priv;
3521         GtkTreeModel *filter_model;
3522
3523         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3524         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3525
3526         priv->filter = filter;
3527
3528         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3529         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3530                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3531         }
3532 }