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