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