93f8eccb164f2bf8380f5ef74dc922376887e1d6
[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 <modest-tny-folder.h>
45 #include <modest-tny-local-folders-account.h>
46 #include <modest-tny-outbox-account.h>
47 #include <modest-marshal.h>
48 #include <modest-icon-names.h>
49 #include <modest-tny-account-store.h>
50 #include <modest-text-utils.h>
51 #include <modest-runtime.h>
52 #include "modest-folder-view.h"
53 #include <modest-dnd.h>
54 #include <modest-platform.h>
55 #include <modest-widget-memory.h>
56 #include <modest-ui-actions.h>
57
58 /* 'private'/'protected' functions */
59 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
60 static void modest_folder_view_init        (ModestFolderView *obj);
61 static void modest_folder_view_finalize    (GObject *obj);
62
63 static void         tny_account_store_view_init (gpointer g, 
64                                                  gpointer iface_data);
65
66 static void         modest_folder_view_set_account_store (TnyAccountStoreView *self, 
67                                                           TnyAccountStore     *account_store);
68
69 static void         on_selection_changed   (GtkTreeSelection *sel, gpointer data);
70
71 static void         on_account_update      (TnyAccountStore *account_store, 
72                                             const gchar *account,
73                                             gpointer user_data);
74
75 static void         on_account_removed     (TnyAccountStore *self, 
76                                             TnyAccount *account,
77                                             gpointer user_data);
78
79 static void         on_accounts_reloaded   (TnyAccountStore *store, 
80                                             gpointer user_data);
81
82 static gint         cmp_rows               (GtkTreeModel *tree_model, 
83                                             GtkTreeIter *iter1, 
84                                             GtkTreeIter *iter2,
85                                             gpointer user_data);
86
87 static gboolean     filter_row             (GtkTreeModel *model,
88                                             GtkTreeIter *iter,
89                                             gpointer data);
90
91 static gboolean     on_key_pressed         (GtkWidget *self,
92                                             GdkEventKey *event,
93                                             gpointer user_data);
94
95 static void         on_configuration_key_changed         (ModestConf* conf, 
96                                                           const gchar *key, 
97                                                           ModestConfEvent event, 
98                                                           ModestFolderView *self);
99
100 /* DnD functions */
101 static void         on_drag_data_get       (GtkWidget *widget, 
102                                             GdkDragContext *context, 
103                                             GtkSelectionData *selection_data, 
104                                             guint info, 
105                                             guint time, 
106                                             gpointer data);
107
108 static void         on_drag_data_received  (GtkWidget *widget, 
109                                             GdkDragContext *context, 
110                                             gint x, 
111                                             gint y, 
112                                             GtkSelectionData *selection_data, 
113                                             guint info, 
114                                             guint time, 
115                                             gpointer data);
116
117 static gboolean     on_drag_motion         (GtkWidget      *widget,
118                                             GdkDragContext *context,
119                                             gint            x,
120                                             gint            y,
121                                             guint           time,
122                                             gpointer        user_data);
123
124 static gint         expand_row_timeout     (gpointer data);
125
126 static void         setup_drag_and_drop    (GtkTreeView *self);
127
128 static void          _clipboard_set_selected_data (ModestFolderView *folder_view, gboolean delete);
129
130 static void          _clear_hidding_filter (ModestFolderView *folder_view);
131
132
133
134 enum {
135         FOLDER_SELECTION_CHANGED_SIGNAL,
136         FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
137         LAST_SIGNAL
138 };
139
140 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
141 struct _ModestFolderViewPrivate {
142         TnyAccountStore      *account_store;
143         TnyFolderStore       *cur_folder_store;
144
145         gulong                account_update_signal;
146         gulong                changed_signal;
147         gulong                accounts_reloaded_signal;
148         gulong                account_removed_signal;
149         gulong                conf_key_signal;
150         
151         /* not unref this object, its a singlenton */
152         ModestEmailClipboard *clipboard;
153
154         /* Filter tree model */
155         gchar **hidding_ids;
156         guint n_selected;
157
158         TnyFolderStoreQuery  *query;
159         guint                 timer_expander;
160
161         gchar                *local_account_name;
162         gchar                *visible_account_id;
163         ModestFolderViewStyle style;
164
165         gboolean              reselect; /* we use this to force a reselection of the INBOX */
166 };
167 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                       \
168         (G_TYPE_INSTANCE_GET_PRIVATE((o),                       \
169                                      MODEST_TYPE_FOLDER_VIEW,   \
170                                      ModestFolderViewPrivate))
171 /* globals */
172 static GObjectClass *parent_class = NULL;
173
174 static guint signals[LAST_SIGNAL] = {0}; 
175
176 GType
177 modest_folder_view_get_type (void)
178 {
179         static GType my_type = 0;
180         if (!my_type) {
181                 static const GTypeInfo my_info = {
182                         sizeof(ModestFolderViewClass),
183                         NULL,           /* base init */
184                         NULL,           /* base finalize */
185                         (GClassInitFunc) modest_folder_view_class_init,
186                         NULL,           /* class finalize */
187                         NULL,           /* class data */
188                         sizeof(ModestFolderView),
189                         1,              /* n_preallocs */
190                         (GInstanceInitFunc) modest_folder_view_init,
191                         NULL
192                 };
193
194                 static const GInterfaceInfo tny_account_store_view_info = {
195                         (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
196                         NULL,         /* interface_finalize */
197                         NULL          /* interface_data */
198                 };
199
200                                 
201                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
202                                                   "ModestFolderView",
203                                                   &my_info, 0);
204
205                 g_type_add_interface_static (my_type, 
206                                              TNY_TYPE_ACCOUNT_STORE_VIEW, 
207                                              &tny_account_store_view_info);
208         }
209         return my_type;
210 }
211
212 static void
213 modest_folder_view_class_init (ModestFolderViewClass *klass)
214 {
215         GObjectClass *gobject_class;
216         gobject_class = (GObjectClass*) klass;
217
218         parent_class            = g_type_class_peek_parent (klass);
219         gobject_class->finalize = modest_folder_view_finalize;
220
221         g_type_class_add_private (gobject_class,
222                                   sizeof(ModestFolderViewPrivate));
223         
224         signals[FOLDER_SELECTION_CHANGED_SIGNAL] = 
225                 g_signal_new ("folder_selection_changed",
226                               G_TYPE_FROM_CLASS (gobject_class),
227                               G_SIGNAL_RUN_FIRST,
228                               G_STRUCT_OFFSET (ModestFolderViewClass,
229                                                folder_selection_changed),
230                               NULL, NULL,
231                               modest_marshal_VOID__POINTER_BOOLEAN,
232                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
233
234         /*
235          * This signal is emitted whenever the currently selected
236          * folder display name is computed. Note that the name could
237          * be different to the folder name, because we could append
238          * the unread messages count to the folder name to build the
239          * folder display name
240          */
241         signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] = 
242                 g_signal_new ("folder-display-name-changed",
243                               G_TYPE_FROM_CLASS (gobject_class),
244                               G_SIGNAL_RUN_FIRST,
245                               G_STRUCT_OFFSET (ModestFolderViewClass,
246                                                folder_display_name_changed),
247                               NULL, NULL,
248                               g_cclosure_marshal_VOID__STRING,
249                               G_TYPE_NONE, 1, G_TYPE_STRING);
250 }
251
252 static void
253 text_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
254                  GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer data)
255 {
256         ModestFolderViewPrivate *priv;
257         GObject *rendobj;
258         gchar *fname = NULL;
259         gint unread = 0;
260         gint all = 0;
261         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
262         GObject *instance = NULL;
263         
264         g_return_if_fail (column);
265         g_return_if_fail (tree_model);
266
267         gtk_tree_model_get (tree_model, iter,
268                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
269                             TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
270                             TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
271                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
272                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
273                             -1);
274         rendobj = G_OBJECT(renderer);
275  
276         if (!fname)
277                 return;
278
279         if (!instance) {
280                 g_free (fname);
281                 return;
282         }
283
284         
285         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (data);
286         
287         gchar *item_name = NULL;
288         gint item_weight = 400;
289         
290         if (type != TNY_FOLDER_TYPE_ROOT) {
291                 gint number = 0;
292                 
293                 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
294                         type = modest_tny_folder_get_local_folder_type (TNY_FOLDER (instance));
295                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
296                                 g_free (fname);
297                                 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
298                         }
299                 }
300
301                 /* Select the number to show: the unread or unsent messages */
302                 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
303                         number = all;
304                 else
305                         number = unread;
306                 
307                 /* Use bold font style if there are unread or unset messages */
308                 if (number > 0) {
309                         item_name = g_strdup_printf ("%s (%d)", fname, number);
310                         item_weight = 800;
311                 } else {
312                         item_name = g_strdup (fname);
313                         item_weight = 400;
314                 }
315                 
316         } else if (TNY_IS_ACCOUNT (instance)) {
317                 /* If it's a server account */
318                 if (modest_tny_account_is_virtual_local_folders (
319                                 TNY_ACCOUNT (instance))) {
320                         item_name = g_strdup (priv->local_account_name);
321                         item_weight = 800;
322                 } else {
323                         item_name = g_strdup (fname);
324                         item_weight = 800;
325                 }
326         }
327         
328         if (!item_name)
329                 item_name = g_strdup ("unknown");
330                         
331         if (item_name && item_weight) {
332                 /* Set the name in the treeview cell: */
333                 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
334                 
335                 /* Notify display name observers */
336                 if (G_OBJECT (priv->cur_folder_store) == instance) {
337                         g_signal_emit (G_OBJECT(data),
338                                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
339                                                item_name);
340                 }
341                 
342                 g_free (item_name);
343                 
344         }
345         
346         g_object_unref (G_OBJECT (instance));
347         g_free (fname);
348 }
349
350
351
352 static void
353 icon_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
354                  GtkTreeModel *tree_model,  GtkTreeIter *iter, gpointer data)
355 {
356         GObject *rendobj = NULL, *instance = NULL;
357         GdkPixbuf *pixbuf = NULL;
358         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
359         gchar *fname = NULL;
360         const gchar *account_id = NULL;
361         gint unread = 0;
362         
363         rendobj = G_OBJECT(renderer);
364         gtk_tree_model_get (tree_model, iter,
365                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
366                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
367                             TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
368                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
369                             -1);
370
371         if (!fname)
372                 return;
373
374         if (!instance) {
375                 g_free (fname);
376                 return;
377         }
378
379         /* We include the MERGE type here because it's used to create
380            the local OUTBOX folder */
381         if (type == TNY_FOLDER_TYPE_NORMAL || 
382             type == TNY_FOLDER_TYPE_UNKNOWN ||
383             type == TNY_FOLDER_TYPE_MERGE) {
384                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
385         }
386
387         switch (type) {
388         case TNY_FOLDER_TYPE_ROOT:
389                 if (TNY_IS_ACCOUNT (instance)) {
390                         
391                         if (modest_tny_account_is_virtual_local_folders (
392                                 TNY_ACCOUNT (instance))) {
393                                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
394                         }
395                         else {
396                                 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
397                                 
398                                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
399                                         pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
400                                 else
401                                         pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
402                         }
403                 }
404                 break;
405         case TNY_FOLDER_TYPE_INBOX:
406             pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
407             break;
408         case TNY_FOLDER_TYPE_OUTBOX:
409                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
410                 break;
411         case TNY_FOLDER_TYPE_JUNK:
412                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
413                 break;
414         case TNY_FOLDER_TYPE_SENT:
415                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
416                 break;
417         case TNY_FOLDER_TYPE_TRASH:
418                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
419                 break;
420         case TNY_FOLDER_TYPE_DRAFTS:
421                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
422                 break;
423         case TNY_FOLDER_TYPE_NORMAL:
424         default:
425                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
426                 break;
427         }
428         
429         g_object_unref (G_OBJECT (instance));
430         g_free (fname);
431
432         /* Set pixbuf */
433         g_object_set (rendobj, "pixbuf", pixbuf, NULL);
434
435         if (pixbuf != NULL)
436                 g_object_unref (pixbuf);
437 }
438
439 static void
440 add_columns (GtkWidget *treeview)
441 {
442         GtkTreeViewColumn *column;
443         GtkCellRenderer *renderer;
444         GtkTreeSelection *sel;
445
446         /* Create column */
447         column = gtk_tree_view_column_new ();   
448         
449         /* Set icon and text render function */
450         renderer = gtk_cell_renderer_pixbuf_new();
451         gtk_tree_view_column_pack_start (column, renderer, FALSE);
452         gtk_tree_view_column_set_cell_data_func(column, renderer,
453                                                 icon_cell_data, treeview, NULL);
454         
455         renderer = gtk_cell_renderer_text_new();
456         gtk_tree_view_column_pack_start (column, renderer, FALSE);
457         gtk_tree_view_column_set_cell_data_func(column, renderer,
458                                                 text_cell_data, treeview, NULL);
459         
460         /* Set selection mode */
461         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
462         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
463
464         /* Set treeview appearance */
465         gtk_tree_view_column_set_spacing (column, 2);
466         gtk_tree_view_column_set_resizable (column, TRUE);
467         gtk_tree_view_column_set_fixed_width (column, TRUE);            
468         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
469         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
470
471         /* Add column */
472         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
473 }
474
475 static void
476 modest_folder_view_init (ModestFolderView *obj)
477 {
478         ModestFolderViewPrivate *priv;
479         ModestConf *conf;
480         
481         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
482         
483         priv->timer_expander = 0;
484         priv->account_store  = NULL;
485         priv->query          = NULL;
486         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
487         priv->cur_folder_store   = NULL;
488         priv->visible_account_id = NULL;
489
490         /* Initialize the local account name */
491         conf = modest_runtime_get_conf();
492         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
493
494         /* Init email clipboard */
495         priv->clipboard = modest_runtime_get_email_clipboard ();
496         priv->hidding_ids = NULL;
497         priv->n_selected = 0;
498         priv->reselect = FALSE;
499
500         /* Build treeview */
501         add_columns (GTK_WIDGET (obj));
502
503         /* Setup drag and drop */
504         setup_drag_and_drop (GTK_TREE_VIEW(obj));
505
506         /* Connect signals */
507         g_signal_connect (G_OBJECT (obj), 
508                           "key-press-event", 
509                           G_CALLBACK (on_key_pressed), NULL);
510
511         /*
512          * Track changes in the local account name (in the device it
513          * will be the device name)
514          */
515         priv->conf_key_signal = 
516                 g_signal_connect (G_OBJECT(conf), 
517                                   "key_changed",
518                                   G_CALLBACK(on_configuration_key_changed), obj);
519 }
520
521 static void
522 tny_account_store_view_init (gpointer g, gpointer iface_data)
523 {
524         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
525
526         klass->set_account_store_func = modest_folder_view_set_account_store;
527
528         return;
529 }
530
531 static void
532 modest_folder_view_finalize (GObject *obj)
533 {
534         ModestFolderViewPrivate *priv;
535         GtkTreeSelection    *sel;
536         
537         g_return_if_fail (obj);
538         
539         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
540
541         if (priv->timer_expander != 0) {
542                 g_source_remove (priv->timer_expander);
543                 priv->timer_expander = 0;
544         }
545
546         if (priv->account_store) {
547                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
548                                              priv->account_update_signal);
549                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
550                                              priv->accounts_reloaded_signal);
551                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
552                                              priv->account_removed_signal);
553                 g_object_unref (G_OBJECT(priv->account_store));
554                 priv->account_store = NULL;
555         }
556
557         if (priv->query) {
558                 g_object_unref (G_OBJECT (priv->query));
559                 priv->query = NULL;
560         }
561
562         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
563         if (sel)
564                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
565
566         g_free (priv->local_account_name);
567         g_free (priv->visible_account_id);
568         
569         if (priv->conf_key_signal) {
570                 g_signal_handler_disconnect (modest_runtime_get_conf (),
571                                              priv->conf_key_signal);
572                 priv->conf_key_signal = 0;
573         }
574
575         if (priv->cur_folder_store) {
576                 if (TNY_IS_FOLDER(priv->cur_folder_store))
577                         tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), TRUE, NULL);
578                         /* expunge the message */
579
580                 g_object_unref (priv->cur_folder_store);
581                 priv->cur_folder_store = NULL;
582         }
583
584         /* Clear hidding array created by cut operation */
585         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
586
587         G_OBJECT_CLASS(parent_class)->finalize (obj);
588 }
589
590
591 static void
592 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
593 {
594         ModestFolderViewPrivate *priv;
595         TnyDevice *device;
596
597         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
598         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
599
600         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
601         device = tny_account_store_get_device (account_store);
602
603         if (G_UNLIKELY (priv->account_store)) {
604
605                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
606                                                    priv->account_update_signal))
607                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
608                                                      priv->account_update_signal);
609                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
610                                                    priv->accounts_reloaded_signal))
611                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
612                                                      priv->accounts_reloaded_signal);
613                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
614                                                    priv->account_removed_signal))
615                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
616                                                      priv->account_removed_signal);
617
618                 g_object_unref (G_OBJECT (priv->account_store));
619         }
620
621         priv->account_store = g_object_ref (G_OBJECT (account_store));
622
623         priv->account_update_signal = 
624                 g_signal_connect (G_OBJECT(account_store), "account_update",
625                                   G_CALLBACK (on_account_update), self);
626
627         priv->account_removed_signal = 
628                 g_signal_connect (G_OBJECT(account_store), "account_removed",
629                                   G_CALLBACK (on_account_removed), self);
630
631         priv->accounts_reloaded_signal = 
632                 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
633                                   G_CALLBACK (on_accounts_reloaded), self);
634
635         g_signal_connect (G_OBJECT(account_store), "connecting_finished",
636                                 G_CALLBACK (on_accounts_reloaded), self);
637
638         on_accounts_reloaded (account_store, (gpointer ) self);
639         
640         g_object_unref (G_OBJECT (device));
641 }
642
643 static void
644 on_account_removed (TnyAccountStore *account_store, 
645                     TnyAccount *account,
646                     gpointer user_data)
647 {
648         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
649         ModestFolderViewPrivate *priv;
650
651         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
652
653         /* If the removed account is the currently viewed one then
654            clear the configuration value. The new visible account will be the default account */
655         if (!strcmp (priv->visible_account_id, tny_account_get_id (account))) {
656                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
657         }
658 }
659
660 static void
661 on_account_update (TnyAccountStore *account_store, 
662                    const gchar *account,
663                    gpointer user_data)
664 {
665         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
666         ModestFolderViewPrivate *priv;
667
668         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
669         if (!priv->visible_account_id)
670                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
671                                               MODEST_CONF_FOLDER_VIEW_KEY);
672
673         if (!modest_folder_view_update_model (self, account_store))
674                 g_printerr ("modest: failed to update model for changes in '%s'",
675                             account);
676 }
677
678 static void 
679 on_accounts_reloaded   (TnyAccountStore *account_store, 
680                         gpointer user_data)
681 {
682         modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
683 }
684
685 void
686 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
687 {
688         GtkTreeViewColumn *col;
689         
690         g_return_if_fail (self);
691
692         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
693         if (!col) {
694                 g_printerr ("modest: failed get column for title\n");
695                 return;
696         }
697
698         gtk_tree_view_column_set_title (col, title);
699         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
700                                            title != NULL);
701 }
702
703 static gboolean
704 modest_folder_view_on_map (ModestFolderView *self, 
705                            GdkEventExpose *event,
706                            gpointer data)
707 {
708         ModestFolderViewPrivate *priv;
709
710         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
711
712         /* This won't happen often */
713         if (G_UNLIKELY (priv->reselect)) {
714                 /* Select the first inbox or the local account if not found */
715                 modest_folder_view_select_first_inbox_or_local (self);
716                 priv->reselect = FALSE;
717         }
718         return FALSE;
719 }
720
721 GtkWidget*
722 modest_folder_view_new (TnyFolderStoreQuery *query)
723 {
724         GObject *self;
725         ModestFolderViewPrivate *priv;
726         GtkTreeSelection *sel;
727         
728         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
729         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
730
731         if (query)
732                 priv->query = g_object_ref (query);
733         
734         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
735         priv->changed_signal = g_signal_connect (sel, "changed",
736                                                  G_CALLBACK (on_selection_changed), self);
737
738         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
739
740         return GTK_WIDGET(self);
741 }
742
743 /* this feels dirty; any other way to expand all the root items? */
744 static void
745 expand_root_items (ModestFolderView *self)
746 {
747         GtkTreePath *path;
748         path = gtk_tree_path_new_first ();
749
750         /* all folders should have child items, so.. */
751         while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
752                 gtk_tree_path_next (path);
753         
754         gtk_tree_path_free (path);
755 }
756
757 /*
758  * We use this function to implement the
759  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
760  * account in this case, and the local folders.
761  */
762 static gboolean 
763 filter_row (GtkTreeModel *model,
764             GtkTreeIter *iter,
765             gpointer data)
766 {
767         ModestFolderViewPrivate *priv;
768         gboolean retval = TRUE;
769         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
770         GObject *instance = NULL;
771         const gchar *id = NULL;
772         guint i;
773         gboolean found = FALSE;
774         gboolean cleared = FALSE;
775         
776         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
777         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
778
779         gtk_tree_model_get (model, iter,
780                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
781                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
782                             -1);
783
784         /* Do not show if there is no instance, this could indeed
785            happen when the model is being modified while it's being
786            drawn. This could occur for example when moving folders
787            using drag&drop */
788         if (!instance)
789                 return FALSE;
790
791         if (type == TNY_FOLDER_TYPE_ROOT) {
792                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
793                    account instead of a folder. */
794                 if (TNY_IS_ACCOUNT (instance)) {
795                         TnyAccount *acc = TNY_ACCOUNT (instance);
796                         const gchar *account_id = tny_account_get_id (acc);
797         
798                         /* If it isn't a special folder, 
799                          * don't show it unless it is the visible account: */
800                         if (!modest_tny_account_is_virtual_local_folders (acc) &&
801                                 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) { 
802                                 /* Show only the visible account id */
803                                 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
804                                         retval = FALSE;
805                         }
806                         
807                         /* Never show these to the user. They are merged into one folder 
808                          * in the local-folders account instead: */
809                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
810                                 retval = FALSE;
811                 }
812         }
813         
814         /* The virtual local-folders folder store is also shown by default. */
815
816         /* Check hiding (if necessary) */
817         cleared = modest_email_clipboard_cleared (priv->clipboard);            
818         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
819                 id = tny_folder_get_id (TNY_FOLDER(instance));
820                 if (priv->hidding_ids != NULL)
821                         for (i=0; i < priv->n_selected && !found; i++)
822                                 if (priv->hidding_ids[i] != NULL && id != NULL)
823                                         found = (!strcmp (priv->hidding_ids[i], id));
824                 
825                 retval = !found;
826         }
827
828         /* Free */
829         g_object_unref (instance);
830
831         return retval;
832 }
833
834 gboolean
835 modest_folder_view_update_model (ModestFolderView *self,
836                                  TnyAccountStore *account_store)
837 {
838         ModestFolderViewPrivate *priv;
839         GtkTreeModel *model /* , *old_model */;
840         /* TnyAccount *local_account; */
841         TnyList *model_as_list;
842
843         g_return_val_if_fail (account_store, FALSE);
844
845         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
846         
847         /* Notify that there is no folder selected */
848         g_signal_emit (G_OBJECT(self), 
849                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
850                        NULL, TRUE);
851
852         /* FIXME: the local accounts are not shown when the query
853            selects only the subscribed folders. */
854 /*      model        = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
855         model        = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
856         
857         /* Deal with the model via its TnyList Interface,
858          * filling the TnyList via a get_accounts() call: */
859         model_as_list = TNY_LIST(model);
860
861         /* Get the accounts: */
862         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
863                                         model_as_list,
864                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
865         g_object_unref (model_as_list);
866         model_as_list = NULL;   
867                                                      
868         GtkTreeModel *filter_model = NULL, *sortable = NULL;
869
870         sortable = gtk_tree_model_sort_new_with_model (model);
871         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
872                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
873                                               GTK_SORT_ASCENDING);
874         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
875                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
876                                          cmp_rows, NULL, NULL);
877
878         /* Create filter model */
879         filter_model = gtk_tree_model_filter_new (sortable, NULL);
880         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
881                                                 filter_row,
882                                                 self,
883                                                 NULL);
884 /*      if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) { */
885 /*              filter_model = gtk_tree_model_filter_new (sortable, NULL); */
886 /*              gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model), */
887 /*                                                      filter_row, */
888 /*                                                      self, */
889 /*                                                      NULL); */
890 /*      } */
891
892         /* Set new model */
893         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
894 /*      gtk_tree_view_set_model (GTK_TREE_VIEW(self),  */
895 /*                               (filter_model) ? filter_model : sortable); */
896
897         g_object_unref (model);
898         g_object_unref (filter_model);
899 /*      if (filter_model) */
900 /*              g_object_unref (filter_model); */
901                         
902         g_object_unref (sortable);
903
904         /* Force a reselection of the INBOX next time the widget is shown */
905         priv->reselect = TRUE;
906                         
907         return TRUE;
908 }
909
910
911 static void
912 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
913 {
914         GtkTreeModel            *model;
915         TnyFolderStore          *folder = NULL;
916         GtkTreeIter             iter;
917         ModestFolderView        *tree_view;
918         ModestFolderViewPrivate *priv;
919
920         g_return_if_fail (sel);
921         g_return_if_fail (user_data);
922         
923         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
924
925         if(!gtk_tree_selection_get_selected (sel, &model, &iter))
926                 return;
927
928         /* Notify the display name observers */
929         g_signal_emit (G_OBJECT(user_data),
930                        signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
931                        NULL);
932
933         tree_view = MODEST_FOLDER_VIEW (user_data);
934         gtk_tree_model_get (model, &iter,
935                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
936                             -1);
937
938         /* If the folder is the same do not notify */
939         if (priv->cur_folder_store == folder && folder) {
940                 g_object_unref (folder);
941                 return;
942         }
943         
944         /* Current folder was unselected */
945         if (priv->cur_folder_store) {
946                 if (TNY_IS_FOLDER(priv->cur_folder_store))
947                         tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), TRUE, NULL);
948                 /* expunge the message */
949
950                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
951                                priv->cur_folder_store, FALSE);
952                 g_object_unref (priv->cur_folder_store);
953                 priv->cur_folder_store = NULL;
954         }
955
956         /* New current references */
957         priv->cur_folder_store = folder;
958
959         /* New folder has been selected */
960         g_signal_emit (G_OBJECT(tree_view),
961                        signals[FOLDER_SELECTION_CHANGED_SIGNAL],
962                        0, priv->cur_folder_store, TRUE);
963 }
964
965 TnyFolderStore *
966 modest_folder_view_get_selected (ModestFolderView *self)
967 {
968         ModestFolderViewPrivate *priv;
969
970         g_return_val_if_fail (self, NULL);
971         
972         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
973         if (priv->cur_folder_store)
974                 g_object_ref (priv->cur_folder_store);
975
976         return priv->cur_folder_store;
977 }
978
979 static gint
980 get_cmp_rows_type_pos (GObject *folder)
981 {
982         /* Remote accounts -> Local account -> MMC account .*/
983         /* 0, 1, 2 */
984         
985         if (TNY_IS_ACCOUNT (folder) && 
986                 modest_tny_account_is_virtual_local_folders (
987                         TNY_ACCOUNT (folder))) {
988                 return 1;
989         } else if (TNY_IS_ACCOUNT (folder)) {
990                 TnyAccount *account = TNY_ACCOUNT (folder);
991                 const gchar *account_id = tny_account_get_id (account);
992                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
993                         return 2;
994                 else
995                         return 0;
996         }
997         else {
998                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
999                 return -1; /* Should never happen */
1000         }
1001 }
1002
1003 static gint
1004 get_cmp_subfolder_type_pos (TnyFolderType t)
1005 {
1006         /* Outbox, Drafts, Sent, User */
1007         /* 0, 1, 2, 3 */
1008
1009         switch (t) {
1010         case TNY_FOLDER_TYPE_OUTBOX:
1011                 return 0;
1012                 break;
1013         case TNY_FOLDER_TYPE_DRAFTS:
1014                 return 1;
1015                 break;
1016         case TNY_FOLDER_TYPE_SENT:
1017                 return 2;
1018                 break;
1019         default:
1020                 return 3;
1021         }
1022 }
1023
1024 /*
1025  * This function orders the mail accounts according to these rules:
1026  * 1st - remote accounts
1027  * 2nd - local account
1028  * 3rd - MMC account
1029  */
1030 static gint
1031 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1032           gpointer user_data)
1033 {
1034         gint cmp;
1035         gchar *name1 = NULL;
1036         gchar *name2 = NULL;
1037         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1038         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1039         GObject *folder1 = NULL;
1040         GObject *folder2 = NULL;
1041
1042         gtk_tree_model_get (tree_model, iter1,
1043                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1044                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1045                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1046                             -1);
1047         gtk_tree_model_get (tree_model, iter2,
1048                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1049                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1050                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1051                             -1);
1052
1053         if (type == TNY_FOLDER_TYPE_ROOT) {
1054                 /* Compare the types, so that 
1055                  * Remote accounts -> Local account -> MMC account .*/
1056                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1057                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1058                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1059                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1060                 if (pos1 <  pos2)
1061                         cmp = -1;
1062                 else if (pos1 > pos2)
1063                         cmp = 1;
1064                 else {
1065                         /* Compare items of the same type: */
1066                         
1067                         TnyAccount *account1 = NULL;
1068                         if (TNY_IS_ACCOUNT (folder1))
1069                                 account1 = TNY_ACCOUNT (folder1);
1070                                 
1071                         TnyAccount *account2 = NULL;
1072                         if (TNY_IS_ACCOUNT (folder2))
1073                                 account2 = TNY_ACCOUNT (folder2);
1074                                 
1075                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1076                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1077         
1078                         if (!account_id && !account_id2)
1079                                 return 0;
1080                         else if (!account_id)
1081                                 return -1;
1082                         else if (!account_id2)
1083                                 return +1;
1084                         else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1085                                 cmp = +1;
1086                         else
1087                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1088                 }
1089         } else {
1090                 GtkTreeIter parent;
1091                 gboolean has_parent;
1092                 gint cmp1 = 0, cmp2 = 0;
1093                 /* get the parent to know if it's a local folder */
1094                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1095                 if (has_parent) {
1096                         GObject *parent_folder;
1097                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1098                         gtk_tree_model_get (tree_model, &parent, 
1099                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1100                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1101                                             -1);
1102                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1103                             TNY_IS_ACCOUNT (parent_folder) &&
1104                             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1105                                 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_folder_type (TNY_FOLDER (folder1)));
1106                                 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_folder_type (TNY_FOLDER (folder2)));
1107                         }
1108                         g_object_unref (parent_folder);
1109                 }
1110                 if (cmp1 == cmp2)
1111                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1112                 else 
1113                         cmp = (cmp1 - cmp2);
1114         }
1115         
1116         if (folder1)
1117                 g_object_unref(G_OBJECT(folder1));
1118         if (folder2)
1119                 g_object_unref(G_OBJECT(folder2));
1120
1121         g_free (name1);
1122         g_free (name2);
1123
1124         return cmp;     
1125 }
1126
1127 /*****************************************************************************/
1128 /*                        DRAG and DROP stuff                                */
1129 /*****************************************************************************/
1130
1131 /*
1132  * This function fills the #GtkSelectionData with the row and the
1133  * model that has been dragged. It's called when this widget is a
1134  * source for dnd after the event drop happened
1135  */
1136 static void
1137 on_drag_data_get (GtkWidget *widget, 
1138                   GdkDragContext *context, 
1139                   GtkSelectionData *selection_data, 
1140                   guint info, 
1141                   guint time, 
1142                   gpointer data)
1143 {
1144         GtkTreeSelection *selection;
1145         GtkTreeModel *model;
1146         GtkTreeIter iter;
1147         GtkTreePath *source_row;
1148
1149         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1150         gtk_tree_selection_get_selected (selection, &model, &iter);
1151         source_row = gtk_tree_model_get_path (model, &iter);
1152
1153         gtk_tree_set_row_drag_data (selection_data,
1154                                     model,
1155                                     source_row);
1156
1157         gtk_tree_path_free (source_row);
1158 }
1159
1160 typedef struct _DndHelper {
1161         gboolean delete_source;
1162         GtkTreePath *source_row;
1163         GdkDragContext *context;
1164         guint time;
1165 } DndHelper;
1166
1167
1168 /*
1169  * This function is the callback of the
1170  * modest_mail_operation_xfer_msgs () and
1171  * modest_mail_operation_xfer_folder() calls. We check here if the
1172  * message/folder was correctly asynchronously transferred. The reason
1173  * to use the same callback is that the code is the same, it only has
1174  * to check that the operation went fine and then finalize the drag
1175  * and drop action
1176  */
1177 static void
1178 on_progress_changed (ModestMailOperation *mail_op, 
1179                      ModestMailOperationState *state,
1180                      gpointer user_data)
1181 {
1182         gboolean success;
1183         DndHelper *helper;
1184
1185         helper = (DndHelper *) user_data;
1186
1187         if (!state->finished)
1188                 return;
1189
1190         if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1191                 success = TRUE;
1192         } else {
1193                 success = FALSE;
1194         }
1195
1196         /* Notify the drag source. Never call delete, the monitor will
1197            do the job if needed */
1198         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1199
1200         /* Free the helper */
1201         gtk_tree_path_free (helper->source_row);        
1202         g_slice_free (DndHelper, helper);
1203 }
1204
1205 /*
1206  * This function is used by drag_data_received_cb to manage drag and
1207  * drop of a header, i.e, and drag from the header view to the folder
1208  * view.
1209  */
1210 static void
1211 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1212                                 GtkTreeModel *dest_model,
1213                                 GtkTreePath  *dest_row,
1214                                 DndHelper    *helper)
1215 {
1216         TnyList *headers = NULL;
1217         TnyHeader *header = NULL;
1218         TnyFolder *folder = NULL;
1219         ModestMailOperation *mail_op = NULL;
1220         GtkTreeIter source_iter, dest_iter;
1221
1222         g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1223         g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1224         g_return_if_fail (dest_row);
1225         g_return_if_fail (helper);
1226         
1227         /* Get header */
1228         gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1229         gtk_tree_model_get (source_model, &source_iter, 
1230                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1231                             &header, -1);
1232         if (!TNY_IS_HEADER(header)) {
1233                 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1234                 goto cleanup;
1235         }
1236         
1237         /* Get Folder */
1238         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1239         gtk_tree_model_get (dest_model, &dest_iter, 
1240                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1241                             &folder, -1);
1242
1243         if (!TNY_IS_FOLDER(folder)) {
1244                 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1245                 goto cleanup;
1246         }
1247
1248         /* Transfer message */
1249         mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1250         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1251                                          mail_op);
1252         g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1253                           G_CALLBACK (on_progress_changed), helper);
1254
1255         headers = tny_simple_list_new ();
1256         tny_list_append (headers, G_OBJECT (header));
1257         modest_mail_operation_xfer_msgs (mail_op, 
1258                                          headers, 
1259                                          folder, 
1260                                          helper->delete_source, 
1261                                          NULL, NULL);
1262         
1263         /* Frees */
1264 cleanup:
1265         if (G_IS_OBJECT(mail_op))
1266                 g_object_unref (G_OBJECT (mail_op));
1267         if (G_IS_OBJECT(header))
1268                 g_object_unref (G_OBJECT (header));
1269         if (G_IS_OBJECT(folder))
1270                 g_object_unref (G_OBJECT (folder));
1271         if (G_IS_OBJECT(headers))
1272                 g_object_unref (headers);
1273 }
1274
1275 /*
1276  * This function is used by drag_data_received_cb to manage drag and
1277  * drop of a folder, i.e, and drag from the folder view to the same
1278  * folder view.
1279  */
1280 static void
1281 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
1282                                 GtkTreeModel     *dest_model,
1283                                 GtkTreePath      *dest_row,
1284                                 GtkSelectionData *selection_data,
1285                                 DndHelper        *helper)
1286 {
1287         ModestMailOperation *mail_op;
1288         GtkTreeIter parent_iter, iter;
1289         TnyFolderStore *parent_folder;
1290         TnyFolder *folder;
1291
1292         /* Check if the drag is possible */
1293 /*      if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1294 /*          !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1295 /*                                                 dest_row, */
1296 /*                                                 selection_data)) { */
1297         if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1298
1299                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1300                 gtk_tree_path_free (helper->source_row);        
1301                 g_slice_free (DndHelper, helper);
1302                 return;
1303         }
1304
1305         /* Get data */
1306         gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1307         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1308         gtk_tree_model_get (source_model, &parent_iter, 
1309                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1310                             &parent_folder, -1);
1311         gtk_tree_model_get (source_model, &iter,
1312                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1313                             &folder, -1);
1314
1315         /* Do the mail operation */
1316         mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1317                                                                  NULL,
1318                                                                  modest_ui_actions_move_folder_error_handler,
1319                                                                  NULL);
1320         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), 
1321                                          mail_op);
1322         g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1323                           G_CALLBACK (on_progress_changed), helper);
1324
1325         modest_mail_operation_xfer_folder (mail_op, 
1326                                            folder, 
1327                                            parent_folder,
1328                                            helper->delete_source);
1329         
1330         /* Frees */
1331         g_object_unref (G_OBJECT (parent_folder));
1332         g_object_unref (G_OBJECT (folder));
1333         g_object_unref (G_OBJECT (mail_op));
1334 }
1335
1336 /*
1337  * This function receives the data set by the "drag-data-get" signal
1338  * handler. This information comes within the #GtkSelectionData. This
1339  * function will manage both the drags of folders of the treeview and
1340  * drags of headers of the header view widget.
1341  */
1342 static void 
1343 on_drag_data_received (GtkWidget *widget, 
1344                        GdkDragContext *context, 
1345                        gint x, 
1346                        gint y, 
1347                        GtkSelectionData *selection_data, 
1348                        guint target_type, 
1349                        guint time, 
1350                        gpointer data)
1351 {
1352         GtkWidget *source_widget;
1353         GtkTreeModel *dest_model, *source_model;
1354         GtkTreePath *source_row, *dest_row;
1355         GtkTreeViewDropPosition pos;
1356         gboolean success = FALSE, delete_source = FALSE;
1357         DndHelper *helper = NULL; 
1358
1359         /* Do not allow further process */
1360         g_signal_stop_emission_by_name (widget, "drag-data-received");
1361         source_widget = gtk_drag_get_source_widget (context);
1362
1363         /* Get the action */
1364         if (context->action == GDK_ACTION_MOVE) {
1365                 delete_source = TRUE;
1366
1367                 /* Notify that there is no folder selected. We need to
1368                    do this in order to update the headers view (and
1369                    its monitors, because when moving, the old folder
1370                    won't longer exist. We can not wait for the end of
1371                    the operation, because the operation won't start if
1372                    the folder is in use */
1373                 if (source_widget == widget)
1374                         g_signal_emit (G_OBJECT (widget), 
1375                                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1376         }
1377
1378         /* Check if the get_data failed */
1379         if (selection_data == NULL || selection_data->length < 0)
1380                 gtk_drag_finish (context, success, FALSE, time);
1381
1382         /* Get the models */
1383         gtk_tree_get_row_drag_data (selection_data,
1384                                     &source_model,
1385                                     &source_row);
1386
1387         /* Select the destination model */
1388         if (source_widget == widget) {
1389                 dest_model = source_model;
1390         } else {
1391                 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1392         }
1393
1394         /* Get the path to the destination row. Can not call
1395            gtk_tree_view_get_drag_dest_row() because the source row
1396            is not selected anymore */
1397         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1398                                            &dest_row, &pos);
1399
1400         /* Only allow drops IN other rows */
1401         if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1402                 gtk_drag_finish (context, success, FALSE, time);
1403
1404         /* Create the helper */
1405         helper = g_slice_new0 (DndHelper);
1406         helper->delete_source = delete_source;
1407         helper->source_row = gtk_tree_path_copy (source_row);
1408         helper->context = context;
1409         helper->time = time;
1410
1411         /* Drags from the header view */
1412         if (source_widget != widget) {
1413
1414                 drag_and_drop_from_header_view (source_model,
1415                                                 dest_model,
1416                                                 dest_row,
1417                                                 helper);
1418         } else {
1419
1420
1421                 drag_and_drop_from_folder_view (source_model,
1422                                                 dest_model,
1423                                                 dest_row,
1424                                                 selection_data, 
1425                                                 helper);
1426         }
1427
1428         /* Frees */
1429         gtk_tree_path_free (source_row);
1430         gtk_tree_path_free (dest_row);
1431 }
1432
1433 /*
1434  * We define a "drag-drop" signal handler because we do not want to
1435  * use the default one, because the default one always calls
1436  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1437  * signal handler, because there we have all the information available
1438  * to know if the dnd was a success or not.
1439  */
1440 static gboolean
1441 drag_drop_cb (GtkWidget      *widget,
1442               GdkDragContext *context,
1443               gint            x,
1444               gint            y,
1445               guint           time,
1446               gpointer        user_data) 
1447 {
1448         gpointer target;
1449
1450         if (!context->targets)
1451                 return FALSE;
1452
1453         /* Check if we're dragging a folder row */
1454         target = gtk_drag_dest_find_target (widget, context, NULL);
1455
1456         /* Request the data from the source. */
1457         gtk_drag_get_data(widget, context, target, time);
1458
1459     return TRUE;
1460 }
1461
1462 /*
1463  * This function expands a node of a tree view if it's not expanded
1464  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1465  * does that, so that's why they're here.
1466  */
1467 static gint
1468 expand_row_timeout (gpointer data)
1469 {
1470         GtkTreeView *tree_view = data;
1471         GtkTreePath *dest_path = NULL;
1472         GtkTreeViewDropPosition pos;
1473         gboolean result = FALSE;
1474         
1475         GDK_THREADS_ENTER ();
1476         
1477         gtk_tree_view_get_drag_dest_row (tree_view,
1478                                          &dest_path,
1479                                          &pos);
1480         
1481         if (dest_path &&
1482             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1483              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1484                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1485                 gtk_tree_path_free (dest_path);
1486         }
1487         else {
1488                 if (dest_path)
1489                         gtk_tree_path_free (dest_path);
1490                 
1491                 result = TRUE;
1492         }
1493         
1494         GDK_THREADS_LEAVE ();
1495
1496         return result;
1497 }
1498
1499 /*
1500  * This function is called whenever the pointer is moved over a widget
1501  * while dragging some data. It installs a timeout that will expand a
1502  * node of the treeview if not expanded yet. This function also calls
1503  * gdk_drag_status in order to set the suggested action that will be
1504  * used by the "drag-data-received" signal handler to know if we
1505  * should do a move or just a copy of the data.
1506  */
1507 static gboolean
1508 on_drag_motion (GtkWidget      *widget,
1509                 GdkDragContext *context,
1510                 gint            x,
1511                 gint            y,
1512                 guint           time,
1513                 gpointer        user_data)  
1514 {
1515         GtkTreeViewDropPosition pos;
1516         GtkTreePath *dest_row;
1517         ModestFolderViewPrivate *priv;
1518         GdkDragAction suggested_action;
1519         gboolean valid_location = FALSE;
1520
1521         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1522
1523         if (priv->timer_expander != 0) {
1524                 g_source_remove (priv->timer_expander);
1525                 priv->timer_expander = 0;
1526         }
1527
1528         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1529                                            x, y,
1530                                            &dest_row,
1531                                            &pos);
1532
1533         /* Do not allow drops between folders */
1534         if (!dest_row ||
1535             pos == GTK_TREE_VIEW_DROP_BEFORE ||
1536             pos == GTK_TREE_VIEW_DROP_AFTER) {
1537                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1538                 gdk_drag_status(context, 0, time);
1539                 valid_location = FALSE;
1540                 goto out;
1541         } else {
1542                 valid_location = TRUE;
1543         }
1544
1545         /* Expand the selected row after 1/2 second */
1546         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1547                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1548                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1549         }
1550
1551         /* Select the desired action. By default we pick MOVE */
1552         suggested_action = GDK_ACTION_MOVE;
1553
1554         if (context->actions == GDK_ACTION_COPY)
1555             gdk_drag_status(context, GDK_ACTION_COPY, time);
1556         else if (context->actions == GDK_ACTION_MOVE)
1557             gdk_drag_status(context, GDK_ACTION_MOVE, time);
1558         else if (context->actions & suggested_action)
1559             gdk_drag_status(context, suggested_action, time);
1560         else
1561             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1562
1563  out:
1564         if (dest_row)
1565                 gtk_tree_path_free (dest_row);
1566         g_signal_stop_emission_by_name (widget, "drag-motion");
1567         return valid_location;
1568 }
1569
1570
1571 /* Folder view drag types */
1572 const GtkTargetEntry folder_view_drag_types[] =
1573 {
1574         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1575         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,    MODEST_HEADER_ROW }
1576 };
1577
1578 /*
1579  * This function sets the treeview as a source and a target for dnd
1580  * events. It also connects all the requirede signals.
1581  */
1582 static void
1583 setup_drag_and_drop (GtkTreeView *self)
1584 {
1585         /* Set up the folder view as a dnd destination. Set only the
1586            highlight flag, otherwise gtk will have a different
1587            behaviour */
1588         gtk_drag_dest_set (GTK_WIDGET (self),
1589                            GTK_DEST_DEFAULT_HIGHLIGHT,
1590                            folder_view_drag_types,
1591                            G_N_ELEMENTS (folder_view_drag_types),
1592                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
1593
1594         g_signal_connect (G_OBJECT (self),
1595                           "drag_data_received",
1596                           G_CALLBACK (on_drag_data_received),
1597                           NULL);
1598
1599
1600         /* Set up the treeview as a dnd source */
1601         gtk_drag_source_set (GTK_WIDGET (self),
1602                              GDK_BUTTON1_MASK,
1603                              folder_view_drag_types,
1604                              G_N_ELEMENTS (folder_view_drag_types),
1605                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1606
1607         g_signal_connect (G_OBJECT (self),
1608                           "drag_motion",
1609                           G_CALLBACK (on_drag_motion),
1610                           NULL);
1611         
1612         g_signal_connect (G_OBJECT (self),
1613                           "drag_data_get",
1614                           G_CALLBACK (on_drag_data_get),
1615                           NULL);
1616
1617         g_signal_connect (G_OBJECT (self),
1618                           "drag_drop",
1619                           G_CALLBACK (drag_drop_cb),
1620                           NULL);
1621 }
1622
1623 /*
1624  * This function manages the navigation through the folders using the
1625  * keyboard or the hardware keys in the device
1626  */
1627 static gboolean
1628 on_key_pressed (GtkWidget *self,
1629                 GdkEventKey *event,
1630                 gpointer user_data)
1631 {
1632         GtkTreeSelection *selection;
1633         GtkTreeIter iter;
1634         GtkTreeModel *model;
1635         gboolean retval = FALSE;
1636
1637         /* Up and Down are automatically managed by the treeview */
1638         if (event->keyval == GDK_Return) {
1639                 /* Expand/Collapse the selected row */
1640                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1641                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1642                         GtkTreePath *path;
1643
1644                         path = gtk_tree_model_get_path (model, &iter);
1645
1646                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1647                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1648                         else
1649                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1650                         gtk_tree_path_free (path);
1651                 }
1652                 /* No further processing */
1653                 retval = TRUE;
1654         }
1655
1656         return retval;
1657 }
1658
1659 /*
1660  * We listen to the changes in the local folder account name key,
1661  * because we want to show the right name in the view. The local
1662  * folder account name corresponds to the device name in the Maemo
1663  * version. We do this because we do not want to query gconf on each
1664  * tree view refresh. It's better to cache it and change whenever
1665  * necessary.
1666  */
1667 static void 
1668 on_configuration_key_changed (ModestConf* conf, 
1669                               const gchar *key, 
1670                               ModestConfEvent event, 
1671                               ModestFolderView *self)
1672 {
1673         ModestFolderViewPrivate *priv;
1674
1675         if (!key)
1676                 return;
1677
1678         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1679         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1680
1681         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1682                 g_free (priv->local_account_name);
1683
1684                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1685                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1686                 else
1687                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1688                                                                            MODEST_CONF_DEVICE_NAME, NULL);
1689
1690                 /* Force a redraw */
1691 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1692                 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
1693                                                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1694                 gtk_tree_view_column_queue_resize (tree_column);
1695 #endif
1696         }
1697 }
1698
1699 void 
1700 modest_folder_view_set_style (ModestFolderView *self,
1701                               ModestFolderViewStyle style)
1702 {
1703         ModestFolderViewPrivate *priv;
1704
1705         g_return_if_fail (self);
1706         
1707         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1708
1709         priv->style = style;
1710 }
1711
1712 void
1713 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1714                                                              const gchar *account_id)
1715 {
1716         ModestFolderViewPrivate *priv;
1717         GtkTreeModel *model;
1718
1719         g_return_if_fail (self);
1720         
1721         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1722
1723         /* This will be used by the filter_row callback,
1724          * to decided which rows to show: */
1725         if (priv->visible_account_id) {
1726                 g_free (priv->visible_account_id);
1727                 priv->visible_account_id = NULL;
1728         }
1729         if (account_id)
1730                 priv->visible_account_id = g_strdup (account_id);
1731
1732         /* Refilter */
1733         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1734         if (GTK_IS_TREE_MODEL_FILTER (model))
1735                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1736
1737         /* Save settings to gconf */
1738         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1739                                    MODEST_CONF_FOLDER_VIEW_KEY);
1740 }
1741
1742 const gchar *
1743 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1744 {
1745         ModestFolderViewPrivate *priv;
1746
1747         g_return_val_if_fail (self, NULL);
1748         
1749         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1750
1751         return (const gchar *) priv->visible_account_id;
1752 }
1753
1754 static gboolean
1755 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1756 {
1757         do {
1758                 GtkTreeIter child;
1759                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1760                 gchar *name = NULL;
1761
1762                 gtk_tree_model_get (model, iter, 
1763                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1764                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
1765                                     &type, -1);
1766
1767                 /*
1768                 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n", 
1769                         __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1770                 */
1771                         
1772                 gboolean result = FALSE;
1773                 if (type == TNY_FOLDER_TYPE_INBOX) {
1774                         result = TRUE;
1775                 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1776                         /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1777                          * when getting folders from the cache, before connectin, so we do 
1778                          * an extra check. We could fix this in tinymail, but it's easier 
1779                          * to do here.
1780                          */
1781                          if (strcmp (name, "Inbox") == 0)
1782                                 result = TRUE;
1783                 }
1784                 
1785                 g_free (name);
1786                 
1787                 if (result) {
1788                         *inbox_iter = *iter;
1789                         return TRUE;
1790                 }
1791
1792                 if (gtk_tree_model_iter_children (model, &child, iter)) {
1793                         if (find_inbox_iter (model, &child, inbox_iter))
1794                                 return TRUE;
1795                 }
1796
1797         } while (gtk_tree_model_iter_next (model, iter));
1798
1799         return FALSE;
1800 }
1801
1802 void 
1803 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1804 {
1805         GtkTreeModel *model;
1806         GtkTreeIter iter, inbox_iter;
1807         GtkTreeSelection *sel;
1808
1809 /*      /\* Do not set it if the folder view was not painted *\/ */
1810 /*      if (!GTK_WIDGET_MAPPED (self)) */
1811 /*              return; */
1812
1813         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1814         if (!model)
1815                 return;
1816
1817         expand_root_items (self);
1818         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1819
1820         gtk_tree_model_get_iter_first (model, &iter);
1821         if (find_inbox_iter (model, &iter, &inbox_iter)) {
1822                 gtk_tree_selection_select_iter (sel, &inbox_iter);
1823         }
1824         else {
1825                 gtk_tree_model_get_iter_first (model, &iter);
1826                 gtk_tree_selection_select_iter (sel, &iter);
1827         }
1828 }
1829
1830 void 
1831 modest_folder_view_copy_selection (ModestFolderView *folder_view)
1832 {
1833         /* Copy selection */
1834         _clipboard_set_selected_data (folder_view, FALSE);
1835 }
1836
1837 void 
1838 modest_folder_view_cut_selection (ModestFolderView *folder_view)
1839 {
1840         ModestFolderViewPrivate *priv = NULL;
1841         GtkTreeModel *model = NULL;
1842         const gchar **hidding = NULL;
1843         guint i, n_selected;
1844
1845         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1846         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1847
1848         /* Copy selection */
1849         _clipboard_set_selected_data (folder_view, TRUE);
1850
1851         /* Get hidding ids */
1852         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1853         
1854         /* Clear hidding array created by previous cut operation */
1855         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
1856
1857         /* Copy hidding array */
1858         priv->n_selected = n_selected;
1859         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1860         for (i=0; i < n_selected; i++) 
1861                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1862
1863         /* Hide cut folders */
1864         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
1865         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1866 }
1867
1868 static void
1869 _clipboard_set_selected_data (ModestFolderView *folder_view,
1870                               gboolean delete)
1871 {
1872         ModestFolderViewPrivate *priv = NULL;
1873         TnyFolderStore *folder = NULL;
1874
1875         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1876         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1877                 
1878         /* Set selected data on clipboard   */
1879         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1880         folder = modest_folder_view_get_selected (folder_view);
1881         modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
1882
1883         /* Free */
1884         g_object_unref (folder);
1885 }
1886
1887 static void
1888 _clear_hidding_filter (ModestFolderView *folder_view) 
1889 {
1890         ModestFolderViewPrivate *priv;
1891         guint i;
1892         
1893         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
1894         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
1895
1896         if (priv->hidding_ids != NULL) {
1897                 for (i=0; i < priv->n_selected; i++) 
1898                         g_free (priv->hidding_ids[i]);
1899                 g_free(priv->hidding_ids);
1900         }       
1901 }