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