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