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