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