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