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