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