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