* Improved my last commit, only invalidate cur_folder_store and folder_to_select...
[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-account.h>
45 #include <modest-tny-folder.h>
46 #include <modest-tny-local-folders-account.h>
47 #include <modest-tny-outbox-account.h>
48 #include <modest-marshal.h>
49 #include <modest-icon-names.h>
50 #include <modest-tny-account-store.h>
51 #include <modest-text-utils.h>
52 #include <modest-runtime.h>
53 #include "modest-folder-view.h"
54 #include <modest-dnd.h>
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
58
59 /* 'private'/'protected' functions */
60 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
61 static void modest_folder_view_init        (ModestFolderView *obj);
62 static void modest_folder_view_finalize    (GObject *obj);
63
64 static void         tny_account_store_view_init (gpointer g, 
65                                                  gpointer iface_data);
66
67 static void         modest_folder_view_set_account_store (TnyAccountStoreView *self, 
68                                                           TnyAccountStore     *account_store);
69
70 static void         on_selection_changed   (GtkTreeSelection *sel, gpointer data);
71
72 static void         on_account_removed     (TnyAccountStore *self, 
73                                             TnyAccount *account,
74                                             gpointer user_data);
75
76 static void         on_account_inserted    (TnyAccountStore *self, 
77                                             TnyAccount *account,
78                                             gpointer user_data);
79
80 static void         on_account_changed    (TnyAccountStore *self, 
81                                             TnyAccount *account,
82                                             gpointer user_data);
83
84 static gint         cmp_rows               (GtkTreeModel *tree_model, 
85                                             GtkTreeIter *iter1, 
86                                             GtkTreeIter *iter2,
87                                             gpointer user_data);
88
89 static gboolean     filter_row             (GtkTreeModel *model,
90                                             GtkTreeIter *iter,
91                                             gpointer data);
92
93 static gboolean     on_key_pressed         (GtkWidget *self,
94                                             GdkEventKey *event,
95                                             gpointer user_data);
96
97 static void         on_configuration_key_changed  (ModestConf* conf, 
98                                                    const gchar *key, 
99                                                    ModestConfEvent event,
100                                                    ModestConfNotificationId notification_id, 
101                                                    ModestFolderView *self);
102
103 /* DnD functions */
104 static void         on_drag_data_get       (GtkWidget *widget, 
105                                             GdkDragContext *context, 
106                                             GtkSelectionData *selection_data, 
107                                             guint info, 
108                                             guint time, 
109                                             gpointer data);
110
111 static void         on_drag_data_received  (GtkWidget *widget, 
112                                             GdkDragContext *context, 
113                                             gint x, 
114                                             gint y, 
115                                             GtkSelectionData *selection_data, 
116                                             guint info, 
117                                             guint time, 
118                                             gpointer data);
119
120 static gboolean     on_drag_motion         (GtkWidget      *widget,
121                                             GdkDragContext *context,
122                                             gint            x,
123                                             gint            y,
124                                             guint           time,
125                                             gpointer        user_data);
126
127 static void expand_root_items (ModestFolderView *self);
128
129 static gint         expand_row_timeout     (gpointer data);
130
131 static void         setup_drag_and_drop    (GtkTreeView *self);
132
133 static gboolean     _clipboard_set_selected_data (ModestFolderView *folder_view, 
134                                                   gboolean delete);
135
136 static void         _clear_hidding_filter (ModestFolderView *folder_view);
137
138 static void         on_row_inserted_maybe_select_folder (GtkTreeModel     *tree_model, 
139                                                          GtkTreePath      *path, 
140                                                          GtkTreeIter      *iter,
141                                                          ModestFolderView *self);
142
143 enum {
144         FOLDER_SELECTION_CHANGED_SIGNAL,
145         FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
146         LAST_SIGNAL
147 };
148
149 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
150 struct _ModestFolderViewPrivate {
151         TnyAccountStore      *account_store;
152         TnyFolderStore       *cur_folder_store;
153
154         TnyFolder            *folder_to_select; /* folder to select after the next update */
155
156         ModestConfNotificationId notification_id;
157
158         gulong                changed_signal;
159         gulong                account_inserted_signal;
160         gulong                account_removed_signal;
161         gulong                account_changed_signal;
162         gulong                conf_key_signal;
163         
164         /* not unref this object, its a singlenton */
165         ModestEmailClipboard *clipboard;
166
167         /* Filter tree model */
168         gchar **hidding_ids;
169         guint n_selected;
170
171         TnyFolderStoreQuery  *query;
172         guint                 timer_expander;
173
174         gchar                *local_account_name;
175         gchar                *visible_account_id;
176         ModestFolderViewStyle style;
177
178         gboolean  reselect; /* we use this to force a reselection of the INBOX */
179         gboolean  show_non_move;
180 };
181 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                       \
182         (G_TYPE_INSTANCE_GET_PRIVATE((o),                       \
183                                      MODEST_TYPE_FOLDER_VIEW,   \
184                                      ModestFolderViewPrivate))
185 /* globals */
186 static GObjectClass *parent_class = NULL;
187
188 static guint signals[LAST_SIGNAL] = {0}; 
189
190 GType
191 modest_folder_view_get_type (void)
192 {
193         static GType my_type = 0;
194         if (!my_type) {
195                 static const GTypeInfo my_info = {
196                         sizeof(ModestFolderViewClass),
197                         NULL,           /* base init */
198                         NULL,           /* base finalize */
199                         (GClassInitFunc) modest_folder_view_class_init,
200                         NULL,           /* class finalize */
201                         NULL,           /* class data */
202                         sizeof(ModestFolderView),
203                         1,              /* n_preallocs */
204                         (GInstanceInitFunc) modest_folder_view_init,
205                         NULL
206                 };
207
208                 static const GInterfaceInfo tny_account_store_view_info = {
209                         (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
210                         NULL,         /* interface_finalize */
211                         NULL          /* interface_data */
212                 };
213
214                                 
215                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
216                                                   "ModestFolderView",
217                                                   &my_info, 0);
218
219                 g_type_add_interface_static (my_type, 
220                                              TNY_TYPE_ACCOUNT_STORE_VIEW, 
221                                              &tny_account_store_view_info);
222         }
223         return my_type;
224 }
225
226 static void
227 modest_folder_view_class_init (ModestFolderViewClass *klass)
228 {
229         GObjectClass *gobject_class;
230         gobject_class = (GObjectClass*) klass;
231
232         parent_class            = g_type_class_peek_parent (klass);
233         gobject_class->finalize = modest_folder_view_finalize;
234
235         g_type_class_add_private (gobject_class,
236                                   sizeof(ModestFolderViewPrivate));
237         
238         signals[FOLDER_SELECTION_CHANGED_SIGNAL] = 
239                 g_signal_new ("folder_selection_changed",
240                               G_TYPE_FROM_CLASS (gobject_class),
241                               G_SIGNAL_RUN_FIRST,
242                               G_STRUCT_OFFSET (ModestFolderViewClass,
243                                                folder_selection_changed),
244                               NULL, NULL,
245                               modest_marshal_VOID__POINTER_BOOLEAN,
246                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
247
248         /*
249          * This signal is emitted whenever the currently selected
250          * folder display name is computed. Note that the name could
251          * be different to the folder name, because we could append
252          * the unread messages count to the folder name to build the
253          * folder display name
254          */
255         signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] = 
256                 g_signal_new ("folder-display-name-changed",
257                               G_TYPE_FROM_CLASS (gobject_class),
258                               G_SIGNAL_RUN_FIRST,
259                               G_STRUCT_OFFSET (ModestFolderViewClass,
260                                                folder_display_name_changed),
261                               NULL, NULL,
262                               g_cclosure_marshal_VOID__STRING,
263                               G_TYPE_NONE, 1, G_TYPE_STRING);
264 }
265
266 /* Simplify checks for NULLs: */
267 static gboolean
268 strings_are_equal (const gchar *a, const gchar *b)
269 {
270         if (!a && !b)
271                 return TRUE;
272         if (a && b)
273         {
274                 return (strcmp (a, b) == 0);
275         }
276         else
277                 return FALSE;
278 }
279
280 static gboolean
281 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path,  GtkTreeIter *iter, gpointer data)
282 {
283         GObject *instance = NULL;
284         
285         gtk_tree_model_get (model, iter,
286                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
287                             -1);
288                             
289         if (!instance)
290                 return FALSE; /* keep walking */
291                         
292         if (!TNY_IS_ACCOUNT (instance)) {
293                 g_object_unref (instance);
294                 return FALSE; /* keep walking */        
295         }    
296         
297         /* Check if this is the looked-for account: */
298         TnyAccount *this_account = TNY_ACCOUNT (instance);
299         TnyAccount *account = TNY_ACCOUNT (data);
300         
301         const gchar *this_account_id = tny_account_get_id(this_account);
302         const gchar *account_id = tny_account_get_id(account);
303         g_object_unref (instance);
304         instance = NULL;
305
306         /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
307         if (strings_are_equal(this_account_id, account_id)) {
308                 /* Tell the model that the data has changed, so that
309                  * it calls the cell_data_func callbacks again: */
310                 /* TODO: This does not seem to actually cause the new string to be shown: */
311                 gtk_tree_model_row_changed (model, path, iter);
312                 
313                 return TRUE; /* stop walking */
314         }
315         
316         return FALSE; /* keep walking */
317 }
318
319 typedef struct 
320 {
321         ModestFolderView *self;
322         gchar *previous_name;
323 } GetMmcAccountNameData;
324
325 static void
326 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
327 {
328         /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
329
330         GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
331         
332         if (!strings_are_equal (
333                 tny_account_get_name(TNY_ACCOUNT(account)), 
334                 data->previous_name)) {
335         
336                 /* Tell the model that the data has changed, so that 
337                  * it calls the cell_data_func callbacks again: */
338                 ModestFolderView *self = data->self;
339                 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
340                 if (model)
341                         gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
342         }
343
344         g_free (data->previous_name);
345         g_slice_free (GetMmcAccountNameData, data);
346 }
347
348 static void
349 text_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
350                  GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer data)
351 {
352         ModestFolderViewPrivate *priv;
353         GObject *rendobj;
354         gchar *fname = NULL;
355         gint unread = 0;
356         gint all = 0;
357         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
358         GObject *instance = NULL;
359         
360         g_return_if_fail (column);
361         g_return_if_fail (tree_model);
362
363         gtk_tree_model_get (tree_model, iter,
364                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
365                             TNY_GTK_FOLDER_STORE_TREE_MODEL_ALL_COLUMN, &all,
366                             TNY_GTK_FOLDER_STORE_TREE_MODEL_UNREAD_COLUMN, &unread,
367                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
368                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
369                             -1);
370         rendobj = G_OBJECT(renderer);
371
372         if (!fname)
373                 return;
374
375         if (!instance) {
376                 g_free (fname);
377                 return;
378         }
379
380         ModestFolderView *self = MODEST_FOLDER_VIEW (data);
381         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
382         
383         gchar *item_name = NULL;
384         gint item_weight = 400;
385         
386         if (type != TNY_FOLDER_TYPE_ROOT) {
387                 gint number = 0;
388                 
389                 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
390                     modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
391                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
392                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
393                                 g_free (fname);
394                                 fname = g_strdup(modest_local_folder_info_get_type_display_name (type));
395                         }
396                 }
397
398                 /* Select the number to show: the unread or unsent messages */
399                 if ((type == TNY_FOLDER_TYPE_DRAFTS) || (type == TNY_FOLDER_TYPE_OUTBOX))
400                         number = all;
401                 else
402                         number = unread;
403                 
404                 /* Use bold font style if there are unread or unset messages */
405                 if (number > 0) {
406                         item_name = g_strdup_printf ("%s (%d)", fname, number);
407                         item_weight = 800;
408                 } else {
409                         item_name = g_strdup (fname);
410                         item_weight = 400;
411                 }
412                 
413         } else if (TNY_IS_ACCOUNT (instance)) {
414                 /* If it's a server account */
415                 if (modest_tny_account_is_virtual_local_folders (
416                                 TNY_ACCOUNT (instance))) {
417                         item_name = g_strdup (priv->local_account_name);
418                         item_weight = 800;
419                 } else if (modest_tny_account_is_memory_card_account (
420                                 TNY_ACCOUNT (instance))) {
421                         /* fname is only correct when the items are first 
422                          * added to the model, not when the account is 
423                          * changed later, so get the name from the account
424                          * instance: */
425                         item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
426                         item_weight = 800;
427                 } else {
428                         item_name = g_strdup (fname);
429                         item_weight = 800;
430                 }
431         }
432         
433         if (!item_name)
434                 item_name = g_strdup ("unknown");
435                         
436         if (item_name && item_weight) {
437                 /* Set the name in the treeview cell: */
438                 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
439                 
440                 /* Notify display name observers */
441                 /* TODO: What listens for this signal, and how can it use only the new name? */
442                 if (G_OBJECT (priv->cur_folder_store) == instance) {
443                         g_signal_emit (G_OBJECT(self),
444                                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
445                                                item_name);
446                 }
447                 g_free (item_name);
448                 
449         }
450         
451         /* If it is a Memory card account, make sure that we have the correct name.
452          * This function will be trigerred again when the name has been retrieved: */
453         if (TNY_IS_STORE_ACCOUNT (instance) && 
454                 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
455
456                 /* Get the account name asynchronously: */
457                 GetMmcAccountNameData *callback_data = 
458                         g_slice_new0(GetMmcAccountNameData);
459                 callback_data->self = self;
460
461                 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
462                 if (name)
463                         callback_data->previous_name = g_strdup (name); 
464
465                 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance), 
466                         on_get_mmc_account_name, callback_data);
467         }
468                         
469         g_object_unref (G_OBJECT (instance));
470         g_free (fname);
471 }
472
473 static void
474 icon_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
475                  GtkTreeModel *tree_model,  GtkTreeIter *iter, gpointer data)
476 {
477         GObject *rendobj = NULL, *instance = NULL;
478         GdkPixbuf *pixbuf = NULL;
479         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
480         const gchar *account_id = NULL;
481         gboolean has_children;
482         
483         rendobj = G_OBJECT(renderer);
484         gtk_tree_model_get (tree_model, iter,
485                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
486                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
487                             -1);
488         has_children = gtk_tree_model_iter_has_child (tree_model, iter);
489
490         if (!instance) 
491                 return;
492
493         /* MERGE is not needed anymore as the folder now has the correct type jschmid */
494         /* We include the MERGE type here because it's used to create
495            the local OUTBOX folder */
496         if (type == TNY_FOLDER_TYPE_NORMAL || 
497             type == TNY_FOLDER_TYPE_UNKNOWN) {
498                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
499         }
500
501         switch (type) {
502         case TNY_FOLDER_TYPE_ROOT:
503                 if (TNY_IS_ACCOUNT (instance)) {
504                         
505                         if (modest_tny_account_is_virtual_local_folders (
506                                 TNY_ACCOUNT (instance))) {
507                                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS);
508                         }
509                         else {
510                                 account_id = tny_account_get_id (TNY_ACCOUNT (instance));
511                                 
512                                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
513                                         pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_MMC);
514                                 else
515                                         pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT);
516                         }
517                 }
518                 break;
519         case TNY_FOLDER_TYPE_INBOX:
520             pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX);
521             break;
522         case TNY_FOLDER_TYPE_OUTBOX:
523                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX);
524                 break;
525         case TNY_FOLDER_TYPE_JUNK:
526                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK);
527                 break;
528         case TNY_FOLDER_TYPE_SENT:
529                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_SENT);
530                 break;
531         case TNY_FOLDER_TYPE_TRASH:
532                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH);
533                 break;
534         case TNY_FOLDER_TYPE_DRAFTS:
535                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS);
536                 break;
537         case TNY_FOLDER_TYPE_NORMAL:
538         default:
539                 pixbuf = modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL);
540                 break;
541         }
542         
543         g_object_unref (G_OBJECT (instance));
544
545         /* Set pixbuf */
546         g_object_set (rendobj, "pixbuf", pixbuf, NULL);
547         if (has_children && (pixbuf != NULL)) {
548                 GdkPixbuf *open_pixbuf, *closed_pixbuf;
549                 GdkPixbuf *open_emblem, *closed_emblem;
550                 open_pixbuf = gdk_pixbuf_copy (pixbuf);
551                 closed_pixbuf = gdk_pixbuf_copy (pixbuf);
552                 open_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp");
553                 closed_emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp");
554
555                 if (open_emblem) {
556                         gdk_pixbuf_composite (open_emblem, open_pixbuf, 0, 0, 
557                                               MIN (gdk_pixbuf_get_width (open_emblem), 
558                                                    gdk_pixbuf_get_width (open_pixbuf)),
559                                               MIN (gdk_pixbuf_get_height (open_emblem), 
560                                                    gdk_pixbuf_get_height (open_pixbuf)),
561                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
562                         g_object_set (rendobj, "pixbuf-expander-open", open_pixbuf, NULL);
563                         g_object_unref (open_emblem);
564                 }
565                 if (closed_emblem) {
566                         gdk_pixbuf_composite (closed_emblem, closed_pixbuf, 0, 0, 
567                                               MIN (gdk_pixbuf_get_width (closed_emblem), 
568                                                    gdk_pixbuf_get_width (closed_pixbuf)),
569                                               MIN (gdk_pixbuf_get_height (closed_emblem), 
570                                                    gdk_pixbuf_get_height (closed_pixbuf)),
571                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
572                         g_object_set (rendobj, "pixbuf-expander-closed", closed_pixbuf, NULL);
573                         g_object_unref (closed_emblem);
574                 }
575                 if (closed_pixbuf)
576                         g_object_unref (closed_pixbuf);
577                 if (open_pixbuf)
578                         g_object_unref (open_pixbuf);
579         }
580
581         if (pixbuf != NULL)
582                 g_object_unref (pixbuf);
583 }
584
585 static void
586 add_columns (GtkWidget *treeview)
587 {
588         GtkTreeViewColumn *column;
589         GtkCellRenderer *renderer;
590         GtkTreeSelection *sel;
591
592         /* Create column */
593         column = gtk_tree_view_column_new ();   
594         
595         /* Set icon and text render function */
596         renderer = gtk_cell_renderer_pixbuf_new();
597         gtk_tree_view_column_pack_start (column, renderer, FALSE);
598         gtk_tree_view_column_set_cell_data_func(column, renderer,
599                                                 icon_cell_data, treeview, NULL);
600         
601         renderer = gtk_cell_renderer_text_new();
602         gtk_tree_view_column_pack_start (column, renderer, FALSE);
603         gtk_tree_view_column_set_cell_data_func(column, renderer,
604                                                 text_cell_data, treeview, NULL);
605         
606         /* Set selection mode */
607         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
608         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
609
610         /* Set treeview appearance */
611         gtk_tree_view_column_set_spacing (column, 2);
612         gtk_tree_view_column_set_resizable (column, TRUE);
613         gtk_tree_view_column_set_fixed_width (column, TRUE);            
614         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
615         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
616
617         /* Add column */
618         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
619 }
620
621 static void
622 modest_folder_view_init (ModestFolderView *obj)
623 {
624         ModestFolderViewPrivate *priv;
625         ModestConf *conf;
626         
627         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
628         
629         priv->timer_expander = 0;
630         priv->account_store  = NULL;
631         priv->query          = NULL;
632         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
633         priv->cur_folder_store   = NULL;
634         priv->visible_account_id = NULL;
635         priv->folder_to_select = NULL;
636
637         /* Initialize the local account name */
638         conf = modest_runtime_get_conf();
639         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
640
641         /* Init email clipboard */
642         priv->clipboard = modest_runtime_get_email_clipboard ();
643         priv->hidding_ids = NULL;
644         priv->n_selected = 0;
645         priv->reselect = FALSE;
646         priv->show_non_move = TRUE;
647
648         /* Build treeview */
649         add_columns (GTK_WIDGET (obj));
650
651         /* Setup drag and drop */
652         setup_drag_and_drop (GTK_TREE_VIEW(obj));
653
654         /* Connect signals */
655         g_signal_connect (G_OBJECT (obj), 
656                           "key-press-event", 
657                           G_CALLBACK (on_key_pressed), NULL);
658
659         /*
660          * Track changes in the local account name (in the device it
661          * will be the device name)
662          */
663         priv->notification_id = modest_conf_listen_to_namespace (conf, 
664                                                                  MODEST_CONF_NAMESPACE);
665         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf), 
666                                                   "key_changed",
667                                                   G_CALLBACK(on_configuration_key_changed), 
668                                                   obj);
669 }
670
671 static void
672 tny_account_store_view_init (gpointer g, gpointer iface_data)
673 {
674         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
675
676         klass->set_account_store_func = modest_folder_view_set_account_store;
677
678         return;
679 }
680
681 static void
682 modest_folder_view_finalize (GObject *obj)
683 {
684         ModestFolderViewPrivate *priv;
685         GtkTreeSelection    *sel;
686         
687         g_return_if_fail (obj);
688         
689         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
690
691         if (priv->notification_id) {
692                 modest_conf_forget_namespace (modest_runtime_get_conf (),
693                                               MODEST_CONF_NAMESPACE,
694                                               priv->notification_id);
695         }
696
697         if (priv->timer_expander != 0) {
698                 g_source_remove (priv->timer_expander);
699                 priv->timer_expander = 0;
700         }
701
702         if (priv->account_store) {
703                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
704                                              priv->account_inserted_signal);
705                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
706                                              priv->account_removed_signal);
707                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
708                                              priv->account_changed_signal);
709                 g_object_unref (G_OBJECT(priv->account_store));
710                 priv->account_store = NULL;
711         }
712
713         if (priv->query) {
714                 g_object_unref (G_OBJECT (priv->query));
715                 priv->query = NULL;
716         }
717
718         if (priv->folder_to_select) {
719                 g_object_unref (G_OBJECT(priv->folder_to_select));
720                 priv->folder_to_select = NULL;
721         }
722    
723         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
724         if (sel)
725                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
726
727         g_free (priv->local_account_name);
728         g_free (priv->visible_account_id);
729         
730         if (priv->conf_key_signal) {
731                 g_signal_handler_disconnect (modest_runtime_get_conf (),
732                                              priv->conf_key_signal);
733                 priv->conf_key_signal = 0;
734         }
735
736         if (priv->cur_folder_store) {
737                 if (TNY_IS_FOLDER(priv->cur_folder_store))
738                         tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), FALSE, NULL);
739                         /* FALSE --> expunge the message */
740
741                 g_object_unref (priv->cur_folder_store);
742                 priv->cur_folder_store = NULL;
743         }
744
745         /* Clear hidding array created by cut operation */
746         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
747
748         G_OBJECT_CLASS(parent_class)->finalize (obj);
749 }
750
751
752 static void
753 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
754 {
755         ModestFolderViewPrivate *priv;
756         TnyDevice *device;
757
758         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
759         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
760
761         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
762         device = tny_account_store_get_device (account_store);
763
764         if (G_UNLIKELY (priv->account_store)) {
765
766                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
767                                                    priv->account_inserted_signal))
768                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
769                                                      priv->account_inserted_signal);
770                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
771                                                    priv->account_removed_signal))
772                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
773                                                      priv->account_removed_signal);
774                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
775                                                    priv->account_changed_signal))
776                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
777                                                      priv->account_changed_signal);
778                 g_object_unref (G_OBJECT (priv->account_store));
779         }
780
781         priv->account_store = g_object_ref (G_OBJECT (account_store));
782
783         priv->account_removed_signal = 
784                 g_signal_connect (G_OBJECT(account_store), "account_removed",
785                                   G_CALLBACK (on_account_removed), self);
786
787         priv->account_inserted_signal =
788                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
789                                   G_CALLBACK (on_account_inserted), self);
790
791         priv->account_changed_signal =
792                 g_signal_connect (G_OBJECT(account_store), "account_changed",
793                                   G_CALLBACK (on_account_changed), self);
794
795         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
796         
797         g_object_unref (G_OBJECT (device));
798 }
799
800 static void
801 on_account_inserted (TnyAccountStore *account_store, 
802                      TnyAccount *account,
803                      gpointer user_data)
804 {
805         ModestFolderViewPrivate *priv;
806         GtkTreeModel *sort_model, *filter_model;
807
808         /* Ignore transport account insertions, we're not showing them
809            in the folder view */
810         if (TNY_IS_TRANSPORT_ACCOUNT (account))
811                 return;
812
813         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
814
815         /* If we're adding a new account, and there is no previous
816            one, we need to select the visible server account */
817         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
818             !priv->visible_account_id)
819                 modest_widget_memory_restore (modest_runtime_get_conf(), 
820                                               G_OBJECT (user_data),
821                                               MODEST_CONF_FOLDER_VIEW_KEY);
822
823         /* Get the inner model */
824         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
825         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
826
827         /* Insert the account in the model */
828         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
829                          G_OBJECT (account));
830 }
831
832
833 static void
834 on_account_changed (TnyAccountStore *account_store, TnyAccount *tny_account,
835                     gpointer user_data)
836 {
837         /* do nothing */
838 }
839
840
841
842 static void
843 on_account_removed (TnyAccountStore *account_store, 
844                     TnyAccount *account,
845                     gpointer user_data)
846 {
847         ModestFolderView *self = NULL;
848         ModestFolderViewPrivate *priv;
849         GtkTreeModel *sort_model, *filter_model;
850         GtkTreeSelection *sel = NULL;
851
852         /* Ignore transport account removals, we're not showing them
853            in the folder view */
854         if (TNY_IS_TRANSPORT_ACCOUNT (account))
855                 return;
856
857         self = MODEST_FOLDER_VIEW (user_data);
858         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
859
860         /* Invalidate the cur_folder_store only if the selected folder
861            belongs to the account that is being removed */
862         if (priv->cur_folder_store) {
863                 TnyAccount *selected_folder_account = NULL;
864
865                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
866                         selected_folder_account = 
867                                 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
868                 } else {
869                         selected_folder_account = 
870                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
871                 }
872
873                 if (selected_folder_account == account) {
874                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
875                         gtk_tree_selection_unselect_all (sel);
876                 }
877                 g_object_unref (selected_folder_account);
878         }
879
880         /* Invalidate row to select only if the folder to select
881            belongs to the account that is being removed*/
882         if (priv->folder_to_select) {
883                 TnyAccount *folder_to_select_account = NULL;
884
885                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
886                 if (folder_to_select_account == account) {
887                         g_object_unref (priv->folder_to_select);
888                         priv->folder_to_select = NULL;
889                 }
890                 g_object_unref (folder_to_select_account);
891         }
892
893         /* Remove the account from the model */
894         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
895         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
896         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
897                          G_OBJECT (account));
898
899         /* If the removed account is the currently viewed one then
900            clear the configuration value. The new visible account will be the default account */
901         if (priv->visible_account_id &&
902             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
903
904                 /* Clear the current visible account_id */
905                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
906
907                 /* Call the restore method, this will set the new visible account */
908                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
909                                               MODEST_CONF_FOLDER_VIEW_KEY);
910         }
911
912         /* Select the INBOX */
913         modest_folder_view_select_first_inbox_or_local (self);
914 }
915
916 void
917 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
918 {
919         GtkTreeViewColumn *col;
920         
921         g_return_if_fail (self);
922
923         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
924         if (!col) {
925                 g_printerr ("modest: failed get column for title\n");
926                 return;
927         }
928
929         gtk_tree_view_column_set_title (col, title);
930         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
931                                            title != NULL);
932 }
933
934 static gboolean
935 modest_folder_view_on_map (ModestFolderView *self, 
936                            GdkEventExpose *event,
937                            gpointer data)
938 {
939         ModestFolderViewPrivate *priv;
940
941         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
942
943         /* This won't happen often */
944         if (G_UNLIKELY (priv->reselect)) {
945                 /* Select the first inbox or the local account if not found */
946
947                 /* TODO: this could cause a lock at startup, so we
948                    comment it for the moment. We know that this will
949                    be a bug, because the INBOX is not selected, but we
950                    need to rewrite some parts of Modest to avoid the
951                    deathlock situation */
952                 /* TODO: check if this is still the case */
953                 priv->reselect = FALSE;
954                 modest_folder_view_select_first_inbox_or_local (self);
955                 /* Notify the display name observers */
956                 g_signal_emit (G_OBJECT(self),
957                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
958                                NULL);
959         }
960
961         expand_root_items (self); 
962
963         return FALSE;
964 }
965
966 GtkWidget*
967 modest_folder_view_new (TnyFolderStoreQuery *query)
968 {
969         GObject *self;
970         ModestFolderViewPrivate *priv;
971         GtkTreeSelection *sel;
972         
973         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
974         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
975
976         if (query)
977                 priv->query = g_object_ref (query);
978         
979         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
980         priv->changed_signal = g_signal_connect (sel, "changed",
981                                                  G_CALLBACK (on_selection_changed), self);
982
983         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
984
985         return GTK_WIDGET(self);
986 }
987
988 /* this feels dirty; any other way to expand all the root items? */
989 static void
990 expand_root_items (ModestFolderView *self)
991 {
992         GtkTreePath *path;
993         path = gtk_tree_path_new_first ();
994
995         /* all folders should have child items, so.. */
996         while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
997                 gtk_tree_path_next (path);
998         
999         gtk_tree_path_free (path);
1000 }
1001
1002 /*
1003  * We use this function to implement the
1004  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1005  * account in this case, and the local folders.
1006  */
1007 static gboolean 
1008 filter_row (GtkTreeModel *model,
1009             GtkTreeIter *iter,
1010             gpointer data)
1011 {
1012         ModestFolderViewPrivate *priv;
1013         gboolean retval = TRUE;
1014         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1015         GObject *instance = NULL;
1016         const gchar *id = NULL;
1017         guint i;
1018         gboolean found = FALSE;
1019         gboolean cleared = FALSE;
1020
1021         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1022         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1023
1024         gtk_tree_model_get (model, iter,
1025                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1026                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1027                             -1);
1028
1029         /* Do not show if there is no instance, this could indeed
1030            happen when the model is being modified while it's being
1031            drawn. This could occur for example when moving folders
1032            using drag&drop */
1033         if (!instance)
1034                 return FALSE;
1035
1036         if (type == TNY_FOLDER_TYPE_ROOT) {
1037                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1038                    account instead of a folder. */
1039                 if (TNY_IS_ACCOUNT (instance)) {
1040                         TnyAccount *acc = TNY_ACCOUNT (instance);
1041                         const gchar *account_id = tny_account_get_id (acc);
1042         
1043                         /* If it isn't a special folder, 
1044                          * don't show it unless it is the visible account: */
1045                         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1046                             !modest_tny_account_is_virtual_local_folders (acc) &&
1047                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1048                                 
1049                                 /* Show only the visible account id */
1050                                 if (priv->visible_account_id) {
1051                                         if (strcmp (account_id, priv->visible_account_id))
1052                                                 retval = FALSE;
1053                                 } else {
1054                                         retval = FALSE;
1055                                 }                               
1056                         }
1057                         
1058                         /* Never show these to the user. They are merged into one folder 
1059                          * in the local-folders account instead: */
1060                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1061                                 retval = FALSE;
1062                 }
1063         }
1064
1065         /* Check hiding (if necessary) */
1066         cleared = modest_email_clipboard_cleared (priv->clipboard);            
1067         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1068                 id = tny_folder_get_id (TNY_FOLDER(instance));
1069                 if (priv->hidding_ids != NULL)
1070                         for (i=0; i < priv->n_selected && !found; i++)
1071                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1072                                         found = (!strcmp (priv->hidding_ids[i], id));
1073                 
1074                 retval = !found;
1075         }
1076         
1077         
1078         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1079         folder as no message can be move there according to UI specs */
1080         if (!priv->show_non_move)
1081         {
1082                 switch (type)
1083                 {
1084                         case TNY_FOLDER_TYPE_OUTBOX:
1085                         case TNY_FOLDER_TYPE_SENT:
1086                         case TNY_FOLDER_TYPE_DRAFTS:
1087                                 retval = FALSE;
1088                                 break;
1089                         case TNY_FOLDER_TYPE_UNKNOWN:
1090                         case TNY_FOLDER_TYPE_NORMAL:
1091                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1092                                 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1093                                                 || type == TNY_FOLDER_TYPE_DRAFTS)
1094                                 {
1095                                         retval = FALSE;
1096                                 }
1097                                 break;
1098                         default:
1099                                 break;  
1100                 }       
1101         }
1102         
1103         /* Free */
1104         g_object_unref (instance);
1105
1106         return retval;
1107 }
1108
1109
1110 gboolean
1111 modest_folder_view_update_model (ModestFolderView *self,
1112                                  TnyAccountStore *account_store)
1113 {
1114         ModestFolderViewPrivate *priv;
1115         GtkTreeModel *model /* , *old_model */;
1116         /* TnyAccount *local_account; */
1117         TnyList *model_as_list;
1118
1119         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1120         g_return_val_if_fail (account_store, FALSE);
1121
1122         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1123         
1124         /* Notify that there is no folder selected */
1125         g_signal_emit (G_OBJECT(self), 
1126                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1127                        NULL, FALSE);
1128         if (priv->cur_folder_store) {
1129                 g_object_unref (priv->cur_folder_store);
1130                 priv->cur_folder_store = NULL;
1131         }
1132
1133         /* FIXME: the local accounts are not shown when the query
1134            selects only the subscribed folders. */
1135 /*      model        = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1136         model        = tny_gtk_folder_store_tree_model_new (NULL);
1137         
1138         /* Deal with the model via its TnyList Interface,
1139          * filling the TnyList via a get_accounts() call: */
1140         model_as_list = TNY_LIST(model);
1141
1142         /* Get the accounts: */
1143         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1144                                         model_as_list,
1145                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1146         g_object_unref (model_as_list);
1147         model_as_list = NULL;   
1148                                                      
1149         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1150
1151         sortable = gtk_tree_model_sort_new_with_model (model);
1152         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1153                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
1154                                               GTK_SORT_ASCENDING);
1155         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1156                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1157                                          cmp_rows, NULL, NULL);
1158
1159         /* Create filter model */
1160         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1161         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1162                                                 filter_row,
1163                                                 self,
1164                                                 NULL);
1165
1166         /* Set new model */
1167         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1168         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1169                           (GCallback) on_row_inserted_maybe_select_folder, self);
1170
1171
1172         g_object_unref (model);
1173         g_object_unref (filter_model);          
1174         g_object_unref (sortable);
1175         
1176         /* Force a reselection of the INBOX next time the widget is shown */
1177         priv->reselect = TRUE;
1178                         
1179         return TRUE;
1180 }
1181
1182
1183 static void
1184 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1185 {
1186         GtkTreeModel *model = NULL;
1187         TnyFolderStore *folder = NULL;
1188         GtkTreeIter iter;
1189         ModestFolderView *tree_view = NULL;
1190         ModestFolderViewPrivate *priv = NULL;
1191         gboolean selected = FALSE;
1192
1193         g_return_if_fail (sel);
1194         g_return_if_fail (user_data);
1195         
1196         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1197
1198         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1199 /*      if(!gtk_tree_selection_get_selected (sel, &model, &iter)) */
1200 /*              return; */
1201
1202         /* Notify the display name observers */
1203         g_signal_emit (G_OBJECT(user_data),
1204                        signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1205                        NULL);
1206
1207         tree_view = MODEST_FOLDER_VIEW (user_data);
1208
1209         if (selected) {
1210                 gtk_tree_model_get (model, &iter,
1211                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1212                                     -1);
1213
1214                 /* If the folder is the same do not notify */
1215                 if (priv->cur_folder_store == folder && folder) {
1216                         g_object_unref (folder);
1217                         return;
1218                 }
1219         }
1220         
1221         /* Current folder was unselected */
1222         if (priv->cur_folder_store) {
1223                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1224                        priv->cur_folder_store, FALSE);
1225
1226                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1227                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1228                                                FALSE, NULL, NULL, NULL);
1229
1230                 /* FALSE --> don't expunge the messages */
1231
1232                 g_object_unref (priv->cur_folder_store);
1233                 priv->cur_folder_store = NULL;
1234         }
1235
1236         /* New current references */
1237         priv->cur_folder_store = folder;
1238
1239         /* New folder has been selected */
1240         g_signal_emit (G_OBJECT(tree_view),
1241                        signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1242                        0, priv->cur_folder_store, TRUE);
1243 }
1244
1245 TnyFolderStore *
1246 modest_folder_view_get_selected (ModestFolderView *self)
1247 {
1248         ModestFolderViewPrivate *priv;
1249
1250         g_return_val_if_fail (self, NULL);
1251         
1252         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1253         if (priv->cur_folder_store)
1254                 g_object_ref (priv->cur_folder_store);
1255
1256         return priv->cur_folder_store;
1257 }
1258
1259 static gint
1260 get_cmp_rows_type_pos (GObject *folder)
1261 {
1262         /* Remote accounts -> Local account -> MMC account .*/
1263         /* 0, 1, 2 */
1264         
1265         if (TNY_IS_ACCOUNT (folder) && 
1266                 modest_tny_account_is_virtual_local_folders (
1267                         TNY_ACCOUNT (folder))) {
1268                 return 1;
1269         } else if (TNY_IS_ACCOUNT (folder)) {
1270                 TnyAccount *account = TNY_ACCOUNT (folder);
1271                 const gchar *account_id = tny_account_get_id (account);
1272                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1273                         return 2;
1274                 else
1275                         return 0;
1276         }
1277         else {
1278                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1279                 return -1; /* Should never happen */
1280         }
1281 }
1282
1283 static gint
1284 get_cmp_subfolder_type_pos (TnyFolderType t)
1285 {
1286         /* Inbox, Outbox, Drafts, Sent, User */
1287         /* 0, 1, 2, 3, 4 */
1288
1289         switch (t) {
1290         case TNY_FOLDER_TYPE_INBOX:
1291                 return 0;
1292                 break;
1293         case TNY_FOLDER_TYPE_OUTBOX:
1294                 return 1;
1295                 break;
1296         case TNY_FOLDER_TYPE_DRAFTS:
1297                 return 2;
1298                 break;
1299         case TNY_FOLDER_TYPE_SENT:
1300                 return 3;
1301                 break;
1302         default:
1303                 return 4;
1304         }
1305 }
1306
1307 /*
1308  * This function orders the mail accounts according to these rules:
1309  * 1st - remote accounts
1310  * 2nd - local account
1311  * 3rd - MMC account
1312  */
1313 static gint
1314 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1315           gpointer user_data)
1316 {
1317         gint cmp = 0;
1318         gchar *name1 = NULL;
1319         gchar *name2 = NULL;
1320         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1321         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1322         GObject *folder1 = NULL;
1323         GObject *folder2 = NULL;
1324
1325         gtk_tree_model_get (tree_model, iter1,
1326                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1327                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1328                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1329                             -1);
1330         gtk_tree_model_get (tree_model, iter2,
1331                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1332                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1333                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1334                             -1);
1335
1336         /* Return if we get no folder. This could happen when folder
1337            operations are happening. The model is updated after the
1338            folder copy/move actually occurs, so there could be
1339            situations where the model to be drawn is not correct */
1340         if (!folder1 || !folder2)
1341                 goto finish;
1342
1343         if (type == TNY_FOLDER_TYPE_ROOT) {
1344                 /* Compare the types, so that 
1345                  * Remote accounts -> Local account -> MMC account .*/
1346                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1347                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1348                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1349                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1350                 if (pos1 <  pos2)
1351                         cmp = -1;
1352                 else if (pos1 > pos2)
1353                         cmp = 1;
1354                 else {
1355                         /* Compare items of the same type: */
1356                         
1357                         TnyAccount *account1 = NULL;
1358                         if (TNY_IS_ACCOUNT (folder1))
1359                                 account1 = TNY_ACCOUNT (folder1);
1360                                 
1361                         TnyAccount *account2 = NULL;
1362                         if (TNY_IS_ACCOUNT (folder2))
1363                                 account2 = TNY_ACCOUNT (folder2);
1364                                 
1365                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1366                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1367         
1368                         if (!account_id && !account_id2) {
1369                                 cmp = 0;
1370                         } else if (!account_id) {
1371                                 cmp = -1;
1372                         } else if (!account_id2) {
1373                                 cmp = +1;
1374                         } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1375                                 cmp = +1;
1376                         } else {
1377                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1378                         }
1379                 }
1380         } else {
1381                 gint cmp1 = 0, cmp2 = 0;
1382                 /* get the parent to know if it's a local folder */
1383
1384                 GtkTreeIter parent;
1385                 gboolean has_parent;
1386                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1387                 if (has_parent) {
1388                         GObject *parent_folder;
1389                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1390                         gtk_tree_model_get (tree_model, &parent, 
1391                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1392                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1393                                             -1);
1394                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1395                             TNY_IS_ACCOUNT (parent_folder) &&
1396                             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1397                                 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1398                                 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1399                         }
1400                         g_object_unref (parent_folder);
1401                 }
1402
1403                 /* if they are not local folders */
1404                 if (cmp1 == cmp2) {
1405                         cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1406                         cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1407                 }
1408
1409                 if (cmp1 == cmp2)
1410                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1411                 else 
1412                         cmp = (cmp1 - cmp2);
1413         }
1414
1415 finish: 
1416         if (folder1)
1417                 g_object_unref(G_OBJECT(folder1));
1418         if (folder2)
1419                 g_object_unref(G_OBJECT(folder2));
1420
1421         g_free (name1);
1422         g_free (name2);
1423
1424         return cmp;     
1425 }
1426
1427 /*****************************************************************************/
1428 /*                        DRAG and DROP stuff                                */
1429 /*****************************************************************************/
1430
1431 /*
1432  * This function fills the #GtkSelectionData with the row and the
1433  * model that has been dragged. It's called when this widget is a
1434  * source for dnd after the event drop happened
1435  */
1436 static void
1437 on_drag_data_get (GtkWidget *widget, 
1438                   GdkDragContext *context, 
1439                   GtkSelectionData *selection_data, 
1440                   guint info, 
1441                   guint time, 
1442                   gpointer data)
1443 {
1444         GtkTreeSelection *selection;
1445         GtkTreeModel *model;
1446         GtkTreeIter iter;
1447         GtkTreePath *source_row;
1448
1449         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1450         gtk_tree_selection_get_selected (selection, &model, &iter);
1451         source_row = gtk_tree_model_get_path (model, &iter);
1452
1453         gtk_tree_set_row_drag_data (selection_data,
1454                                     model,
1455                                     source_row);
1456
1457         gtk_tree_path_free (source_row);
1458 }
1459
1460 typedef struct _DndHelper {
1461         gboolean delete_source;
1462         GtkTreePath *source_row;
1463         GdkDragContext *context;
1464         guint time;
1465 } DndHelper;
1466
1467
1468 /*
1469  * This function is the callback of the
1470  * modest_mail_operation_xfer_msgs () and
1471  * modest_mail_operation_xfer_folder() calls. We check here if the
1472  * message/folder was correctly asynchronously transferred. The reason
1473  * to use the same callback is that the code is the same, it only has
1474  * to check that the operation went fine and then finalize the drag
1475  * and drop action
1476  */
1477 static void
1478 on_progress_changed (ModestMailOperation *mail_op, 
1479                      ModestMailOperationState *state,
1480                      gpointer user_data)
1481 {
1482         gboolean success;
1483         DndHelper *helper;
1484
1485         helper = (DndHelper *) user_data;
1486
1487         if (!state->finished)
1488                 return;
1489
1490         if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1491                 success = TRUE;
1492         } else {
1493                 success = FALSE;
1494         }
1495
1496         /* Notify the drag source. Never call delete, the monitor will
1497            do the job if needed */
1498         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1499
1500         /* Free the helper */
1501         gtk_tree_path_free (helper->source_row);
1502         g_slice_free (DndHelper, helper);
1503 }
1504
1505
1506 /* get the folder for the row the treepath refers to. */
1507 /* folder must be unref'd */
1508 static TnyFolder*
1509 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1510 {
1511         GtkTreeIter iter;
1512         TnyFolder *folder = NULL;
1513         
1514         if (gtk_tree_model_get_iter (model,&iter, path))
1515                 gtk_tree_model_get (model, &iter,
1516                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1517                                     -1);
1518         return folder;
1519 }
1520
1521 static void 
1522 show_banner_move_target_error ()
1523 {
1524         ModestWindow *main_window;
1525
1526         main_window = modest_window_mgr_get_main_window(
1527                         modest_runtime_get_window_mgr());
1528                                 
1529         modest_platform_information_banner(GTK_WIDGET(main_window),
1530                         NULL, _("mail_in_ui_folder_move_target_error"));
1531 }
1532
1533 /*
1534  * This function is used by drag_data_received_cb to manage drag and
1535  * drop of a header, i.e, and drag from the header view to the folder
1536  * view.
1537  */
1538 static void
1539 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1540                                 GtkTreeModel *dest_model,
1541                                 GtkTreePath  *dest_row,
1542                                 DndHelper    *helper)
1543 {
1544         TnyList *headers = NULL;
1545         TnyHeader *header = NULL;
1546         TnyFolder *folder = NULL;
1547         ModestMailOperation *mail_op = NULL;
1548         GtkTreeIter source_iter;
1549         ModestWindowMgr *mgr = NULL; /*no need for unref*/
1550         ModestWindow *main_win = NULL; /*no need for unref*/
1551
1552         g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1553         g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1554         g_return_if_fail (dest_row);
1555         g_return_if_fail (helper);
1556
1557         /* Get header */
1558         gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1559         gtk_tree_model_get (source_model, &source_iter, 
1560                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1561                             &header, -1);
1562         if (!TNY_IS_HEADER(header)) {
1563                 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1564                 goto cleanup;
1565         }
1566         
1567         /* Check if the selected message is in msg-view. If it is than
1568          * do not enable drag&drop on that. */
1569         mgr = modest_runtime_get_window_mgr ();
1570         if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1571                 goto cleanup;
1572
1573         /* Get Folder */
1574         folder = tree_path_to_folder (dest_model, dest_row);
1575         if (!TNY_IS_FOLDER(folder)) {
1576                 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1577                 show_banner_move_target_error();
1578                 goto cleanup;
1579         }
1580         if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1581                 g_debug ("folder rules: cannot write to that folder");
1582                 goto cleanup;
1583         }
1584         
1585         headers = tny_simple_list_new ();
1586         tny_list_append (headers, G_OBJECT (header));
1587
1588         main_win = modest_window_mgr_get_main_window(mgr);
1589         if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1590                         == GTK_RESPONSE_CANCEL)
1591                 goto cleanup;
1592
1593         /* Transfer message */
1594         mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1595                                                                  NULL,
1596                                                                  modest_ui_actions_move_folder_error_handler,
1597                                                                  NULL);
1598         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1599                                          mail_op);
1600         g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1601                           G_CALLBACK (on_progress_changed), helper);
1602
1603         modest_mail_operation_xfer_msgs (mail_op, 
1604                                          headers, 
1605                                          folder, 
1606                                          helper->delete_source, 
1607                                          NULL, NULL);
1608         
1609         /* Frees */
1610 cleanup:
1611         if (G_IS_OBJECT(mail_op))
1612                 g_object_unref (G_OBJECT (mail_op));
1613         if (G_IS_OBJECT(header))
1614                 g_object_unref (G_OBJECT (header));
1615         if (G_IS_OBJECT(folder))
1616                 g_object_unref (G_OBJECT (folder));
1617         if (G_IS_OBJECT(headers))
1618                 g_object_unref (headers);
1619 }
1620
1621 /*
1622  * This function is used by drag_data_received_cb to manage drag and
1623  * drop of a folder, i.e, and drag from the folder view to the same
1624  * folder view.
1625  */
1626 static void
1627 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
1628                                 GtkTreeModel     *dest_model,
1629                                 GtkTreePath      *dest_row,
1630                                 GtkSelectionData *selection_data,
1631                                 DndHelper        *helper)
1632 {
1633         ModestMailOperation *mail_op = NULL;
1634         GtkTreeIter dest_iter, iter;
1635         TnyFolderStore *dest_folder = NULL;
1636         TnyFolder *folder = NULL;
1637         gboolean forbidden = FALSE;
1638
1639         if (!forbidden) {
1640                 /* check the folder rules for the destination */
1641                 folder = tree_path_to_folder (dest_model, dest_row);
1642                 if (TNY_IS_FOLDER(folder)) {
1643                         ModestTnyFolderRules rules =
1644                                         modest_tny_folder_get_rules (folder);
1645                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1646
1647                         if (forbidden)
1648                                 g_debug ("folder rules: cannot write to that folder");
1649                 } else if (TNY_IS_FOLDER_STORE(folder)){
1650                         /* enable local root as destination for folders */
1651                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1652                                         && TNY_IS_ACCOUNT (folder))
1653                                 forbidden = TRUE;
1654                 }
1655                 g_object_unref (folder);
1656         }
1657         if (!forbidden) {
1658                 /* check the folder rules for the source */
1659                 folder = tree_path_to_folder (source_model, helper->source_row);
1660                 if (TNY_IS_FOLDER(folder)) {
1661                         ModestTnyFolderRules rules =
1662                                         modest_tny_folder_get_rules (folder);
1663                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1664                         if (forbidden)
1665                                 g_debug ("folder rules: cannot move that folder");
1666                 } else
1667                         forbidden = TRUE;
1668                 g_object_unref (folder);
1669         }
1670
1671         
1672         /* Check if the drag is possible */
1673         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1674                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1675                 gtk_tree_path_free (helper->source_row);        
1676                 g_slice_free (DndHelper, helper);
1677                 return;
1678         }
1679
1680         /* Get data */
1681         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1682         gtk_tree_model_get (dest_model, &dest_iter, 
1683                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1684                             &dest_folder, -1);
1685         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1686         gtk_tree_model_get (source_model, &iter,
1687                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1688                             &folder, -1);
1689
1690         /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1691         if (modest_platform_connect_and_wait_if_network_folderstore (
1692                                 NULL, dest_folder) && 
1693                         modest_platform_connect_and_wait_if_network_folderstore (
1694                                 NULL, TNY_FOLDER_STORE (folder))) {
1695                 /* Do the mail operation */
1696                 mail_op = modest_mail_operation_new_with_error_handling (
1697                                 MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1698                                 NULL,
1699                                 modest_ui_actions_move_folder_error_handler,
1700                                 NULL);
1701                 modest_mail_operation_queue_add (
1702                                 modest_runtime_get_mail_operation_queue (), 
1703                                 mail_op);
1704                 g_signal_connect (
1705                                 G_OBJECT (mail_op),
1706                                 "progress-changed",
1707                                 G_CALLBACK (on_progress_changed),
1708                                 helper);
1709
1710                 modest_mail_operation_xfer_folder (mail_op, 
1711                                 folder, 
1712                                 dest_folder,
1713                                 helper->delete_source,
1714                                 NULL,
1715                                 NULL);
1716
1717                 g_object_unref (G_OBJECT (mail_op));    
1718         }
1719         
1720         /* Frees */
1721         g_object_unref (G_OBJECT (dest_folder));
1722         g_object_unref (G_OBJECT (folder));
1723 }
1724
1725 /*
1726  * This function receives the data set by the "drag-data-get" signal
1727  * handler. This information comes within the #GtkSelectionData. This
1728  * function will manage both the drags of folders of the treeview and
1729  * drags of headers of the header view widget.
1730  */
1731 static void 
1732 on_drag_data_received (GtkWidget *widget, 
1733                        GdkDragContext *context, 
1734                        gint x, 
1735                        gint y, 
1736                        GtkSelectionData *selection_data, 
1737                        guint target_type, 
1738                        guint time, 
1739                        gpointer data)
1740 {
1741         GtkWidget *source_widget;
1742         GtkTreeModel *dest_model, *source_model;
1743         GtkTreePath *source_row, *dest_row;
1744         GtkTreeViewDropPosition pos;
1745         gboolean success = FALSE, delete_source = FALSE;
1746         DndHelper *helper = NULL; 
1747
1748         /* Do not allow further process */
1749         g_signal_stop_emission_by_name (widget, "drag-data-received");
1750         source_widget = gtk_drag_get_source_widget (context);
1751
1752         /* Get the action */
1753         if (context->action == GDK_ACTION_MOVE) {
1754                 delete_source = TRUE;
1755
1756                 /* Notify that there is no folder selected. We need to
1757                    do this in order to update the headers view (and
1758                    its monitors, because when moving, the old folder
1759                    won't longer exist. We can not wait for the end of
1760                    the operation, because the operation won't start if
1761                    the folder is in use */
1762                 if (source_widget == widget) {
1763                         ModestFolderViewPrivate *priv;
1764
1765                         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1766                         if (priv->cur_folder_store) {
1767                                 g_object_unref (priv->cur_folder_store);
1768                                 priv->cur_folder_store = NULL;
1769                         }
1770
1771                         g_signal_emit (G_OBJECT (widget), 
1772                                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1773                 }
1774         }
1775
1776         /* Check if the get_data failed */
1777         if (selection_data == NULL || selection_data->length < 0)
1778                 gtk_drag_finish (context, success, FALSE, time);
1779
1780         /* Get the models */
1781         gtk_tree_get_row_drag_data (selection_data,
1782                                     &source_model,
1783                                     &source_row);
1784
1785         /* Select the destination model */
1786         if (source_widget == widget) {
1787                 dest_model = source_model;
1788         } else {
1789                 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1790         }
1791
1792         /* Get the path to the destination row. Can not call
1793            gtk_tree_view_get_drag_dest_row() because the source row
1794            is not selected anymore */
1795         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1796                                            &dest_row, &pos);
1797
1798         /* Only allow drops IN other rows */
1799         if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1800                 gtk_drag_finish (context, success, FALSE, time);
1801
1802         /* Create the helper */
1803         helper = g_slice_new0 (DndHelper);
1804         helper->delete_source = delete_source;
1805         helper->source_row = gtk_tree_path_copy (source_row);
1806         helper->context = context;
1807         helper->time = time;
1808
1809         /* Drags from the header view */
1810         if (source_widget != widget) {
1811
1812                 drag_and_drop_from_header_view (source_model,
1813                                                 dest_model,
1814                                                 dest_row,
1815                                                 helper);
1816         } else {
1817
1818
1819                 drag_and_drop_from_folder_view (source_model,
1820                                                 dest_model,
1821                                                 dest_row,
1822                                                 selection_data, 
1823                                                 helper);
1824         }
1825
1826         /* Frees */
1827         gtk_tree_path_free (source_row);
1828         gtk_tree_path_free (dest_row);
1829 }
1830
1831 /*
1832  * We define a "drag-drop" signal handler because we do not want to
1833  * use the default one, because the default one always calls
1834  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1835  * signal handler, because there we have all the information available
1836  * to know if the dnd was a success or not.
1837  */
1838 static gboolean
1839 drag_drop_cb (GtkWidget      *widget,
1840               GdkDragContext *context,
1841               gint            x,
1842               gint            y,
1843               guint           time,
1844               gpointer        user_data) 
1845 {
1846         gpointer target;
1847
1848         if (!context->targets)
1849                 return FALSE;
1850
1851         /* Check if we're dragging a folder row */
1852         target = gtk_drag_dest_find_target (widget, context, NULL);
1853
1854         /* Request the data from the source. */
1855         gtk_drag_get_data(widget, context, target, time);
1856
1857     return TRUE;
1858 }
1859
1860 /*
1861  * This function expands a node of a tree view if it's not expanded
1862  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1863  * does that, so that's why they're here.
1864  */
1865 static gint
1866 expand_row_timeout (gpointer data)
1867 {
1868         GtkTreeView *tree_view = data;
1869         GtkTreePath *dest_path = NULL;
1870         GtkTreeViewDropPosition pos;
1871         gboolean result = FALSE;
1872         
1873         GDK_THREADS_ENTER ();
1874         
1875         gtk_tree_view_get_drag_dest_row (tree_view,
1876                                          &dest_path,
1877                                          &pos);
1878         
1879         if (dest_path &&
1880             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1881              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1882                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1883                 gtk_tree_path_free (dest_path);
1884         }
1885         else {
1886                 if (dest_path)
1887                         gtk_tree_path_free (dest_path);
1888                 
1889                 result = TRUE;
1890         }
1891         
1892         GDK_THREADS_LEAVE ();
1893
1894         return result;
1895 }
1896
1897 /*
1898  * This function is called whenever the pointer is moved over a widget
1899  * while dragging some data. It installs a timeout that will expand a
1900  * node of the treeview if not expanded yet. This function also calls
1901  * gdk_drag_status in order to set the suggested action that will be
1902  * used by the "drag-data-received" signal handler to know if we
1903  * should do a move or just a copy of the data.
1904  */
1905 static gboolean
1906 on_drag_motion (GtkWidget      *widget,
1907                 GdkDragContext *context,
1908                 gint            x,
1909                 gint            y,
1910                 guint           time,
1911                 gpointer        user_data)  
1912 {
1913         GtkTreeViewDropPosition pos;
1914         GtkTreePath *dest_row;
1915         ModestFolderViewPrivate *priv;
1916         GdkDragAction suggested_action;
1917         gboolean valid_location = FALSE;
1918
1919         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1920
1921         if (priv->timer_expander != 0) {
1922                 g_source_remove (priv->timer_expander);
1923                 priv->timer_expander = 0;
1924         }
1925
1926         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1927                                            x, y,
1928                                            &dest_row,
1929                                            &pos);
1930
1931         /* Do not allow drops between folders */
1932         if (!dest_row ||
1933             pos == GTK_TREE_VIEW_DROP_BEFORE ||
1934             pos == GTK_TREE_VIEW_DROP_AFTER) {
1935                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1936                 gdk_drag_status(context, 0, time);
1937                 valid_location = FALSE;
1938                 goto out;
1939         } else {
1940                 valid_location = TRUE;
1941         }
1942
1943         /* Expand the selected row after 1/2 second */
1944         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1945                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1946                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1947         }
1948
1949         /* Select the desired action. By default we pick MOVE */
1950         suggested_action = GDK_ACTION_MOVE;
1951
1952         if (context->actions == GDK_ACTION_COPY)
1953             gdk_drag_status(context, GDK_ACTION_COPY, time);
1954         else if (context->actions == GDK_ACTION_MOVE)
1955             gdk_drag_status(context, GDK_ACTION_MOVE, time);
1956         else if (context->actions & suggested_action)
1957             gdk_drag_status(context, suggested_action, time);
1958         else
1959             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1960
1961  out:
1962         if (dest_row)
1963                 gtk_tree_path_free (dest_row);
1964         g_signal_stop_emission_by_name (widget, "drag-motion");
1965         return valid_location;
1966 }
1967
1968
1969 /* Folder view drag types */
1970 const GtkTargetEntry folder_view_drag_types[] =
1971 {
1972         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1973         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,    MODEST_HEADER_ROW }
1974 };
1975
1976 /*
1977  * This function sets the treeview as a source and a target for dnd
1978  * events. It also connects all the requirede signals.
1979  */
1980 static void
1981 setup_drag_and_drop (GtkTreeView *self)
1982 {
1983         /* Set up the folder view as a dnd destination. Set only the
1984            highlight flag, otherwise gtk will have a different
1985            behaviour */
1986         gtk_drag_dest_set (GTK_WIDGET (self),
1987                            GTK_DEST_DEFAULT_HIGHLIGHT,
1988                            folder_view_drag_types,
1989                            G_N_ELEMENTS (folder_view_drag_types),
1990                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
1991
1992         g_signal_connect (G_OBJECT (self),
1993                           "drag_data_received",
1994                           G_CALLBACK (on_drag_data_received),
1995                           NULL);
1996
1997
1998         /* Set up the treeview as a dnd source */
1999         gtk_drag_source_set (GTK_WIDGET (self),
2000                              GDK_BUTTON1_MASK,
2001                              folder_view_drag_types,
2002                              G_N_ELEMENTS (folder_view_drag_types),
2003                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2004
2005         g_signal_connect (G_OBJECT (self),
2006                           "drag_motion",
2007                           G_CALLBACK (on_drag_motion),
2008                           NULL);
2009         
2010         g_signal_connect (G_OBJECT (self),
2011                           "drag_data_get",
2012                           G_CALLBACK (on_drag_data_get),
2013                           NULL);
2014
2015         g_signal_connect (G_OBJECT (self),
2016                           "drag_drop",
2017                           G_CALLBACK (drag_drop_cb),
2018                           NULL);
2019 }
2020
2021 /*
2022  * This function manages the navigation through the folders using the
2023  * keyboard or the hardware keys in the device
2024  */
2025 static gboolean
2026 on_key_pressed (GtkWidget *self,
2027                 GdkEventKey *event,
2028                 gpointer user_data)
2029 {
2030         GtkTreeSelection *selection;
2031         GtkTreeIter iter;
2032         GtkTreeModel *model;
2033         gboolean retval = FALSE;
2034
2035         /* Up and Down are automatically managed by the treeview */
2036         if (event->keyval == GDK_Return) {
2037                 /* Expand/Collapse the selected row */
2038                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2039                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2040                         GtkTreePath *path;
2041
2042                         path = gtk_tree_model_get_path (model, &iter);
2043
2044                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2045                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2046                         else
2047                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2048                         gtk_tree_path_free (path);
2049                 }
2050                 /* No further processing */
2051                 retval = TRUE;
2052         }
2053
2054         return retval;
2055 }
2056
2057 /*
2058  * We listen to the changes in the local folder account name key,
2059  * because we want to show the right name in the view. The local
2060  * folder account name corresponds to the device name in the Maemo
2061  * version. We do this because we do not want to query gconf on each
2062  * tree view refresh. It's better to cache it and change whenever
2063  * necessary.
2064  */
2065 static void 
2066 on_configuration_key_changed (ModestConf* conf, 
2067                               const gchar *key, 
2068                               ModestConfEvent event,
2069                               ModestConfNotificationId id, 
2070                               ModestFolderView *self)
2071 {
2072         ModestFolderViewPrivate *priv;
2073
2074
2075         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2076         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2077
2078         /* Do not listen for changes in other namespaces */
2079         if (priv->notification_id != id)
2080                  return;
2081          
2082         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2083                 g_free (priv->local_account_name);
2084
2085                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2086                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2087                 else
2088                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2089                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2090
2091                 /* Force a redraw */
2092 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2093                 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2094                                                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2095                 gtk_tree_view_column_queue_resize (tree_column);
2096 #endif
2097         }
2098 }
2099
2100 void
2101 modest_folder_view_set_style (ModestFolderView *self,
2102                               ModestFolderViewStyle style)
2103 {
2104         ModestFolderViewPrivate *priv;
2105
2106         g_return_if_fail (self);
2107         
2108         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2109
2110         priv->style = style;
2111 }
2112
2113 void
2114 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2115                                                              const gchar *account_id)
2116 {
2117         ModestFolderViewPrivate *priv;
2118         GtkTreeModel *model;
2119
2120         g_return_if_fail (self);
2121         
2122         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2123
2124         /* This will be used by the filter_row callback,
2125          * to decided which rows to show: */
2126         if (priv->visible_account_id) {
2127                 g_free (priv->visible_account_id);
2128                 priv->visible_account_id = NULL;
2129         }
2130         if (account_id)
2131                 priv->visible_account_id = g_strdup (account_id);
2132
2133         /* Refilter */
2134         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2135         if (GTK_IS_TREE_MODEL_FILTER (model))
2136                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2137
2138         /* Save settings to gconf */
2139         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2140                                    MODEST_CONF_FOLDER_VIEW_KEY);
2141 }
2142
2143 const gchar *
2144 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2145 {
2146         ModestFolderViewPrivate *priv;
2147
2148         g_return_val_if_fail (self, NULL);
2149         
2150         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2151
2152         return (const gchar *) priv->visible_account_id;
2153 }
2154
2155 static gboolean
2156 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2157 {
2158         do {
2159                 GtkTreeIter child;
2160                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2161
2162                 gtk_tree_model_get (model, iter, 
2163                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
2164                                     &type, -1);
2165                         
2166                 gboolean result = FALSE;
2167                 if (type == TNY_FOLDER_TYPE_INBOX) {
2168                         result = TRUE;
2169                 }               
2170                 if (result) {
2171                         *inbox_iter = *iter;
2172                         return TRUE;
2173                 }
2174
2175                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2176                         if (find_inbox_iter (model, &child, inbox_iter))
2177                                 return TRUE;
2178                 }
2179
2180         } while (gtk_tree_model_iter_next (model, iter));
2181
2182         return FALSE;
2183 }
2184
2185
2186
2187
2188 void 
2189 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2190 {
2191         GtkTreeModel *model;
2192         GtkTreeIter iter, inbox_iter;
2193         GtkTreeSelection *sel;
2194         GtkTreePath *path = NULL;
2195
2196         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2197         if (!model)
2198                 return;
2199
2200         expand_root_items (self);
2201         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2202
2203         gtk_tree_model_get_iter_first (model, &iter);
2204
2205         if (find_inbox_iter (model, &iter, &inbox_iter))
2206                 path = gtk_tree_model_get_path (model, &inbox_iter);
2207         else
2208                 path = gtk_tree_path_new_first ();
2209
2210         /* Select the row and free */
2211         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2212         gtk_tree_path_free (path);
2213
2214         /* set focus */
2215         gtk_widget_grab_focus (GTK_WIDGET(self));
2216 }
2217
2218
2219 /* recursive */
2220 static gboolean
2221 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, 
2222                   TnyFolder* folder)
2223 {
2224         do {
2225                 GtkTreeIter child;
2226                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2227                 TnyFolder* a_folder;
2228                 gchar *name = NULL;
2229                 
2230                 gtk_tree_model_get (model, iter, 
2231                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2232                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2233                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type, 
2234                                     -1);                
2235                 g_free (name);
2236
2237                 if (folder == a_folder) {
2238                         g_object_unref (a_folder);
2239                         *folder_iter = *iter;
2240                         return TRUE;
2241                 }
2242                 g_object_unref (a_folder);
2243                 
2244                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2245                         if (find_folder_iter (model, &child, folder_iter, folder)) 
2246                                 return TRUE;
2247                 }
2248
2249         } while (gtk_tree_model_iter_next (model, iter));
2250
2251         return FALSE;
2252 }
2253
2254
2255 static void
2256 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath  *path, GtkTreeIter *iter,
2257                                      ModestFolderView *self)
2258 {
2259         ModestFolderViewPrivate *priv = NULL;
2260         GtkTreeSelection *sel;
2261
2262         if (!MODEST_IS_FOLDER_VIEW(self))
2263                 return;
2264         
2265         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2266         
2267         if (priv->folder_to_select) {
2268                 
2269                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2270                                                        FALSE)) {
2271                         GtkTreePath *path;
2272                         path = gtk_tree_model_get_path (tree_model, iter);
2273                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2274                         
2275                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2276
2277                         gtk_tree_selection_select_iter (sel, iter);
2278                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2279
2280                         gtk_tree_path_free (path);
2281                 
2282                 }
2283                 g_object_unref (priv->folder_to_select);
2284                 priv->folder_to_select = NULL;
2285         }
2286 }
2287
2288
2289 gboolean
2290 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder, 
2291                                   gboolean after_change)
2292 {
2293         GtkTreeModel *model;
2294         GtkTreeIter iter, folder_iter;
2295         GtkTreeSelection *sel;
2296         ModestFolderViewPrivate *priv = NULL;
2297         
2298         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);     
2299         g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);   
2300                 
2301         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2302
2303         if (after_change) {
2304
2305                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2306                 gtk_tree_selection_unselect_all (sel);
2307
2308                 if (priv->folder_to_select)
2309                         g_object_unref(priv->folder_to_select);
2310                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2311                 return TRUE;
2312         }
2313                 
2314         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2315         if (!model)
2316                 return FALSE;
2317
2318                 
2319         gtk_tree_model_get_iter_first (model, &iter);
2320         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2321                 GtkTreePath *path;
2322
2323                 path = gtk_tree_model_get_path (model, &folder_iter);
2324                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2325
2326                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2327                 gtk_tree_selection_select_iter (sel, &folder_iter);
2328                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2329
2330                 gtk_tree_path_free (path);
2331                 return TRUE;
2332         }
2333         return FALSE;
2334 }
2335
2336
2337 void 
2338 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2339 {
2340         /* Copy selection */
2341         _clipboard_set_selected_data (folder_view, FALSE);
2342 }
2343
2344 void 
2345 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2346 {
2347         ModestFolderViewPrivate *priv = NULL;
2348         GtkTreeModel *model = NULL;
2349         const gchar **hidding = NULL;
2350         guint i, n_selected;
2351
2352         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2353         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2354
2355         /* Copy selection */
2356         if (!_clipboard_set_selected_data (folder_view, TRUE))
2357                 return;
2358
2359         /* Get hidding ids */
2360         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
2361         
2362         /* Clear hidding array created by previous cut operation */
2363         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2364
2365         /* Copy hidding array */
2366         priv->n_selected = n_selected;
2367         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2368         for (i=0; i < n_selected; i++) 
2369                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2370
2371         /* Hide cut folders */
2372         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2373         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2374 }
2375
2376 void
2377 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2378                                ModestFolderView *folder_view_dst)
2379 {
2380         GtkTreeModel *filter_model = NULL;
2381         GtkTreeModel *model = NULL;
2382         GtkTreeModel *new_filter_model = NULL;
2383         
2384         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2385         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2386
2387         /* Get src model*/
2388         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2389         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2390
2391         /* Build new filter model */
2392         new_filter_model = gtk_tree_model_filter_new (model, NULL);     
2393         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2394                                                 filter_row,
2395                                                 folder_view_dst,
2396                                                 NULL);
2397         /* Set copied model */
2398         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2399
2400         /* Free */
2401         g_object_unref (new_filter_model);
2402 }
2403
2404 void
2405 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2406                                           gboolean show)
2407 {
2408         GtkTreeModel *model = NULL;
2409         ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2410         priv->show_non_move = show;
2411 /*      modest_folder_view_update_model(folder_view, */
2412 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2413
2414         /* Hide special folders */
2415         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2416         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2417                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2418         }
2419 }
2420
2421 /* Returns FALSE if it did not selected anything */
2422 static gboolean
2423 _clipboard_set_selected_data (ModestFolderView *folder_view,
2424                               gboolean delete)
2425 {
2426         ModestFolderViewPrivate *priv = NULL;
2427         TnyFolderStore *folder = NULL;
2428         gboolean retval = FALSE;
2429
2430         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2431         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2432                 
2433         /* Set selected data on clipboard   */
2434         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2435         folder = modest_folder_view_get_selected (folder_view);
2436
2437         /* Do not allow to select an account */
2438         if (TNY_IS_FOLDER (folder)) {
2439                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2440                 retval = TRUE;
2441         }
2442
2443         /* Free */
2444         g_object_unref (folder);
2445
2446         return retval;
2447 }
2448
2449 static void
2450 _clear_hidding_filter (ModestFolderView *folder_view) 
2451 {
2452         ModestFolderViewPrivate *priv;
2453         guint i;
2454         
2455         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
2456         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2457
2458         if (priv->hidding_ids != NULL) {
2459                 for (i=0; i < priv->n_selected; i++) 
2460                         g_free (priv->hidding_ids[i]);
2461                 g_free(priv->hidding_ids);
2462         }       
2463 }
2464
2465