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