* Fixes NB#64415, now the show_toolbar options are per window type specific
[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, 
837                     TnyAccount *tny_account,
838                     gpointer user_data)
839 {
840         /* do nothing */
841         ModestFolderViewPrivate *priv;
842         GtkTreeModel *sort_model, *filter_model;
843
844         /* Ignore transport account insertions, we're not showing them
845            in the folder view */
846         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
847                 return;
848
849         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
850
851         /* Get the inner model */
852         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
853         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
854
855         /* Remove the account from the model */
856         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
857                          G_OBJECT (tny_account));
858
859         /* Insert the account in the model */
860         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
861                          G_OBJECT (tny_account));
862 }
863
864
865
866 static void
867 on_account_removed (TnyAccountStore *account_store, 
868                     TnyAccount *account,
869                     gpointer user_data)
870 {
871         ModestFolderView *self = NULL;
872         ModestFolderViewPrivate *priv;
873         GtkTreeModel *sort_model, *filter_model;
874         GtkTreeSelection *sel = NULL;
875
876         /* Ignore transport account removals, we're not showing them
877            in the folder view */
878         if (TNY_IS_TRANSPORT_ACCOUNT (account))
879                 return;
880
881         self = MODEST_FOLDER_VIEW (user_data);
882         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
883
884         /* Invalidate the cur_folder_store only if the selected folder
885            belongs to the account that is being removed */
886         if (priv->cur_folder_store) {
887                 TnyAccount *selected_folder_account = NULL;
888
889                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
890                         selected_folder_account = 
891                                 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
892                 } else {
893                         selected_folder_account = 
894                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
895                 }
896
897                 if (selected_folder_account == account) {
898                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
899                         gtk_tree_selection_unselect_all (sel);
900                 }
901                 g_object_unref (selected_folder_account);
902         }
903
904         /* Invalidate row to select only if the folder to select
905            belongs to the account that is being removed*/
906         if (priv->folder_to_select) {
907                 TnyAccount *folder_to_select_account = NULL;
908
909                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
910                 if (folder_to_select_account == account) {
911 /*                      modest_folder_view_disable_next_folder_selection (self); */
912                         g_object_unref (priv->folder_to_select);
913                         priv->folder_to_select = NULL;
914                 }
915                 g_object_unref (folder_to_select_account);
916         }
917
918         /* Remove the account from the model */
919         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
920         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
921         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
922                          G_OBJECT (account));
923
924         /* If the removed account is the currently viewed one then
925            clear the configuration value. The new visible account will be the default account */
926         if (priv->visible_account_id &&
927             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
928
929                 /* Clear the current visible account_id */
930                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
931
932                 /* Call the restore method, this will set the new visible account */
933                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
934                                               MODEST_CONF_FOLDER_VIEW_KEY);
935         }
936
937         /* Select the INBOX */
938         modest_folder_view_select_first_inbox_or_local (self);
939 }
940
941 void
942 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
943 {
944         GtkTreeViewColumn *col;
945         
946         g_return_if_fail (self);
947
948         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
949         if (!col) {
950                 g_printerr ("modest: failed get column for title\n");
951                 return;
952         }
953
954         gtk_tree_view_column_set_title (col, title);
955         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
956                                            title != NULL);
957 }
958
959 static gboolean
960 modest_folder_view_on_map (ModestFolderView *self, 
961                            GdkEventExpose *event,
962                            gpointer data)
963 {
964         ModestFolderViewPrivate *priv;
965
966         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
967
968         /* This won't happen often */
969         if (G_UNLIKELY (priv->reselect)) {
970                 /* Select the first inbox or the local account if not found */
971
972                 /* TODO: this could cause a lock at startup, so we
973                    comment it for the moment. We know that this will
974                    be a bug, because the INBOX is not selected, but we
975                    need to rewrite some parts of Modest to avoid the
976                    deathlock situation */
977                 /* TODO: check if this is still the case */
978                 priv->reselect = FALSE;
979                 modest_folder_view_select_first_inbox_or_local (self);
980                 /* Notify the display name observers */
981                 g_signal_emit (G_OBJECT(self),
982                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
983                                NULL);
984         }
985
986         expand_root_items (self); 
987
988         return FALSE;
989 }
990
991 GtkWidget*
992 modest_folder_view_new (TnyFolderStoreQuery *query)
993 {
994         GObject *self;
995         ModestFolderViewPrivate *priv;
996         GtkTreeSelection *sel;
997         
998         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
999         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1000
1001         if (query)
1002                 priv->query = g_object_ref (query);
1003         
1004         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1005         priv->changed_signal = g_signal_connect (sel, "changed",
1006                                                  G_CALLBACK (on_selection_changed), self);
1007
1008         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1009
1010         return GTK_WIDGET(self);
1011 }
1012
1013 /* this feels dirty; any other way to expand all the root items? */
1014 static void
1015 expand_root_items (ModestFolderView *self)
1016 {
1017         GtkTreePath *path;
1018         path = gtk_tree_path_new_first ();
1019
1020         /* all folders should have child items, so.. */
1021         while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
1022                 gtk_tree_path_next (path);
1023         
1024         gtk_tree_path_free (path);
1025 }
1026
1027 /*
1028  * We use this function to implement the
1029  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1030  * account in this case, and the local folders.
1031  */
1032 static gboolean 
1033 filter_row (GtkTreeModel *model,
1034             GtkTreeIter *iter,
1035             gpointer data)
1036 {
1037         ModestFolderViewPrivate *priv;
1038         gboolean retval = TRUE;
1039         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1040         GObject *instance = NULL;
1041         const gchar *id = NULL;
1042         guint i;
1043         gboolean found = FALSE;
1044         gboolean cleared = FALSE;
1045
1046         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1047         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1048
1049         gtk_tree_model_get (model, iter,
1050                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1051                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1052                             -1);
1053
1054         /* Do not show if there is no instance, this could indeed
1055            happen when the model is being modified while it's being
1056            drawn. This could occur for example when moving folders
1057            using drag&drop */
1058         if (!instance)
1059                 return FALSE;
1060
1061         if (type == TNY_FOLDER_TYPE_ROOT) {
1062                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1063                    account instead of a folder. */
1064                 if (TNY_IS_ACCOUNT (instance)) {
1065                         TnyAccount *acc = TNY_ACCOUNT (instance);
1066                         const gchar *account_id = tny_account_get_id (acc);
1067         
1068                         /* If it isn't a special folder, 
1069                          * don't show it unless it is the visible account: */
1070                         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1071                             !modest_tny_account_is_virtual_local_folders (acc) &&
1072                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1073                                 
1074                                 /* Show only the visible account id */
1075                                 if (priv->visible_account_id) {
1076                                         if (strcmp (account_id, priv->visible_account_id))
1077                                                 retval = FALSE;
1078                                 } else {
1079                                         retval = FALSE;
1080                                 }                               
1081                         }
1082                         
1083                         /* Never show these to the user. They are merged into one folder 
1084                          * in the local-folders account instead: */
1085                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1086                                 retval = FALSE;
1087                 }
1088         }
1089
1090         /* Check hiding (if necessary) */
1091         cleared = modest_email_clipboard_cleared (priv->clipboard);            
1092         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1093                 id = tny_folder_get_id (TNY_FOLDER(instance));
1094                 if (priv->hidding_ids != NULL)
1095                         for (i=0; i < priv->n_selected && !found; i++)
1096                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1097                                         found = (!strcmp (priv->hidding_ids[i], id));
1098                 
1099                 retval = !found;
1100         }
1101         
1102         
1103         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1104         folder as no message can be move there according to UI specs */
1105         if (!priv->show_non_move)
1106         {
1107                 switch (type)
1108                 {
1109                         case TNY_FOLDER_TYPE_OUTBOX:
1110                         case TNY_FOLDER_TYPE_SENT:
1111                         case TNY_FOLDER_TYPE_DRAFTS:
1112                                 retval = FALSE;
1113                                 break;
1114                         case TNY_FOLDER_TYPE_UNKNOWN:
1115                         case TNY_FOLDER_TYPE_NORMAL:
1116                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1117                                 if (type == TNY_FOLDER_TYPE_OUTBOX || type == TNY_FOLDER_TYPE_SENT
1118                                                 || type == TNY_FOLDER_TYPE_DRAFTS)
1119                                 {
1120                                         retval = FALSE;
1121                                 }
1122                                 break;
1123                         default:
1124                                 break;  
1125                 }       
1126         }
1127         
1128         /* Free */
1129         g_object_unref (instance);
1130
1131         return retval;
1132 }
1133
1134
1135 gboolean
1136 modest_folder_view_update_model (ModestFolderView *self,
1137                                  TnyAccountStore *account_store)
1138 {
1139         ModestFolderViewPrivate *priv;
1140         GtkTreeModel *model /* , *old_model */;
1141         /* TnyAccount *local_account; */
1142         TnyList *model_as_list;
1143
1144         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1145         g_return_val_if_fail (account_store, FALSE);
1146
1147         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1148         
1149         /* Notify that there is no folder selected */
1150         g_signal_emit (G_OBJECT(self), 
1151                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1152                        NULL, FALSE);
1153         if (priv->cur_folder_store) {
1154                 g_object_unref (priv->cur_folder_store);
1155                 priv->cur_folder_store = NULL;
1156         }
1157
1158         /* FIXME: the local accounts are not shown when the query
1159            selects only the subscribed folders. */
1160 /*      model        = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
1161         model        = tny_gtk_folder_store_tree_model_new (NULL);
1162         
1163         /* Deal with the model via its TnyList Interface,
1164          * filling the TnyList via a get_accounts() call: */
1165         model_as_list = TNY_LIST(model);
1166
1167         /* Get the accounts: */
1168         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1169                                         model_as_list,
1170                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1171         g_object_unref (model_as_list);
1172         model_as_list = NULL;   
1173                                                      
1174         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1175
1176         sortable = gtk_tree_model_sort_new_with_model (model);
1177         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1178                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
1179                                               GTK_SORT_ASCENDING);
1180         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1181                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1182                                          cmp_rows, NULL, NULL);
1183
1184         /* Create filter model */
1185         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1186         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1187                                                 filter_row,
1188                                                 self,
1189                                                 NULL);
1190
1191         /* Set new model */
1192         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1193         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1194                           (GCallback) on_row_inserted_maybe_select_folder, self);
1195
1196
1197         g_object_unref (model);
1198         g_object_unref (filter_model);          
1199         g_object_unref (sortable);
1200         
1201         /* Force a reselection of the INBOX next time the widget is shown */
1202         priv->reselect = TRUE;
1203                         
1204         return TRUE;
1205 }
1206
1207
1208 static void
1209 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1210 {
1211         GtkTreeModel *model = NULL;
1212         TnyFolderStore *folder = NULL;
1213         GtkTreeIter iter;
1214         ModestFolderView *tree_view = NULL;
1215         ModestFolderViewPrivate *priv = NULL;
1216         gboolean selected = FALSE;
1217
1218         g_return_if_fail (sel);
1219         g_return_if_fail (user_data);
1220         
1221         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1222
1223         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1224
1225         /* Notify the display name observers */
1226         g_signal_emit (G_OBJECT(user_data),
1227                        signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1228                        NULL);
1229
1230         tree_view = MODEST_FOLDER_VIEW (user_data);
1231
1232         if (selected) {
1233                 gtk_tree_model_get (model, &iter,
1234                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1235                                     -1);
1236
1237                 /* If the folder is the same do not notify */
1238                 if (priv->cur_folder_store == folder && folder) {
1239                         g_object_unref (folder);
1240                         return;
1241                 }
1242         }
1243         
1244         /* Current folder was unselected */
1245         if (priv->cur_folder_store) {
1246                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1247                        priv->cur_folder_store, FALSE);
1248
1249                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1250                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1251                                                FALSE, NULL, NULL, NULL);
1252
1253                 /* FALSE --> don't expunge the messages */
1254
1255                 g_object_unref (priv->cur_folder_store);
1256                 priv->cur_folder_store = NULL;
1257         }
1258
1259         /* New current references */
1260         priv->cur_folder_store = folder;
1261
1262         /* New folder has been selected */
1263         g_signal_emit (G_OBJECT(tree_view),
1264                        signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1265                        0, priv->cur_folder_store, TRUE);
1266 }
1267
1268 TnyFolderStore *
1269 modest_folder_view_get_selected (ModestFolderView *self)
1270 {
1271         ModestFolderViewPrivate *priv;
1272
1273         g_return_val_if_fail (self, NULL);
1274         
1275         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1276         if (priv->cur_folder_store)
1277                 g_object_ref (priv->cur_folder_store);
1278
1279         return priv->cur_folder_store;
1280 }
1281
1282 static gint
1283 get_cmp_rows_type_pos (GObject *folder)
1284 {
1285         /* Remote accounts -> Local account -> MMC account .*/
1286         /* 0, 1, 2 */
1287         
1288         if (TNY_IS_ACCOUNT (folder) && 
1289                 modest_tny_account_is_virtual_local_folders (
1290                         TNY_ACCOUNT (folder))) {
1291                 return 1;
1292         } else if (TNY_IS_ACCOUNT (folder)) {
1293                 TnyAccount *account = TNY_ACCOUNT (folder);
1294                 const gchar *account_id = tny_account_get_id (account);
1295                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1296                         return 2;
1297                 else
1298                         return 0;
1299         }
1300         else {
1301                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1302                 return -1; /* Should never happen */
1303         }
1304 }
1305
1306 static gint
1307 get_cmp_subfolder_type_pos (TnyFolderType t)
1308 {
1309         /* Inbox, Outbox, Drafts, Sent, User */
1310         /* 0, 1, 2, 3, 4 */
1311
1312         switch (t) {
1313         case TNY_FOLDER_TYPE_INBOX:
1314                 return 0;
1315                 break;
1316         case TNY_FOLDER_TYPE_OUTBOX:
1317                 return 1;
1318                 break;
1319         case TNY_FOLDER_TYPE_DRAFTS:
1320                 return 2;
1321                 break;
1322         case TNY_FOLDER_TYPE_SENT:
1323                 return 3;
1324                 break;
1325         default:
1326                 return 4;
1327         }
1328 }
1329
1330 /*
1331  * This function orders the mail accounts according to these rules:
1332  * 1st - remote accounts
1333  * 2nd - local account
1334  * 3rd - MMC account
1335  */
1336 static gint
1337 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1338           gpointer user_data)
1339 {
1340         gint cmp = 0;
1341         gchar *name1 = NULL;
1342         gchar *name2 = NULL;
1343         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1344         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1345         GObject *folder1 = NULL;
1346         GObject *folder2 = NULL;
1347
1348         gtk_tree_model_get (tree_model, iter1,
1349                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1350                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1351                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1352                             -1);
1353         gtk_tree_model_get (tree_model, iter2,
1354                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1355                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1356                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1357                             -1);
1358
1359         /* Return if we get no folder. This could happen when folder
1360            operations are happening. The model is updated after the
1361            folder copy/move actually occurs, so there could be
1362            situations where the model to be drawn is not correct */
1363         if (!folder1 || !folder2)
1364                 goto finish;
1365
1366         if (type == TNY_FOLDER_TYPE_ROOT) {
1367                 /* Compare the types, so that 
1368                  * Remote accounts -> Local account -> MMC account .*/
1369                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1370                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1371                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1372                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1373                 if (pos1 <  pos2)
1374                         cmp = -1;
1375                 else if (pos1 > pos2)
1376                         cmp = 1;
1377                 else {
1378                         /* Compare items of the same type: */
1379                         
1380                         TnyAccount *account1 = NULL;
1381                         if (TNY_IS_ACCOUNT (folder1))
1382                                 account1 = TNY_ACCOUNT (folder1);
1383                                 
1384                         TnyAccount *account2 = NULL;
1385                         if (TNY_IS_ACCOUNT (folder2))
1386                                 account2 = TNY_ACCOUNT (folder2);
1387                                 
1388                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1389                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1390         
1391                         if (!account_id && !account_id2) {
1392                                 cmp = 0;
1393                         } else if (!account_id) {
1394                                 cmp = -1;
1395                         } else if (!account_id2) {
1396                                 cmp = +1;
1397                         } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1398                                 cmp = +1;
1399                         } else {
1400                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1401                         }
1402                 }
1403         } else {
1404                 gint cmp1 = 0, cmp2 = 0;
1405                 /* get the parent to know if it's a local folder */
1406
1407                 GtkTreeIter parent;
1408                 gboolean has_parent;
1409                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1410                 if (has_parent) {
1411                         GObject *parent_folder;
1412                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1413                         gtk_tree_model_get (tree_model, &parent, 
1414                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1415                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1416                                             -1);
1417                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1418                             TNY_IS_ACCOUNT (parent_folder) &&
1419                             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1420                                 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1421                                 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1422                         }
1423                         g_object_unref (parent_folder);
1424                 }
1425
1426                 /* if they are not local folders */
1427                 if (cmp1 == cmp2) {
1428                         cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1429                         cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1430                 }
1431
1432                 if (cmp1 == cmp2)
1433                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1434                 else 
1435                         cmp = (cmp1 - cmp2);
1436         }
1437
1438 finish: 
1439         if (folder1)
1440                 g_object_unref(G_OBJECT(folder1));
1441         if (folder2)
1442                 g_object_unref(G_OBJECT(folder2));
1443
1444         g_free (name1);
1445         g_free (name2);
1446
1447         return cmp;     
1448 }
1449
1450 /*****************************************************************************/
1451 /*                        DRAG and DROP stuff                                */
1452 /*****************************************************************************/
1453
1454 /*
1455  * This function fills the #GtkSelectionData with the row and the
1456  * model that has been dragged. It's called when this widget is a
1457  * source for dnd after the event drop happened
1458  */
1459 static void
1460 on_drag_data_get (GtkWidget *widget, 
1461                   GdkDragContext *context, 
1462                   GtkSelectionData *selection_data, 
1463                   guint info, 
1464                   guint time, 
1465                   gpointer data)
1466 {
1467         GtkTreeSelection *selection;
1468         GtkTreeModel *model;
1469         GtkTreeIter iter;
1470         GtkTreePath *source_row;
1471
1472         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1473         gtk_tree_selection_get_selected (selection, &model, &iter);
1474         source_row = gtk_tree_model_get_path (model, &iter);
1475
1476         gtk_tree_set_row_drag_data (selection_data,
1477                                     model,
1478                                     source_row);
1479
1480         gtk_tree_path_free (source_row);
1481 }
1482
1483 typedef struct _DndHelper {
1484         gboolean delete_source;
1485         GtkTreePath *source_row;
1486         GdkDragContext *context;
1487         guint time;
1488 } DndHelper;
1489
1490
1491 /*
1492  * This function is the callback of the
1493  * modest_mail_operation_xfer_msgs () and
1494  * modest_mail_operation_xfer_folder() calls. We check here if the
1495  * message/folder was correctly asynchronously transferred. The reason
1496  * to use the same callback is that the code is the same, it only has
1497  * to check that the operation went fine and then finalize the drag
1498  * and drop action
1499  */
1500 static void
1501 on_progress_changed (ModestMailOperation *mail_op, 
1502                      ModestMailOperationState *state,
1503                      gpointer user_data)
1504 {
1505         gboolean success;
1506         DndHelper *helper;
1507
1508         helper = (DndHelper *) user_data;
1509
1510         if (!state->finished)
1511                 return;
1512
1513         if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1514                 success = TRUE;
1515         } else {
1516                 success = FALSE;
1517         }
1518
1519         /* Notify the drag source. Never call delete, the monitor will
1520            do the job if needed */
1521         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1522
1523         /* Free the helper */
1524         gtk_tree_path_free (helper->source_row);
1525         g_slice_free (DndHelper, helper);
1526 }
1527
1528
1529 /* get the folder for the row the treepath refers to. */
1530 /* folder must be unref'd */
1531 static TnyFolder*
1532 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1533 {
1534         GtkTreeIter iter;
1535         TnyFolder *folder = NULL;
1536         
1537         if (gtk_tree_model_get_iter (model,&iter, path))
1538                 gtk_tree_model_get (model, &iter,
1539                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1540                                     -1);
1541         return folder;
1542 }
1543
1544 static void 
1545 show_banner_move_target_error ()
1546 {
1547         ModestWindow *main_window;
1548
1549         main_window = modest_window_mgr_get_main_window(
1550                         modest_runtime_get_window_mgr());
1551                                 
1552         modest_platform_information_banner(GTK_WIDGET(main_window),
1553                         NULL, _("mail_in_ui_folder_move_target_error"));
1554 }
1555
1556 /*
1557  * This function is used by drag_data_received_cb to manage drag and
1558  * drop of a header, i.e, and drag from the header view to the folder
1559  * view.
1560  */
1561 static void
1562 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1563                                 GtkTreeModel *dest_model,
1564                                 GtkTreePath  *dest_row,
1565                                 DndHelper    *helper)
1566 {
1567         TnyList *headers = NULL;
1568         TnyHeader *header = NULL;
1569         TnyFolder *folder = NULL;
1570         ModestMailOperation *mail_op = NULL;
1571         GtkTreeIter source_iter;
1572         ModestWindowMgr *mgr = NULL; /*no need for unref*/
1573         ModestWindow *main_win = NULL; /*no need for unref*/
1574
1575         g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1576         g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1577         g_return_if_fail (dest_row);
1578         g_return_if_fail (helper);
1579
1580         /* Get header */
1581         gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1582         gtk_tree_model_get (source_model, &source_iter, 
1583                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1584                             &header, -1);
1585         if (!TNY_IS_HEADER(header)) {
1586                 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1587                 goto cleanup;
1588         }
1589         
1590         /* Check if the selected message is in msg-view. If it is than
1591          * do not enable drag&drop on that. */
1592         mgr = modest_runtime_get_window_mgr ();
1593         if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1594                 goto cleanup;
1595
1596         /* Get Folder */
1597         folder = tree_path_to_folder (dest_model, dest_row);
1598         if (!TNY_IS_FOLDER(folder)) {
1599                 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1600                 show_banner_move_target_error();
1601                 goto cleanup;
1602         }
1603         if (modest_tny_folder_get_rules(folder) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1604                 g_debug ("folder rules: cannot write to that folder");
1605                 goto cleanup;
1606         }
1607         
1608         headers = tny_simple_list_new ();
1609         tny_list_append (headers, G_OBJECT (header));
1610
1611         main_win = modest_window_mgr_get_main_window(mgr);
1612         if(msgs_move_to_confirmation(GTK_WINDOW(main_win), folder, TRUE, headers)
1613                         == GTK_RESPONSE_CANCEL)
1614                 goto cleanup;
1615
1616         /* Transfer message */
1617         mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1618                                                                  NULL,
1619                                                                  modest_ui_actions_move_folder_error_handler,
1620                                                                  NULL);
1621         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1622                                          mail_op);
1623         g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1624                           G_CALLBACK (on_progress_changed), helper);
1625
1626         modest_mail_operation_xfer_msgs (mail_op, 
1627                                          headers, 
1628                                          folder, 
1629                                          helper->delete_source, 
1630                                          NULL, NULL);
1631         
1632         /* Frees */
1633 cleanup:
1634         if (G_IS_OBJECT(mail_op))
1635                 g_object_unref (G_OBJECT (mail_op));
1636         if (G_IS_OBJECT(header))
1637                 g_object_unref (G_OBJECT (header));
1638         if (G_IS_OBJECT(folder))
1639                 g_object_unref (G_OBJECT (folder));
1640         if (G_IS_OBJECT(headers))
1641                 g_object_unref (headers);
1642 }
1643
1644 /*
1645  * This function is used by drag_data_received_cb to manage drag and
1646  * drop of a folder, i.e, and drag from the folder view to the same
1647  * folder view.
1648  */
1649 static void
1650 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
1651                                 GtkTreeModel     *dest_model,
1652                                 GtkTreePath      *dest_row,
1653                                 GtkSelectionData *selection_data,
1654                                 DndHelper        *helper)
1655 {
1656         ModestMailOperation *mail_op = NULL;
1657         GtkTreeIter dest_iter, iter;
1658         TnyFolderStore *dest_folder = NULL;
1659         TnyFolder *folder = NULL;
1660         gboolean forbidden = FALSE;
1661
1662         if (!forbidden) {
1663                 /* check the folder rules for the destination */
1664                 folder = tree_path_to_folder (dest_model, dest_row);
1665                 if (TNY_IS_FOLDER(folder)) {
1666                         ModestTnyFolderRules rules =
1667                                         modest_tny_folder_get_rules (folder);
1668                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1669
1670                         if (forbidden)
1671                                 g_debug ("folder rules: cannot write to that folder");
1672                 } else if (TNY_IS_FOLDER_STORE(folder)){
1673                         /* enable local root as destination for folders */
1674                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1675                                         && TNY_IS_ACCOUNT (folder))
1676                                 forbidden = TRUE;
1677                 }
1678                 g_object_unref (folder);
1679         }
1680         if (!forbidden) {
1681                 /* check the folder rules for the source */
1682                 folder = tree_path_to_folder (source_model, helper->source_row);
1683                 if (TNY_IS_FOLDER(folder)) {
1684                         ModestTnyFolderRules rules =
1685                                         modest_tny_folder_get_rules (folder);
1686                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1687                         if (forbidden)
1688                                 g_debug ("folder rules: cannot move that folder");
1689                 } else
1690                         forbidden = TRUE;
1691                 g_object_unref (folder);
1692         }
1693
1694         
1695         /* Check if the drag is possible */
1696         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1697                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1698                 gtk_tree_path_free (helper->source_row);        
1699                 g_slice_free (DndHelper, helper);
1700                 return;
1701         }
1702
1703         /* Get data */
1704         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1705         gtk_tree_model_get (dest_model, &dest_iter, 
1706                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1707                             &dest_folder, -1);
1708         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1709         gtk_tree_model_get (source_model, &iter,
1710                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1711                             &folder, -1);
1712
1713         /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1714         if (modest_platform_connect_and_wait_if_network_folderstore (
1715                                 NULL, dest_folder) && 
1716                         modest_platform_connect_and_wait_if_network_folderstore (
1717                                 NULL, TNY_FOLDER_STORE (folder))) {
1718                 /* Do the mail operation */
1719                 mail_op = modest_mail_operation_new_with_error_handling (
1720                                 MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1721                                 NULL,
1722                                 modest_ui_actions_move_folder_error_handler,
1723                                 NULL);
1724                 modest_mail_operation_queue_add (
1725                                 modest_runtime_get_mail_operation_queue (), 
1726                                 mail_op);
1727                 g_signal_connect (
1728                                 G_OBJECT (mail_op),
1729                                 "progress-changed",
1730                                 G_CALLBACK (on_progress_changed),
1731                                 helper);
1732
1733                 modest_mail_operation_xfer_folder (mail_op, 
1734                                 folder, 
1735                                 dest_folder,
1736                                 helper->delete_source,
1737                                 NULL,
1738                                 NULL);
1739
1740                 g_object_unref (G_OBJECT (mail_op));    
1741         }
1742         
1743         /* Frees */
1744         g_object_unref (G_OBJECT (dest_folder));
1745         g_object_unref (G_OBJECT (folder));
1746 }
1747
1748 /*
1749  * This function receives the data set by the "drag-data-get" signal
1750  * handler. This information comes within the #GtkSelectionData. This
1751  * function will manage both the drags of folders of the treeview and
1752  * drags of headers of the header view widget.
1753  */
1754 static void 
1755 on_drag_data_received (GtkWidget *widget, 
1756                        GdkDragContext *context, 
1757                        gint x, 
1758                        gint y, 
1759                        GtkSelectionData *selection_data, 
1760                        guint target_type, 
1761                        guint time, 
1762                        gpointer data)
1763 {
1764         GtkWidget *source_widget;
1765         GtkTreeModel *dest_model, *source_model;
1766         GtkTreePath *source_row, *dest_row;
1767         GtkTreeViewDropPosition pos;
1768         gboolean success = FALSE, delete_source = FALSE;
1769         DndHelper *helper = NULL; 
1770
1771         /* Do not allow further process */
1772         g_signal_stop_emission_by_name (widget, "drag-data-received");
1773         source_widget = gtk_drag_get_source_widget (context);
1774
1775         /* Get the action */
1776         if (context->action == GDK_ACTION_MOVE) {
1777                 delete_source = TRUE;
1778
1779                 /* Notify that there is no folder selected. We need to
1780                    do this in order to update the headers view (and
1781                    its monitors, because when moving, the old folder
1782                    won't longer exist. We can not wait for the end of
1783                    the operation, because the operation won't start if
1784                    the folder is in use */
1785                 if (source_widget == widget) {
1786                         ModestFolderViewPrivate *priv;
1787
1788                         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1789                         if (priv->cur_folder_store) {
1790                                 g_object_unref (priv->cur_folder_store);
1791                                 priv->cur_folder_store = NULL;
1792                         }
1793
1794                         g_signal_emit (G_OBJECT (widget), 
1795                                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1796                 }
1797         }
1798
1799         /* Check if the get_data failed */
1800         if (selection_data == NULL || selection_data->length < 0)
1801                 gtk_drag_finish (context, success, FALSE, time);
1802
1803         /* Get the models */
1804         gtk_tree_get_row_drag_data (selection_data,
1805                                     &source_model,
1806                                     &source_row);
1807
1808         /* Select the destination model */
1809         if (source_widget == widget) {
1810                 dest_model = source_model;
1811         } else {
1812                 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1813         }
1814
1815         /* Get the path to the destination row. Can not call
1816            gtk_tree_view_get_drag_dest_row() because the source row
1817            is not selected anymore */
1818         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1819                                            &dest_row, &pos);
1820
1821         /* Only allow drops IN other rows */
1822         if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1823                 gtk_drag_finish (context, success, FALSE, time);
1824
1825         /* Create the helper */
1826         helper = g_slice_new0 (DndHelper);
1827         helper->delete_source = delete_source;
1828         helper->source_row = gtk_tree_path_copy (source_row);
1829         helper->context = context;
1830         helper->time = time;
1831
1832         /* Drags from the header view */
1833         if (source_widget != widget) {
1834
1835                 drag_and_drop_from_header_view (source_model,
1836                                                 dest_model,
1837                                                 dest_row,
1838                                                 helper);
1839         } else {
1840
1841
1842                 drag_and_drop_from_folder_view (source_model,
1843                                                 dest_model,
1844                                                 dest_row,
1845                                                 selection_data, 
1846                                                 helper);
1847         }
1848
1849         /* Frees */
1850         gtk_tree_path_free (source_row);
1851         gtk_tree_path_free (dest_row);
1852 }
1853
1854 /*
1855  * We define a "drag-drop" signal handler because we do not want to
1856  * use the default one, because the default one always calls
1857  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1858  * signal handler, because there we have all the information available
1859  * to know if the dnd was a success or not.
1860  */
1861 static gboolean
1862 drag_drop_cb (GtkWidget      *widget,
1863               GdkDragContext *context,
1864               gint            x,
1865               gint            y,
1866               guint           time,
1867               gpointer        user_data) 
1868 {
1869         gpointer target;
1870
1871         if (!context->targets)
1872                 return FALSE;
1873
1874         /* Check if we're dragging a folder row */
1875         target = gtk_drag_dest_find_target (widget, context, NULL);
1876
1877         /* Request the data from the source. */
1878         gtk_drag_get_data(widget, context, target, time);
1879
1880     return TRUE;
1881 }
1882
1883 /*
1884  * This function expands a node of a tree view if it's not expanded
1885  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1886  * does that, so that's why they're here.
1887  */
1888 static gint
1889 expand_row_timeout (gpointer data)
1890 {
1891         GtkTreeView *tree_view = data;
1892         GtkTreePath *dest_path = NULL;
1893         GtkTreeViewDropPosition pos;
1894         gboolean result = FALSE;
1895         
1896         GDK_THREADS_ENTER ();
1897         
1898         gtk_tree_view_get_drag_dest_row (tree_view,
1899                                          &dest_path,
1900                                          &pos);
1901         
1902         if (dest_path &&
1903             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1904              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1905                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1906                 gtk_tree_path_free (dest_path);
1907         }
1908         else {
1909                 if (dest_path)
1910                         gtk_tree_path_free (dest_path);
1911                 
1912                 result = TRUE;
1913         }
1914         
1915         GDK_THREADS_LEAVE ();
1916
1917         return result;
1918 }
1919
1920 /*
1921  * This function is called whenever the pointer is moved over a widget
1922  * while dragging some data. It installs a timeout that will expand a
1923  * node of the treeview if not expanded yet. This function also calls
1924  * gdk_drag_status in order to set the suggested action that will be
1925  * used by the "drag-data-received" signal handler to know if we
1926  * should do a move or just a copy of the data.
1927  */
1928 static gboolean
1929 on_drag_motion (GtkWidget      *widget,
1930                 GdkDragContext *context,
1931                 gint            x,
1932                 gint            y,
1933                 guint           time,
1934                 gpointer        user_data)  
1935 {
1936         GtkTreeViewDropPosition pos;
1937         GtkTreePath *dest_row;
1938         ModestFolderViewPrivate *priv;
1939         GdkDragAction suggested_action;
1940         gboolean valid_location = FALSE;
1941
1942         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1943
1944         if (priv->timer_expander != 0) {
1945                 g_source_remove (priv->timer_expander);
1946                 priv->timer_expander = 0;
1947         }
1948
1949         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1950                                            x, y,
1951                                            &dest_row,
1952                                            &pos);
1953
1954         /* Do not allow drops between folders */
1955         if (!dest_row ||
1956             pos == GTK_TREE_VIEW_DROP_BEFORE ||
1957             pos == GTK_TREE_VIEW_DROP_AFTER) {
1958                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1959                 gdk_drag_status(context, 0, time);
1960                 valid_location = FALSE;
1961                 goto out;
1962         } else {
1963                 valid_location = TRUE;
1964         }
1965
1966         /* Expand the selected row after 1/2 second */
1967         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1968                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1969                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1970         }
1971
1972         /* Select the desired action. By default we pick MOVE */
1973         suggested_action = GDK_ACTION_MOVE;
1974
1975         if (context->actions == GDK_ACTION_COPY)
1976             gdk_drag_status(context, GDK_ACTION_COPY, time);
1977         else if (context->actions == GDK_ACTION_MOVE)
1978             gdk_drag_status(context, GDK_ACTION_MOVE, time);
1979         else if (context->actions & suggested_action)
1980             gdk_drag_status(context, suggested_action, time);
1981         else
1982             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1983
1984  out:
1985         if (dest_row)
1986                 gtk_tree_path_free (dest_row);
1987         g_signal_stop_emission_by_name (widget, "drag-motion");
1988         return valid_location;
1989 }
1990
1991
1992 /* Folder view drag types */
1993 const GtkTargetEntry folder_view_drag_types[] =
1994 {
1995         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1996         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,    MODEST_HEADER_ROW }
1997 };
1998
1999 /*
2000  * This function sets the treeview as a source and a target for dnd
2001  * events. It also connects all the requirede signals.
2002  */
2003 static void
2004 setup_drag_and_drop (GtkTreeView *self)
2005 {
2006         /* Set up the folder view as a dnd destination. Set only the
2007            highlight flag, otherwise gtk will have a different
2008            behaviour */
2009         gtk_drag_dest_set (GTK_WIDGET (self),
2010                            GTK_DEST_DEFAULT_HIGHLIGHT,
2011                            folder_view_drag_types,
2012                            G_N_ELEMENTS (folder_view_drag_types),
2013                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2014
2015         g_signal_connect (G_OBJECT (self),
2016                           "drag_data_received",
2017                           G_CALLBACK (on_drag_data_received),
2018                           NULL);
2019
2020
2021         /* Set up the treeview as a dnd source */
2022         gtk_drag_source_set (GTK_WIDGET (self),
2023                              GDK_BUTTON1_MASK,
2024                              folder_view_drag_types,
2025                              G_N_ELEMENTS (folder_view_drag_types),
2026                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2027
2028         g_signal_connect (G_OBJECT (self),
2029                           "drag_motion",
2030                           G_CALLBACK (on_drag_motion),
2031                           NULL);
2032         
2033         g_signal_connect (G_OBJECT (self),
2034                           "drag_data_get",
2035                           G_CALLBACK (on_drag_data_get),
2036                           NULL);
2037
2038         g_signal_connect (G_OBJECT (self),
2039                           "drag_drop",
2040                           G_CALLBACK (drag_drop_cb),
2041                           NULL);
2042 }
2043
2044 /*
2045  * This function manages the navigation through the folders using the
2046  * keyboard or the hardware keys in the device
2047  */
2048 static gboolean
2049 on_key_pressed (GtkWidget *self,
2050                 GdkEventKey *event,
2051                 gpointer user_data)
2052 {
2053         GtkTreeSelection *selection;
2054         GtkTreeIter iter;
2055         GtkTreeModel *model;
2056         gboolean retval = FALSE;
2057
2058         /* Up and Down are automatically managed by the treeview */
2059         if (event->keyval == GDK_Return) {
2060                 /* Expand/Collapse the selected row */
2061                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2062                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2063                         GtkTreePath *path;
2064
2065                         path = gtk_tree_model_get_path (model, &iter);
2066
2067                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2068                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2069                         else
2070                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2071                         gtk_tree_path_free (path);
2072                 }
2073                 /* No further processing */
2074                 retval = TRUE;
2075         }
2076
2077         return retval;
2078 }
2079
2080 /*
2081  * We listen to the changes in the local folder account name key,
2082  * because we want to show the right name in the view. The local
2083  * folder account name corresponds to the device name in the Maemo
2084  * version. We do this because we do not want to query gconf on each
2085  * tree view refresh. It's better to cache it and change whenever
2086  * necessary.
2087  */
2088 static void 
2089 on_configuration_key_changed (ModestConf* conf, 
2090                               const gchar *key, 
2091                               ModestConfEvent event,
2092                               ModestConfNotificationId id, 
2093                               ModestFolderView *self)
2094 {
2095         ModestFolderViewPrivate *priv;
2096
2097
2098         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2099         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2100
2101         /* Do not listen for changes in other namespaces */
2102         if (priv->notification_id != id)
2103                  return;
2104          
2105         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2106                 g_free (priv->local_account_name);
2107
2108                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2109                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2110                 else
2111                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2112                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2113
2114                 /* Force a redraw */
2115 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
2116                 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2117                                                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2118                 gtk_tree_view_column_queue_resize (tree_column);
2119 #endif
2120         }
2121 }
2122
2123 void
2124 modest_folder_view_set_style (ModestFolderView *self,
2125                               ModestFolderViewStyle style)
2126 {
2127         ModestFolderViewPrivate *priv;
2128
2129         g_return_if_fail (self);
2130         
2131         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2132
2133         priv->style = style;
2134 }
2135
2136 void
2137 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2138                                                              const gchar *account_id)
2139 {
2140         ModestFolderViewPrivate *priv;
2141         GtkTreeModel *model;
2142
2143         g_return_if_fail (self);
2144         
2145         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2146
2147         /* This will be used by the filter_row callback,
2148          * to decided which rows to show: */
2149         if (priv->visible_account_id) {
2150                 g_free (priv->visible_account_id);
2151                 priv->visible_account_id = NULL;
2152         }
2153         if (account_id)
2154                 priv->visible_account_id = g_strdup (account_id);
2155
2156         /* Refilter */
2157         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2158         if (GTK_IS_TREE_MODEL_FILTER (model))
2159                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2160
2161         /* Save settings to gconf */
2162         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2163                                    MODEST_CONF_FOLDER_VIEW_KEY);
2164 }
2165
2166 const gchar *
2167 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2168 {
2169         ModestFolderViewPrivate *priv;
2170
2171         g_return_val_if_fail (self, NULL);
2172         
2173         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2174
2175         return (const gchar *) priv->visible_account_id;
2176 }
2177
2178 static gboolean
2179 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2180 {
2181         do {
2182                 GtkTreeIter child;
2183                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2184
2185                 gtk_tree_model_get (model, iter, 
2186                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
2187                                     &type, -1);
2188                         
2189                 gboolean result = FALSE;
2190                 if (type == TNY_FOLDER_TYPE_INBOX) {
2191                         result = TRUE;
2192                 }               
2193                 if (result) {
2194                         *inbox_iter = *iter;
2195                         return TRUE;
2196                 }
2197
2198                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2199                         if (find_inbox_iter (model, &child, inbox_iter))
2200                                 return TRUE;
2201                 }
2202
2203         } while (gtk_tree_model_iter_next (model, iter));
2204
2205         return FALSE;
2206 }
2207
2208
2209
2210
2211 void 
2212 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2213 {
2214         GtkTreeModel *model;
2215         GtkTreeIter iter, inbox_iter;
2216         GtkTreeSelection *sel;
2217         GtkTreePath *path = NULL;
2218
2219         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2220         if (!model)
2221                 return;
2222
2223         expand_root_items (self);
2224         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2225
2226         gtk_tree_model_get_iter_first (model, &iter);
2227
2228         if (find_inbox_iter (model, &iter, &inbox_iter))
2229                 path = gtk_tree_model_get_path (model, &inbox_iter);
2230         else
2231                 path = gtk_tree_path_new_first ();
2232
2233         /* Select the row and free */
2234         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2235         gtk_tree_path_free (path);
2236
2237         /* set focus */
2238         gtk_widget_grab_focus (GTK_WIDGET(self));
2239 }
2240
2241
2242 /* recursive */
2243 static gboolean
2244 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, 
2245                   TnyFolder* folder)
2246 {
2247         do {
2248                 GtkTreeIter child;
2249                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2250                 TnyFolder* a_folder;
2251                 gchar *name = NULL;
2252                 
2253                 gtk_tree_model_get (model, iter, 
2254                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2255                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2256                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type, 
2257                                     -1);                
2258                 g_free (name);
2259
2260                 if (folder == a_folder) {
2261                         g_object_unref (a_folder);
2262                         *folder_iter = *iter;
2263                         return TRUE;
2264                 }
2265                 g_object_unref (a_folder);
2266                 
2267                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2268                         if (find_folder_iter (model, &child, folder_iter, folder)) 
2269                                 return TRUE;
2270                 }
2271
2272         } while (gtk_tree_model_iter_next (model, iter));
2273
2274         return FALSE;
2275 }
2276
2277
2278 static void
2279 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath  *path, GtkTreeIter *iter,
2280                                      ModestFolderView *self)
2281 {
2282         ModestFolderViewPrivate *priv = NULL;
2283         GtkTreeSelection *sel;
2284
2285         if (!MODEST_IS_FOLDER_VIEW(self))
2286                 return;
2287         
2288         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2289         
2290         if (priv->folder_to_select) {
2291                 
2292                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2293                                                        FALSE)) {
2294                         GtkTreePath *path;
2295                         path = gtk_tree_model_get_path (tree_model, iter);
2296                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2297                         
2298                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2299
2300                         gtk_tree_selection_select_iter (sel, iter);
2301                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2302
2303                         gtk_tree_path_free (path);
2304                 
2305                 }
2306
2307                 /* Disable next */
2308                 modest_folder_view_disable_next_folder_selection (self);
2309 /*              g_object_unref (priv->folder_to_select); */
2310 /*              priv->folder_to_select = NULL; */
2311         }
2312 }
2313
2314
2315 void
2316 modest_folder_view_disable_next_folder_selection (ModestFolderView *self) 
2317 {
2318         ModestFolderViewPrivate *priv = NULL;
2319
2320         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));        
2321         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2322
2323         if (priv->folder_to_select)
2324                 g_object_unref(priv->folder_to_select);
2325         
2326         priv->folder_to_select = NULL;
2327 }
2328
2329 gboolean
2330 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder, 
2331                                   gboolean after_change)
2332 {
2333         GtkTreeModel *model;
2334         GtkTreeIter iter, folder_iter;
2335         GtkTreeSelection *sel;
2336         ModestFolderViewPrivate *priv = NULL;
2337         
2338         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);     
2339         g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);   
2340                 
2341         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2342
2343         if (after_change) {
2344
2345                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2346                 gtk_tree_selection_unselect_all (sel);
2347
2348                 if (priv->folder_to_select)
2349                         g_object_unref(priv->folder_to_select);
2350                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2351                 return TRUE;
2352         }
2353                 
2354         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2355         if (!model)
2356                 return FALSE;
2357
2358                 
2359         gtk_tree_model_get_iter_first (model, &iter);
2360         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2361                 GtkTreePath *path;
2362
2363                 path = gtk_tree_model_get_path (model, &folder_iter);
2364                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2365
2366                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2367                 gtk_tree_selection_select_iter (sel, &folder_iter);
2368                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2369
2370                 gtk_tree_path_free (path);
2371                 return TRUE;
2372         }
2373         return FALSE;
2374 }
2375
2376
2377 void 
2378 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2379 {
2380         /* Copy selection */
2381         _clipboard_set_selected_data (folder_view, FALSE);
2382 }
2383
2384 void 
2385 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2386 {
2387         ModestFolderViewPrivate *priv = NULL;
2388         GtkTreeModel *model = NULL;
2389         const gchar **hidding = NULL;
2390         guint i, n_selected;
2391
2392         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2393         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2394
2395         /* Copy selection */
2396         if (!_clipboard_set_selected_data (folder_view, TRUE))
2397                 return;
2398
2399         /* Get hidding ids */
2400         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
2401         
2402         /* Clear hidding array created by previous cut operation */
2403         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2404
2405         /* Copy hidding array */
2406         priv->n_selected = n_selected;
2407         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2408         for (i=0; i < n_selected; i++) 
2409                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2410
2411         /* Hide cut folders */
2412         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2413         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2414 }
2415
2416 void
2417 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2418                                ModestFolderView *folder_view_dst)
2419 {
2420         GtkTreeModel *filter_model = NULL;
2421         GtkTreeModel *model = NULL;
2422         GtkTreeModel *new_filter_model = NULL;
2423         
2424         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2425         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2426
2427         /* Get src model*/
2428         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2429         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2430
2431         /* Build new filter model */
2432         new_filter_model = gtk_tree_model_filter_new (model, NULL);     
2433         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2434                                                 filter_row,
2435                                                 folder_view_dst,
2436                                                 NULL);
2437         /* Set copied model */
2438         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2439
2440         /* Free */
2441         g_object_unref (new_filter_model);
2442 }
2443
2444 void
2445 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2446                                           gboolean show)
2447 {
2448         GtkTreeModel *model = NULL;
2449         ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2450         priv->show_non_move = show;
2451 /*      modest_folder_view_update_model(folder_view, */
2452 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2453
2454         /* Hide special folders */
2455         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2456         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2457                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2458         }
2459 }
2460
2461 /* Returns FALSE if it did not selected anything */
2462 static gboolean
2463 _clipboard_set_selected_data (ModestFolderView *folder_view,
2464                               gboolean delete)
2465 {
2466         ModestFolderViewPrivate *priv = NULL;
2467         TnyFolderStore *folder = NULL;
2468         gboolean retval = FALSE;
2469
2470         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2471         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2472                 
2473         /* Set selected data on clipboard   */
2474         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2475         folder = modest_folder_view_get_selected (folder_view);
2476
2477         /* Do not allow to select an account */
2478         if (TNY_IS_FOLDER (folder)) {
2479                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2480                 retval = TRUE;
2481         }
2482
2483         /* Free */
2484         g_object_unref (folder);
2485
2486         return retval;
2487 }
2488
2489 static void
2490 _clear_hidding_filter (ModestFolderView *folder_view) 
2491 {
2492         ModestFolderViewPrivate *priv;
2493         guint i;
2494         
2495         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
2496         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2497
2498         if (priv->hidding_ids != NULL) {
2499                 for (i=0; i < priv->n_selected; i++) 
2500                         g_free (priv->hidding_ids[i]);
2501                 g_free(priv->hidding_ids);
2502         }       
2503 }
2504
2505