* src/maemo/modest-folder-view.c:
[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 = 400;
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                 g_object_unref (priv->cur_folder_store);
577                 priv->cur_folder_store = NULL;
578         }
579
580         /* Clear hidding array created by cut operation */
581         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
582
583         G_OBJECT_CLASS(parent_class)->finalize (obj);
584 }
585
586
587 static void
588 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
589 {
590         ModestFolderViewPrivate *priv;
591         TnyDevice *device;
592
593         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
594         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
595
596         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
597         device = tny_account_store_get_device (account_store);
598
599         if (G_UNLIKELY (priv->account_store)) {
600
601                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
602                                                    priv->account_update_signal))
603                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
604                                                      priv->account_update_signal);
605                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
606                                                    priv->accounts_reloaded_signal))
607                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
608                                                      priv->accounts_reloaded_signal);
609                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
610                                                    priv->account_removed_signal))
611                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
612                                                      priv->account_removed_signal);
613
614                 g_object_unref (G_OBJECT (priv->account_store));
615         }
616
617         priv->account_store = g_object_ref (G_OBJECT (account_store));
618
619         priv->account_update_signal = 
620                 g_signal_connect (G_OBJECT(account_store), "account_update",
621                                   G_CALLBACK (on_account_update), self);
622
623         priv->account_removed_signal = 
624                 g_signal_connect (G_OBJECT(account_store), "account_removed",
625                                   G_CALLBACK (on_account_removed), self);
626
627         priv->accounts_reloaded_signal = 
628                 g_signal_connect (G_OBJECT(account_store), "accounts_reloaded",
629                                   G_CALLBACK (on_accounts_reloaded), self);
630
631         g_signal_connect (G_OBJECT(account_store), "connecting_finished",
632                                 G_CALLBACK (on_accounts_reloaded), self);
633
634         on_accounts_reloaded (account_store, (gpointer ) self);
635         
636         g_object_unref (G_OBJECT (device));
637 }
638
639 static void
640 on_account_removed (TnyAccountStore *account_store, 
641                     TnyAccount *account,
642                     gpointer user_data)
643 {
644         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
645         ModestFolderViewPrivate *priv;
646
647         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
648
649         /* If the removed account is the currently viewed one then
650            clear the configuration value. The new visible account will be the default account */
651         if (!strcmp (priv->visible_account_id, tny_account_get_id (account))) {
652                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
653         }
654 }
655
656 static void
657 on_account_update (TnyAccountStore *account_store, 
658                    const gchar *account,
659                    gpointer user_data)
660 {
661         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
662         ModestFolderViewPrivate *priv;
663
664         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
665         if (!priv->visible_account_id)
666                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
667                                               MODEST_CONF_FOLDER_VIEW_KEY);
668
669         if (!modest_folder_view_update_model (self, account_store))
670                 g_printerr ("modest: failed to update model for changes in '%s'",
671                             account);
672 }
673
674 static void 
675 on_accounts_reloaded   (TnyAccountStore *account_store, 
676                         gpointer user_data)
677 {
678         modest_folder_view_update_model (MODEST_FOLDER_VIEW (user_data), account_store);
679 }
680
681 void
682 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
683 {
684         GtkTreeViewColumn *col;
685         
686         g_return_if_fail (self);
687
688         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
689         if (!col) {
690                 g_printerr ("modest: failed get column for title\n");
691                 return;
692         }
693
694         gtk_tree_view_column_set_title (col, title);
695         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
696                                            title != NULL);
697 }
698
699 static gboolean
700 modest_folder_view_on_map (ModestFolderView *self, 
701                            GdkEventExpose *event,
702                            gpointer data)
703 {
704         ModestFolderViewPrivate *priv;
705
706         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
707
708         /* This won't happen often */
709         if (G_UNLIKELY (priv->reselect)) {
710                 /* Select the first inbox or the local account if not found */
711                 modest_folder_view_select_first_inbox_or_local (self);
712                 priv->reselect = FALSE;
713         }
714         return FALSE;
715 }
716
717 GtkWidget*
718 modest_folder_view_new (TnyFolderStoreQuery *query)
719 {
720         GObject *self;
721         ModestFolderViewPrivate *priv;
722         GtkTreeSelection *sel;
723         
724         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
725         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
726
727         if (query)
728                 priv->query = g_object_ref (query);
729         
730         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
731         priv->changed_signal = g_signal_connect (sel, "changed",
732                                                  G_CALLBACK (on_selection_changed), self);
733
734         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
735
736         return GTK_WIDGET(self);
737 }
738
739 /* this feels dirty; any other way to expand all the root items? */
740 static void
741 expand_root_items (ModestFolderView *self)
742 {
743         GtkTreePath *path;
744         path = gtk_tree_path_new_first ();
745
746         /* all folders should have child items, so.. */
747         while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
748                 gtk_tree_path_next (path);
749         
750         gtk_tree_path_free (path);
751 }
752
753 /*
754  * We use this function to implement the
755  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
756  * account in this case, and the local folders.
757  */
758 static gboolean 
759 filter_row (GtkTreeModel *model,
760             GtkTreeIter *iter,
761             gpointer data)
762 {
763         ModestFolderViewPrivate *priv;
764         gboolean retval = TRUE;
765         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
766         GObject *instance = NULL;
767         const gchar *id = NULL;
768         guint i;
769         gboolean found = FALSE;
770         gboolean cleared = FALSE;
771         
772         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
773         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
774
775         gtk_tree_model_get (model, iter,
776                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
777                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
778                             -1);
779
780         /* Do not show if there is no instance, this could indeed
781            happen when the model is being modified while it's being
782            drawn. This could occur for example when moving folders
783            using drag&drop */
784         if (!instance)
785                 return FALSE;
786
787         if (type == TNY_FOLDER_TYPE_ROOT) {
788                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an account instead of a folder. */
789                 if (TNY_IS_ACCOUNT (instance)) {
790                         TnyAccount *acc = TNY_ACCOUNT (instance);
791                         const gchar *account_id = tny_account_get_id (acc);
792         
793                         /* If it isn't a special folder, 
794                          * don't show it unless it is the visible account: */
795                         if (!modest_tny_account_is_virtual_local_folders (acc) &&
796                                 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) { 
797                                 /* Show only the visible account id */
798                                 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
799                                         retval = FALSE;
800                         }
801                         
802                         /* Never show these to the user. They are merged into one folder 
803                          * in the local-folders account instead: */
804                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
805                                 retval = FALSE;
806                 }
807         }
808         
809         /* The virtual local-folders folder store is also shown by default. */
810
811         /* Check hiding (if necessary) */
812         cleared = modest_email_clipboard_cleared (priv->clipboard);            
813         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
814                 id = tny_folder_get_id (TNY_FOLDER(instance));
815                 if (priv->hidding_ids != NULL)
816                         for (i=0; i < priv->n_selected && !found; i++)
817                                 if (priv->hidding_ids[i] != NULL && id != NULL)
818                                         found = (!strcmp (priv->hidding_ids[i], id));
819                 
820                 retval = !found;
821         }
822
823         /* Free */
824         g_object_unref (instance);
825
826         return retval;
827 }
828
829 gboolean
830 modest_folder_view_update_model (ModestFolderView *self,
831                                  TnyAccountStore *account_store)
832 {
833         ModestFolderViewPrivate *priv;
834         GtkTreeModel *model /* , *old_model */;
835         /* TnyAccount *local_account; */
836         TnyList *model_as_list;
837
838         g_return_val_if_fail (account_store, FALSE);
839
840         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
841         
842         /* Notify that there is no folder selected */
843         g_signal_emit (G_OBJECT(self), 
844                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
845                        NULL, TRUE);
846
847         /* FIXME: the local accounts are not shown when the query
848            selects only the subscribed folders. */
849 /*      model        = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
850         model        = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
851         
852         /* Deal with the model via its TnyList Interface,
853          * filling the TnyList via a get_accounts() call: */
854         model_as_list = TNY_LIST(model);
855
856         /* Get the accounts: */
857         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
858                                         model_as_list,
859                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
860         g_object_unref (model_as_list);
861         model_as_list = NULL;   
862                                                      
863         GtkTreeModel *filter_model = NULL, *sortable = NULL;
864
865         sortable = gtk_tree_model_sort_new_with_model (model);
866         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
867                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
868                                               GTK_SORT_ASCENDING);
869         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
870                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
871                                          cmp_rows, NULL, NULL);
872
873         /* Create filter model */
874         filter_model = gtk_tree_model_filter_new (sortable, NULL);
875         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
876                                                 filter_row,
877                                                 self,
878                                                 NULL);
879 /*      if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) { */
880 /*              filter_model = gtk_tree_model_filter_new (sortable, NULL); */
881 /*              gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model), */
882 /*                                                      filter_row, */
883 /*                                                      self, */
884 /*                                                      NULL); */
885 /*      } */
886
887         /* Set new model */
888         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
889 /*      gtk_tree_view_set_model (GTK_TREE_VIEW(self),  */
890 /*                               (filter_model) ? filter_model : sortable); */
891
892         g_object_unref (model);
893         g_object_unref (filter_model);
894 /*      if (filter_model) */
895 /*              g_object_unref (filter_model); */
896                         
897         g_object_unref (sortable);
898
899         /* Force a reselection of the INBOX next time the widget is shown */
900         priv->reselect = TRUE;
901                         
902         return TRUE;
903 }
904
905
906 static void
907 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
908 {
909         GtkTreeModel            *model;
910         TnyFolderStore          *folder = NULL;
911         GtkTreeIter             iter;
912         ModestFolderView        *tree_view;
913         ModestFolderViewPrivate *priv;
914
915         g_return_if_fail (sel);
916         g_return_if_fail (user_data);
917         
918         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
919
920         if(!gtk_tree_selection_get_selected (sel, &model, &iter))
921                 return;
922
923         /* Notify the display name observers */
924         g_signal_emit (G_OBJECT(user_data),
925                        signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
926                        NULL);
927
928         tree_view = MODEST_FOLDER_VIEW (user_data);
929         gtk_tree_model_get (model, &iter,
930                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
931                             -1);
932
933         /* If the folder is the same do not notify */
934         if (priv->cur_folder_store == folder && folder) {
935                 g_object_unref (folder);
936                 return;
937         }
938         
939         /* Current folder was unselected */
940         if (priv->cur_folder_store) {
941                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
942                                priv->cur_folder_store, FALSE);
943                 g_object_unref (priv->cur_folder_store);
944                 priv->cur_folder_store = NULL;
945         }
946
947         /* New current references */
948         priv->cur_folder_store = folder;
949
950         /* New folder has been selected */
951         g_signal_emit (G_OBJECT(tree_view),
952                        signals[FOLDER_SELECTION_CHANGED_SIGNAL],
953                        0, priv->cur_folder_store, TRUE);
954 }
955
956 TnyFolderStore *
957 modest_folder_view_get_selected (ModestFolderView *self)
958 {
959         ModestFolderViewPrivate *priv;
960
961         g_return_val_if_fail (self, NULL);
962         
963         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
964         if (priv->cur_folder_store)
965                 g_object_ref (priv->cur_folder_store);
966
967         return priv->cur_folder_store;
968 }
969
970 static gint
971 get_cmp_rows_type_pos (GObject *folder)
972 {
973         /* Remote accounts -> Local account -> MMC account .*/
974         /* 0, 1, 2 */
975         
976         if (TNY_IS_ACCOUNT (folder) && 
977                 modest_tny_account_is_virtual_local_folders (
978                         TNY_ACCOUNT (folder))) {
979                 return 1;
980         } else if (TNY_IS_ACCOUNT (folder)) {
981                 TnyAccount *account = TNY_ACCOUNT (folder);
982                 const gchar *account_id = tny_account_get_id (account);
983                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
984                         return 2;
985                 else
986                         return 0;
987         }
988         else {
989                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
990                 return -1; /* Should never happen */
991         }
992 }
993
994 static gint
995 get_cmp_subfolder_type_pos (TnyFolderType t)
996 {
997         /* Outbox, Drafts, Sent, User */
998         /* 0, 1, 2, 3 */
999
1000         switch (t) {
1001         case TNY_FOLDER_TYPE_OUTBOX:
1002                 return 0;
1003                 break;
1004         case TNY_FOLDER_TYPE_DRAFTS:
1005                 return 1;
1006                 break;
1007         case TNY_FOLDER_TYPE_SENT:
1008                 return 2;
1009                 break;
1010         default:
1011                 return 3;
1012         }
1013 }
1014
1015 /*
1016  * This function orders the mail accounts according to these rules:
1017  * 1st - remote accounts
1018  * 2nd - local account
1019  * 3rd - MMC account
1020  */
1021 static gint
1022 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1023           gpointer user_data)
1024 {
1025         gint cmp;
1026         gchar *name1 = NULL;
1027         gchar *name2 = NULL;
1028         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1029         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1030         GObject *folder1 = NULL;
1031         GObject *folder2 = NULL;
1032
1033         gtk_tree_model_get (tree_model, iter1,
1034                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1035                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1036                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1037                             -1);
1038         gtk_tree_model_get (tree_model, iter2,
1039                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1040                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1041                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1042                             -1);
1043
1044         if (type == TNY_FOLDER_TYPE_ROOT) {
1045                 /* Compare the types, so that 
1046                  * Remote accounts -> Local account -> MMC account .*/
1047                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1048                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1049                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1050                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1051                 if (pos1 <  pos2)
1052                         cmp = -1;
1053                 else if (pos1 > pos2)
1054                         cmp = 1;
1055                 else {
1056                         /* Compare items of the same type: */
1057                         
1058                         TnyAccount *account1 = NULL;
1059                         if (TNY_IS_ACCOUNT (folder1))
1060                                 account1 = TNY_ACCOUNT (folder1);
1061                                 
1062                         TnyAccount *account2 = NULL;
1063                         if (TNY_IS_ACCOUNT (folder2))
1064                                 account2 = TNY_ACCOUNT (folder2);
1065                                 
1066                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1067                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1068         
1069                         if (!account_id && !account_id2)
1070                                 return 0;
1071                         else if (!account_id)
1072                                 return -1;
1073                         else if (!account_id2)
1074                                 return +1;
1075                         else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1076                                 cmp = +1;
1077                         else
1078                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1079                 }
1080         } else {
1081                 GtkTreeIter parent;
1082                 gboolean has_parent;
1083                 gint cmp1 = 0, cmp2 = 0;
1084                 /* get the parent to know if it's a local folder */
1085                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1086                 if (has_parent) {
1087                         GObject *parent_folder;
1088                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1089                         gtk_tree_model_get (tree_model, &parent, 
1090                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1091                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1092                                             -1);
1093                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1094                             TNY_IS_ACCOUNT (parent_folder) &&
1095                             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1096                                 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_folder_type (TNY_FOLDER (folder1)));
1097                                 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_folder_type (TNY_FOLDER (folder2)));
1098                         }
1099                         g_object_unref (parent_folder);
1100                 }
1101                 if (cmp1 == cmp2)
1102                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1103                 else 
1104                         cmp = (cmp1 - cmp2);
1105         }
1106         
1107         if (folder1)
1108                 g_object_unref(G_OBJECT(folder1));
1109         if (folder2)
1110                 g_object_unref(G_OBJECT(folder2));
1111
1112         g_free (name1);
1113         g_free (name2);
1114
1115         return cmp;     
1116 }
1117
1118 /*****************************************************************************/
1119 /*                        DRAG and DROP stuff                                */
1120 /*****************************************************************************/
1121
1122 /*
1123  * This function fills the #GtkSelectionData with the row and the
1124  * model that has been dragged. It's called when this widget is a
1125  * source for dnd after the event drop happened
1126  */
1127 static void
1128 on_drag_data_get (GtkWidget *widget, 
1129                   GdkDragContext *context, 
1130                   GtkSelectionData *selection_data, 
1131                   guint info, 
1132                   guint time, 
1133                   gpointer data)
1134 {
1135         GtkTreeSelection *selection;
1136         GtkTreeModel *model;
1137         GtkTreeIter iter;
1138         GtkTreePath *source_row;
1139
1140         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1141         gtk_tree_selection_get_selected (selection, &model, &iter);
1142         source_row = gtk_tree_model_get_path (model, &iter);
1143
1144         gtk_tree_set_row_drag_data (selection_data,
1145                                     model,
1146                                     source_row);
1147
1148         gtk_tree_path_free (source_row);
1149 }
1150
1151 typedef struct _DndHelper {
1152         gboolean delete_source;
1153         GtkTreePath *source_row;
1154         GdkDragContext *context;
1155         guint time;
1156 } DndHelper;
1157
1158
1159 /*
1160  * This function is the callback of the
1161  * modest_mail_operation_xfer_msgs () and
1162  * modest_mail_operation_xfer_folder() calls. We check here if the
1163  * message/folder was correctly asynchronously transferred. The reason
1164  * to use the same callback is that the code is the same, it only has
1165  * to check that the operation went fine and then finalize the drag
1166  * and drop action
1167  */
1168 static void
1169 on_progress_changed (ModestMailOperation *mail_op, 
1170                      ModestMailOperationState *state,
1171                      gpointer user_data)
1172 {
1173         gboolean success;
1174         DndHelper *helper;
1175
1176         helper = (DndHelper *) user_data;
1177
1178         if (!state->finished)
1179                 return;
1180
1181         if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1182                 success = TRUE;
1183         } else {
1184                 success = FALSE;
1185         }
1186
1187         /* Notify the drag source. Never call delete, the monitor will
1188            do the job if needed */
1189         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1190
1191         /* Free the helper */
1192         gtk_tree_path_free (helper->source_row);        
1193         g_slice_free (DndHelper, helper);
1194 }
1195
1196 /*
1197  * This function is used by drag_data_received_cb to manage drag and
1198  * drop of a header, i.e, and drag from the header view to the folder
1199  * view.
1200  */
1201 static void
1202 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1203                                 GtkTreeModel *dest_model,
1204                                 GtkTreePath  *dest_row,
1205                                 DndHelper    *helper)
1206 {
1207         TnyList *headers = NULL;
1208         TnyHeader *header = NULL;
1209         TnyFolder *folder = NULL;
1210         ModestMailOperation *mail_op = NULL;
1211         GtkTreeIter source_iter, dest_iter;
1212
1213         g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1214         g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1215         g_return_if_fail (dest_row);
1216         g_return_if_fail (helper);
1217         
1218         /* Get header */
1219         gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1220         gtk_tree_model_get (source_model, &source_iter, 
1221                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1222                             &header, -1);
1223         if (!TNY_IS_HEADER(header)) {
1224                 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1225                 goto cleanup;
1226         }
1227         
1228         /* Get Folder */
1229         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1230         gtk_tree_model_get (dest_model, &dest_iter, 
1231                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1232                             &folder, -1);
1233
1234         if (!TNY_IS_FOLDER(folder)) {
1235                 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1236                 goto cleanup;
1237         }
1238
1239         /* Transfer message */
1240         mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1241         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1242                                          mail_op);
1243         g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1244                           G_CALLBACK (on_progress_changed), helper);
1245
1246         headers = tny_simple_list_new ();
1247         tny_list_append (headers, G_OBJECT (header));
1248         modest_mail_operation_xfer_msgs (mail_op, 
1249                                          headers, 
1250                                          folder, 
1251                                          helper->delete_source, 
1252                                          NULL, NULL);
1253         
1254         /* Frees */
1255 cleanup:
1256         if (G_IS_OBJECT(mail_op))
1257                 g_object_unref (G_OBJECT (mail_op));
1258         if (G_IS_OBJECT(header))
1259                 g_object_unref (G_OBJECT (header));
1260         if (G_IS_OBJECT(folder))
1261                 g_object_unref (G_OBJECT (folder));
1262         if (G_IS_OBJECT(headers))
1263                 g_object_unref (headers);
1264 }
1265
1266 /*
1267  * This function is used by drag_data_received_cb to manage drag and
1268  * drop of a folder, i.e, and drag from the folder view to the same
1269  * folder view.
1270  */
1271 static void
1272 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
1273                                 GtkTreeModel     *dest_model,
1274                                 GtkTreePath      *dest_row,
1275                                 GtkSelectionData *selection_data,
1276                                 DndHelper        *helper)
1277 {
1278         ModestMailOperation *mail_op;
1279         GtkTreeIter parent_iter, iter;
1280         TnyFolderStore *parent_folder;
1281         TnyFolder *folder;
1282
1283         /* Check if the drag is possible */
1284 /*      if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1285 /*          !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1286 /*                                                 dest_row, */
1287 /*                                                 selection_data)) { */
1288         if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1289
1290                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1291                 gtk_tree_path_free (helper->source_row);        
1292                 g_slice_free (DndHelper, helper);
1293                 return;
1294         }
1295
1296         /* Get data */
1297         gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1298         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1299         gtk_tree_model_get (source_model, &parent_iter, 
1300                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1301                             &parent_folder, -1);
1302         gtk_tree_model_get (source_model, &iter,
1303                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1304                             &folder, -1);
1305
1306         /* Do the mail operation */
1307         mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1308                                                                  NULL,
1309                                                                  modest_ui_actions_move_folder_error_handler,
1310                                                                  NULL);
1311         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), 
1312                                          mail_op);
1313         g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1314                           G_CALLBACK (on_progress_changed), helper);
1315
1316         modest_mail_operation_xfer_folder (mail_op, 
1317                                            folder, 
1318                                            parent_folder,
1319                                            helper->delete_source);
1320         
1321         /* Frees */
1322         g_object_unref (G_OBJECT (parent_folder));
1323         g_object_unref (G_OBJECT (folder));
1324         g_object_unref (G_OBJECT (mail_op));
1325 }
1326
1327 /*
1328  * This function receives the data set by the "drag-data-get" signal
1329  * handler. This information comes within the #GtkSelectionData. This
1330  * function will manage both the drags of folders of the treeview and
1331  * drags of headers of the header view widget.
1332  */
1333 static void 
1334 on_drag_data_received (GtkWidget *widget, 
1335                        GdkDragContext *context, 
1336                        gint x, 
1337                        gint y, 
1338                        GtkSelectionData *selection_data, 
1339                        guint target_type, 
1340                        guint time, 
1341                        gpointer data)
1342 {
1343         GtkWidget *source_widget;
1344         GtkTreeModel *dest_model, *source_model;
1345         GtkTreePath *source_row, *dest_row;
1346         GtkTreeViewDropPosition pos;
1347         gboolean success = FALSE, delete_source = FALSE;
1348         DndHelper *helper = NULL; 
1349
1350         /* Do not allow further process */
1351         g_signal_stop_emission_by_name (widget, "drag-data-received");
1352         source_widget = gtk_drag_get_source_widget (context);
1353
1354         /* Get the action */
1355         if (context->action == GDK_ACTION_MOVE) {
1356                 delete_source = TRUE;
1357
1358                 /* Notify that there is no folder selected. We need to
1359                    do this in order to update the headers view (and
1360                    its monitors, because when moving, the old folder
1361                    won't longer exist. We can not wait for the end of
1362                    the operation, because the operation won't start if
1363                    the folder is in use */
1364                 if (source_widget == widget)
1365                         g_signal_emit (G_OBJECT (widget), 
1366                                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, TRUE);
1367         }
1368
1369         /* Check if the get_data failed */
1370         if (selection_data == NULL || selection_data->length < 0)
1371                 gtk_drag_finish (context, success, FALSE, time);
1372
1373         /* Get the models */
1374         gtk_tree_get_row_drag_data (selection_data,
1375                                     &source_model,
1376                                     &source_row);
1377
1378         /* Select the destination model */
1379         if (source_widget == widget) {
1380                 dest_model = source_model;
1381         } else {
1382                 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1383         }
1384
1385         /* Get the path to the destination row. Can not call
1386            gtk_tree_view_get_drag_dest_row() because the source row
1387            is not selected anymore */
1388         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1389                                            &dest_row, &pos);
1390
1391         /* Only allow drops IN other rows */
1392         if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1393                 gtk_drag_finish (context, success, FALSE, time);
1394
1395         /* Create the helper */
1396         helper = g_slice_new0 (DndHelper);
1397         helper->delete_source = delete_source;
1398         helper->source_row = gtk_tree_path_copy (source_row);
1399         helper->context = context;
1400         helper->time = time;
1401
1402         /* Drags from the header view */
1403         if (source_widget != widget) {
1404
1405                 drag_and_drop_from_header_view (source_model,
1406                                                 dest_model,
1407                                                 dest_row,
1408                                                 helper);
1409         } else {
1410
1411
1412                 drag_and_drop_from_folder_view (source_model,
1413                                                 dest_model,
1414                                                 dest_row,
1415                                                 selection_data, 
1416                                                 helper);
1417         }
1418
1419         /* Frees */
1420         gtk_tree_path_free (source_row);
1421         gtk_tree_path_free (dest_row);
1422 }
1423
1424 /*
1425  * We define a "drag-drop" signal handler because we do not want to
1426  * use the default one, because the default one always calls
1427  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1428  * signal handler, because there we have all the information available
1429  * to know if the dnd was a success or not.
1430  */
1431 static gboolean
1432 drag_drop_cb (GtkWidget      *widget,
1433               GdkDragContext *context,
1434               gint            x,
1435               gint            y,
1436               guint           time,
1437               gpointer        user_data) 
1438 {
1439         gpointer target;
1440
1441         if (!context->targets)
1442                 return FALSE;
1443
1444         /* Check if we're dragging a folder row */
1445         target = gtk_drag_dest_find_target (widget, context, NULL);
1446
1447         /* Request the data from the source. */
1448         gtk_drag_get_data(widget, context, target, time);
1449
1450     return TRUE;
1451 }
1452
1453 /*
1454  * This function expands a node of a tree view if it's not expanded
1455  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1456  * does that, so that's why they're here.
1457  */
1458 static gint
1459 expand_row_timeout (gpointer data)
1460 {
1461         GtkTreeView *tree_view = data;
1462         GtkTreePath *dest_path = NULL;
1463         GtkTreeViewDropPosition pos;
1464         gboolean result = FALSE;
1465         
1466         GDK_THREADS_ENTER ();
1467         
1468         gtk_tree_view_get_drag_dest_row (tree_view,
1469                                          &dest_path,
1470                                          &pos);
1471         
1472         if (dest_path &&
1473             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1474              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1475                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1476                 gtk_tree_path_free (dest_path);
1477         }
1478         else {
1479                 if (dest_path)
1480                         gtk_tree_path_free (dest_path);
1481                 
1482                 result = TRUE;
1483         }
1484         
1485         GDK_THREADS_LEAVE ();
1486
1487         return result;
1488 }
1489
1490 /*
1491  * This function is called whenever the pointer is moved over a widget
1492  * while dragging some data. It installs a timeout that will expand a
1493  * node of the treeview if not expanded yet. This function also calls
1494  * gdk_drag_status in order to set the suggested action that will be
1495  * used by the "drag-data-received" signal handler to know if we
1496  * should do a move or just a copy of the data.
1497  */
1498 static gboolean
1499 on_drag_motion (GtkWidget      *widget,
1500                 GdkDragContext *context,
1501                 gint            x,
1502                 gint            y,
1503                 guint           time,
1504                 gpointer        user_data)  
1505 {
1506         GtkTreeViewDropPosition pos;
1507         GtkTreePath *dest_row;
1508         ModestFolderViewPrivate *priv;
1509         GdkDragAction suggested_action;
1510         gboolean valid_location = FALSE;
1511
1512         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1513
1514         if (priv->timer_expander != 0) {
1515                 g_source_remove (priv->timer_expander);
1516                 priv->timer_expander = 0;
1517         }
1518
1519         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1520                                            x, y,
1521                                            &dest_row,
1522                                            &pos);
1523
1524         /* Do not allow drops between folders */
1525         if (!dest_row ||
1526             pos == GTK_TREE_VIEW_DROP_BEFORE ||
1527             pos == GTK_TREE_VIEW_DROP_AFTER) {
1528                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1529                 gdk_drag_status(context, 0, time);
1530                 valid_location = FALSE;
1531                 goto out;
1532         } else {
1533                 valid_location = TRUE;
1534         }
1535
1536         /* Expand the selected row after 1/2 second */
1537         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1538                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1539                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1540         }
1541
1542         /* Select the desired action. By default we pick MOVE */
1543         suggested_action = GDK_ACTION_MOVE;
1544
1545         if (context->actions == GDK_ACTION_COPY)
1546             gdk_drag_status(context, GDK_ACTION_COPY, time);
1547         else if (context->actions == GDK_ACTION_MOVE)
1548             gdk_drag_status(context, GDK_ACTION_MOVE, time);
1549         else if (context->actions & suggested_action)
1550             gdk_drag_status(context, suggested_action, time);
1551         else
1552             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1553
1554  out:
1555         if (dest_row)
1556                 gtk_tree_path_free (dest_row);
1557         g_signal_stop_emission_by_name (widget, "drag-motion");
1558         return valid_location;
1559 }
1560
1561
1562 /* Folder view drag types */
1563 const GtkTargetEntry folder_view_drag_types[] =
1564 {
1565         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1566         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,    MODEST_HEADER_ROW }
1567 };
1568
1569 /*
1570  * This function sets the treeview as a source and a target for dnd
1571  * events. It also connects all the requirede signals.
1572  */
1573 static void
1574 setup_drag_and_drop (GtkTreeView *self)
1575 {
1576         /* Set up the folder view as a dnd destination. Set only the
1577            highlight flag, otherwise gtk will have a different
1578            behaviour */
1579         gtk_drag_dest_set (GTK_WIDGET (self),
1580                            GTK_DEST_DEFAULT_HIGHLIGHT,
1581                            folder_view_drag_types,
1582                            G_N_ELEMENTS (folder_view_drag_types),
1583                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
1584
1585         g_signal_connect (G_OBJECT (self),
1586                           "drag_data_received",
1587                           G_CALLBACK (on_drag_data_received),
1588                           NULL);
1589
1590
1591         /* Set up the treeview as a dnd source */
1592         gtk_drag_source_set (GTK_WIDGET (self),
1593                              GDK_BUTTON1_MASK,
1594                              folder_view_drag_types,
1595                              G_N_ELEMENTS (folder_view_drag_types),
1596                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1597
1598         g_signal_connect (G_OBJECT (self),
1599                           "drag_motion",
1600                           G_CALLBACK (on_drag_motion),
1601                           NULL);
1602         
1603         g_signal_connect (G_OBJECT (self),
1604                           "drag_data_get",
1605                           G_CALLBACK (on_drag_data_get),
1606                           NULL);
1607
1608         g_signal_connect (G_OBJECT (self),
1609                           "drag_drop",
1610                           G_CALLBACK (drag_drop_cb),
1611                           NULL);
1612 }
1613
1614 /*
1615  * This function manages the navigation through the folders using the
1616  * keyboard or the hardware keys in the device
1617  */
1618 static gboolean
1619 on_key_pressed (GtkWidget *self,
1620                 GdkEventKey *event,
1621                 gpointer user_data)
1622 {
1623         GtkTreeSelection *selection;
1624         GtkTreeIter iter;
1625         GtkTreeModel *model;
1626         gboolean retval = FALSE;
1627
1628         /* Up and Down are automatically managed by the treeview */
1629         if (event->keyval == GDK_Return) {
1630                 /* Expand/Collapse the selected row */
1631                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1632                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1633                         GtkTreePath *path;
1634
1635                         path = gtk_tree_model_get_path (model, &iter);
1636
1637                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1638                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1639                         else
1640                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1641                         gtk_tree_path_free (path);
1642                 }
1643                 /* No further processing */
1644                 retval = TRUE;
1645         }
1646
1647         return retval;
1648 }
1649
1650 /*
1651  * We listen to the changes in the local folder account name key,
1652  * because we want to show the right name in the view. The local
1653  * folder account name corresponds to the device name in the Maemo
1654  * version. We do this because we do not want to query gconf on each
1655  * tree view refresh. It's better to cache it and change whenever
1656  * necessary.
1657  */
1658 static void 
1659 on_configuration_key_changed (ModestConf* conf, 
1660                               const gchar *key, 
1661                               ModestConfEvent event, 
1662                               ModestFolderView *self)
1663 {
1664         ModestFolderViewPrivate *priv;
1665
1666         if (!key)
1667                 return;
1668
1669         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1670         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1671
1672         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1673                 g_free (priv->local_account_name);
1674
1675                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1676                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1677                 else
1678                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1679                                                                            MODEST_CONF_DEVICE_NAME, NULL);
1680
1681                 /* Force a redraw */
1682 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1683                 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
1684                                                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1685                 gtk_tree_view_column_queue_resize (tree_column);
1686 #endif
1687         }
1688 }
1689
1690 void 
1691 modest_folder_view_set_style (ModestFolderView *self,
1692                               ModestFolderViewStyle style)
1693 {
1694         ModestFolderViewPrivate *priv;
1695
1696         g_return_if_fail (self);
1697         
1698         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1699
1700         priv->style = style;
1701 }
1702
1703 void
1704 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1705                                                              const gchar *account_id)
1706 {
1707         ModestFolderViewPrivate *priv;
1708         GtkTreeModel *model;
1709
1710         g_return_if_fail (self);
1711         
1712         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1713
1714         /* This will be used by the filter_row callback,
1715          * to decided which rows to show: */
1716         if (priv->visible_account_id) {
1717                 g_free (priv->visible_account_id);
1718                 priv->visible_account_id = NULL;
1719         }
1720         if (account_id)
1721                 priv->visible_account_id = g_strdup (account_id);
1722
1723         /* Refilter */
1724         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1725         if (GTK_IS_TREE_MODEL_FILTER (model))
1726                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1727
1728         /* Save settings to gconf */
1729         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1730                                    MODEST_CONF_FOLDER_VIEW_KEY);
1731 }
1732
1733 const gchar *
1734 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1735 {
1736         ModestFolderViewPrivate *priv;
1737
1738         g_return_val_if_fail (self, NULL);
1739         
1740         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1741
1742         return (const gchar *) priv->visible_account_id;
1743 }
1744
1745 static gboolean
1746 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1747 {
1748         do {
1749                 GtkTreeIter child;
1750                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1751                 gchar *name = NULL;
1752
1753                 gtk_tree_model_get (model, iter, 
1754                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1755                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
1756                                     &type, -1);
1757
1758                 /*
1759                 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n", 
1760                         __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1761                 */
1762                         
1763                 gboolean result = FALSE;
1764                 if (type == TNY_FOLDER_TYPE_INBOX) {
1765                         result = TRUE;
1766                 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1767                         /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1768                          * when getting folders from the cache, before connectin, so we do 
1769                          * an extra check. We could fix this in tinymail, but it's easier 
1770                          * to do here.
1771                          */
1772                          if (strcmp (name, "Inbox") == 0)
1773                                 result = TRUE;
1774                 }
1775                 
1776                 g_free (name);
1777                 
1778                 if (result) {
1779                         *inbox_iter = *iter;
1780                         return TRUE;
1781                 }
1782
1783                 if (gtk_tree_model_iter_children (model, &child, iter)) {
1784                         if (find_inbox_iter (model, &child, inbox_iter))
1785                                 return TRUE;
1786                 }
1787
1788         } while (gtk_tree_model_iter_next (model, iter));
1789
1790         return FALSE;
1791 }
1792
1793 void 
1794 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1795 {
1796         GtkTreeModel *model;
1797         GtkTreeIter iter, inbox_iter;
1798         GtkTreeSelection *sel;
1799
1800 /*      /\* Do not set it if the folder view was not painted *\/ */
1801 /*      if (!GTK_WIDGET_MAPPED (self)) */
1802 /*              return; */
1803
1804         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1805         if (!model)
1806                 return;
1807
1808         expand_root_items (self);
1809         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1810
1811         gtk_tree_model_get_iter_first (model, &iter);
1812         if (find_inbox_iter (model, &iter, &inbox_iter)) {
1813                 gtk_tree_selection_select_iter (sel, &inbox_iter);
1814         }
1815         else {
1816                 gtk_tree_model_get_iter_first (model, &iter);
1817                 gtk_tree_selection_select_iter (sel, &iter);
1818         }
1819 }
1820
1821 void 
1822 modest_folder_view_copy_selection (ModestFolderView *folder_view)
1823 {
1824         /* Copy selection */
1825         _clipboard_set_selected_data (folder_view, FALSE);
1826 }
1827
1828 void 
1829 modest_folder_view_cut_selection (ModestFolderView *folder_view)
1830 {
1831         ModestFolderViewPrivate *priv = NULL;
1832         GtkTreeModel *model = NULL;
1833         const gchar **hidding = NULL;
1834         guint i, n_selected;
1835
1836         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1837         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1838
1839         /* Copy selection */
1840         _clipboard_set_selected_data (folder_view, TRUE);
1841
1842         /* Get hidding ids */
1843         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1844         
1845         /* Clear hidding array created by previous cut operation */
1846         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
1847
1848         /* Copy hidding array */
1849         priv->n_selected = n_selected;
1850         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1851         for (i=0; i < n_selected; i++) 
1852                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1853
1854         /* Hide cut folders */
1855         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
1856         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1857 }
1858
1859 static void
1860 _clipboard_set_selected_data (ModestFolderView *folder_view,
1861                               gboolean delete)
1862 {
1863         ModestFolderViewPrivate *priv = NULL;
1864         TnyFolderStore *folder = NULL;
1865
1866         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1867         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1868                 
1869         /* Set selected data on clipboard   */
1870         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1871         folder = modest_folder_view_get_selected (folder_view);
1872         modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
1873
1874         /* Free */
1875         g_object_unref (folder);
1876 }
1877
1878 static void
1879 _clear_hidding_filter (ModestFolderView *folder_view) 
1880 {
1881         ModestFolderViewPrivate *priv;
1882         guint i;
1883         
1884         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
1885         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
1886
1887         if (priv->hidding_ids != NULL) {
1888                 for (i=0; i < priv->n_selected; i++) 
1889                         g_free (priv->hidding_ids[i]);
1890                 g_free(priv->hidding_ids);
1891         }       
1892 }