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