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