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