2007-06-27 Murray Cumming <murrayc@murrayc.com
[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                 modest_folder_view_select_first_inbox_or_local (self);
776                 priv->reselect = FALSE;
777         }
778         return FALSE;
779 }
780
781 GtkWidget*
782 modest_folder_view_new (TnyFolderStoreQuery *query)
783 {
784         GObject *self;
785         ModestFolderViewPrivate *priv;
786         GtkTreeSelection *sel;
787         
788         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
789         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
790
791         if (query)
792                 priv->query = g_object_ref (query);
793         
794         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
795         priv->changed_signal = g_signal_connect (sel, "changed",
796                                                  G_CALLBACK (on_selection_changed), self);
797
798         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
799
800         return GTK_WIDGET(self);
801 }
802
803 /* this feels dirty; any other way to expand all the root items? */
804 static void
805 expand_root_items (ModestFolderView *self)
806 {
807         GtkTreePath *path;
808         path = gtk_tree_path_new_first ();
809
810         /* all folders should have child items, so.. */
811         while (gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE))
812                 gtk_tree_path_next (path);
813         
814         gtk_tree_path_free (path);
815 }
816
817 /*
818  * We use this function to implement the
819  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
820  * account in this case, and the local folders.
821  */
822 static gboolean 
823 filter_row (GtkTreeModel *model,
824             GtkTreeIter *iter,
825             gpointer data)
826 {
827         ModestFolderViewPrivate *priv;
828         gboolean retval = TRUE;
829         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
830         GObject *instance = NULL;
831         const gchar *id = NULL;
832         guint i;
833         gboolean found = FALSE;
834         gboolean cleared = FALSE;
835         
836         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
837         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
838
839         gtk_tree_model_get (model, iter,
840                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
841                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
842                             -1);
843
844         /* Do not show if there is no instance, this could indeed
845            happen when the model is being modified while it's being
846            drawn. This could occur for example when moving folders
847            using drag&drop */
848         if (!instance)
849                 return FALSE;
850
851         if (type == TNY_FOLDER_TYPE_ROOT) {
852                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
853                    account instead of a folder. */
854                 if (TNY_IS_ACCOUNT (instance)) {
855                         TnyAccount *acc = TNY_ACCOUNT (instance);
856                         const gchar *account_id = tny_account_get_id (acc);
857         
858                         /* If it isn't a special folder, 
859                          * don't show it unless it is the visible account: */
860                         if (!modest_tny_account_is_virtual_local_folders (acc) &&
861                                 strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) { 
862                                 /* Show only the visible account id */
863                                 if (priv->visible_account_id && strcmp (account_id, priv->visible_account_id))
864                                         retval = FALSE;
865                         }
866                         
867                         /* Never show these to the user. They are merged into one folder 
868                          * in the local-folders account instead: */
869                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
870                                 retval = FALSE;
871                 }
872         }
873         
874         /* The virtual local-folders folder store is also shown by default. */
875
876         /* Check hiding (if necessary) */
877         cleared = modest_email_clipboard_cleared (priv->clipboard);            
878         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
879                 id = tny_folder_get_id (TNY_FOLDER(instance));
880                 if (priv->hidding_ids != NULL)
881                         for (i=0; i < priv->n_selected && !found; i++)
882                                 if (priv->hidding_ids[i] != NULL && id != NULL)
883                                         found = (!strcmp (priv->hidding_ids[i], id));
884                 
885                 retval = !found;
886         }
887
888         /* Free */
889         g_object_unref (instance);
890
891         return retval;
892 }
893
894 gboolean
895 modest_folder_view_update_model (ModestFolderView *self,
896                                  TnyAccountStore *account_store)
897 {
898         ModestFolderViewPrivate *priv;
899         GtkTreeModel *model /* , *old_model */;
900         /* TnyAccount *local_account; */
901         TnyList *model_as_list;
902
903         g_return_val_if_fail (account_store, FALSE);
904
905         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
906         
907         /* Notify that there is no folder selected */
908         g_signal_emit (G_OBJECT(self), 
909                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
910                        NULL, FALSE);
911         if (priv->cur_folder_store) {
912                 g_object_unref (priv->cur_folder_store);
913                 priv->cur_folder_store = NULL;
914         }
915
916         /* FIXME: the local accounts are not shown when the query
917            selects only the subscribed folders. */
918 /*      model        = tny_gtk_folder_store_tree_model_new (TRUE, priv->query); */
919         model        = tny_gtk_folder_store_tree_model_new (TRUE, NULL);
920         
921         /* Deal with the model via its TnyList Interface,
922          * filling the TnyList via a get_accounts() call: */
923         model_as_list = TNY_LIST(model);
924
925         /* Get the accounts: */
926         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
927                                         model_as_list,
928                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
929         g_object_unref (model_as_list);
930         model_as_list = NULL;   
931                                                      
932         GtkTreeModel *filter_model = NULL, *sortable = NULL;
933
934         sortable = gtk_tree_model_sort_new_with_model (model);
935         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
936                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
937                                               GTK_SORT_ASCENDING);
938         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
939                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
940                                          cmp_rows, NULL, NULL);
941
942         /* Create filter model */
943         filter_model = gtk_tree_model_filter_new (sortable, NULL);
944         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
945                                                 filter_row,
946                                                 self,
947                                                 NULL);
948 /*      if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) { */
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 /*      } */
955
956         /* Set new model */
957         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
958 /*      gtk_tree_view_set_model (GTK_TREE_VIEW(self),  */
959 /*                               (filter_model) ? filter_model : sortable); */
960
961         g_object_unref (model);
962         g_object_unref (filter_model);
963 /*      if (filter_model) */
964 /*              g_object_unref (filter_model); */
965                         
966         g_object_unref (sortable);
967
968         /* Force a reselection of the INBOX next time the widget is shown */
969         priv->reselect = TRUE;
970                         
971         return TRUE;
972 }
973
974
975 static void
976 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
977 {
978         GtkTreeModel            *model;
979         TnyFolderStore          *folder = NULL;
980         GtkTreeIter             iter;
981         ModestFolderView        *tree_view;
982         ModestFolderViewPrivate *priv;
983
984         g_return_if_fail (sel);
985         g_return_if_fail (user_data);
986         
987         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
988
989         if(!gtk_tree_selection_get_selected (sel, &model, &iter))
990                 return;
991
992         /* Notify the display name observers */
993         g_signal_emit (G_OBJECT(user_data),
994                        signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
995                        NULL);
996
997         tree_view = MODEST_FOLDER_VIEW (user_data);
998         gtk_tree_model_get (model, &iter,
999                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1000                             -1);
1001
1002         /* If the folder is the same do not notify */
1003         if (priv->cur_folder_store == folder && folder) {
1004                 g_object_unref (folder);
1005                 return;
1006         }
1007         
1008         /* Current folder was unselected */
1009         if (priv->cur_folder_store) {
1010                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1011                         tny_folder_sync (TNY_FOLDER(priv->cur_folder_store), TRUE, NULL);
1012                 /* expunge the message */
1013
1014                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1015                                priv->cur_folder_store, FALSE);
1016                 g_object_unref (priv->cur_folder_store);
1017                 priv->cur_folder_store = NULL;
1018         }
1019
1020         /* New current references */
1021         priv->cur_folder_store = folder;
1022
1023         /* New folder has been selected */
1024         g_signal_emit (G_OBJECT(tree_view),
1025                        signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1026                        0, priv->cur_folder_store, TRUE);
1027 }
1028
1029 TnyFolderStore *
1030 modest_folder_view_get_selected (ModestFolderView *self)
1031 {
1032         ModestFolderViewPrivate *priv;
1033
1034         g_return_val_if_fail (self, NULL);
1035         
1036         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1037         if (priv->cur_folder_store)
1038                 g_object_ref (priv->cur_folder_store);
1039
1040         return priv->cur_folder_store;
1041 }
1042
1043 static gint
1044 get_cmp_rows_type_pos (GObject *folder)
1045 {
1046         /* Remote accounts -> Local account -> MMC account .*/
1047         /* 0, 1, 2 */
1048         
1049         if (TNY_IS_ACCOUNT (folder) && 
1050                 modest_tny_account_is_virtual_local_folders (
1051                         TNY_ACCOUNT (folder))) {
1052                 return 1;
1053         } else if (TNY_IS_ACCOUNT (folder)) {
1054                 TnyAccount *account = TNY_ACCOUNT (folder);
1055                 const gchar *account_id = tny_account_get_id (account);
1056                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1057                         return 2;
1058                 else
1059                         return 0;
1060         }
1061         else {
1062                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1063                 return -1; /* Should never happen */
1064         }
1065 }
1066
1067 static gint
1068 get_cmp_subfolder_type_pos (TnyFolderType t)
1069 {
1070         /* Outbox, Drafts, Sent, User */
1071         /* 0, 1, 2, 3 */
1072
1073         switch (t) {
1074         case TNY_FOLDER_TYPE_OUTBOX:
1075                 return 0;
1076                 break;
1077         case TNY_FOLDER_TYPE_DRAFTS:
1078                 return 1;
1079                 break;
1080         case TNY_FOLDER_TYPE_SENT:
1081                 return 2;
1082                 break;
1083         default:
1084                 return 3;
1085         }
1086 }
1087
1088 /*
1089  * This function orders the mail accounts according to these rules:
1090  * 1st - remote accounts
1091  * 2nd - local account
1092  * 3rd - MMC account
1093  */
1094 static gint
1095 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1096           gpointer user_data)
1097 {
1098         gint cmp;
1099         gchar *name1 = NULL;
1100         gchar *name2 = NULL;
1101         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1102         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1103         GObject *folder1 = NULL;
1104         GObject *folder2 = NULL;
1105
1106         gtk_tree_model_get (tree_model, iter1,
1107                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1108                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1109                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1110                             -1);
1111         gtk_tree_model_get (tree_model, iter2,
1112                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1113                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1114                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1115                             -1);
1116
1117         if (type == TNY_FOLDER_TYPE_ROOT) {
1118                 /* Compare the types, so that 
1119                  * Remote accounts -> Local account -> MMC account .*/
1120                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1121                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1122                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1123                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1124                 if (pos1 <  pos2)
1125                         cmp = -1;
1126                 else if (pos1 > pos2)
1127                         cmp = 1;
1128                 else {
1129                         /* Compare items of the same type: */
1130                         
1131                         TnyAccount *account1 = NULL;
1132                         if (TNY_IS_ACCOUNT (folder1))
1133                                 account1 = TNY_ACCOUNT (folder1);
1134                                 
1135                         TnyAccount *account2 = NULL;
1136                         if (TNY_IS_ACCOUNT (folder2))
1137                                 account2 = TNY_ACCOUNT (folder2);
1138                                 
1139                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1140                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1141         
1142                         if (!account_id && !account_id2)
1143                                 return 0;
1144                         else if (!account_id)
1145                                 return -1;
1146                         else if (!account_id2)
1147                                 return +1;
1148                         else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1149                                 cmp = +1;
1150                         else
1151                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1152                 }
1153         } else {
1154                 GtkTreeIter parent;
1155                 gboolean has_parent;
1156                 gint cmp1 = 0, cmp2 = 0;
1157                 /* get the parent to know if it's a local folder */
1158                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1159                 if (has_parent) {
1160                         GObject *parent_folder;
1161                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1162                         gtk_tree_model_get (tree_model, &parent, 
1163                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1164                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1165                                             -1);
1166                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1167                             TNY_IS_ACCOUNT (parent_folder) &&
1168                             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1169                                 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_folder_type (TNY_FOLDER (folder1)));
1170                                 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_folder_type (TNY_FOLDER (folder2)));
1171                         }
1172                         g_object_unref (parent_folder);
1173                 }
1174                 if (cmp1 == cmp2)
1175                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1176                 else 
1177                         cmp = (cmp1 - cmp2);
1178         }
1179         
1180         if (folder1)
1181                 g_object_unref(G_OBJECT(folder1));
1182         if (folder2)
1183                 g_object_unref(G_OBJECT(folder2));
1184
1185         g_free (name1);
1186         g_free (name2);
1187
1188         return cmp;     
1189 }
1190
1191 /*****************************************************************************/
1192 /*                        DRAG and DROP stuff                                */
1193 /*****************************************************************************/
1194
1195 /*
1196  * This function fills the #GtkSelectionData with the row and the
1197  * model that has been dragged. It's called when this widget is a
1198  * source for dnd after the event drop happened
1199  */
1200 static void
1201 on_drag_data_get (GtkWidget *widget, 
1202                   GdkDragContext *context, 
1203                   GtkSelectionData *selection_data, 
1204                   guint info, 
1205                   guint time, 
1206                   gpointer data)
1207 {
1208         GtkTreeSelection *selection;
1209         GtkTreeModel *model;
1210         GtkTreeIter iter;
1211         GtkTreePath *source_row;
1212
1213         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1214         gtk_tree_selection_get_selected (selection, &model, &iter);
1215         source_row = gtk_tree_model_get_path (model, &iter);
1216
1217         gtk_tree_set_row_drag_data (selection_data,
1218                                     model,
1219                                     source_row);
1220
1221         gtk_tree_path_free (source_row);
1222 }
1223
1224 typedef struct _DndHelper {
1225         gboolean delete_source;
1226         GtkTreePath *source_row;
1227         GdkDragContext *context;
1228         guint time;
1229 } DndHelper;
1230
1231
1232 /*
1233  * This function is the callback of the
1234  * modest_mail_operation_xfer_msgs () and
1235  * modest_mail_operation_xfer_folder() calls. We check here if the
1236  * message/folder was correctly asynchronously transferred. The reason
1237  * to use the same callback is that the code is the same, it only has
1238  * to check that the operation went fine and then finalize the drag
1239  * and drop action
1240  */
1241 static void
1242 on_progress_changed (ModestMailOperation *mail_op, 
1243                      ModestMailOperationState *state,
1244                      gpointer user_data)
1245 {
1246         gboolean success;
1247         DndHelper *helper;
1248
1249         helper = (DndHelper *) user_data;
1250
1251         if (!state->finished)
1252                 return;
1253
1254         if (state->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1255                 success = TRUE;
1256         } else {
1257                 success = FALSE;
1258         }
1259
1260         /* Notify the drag source. Never call delete, the monitor will
1261            do the job if needed */
1262         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1263
1264         /* Free the helper */
1265         gtk_tree_path_free (helper->source_row);        
1266         g_slice_free (DndHelper, helper);
1267 }
1268
1269 /*
1270  * This function is used by drag_data_received_cb to manage drag and
1271  * drop of a header, i.e, and drag from the header view to the folder
1272  * view.
1273  */
1274 static void
1275 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1276                                 GtkTreeModel *dest_model,
1277                                 GtkTreePath  *dest_row,
1278                                 DndHelper    *helper)
1279 {
1280         TnyList *headers = NULL;
1281         TnyHeader *header = NULL;
1282         TnyFolder *folder = NULL;
1283         ModestMailOperation *mail_op = NULL;
1284         GtkTreeIter source_iter, dest_iter;
1285
1286         g_return_if_fail (GTK_IS_TREE_MODEL(source_model));
1287         g_return_if_fail (GTK_IS_TREE_MODEL(dest_model));
1288         g_return_if_fail (dest_row);
1289         g_return_if_fail (helper);
1290         
1291         /* Get header */
1292         gtk_tree_model_get_iter (source_model, &source_iter, helper->source_row);
1293         gtk_tree_model_get (source_model, &source_iter, 
1294                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1295                             &header, -1);
1296         if (!TNY_IS_HEADER(header)) {
1297                 g_warning ("BUG: %s could not get a valid header", __FUNCTION__);
1298                 goto cleanup;
1299         }
1300         
1301         /* Get Folder */
1302         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1303         gtk_tree_model_get (dest_model, &dest_iter, 
1304                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1305                             &folder, -1);
1306
1307         if (!TNY_IS_FOLDER(folder)) {
1308                 g_warning ("BUG: %s could not get a valid folder", __FUNCTION__);
1309                 goto cleanup;
1310         }
1311
1312         /* Transfer message */
1313         mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
1314         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1315                                          mail_op);
1316         g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1317                           G_CALLBACK (on_progress_changed), helper);
1318
1319         headers = tny_simple_list_new ();
1320         tny_list_append (headers, G_OBJECT (header));
1321         modest_mail_operation_xfer_msgs (mail_op, 
1322                                          headers, 
1323                                          folder, 
1324                                          helper->delete_source, 
1325                                          NULL, NULL);
1326         
1327         /* Frees */
1328 cleanup:
1329         if (G_IS_OBJECT(mail_op))
1330                 g_object_unref (G_OBJECT (mail_op));
1331         if (G_IS_OBJECT(header))
1332                 g_object_unref (G_OBJECT (header));
1333         if (G_IS_OBJECT(folder))
1334                 g_object_unref (G_OBJECT (folder));
1335         if (G_IS_OBJECT(headers))
1336                 g_object_unref (headers);
1337 }
1338
1339 /*
1340  * This function is used by drag_data_received_cb to manage drag and
1341  * drop of a folder, i.e, and drag from the folder view to the same
1342  * folder view.
1343  */
1344 static void
1345 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
1346                                 GtkTreeModel     *dest_model,
1347                                 GtkTreePath      *dest_row,
1348                                 GtkSelectionData *selection_data,
1349                                 DndHelper        *helper)
1350 {
1351         ModestMailOperation *mail_op;
1352         GtkTreeIter parent_iter, iter;
1353         TnyFolderStore *parent_folder;
1354         TnyFolder *folder;
1355
1356         /* Check if the drag is possible */
1357 /*      if (!gtk_tree_path_compare (helper->source_row, dest_row) || */
1358 /*          !gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (dest_model), */
1359 /*                                                 dest_row, */
1360 /*                                                 selection_data)) { */
1361         if (!gtk_tree_path_compare (helper->source_row, dest_row)) {
1362
1363                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1364                 gtk_tree_path_free (helper->source_row);        
1365                 g_slice_free (DndHelper, helper);
1366                 return;
1367         }
1368
1369         /* Get data */
1370         gtk_tree_model_get_iter (source_model, &parent_iter, dest_row);
1371         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1372         gtk_tree_model_get (source_model, &parent_iter, 
1373                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1374                             &parent_folder, -1);
1375         gtk_tree_model_get (source_model, &iter,
1376                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1377                             &folder, -1);
1378
1379         /* Do the mail operation */
1380         mail_op = modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1381                                                                  NULL,
1382                                                                  modest_ui_actions_move_folder_error_handler,
1383                                                                  NULL);
1384         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), 
1385                                          mail_op);
1386         g_signal_connect (G_OBJECT (mail_op), "progress-changed",
1387                           G_CALLBACK (on_progress_changed), helper);
1388
1389         modest_mail_operation_xfer_folder (mail_op, 
1390                                            folder, 
1391                                            parent_folder,
1392                                            helper->delete_source);
1393         
1394         /* Frees */
1395         g_object_unref (G_OBJECT (parent_folder));
1396         g_object_unref (G_OBJECT (folder));
1397         g_object_unref (G_OBJECT (mail_op));
1398 }
1399
1400 /*
1401  * This function receives the data set by the "drag-data-get" signal
1402  * handler. This information comes within the #GtkSelectionData. This
1403  * function will manage both the drags of folders of the treeview and
1404  * drags of headers of the header view widget.
1405  */
1406 static void 
1407 on_drag_data_received (GtkWidget *widget, 
1408                        GdkDragContext *context, 
1409                        gint x, 
1410                        gint y, 
1411                        GtkSelectionData *selection_data, 
1412                        guint target_type, 
1413                        guint time, 
1414                        gpointer data)
1415 {
1416         GtkWidget *source_widget;
1417         GtkTreeModel *dest_model, *source_model;
1418         GtkTreePath *source_row, *dest_row;
1419         GtkTreeViewDropPosition pos;
1420         gboolean success = FALSE, delete_source = FALSE;
1421         DndHelper *helper = NULL; 
1422
1423         /* Do not allow further process */
1424         g_signal_stop_emission_by_name (widget, "drag-data-received");
1425         source_widget = gtk_drag_get_source_widget (context);
1426
1427         /* Get the action */
1428         if (context->action == GDK_ACTION_MOVE) {
1429                 delete_source = TRUE;
1430
1431                 /* Notify that there is no folder selected. We need to
1432                    do this in order to update the headers view (and
1433                    its monitors, because when moving, the old folder
1434                    won't longer exist. We can not wait for the end of
1435                    the operation, because the operation won't start if
1436                    the folder is in use */
1437                 if (source_widget == widget) {
1438                         ModestFolderViewPrivate *priv;
1439
1440                         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1441                         if (priv->cur_folder_store) {
1442                                 g_object_unref (priv->cur_folder_store);
1443                                 priv->cur_folder_store = NULL;
1444                         }
1445
1446                         g_signal_emit (G_OBJECT (widget), 
1447                                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0, NULL, FALSE);
1448                 }
1449         }
1450
1451         /* Check if the get_data failed */
1452         if (selection_data == NULL || selection_data->length < 0)
1453                 gtk_drag_finish (context, success, FALSE, time);
1454
1455         /* Get the models */
1456         gtk_tree_get_row_drag_data (selection_data,
1457                                     &source_model,
1458                                     &source_row);
1459
1460         /* Select the destination model */
1461         if (source_widget == widget) {
1462                 dest_model = source_model;
1463         } else {
1464                 dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1465         }
1466
1467         /* Get the path to the destination row. Can not call
1468            gtk_tree_view_get_drag_dest_row() because the source row
1469            is not selected anymore */
1470         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1471                                            &dest_row, &pos);
1472
1473         /* Only allow drops IN other rows */
1474         if (!dest_row || pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)
1475                 gtk_drag_finish (context, success, FALSE, time);
1476
1477         /* Create the helper */
1478         helper = g_slice_new0 (DndHelper);
1479         helper->delete_source = delete_source;
1480         helper->source_row = gtk_tree_path_copy (source_row);
1481         helper->context = context;
1482         helper->time = time;
1483
1484         /* Drags from the header view */
1485         if (source_widget != widget) {
1486
1487                 drag_and_drop_from_header_view (source_model,
1488                                                 dest_model,
1489                                                 dest_row,
1490                                                 helper);
1491         } else {
1492
1493
1494                 drag_and_drop_from_folder_view (source_model,
1495                                                 dest_model,
1496                                                 dest_row,
1497                                                 selection_data, 
1498                                                 helper);
1499         }
1500
1501         /* Frees */
1502         gtk_tree_path_free (source_row);
1503         gtk_tree_path_free (dest_row);
1504 }
1505
1506 /*
1507  * We define a "drag-drop" signal handler because we do not want to
1508  * use the default one, because the default one always calls
1509  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1510  * signal handler, because there we have all the information available
1511  * to know if the dnd was a success or not.
1512  */
1513 static gboolean
1514 drag_drop_cb (GtkWidget      *widget,
1515               GdkDragContext *context,
1516               gint            x,
1517               gint            y,
1518               guint           time,
1519               gpointer        user_data) 
1520 {
1521         gpointer target;
1522
1523         if (!context->targets)
1524                 return FALSE;
1525
1526         /* Check if we're dragging a folder row */
1527         target = gtk_drag_dest_find_target (widget, context, NULL);
1528
1529         /* Request the data from the source. */
1530         gtk_drag_get_data(widget, context, target, time);
1531
1532     return TRUE;
1533 }
1534
1535 /*
1536  * This function expands a node of a tree view if it's not expanded
1537  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1538  * does that, so that's why they're here.
1539  */
1540 static gint
1541 expand_row_timeout (gpointer data)
1542 {
1543         GtkTreeView *tree_view = data;
1544         GtkTreePath *dest_path = NULL;
1545         GtkTreeViewDropPosition pos;
1546         gboolean result = FALSE;
1547         
1548         GDK_THREADS_ENTER ();
1549         
1550         gtk_tree_view_get_drag_dest_row (tree_view,
1551                                          &dest_path,
1552                                          &pos);
1553         
1554         if (dest_path &&
1555             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1556              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1557                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1558                 gtk_tree_path_free (dest_path);
1559         }
1560         else {
1561                 if (dest_path)
1562                         gtk_tree_path_free (dest_path);
1563                 
1564                 result = TRUE;
1565         }
1566         
1567         GDK_THREADS_LEAVE ();
1568
1569         return result;
1570 }
1571
1572 /*
1573  * This function is called whenever the pointer is moved over a widget
1574  * while dragging some data. It installs a timeout that will expand a
1575  * node of the treeview if not expanded yet. This function also calls
1576  * gdk_drag_status in order to set the suggested action that will be
1577  * used by the "drag-data-received" signal handler to know if we
1578  * should do a move or just a copy of the data.
1579  */
1580 static gboolean
1581 on_drag_motion (GtkWidget      *widget,
1582                 GdkDragContext *context,
1583                 gint            x,
1584                 gint            y,
1585                 guint           time,
1586                 gpointer        user_data)  
1587 {
1588         GtkTreeViewDropPosition pos;
1589         GtkTreePath *dest_row;
1590         ModestFolderViewPrivate *priv;
1591         GdkDragAction suggested_action;
1592         gboolean valid_location = FALSE;
1593
1594         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1595
1596         if (priv->timer_expander != 0) {
1597                 g_source_remove (priv->timer_expander);
1598                 priv->timer_expander = 0;
1599         }
1600
1601         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1602                                            x, y,
1603                                            &dest_row,
1604                                            &pos);
1605
1606         /* Do not allow drops between folders */
1607         if (!dest_row ||
1608             pos == GTK_TREE_VIEW_DROP_BEFORE ||
1609             pos == GTK_TREE_VIEW_DROP_AFTER) {
1610                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1611                 gdk_drag_status(context, 0, time);
1612                 valid_location = FALSE;
1613                 goto out;
1614         } else {
1615                 valid_location = TRUE;
1616         }
1617
1618         /* Expand the selected row after 1/2 second */
1619         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1620                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1621                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1622         }
1623
1624         /* Select the desired action. By default we pick MOVE */
1625         suggested_action = GDK_ACTION_MOVE;
1626
1627         if (context->actions == GDK_ACTION_COPY)
1628             gdk_drag_status(context, GDK_ACTION_COPY, time);
1629         else if (context->actions == GDK_ACTION_MOVE)
1630             gdk_drag_status(context, GDK_ACTION_MOVE, time);
1631         else if (context->actions & suggested_action)
1632             gdk_drag_status(context, suggested_action, time);
1633         else
1634             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1635
1636  out:
1637         if (dest_row)
1638                 gtk_tree_path_free (dest_row);
1639         g_signal_stop_emission_by_name (widget, "drag-motion");
1640         return valid_location;
1641 }
1642
1643
1644 /* Folder view drag types */
1645 const GtkTargetEntry folder_view_drag_types[] =
1646 {
1647         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
1648         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,    MODEST_HEADER_ROW }
1649 };
1650
1651 /*
1652  * This function sets the treeview as a source and a target for dnd
1653  * events. It also connects all the requirede signals.
1654  */
1655 static void
1656 setup_drag_and_drop (GtkTreeView *self)
1657 {
1658         /* Set up the folder view as a dnd destination. Set only the
1659            highlight flag, otherwise gtk will have a different
1660            behaviour */
1661         gtk_drag_dest_set (GTK_WIDGET (self),
1662                            GTK_DEST_DEFAULT_HIGHLIGHT,
1663                            folder_view_drag_types,
1664                            G_N_ELEMENTS (folder_view_drag_types),
1665                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
1666
1667         g_signal_connect (G_OBJECT (self),
1668                           "drag_data_received",
1669                           G_CALLBACK (on_drag_data_received),
1670                           NULL);
1671
1672
1673         /* Set up the treeview as a dnd source */
1674         gtk_drag_source_set (GTK_WIDGET (self),
1675                              GDK_BUTTON1_MASK,
1676                              folder_view_drag_types,
1677                              G_N_ELEMENTS (folder_view_drag_types),
1678                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1679
1680         g_signal_connect (G_OBJECT (self),
1681                           "drag_motion",
1682                           G_CALLBACK (on_drag_motion),
1683                           NULL);
1684         
1685         g_signal_connect (G_OBJECT (self),
1686                           "drag_data_get",
1687                           G_CALLBACK (on_drag_data_get),
1688                           NULL);
1689
1690         g_signal_connect (G_OBJECT (self),
1691                           "drag_drop",
1692                           G_CALLBACK (drag_drop_cb),
1693                           NULL);
1694 }
1695
1696 /*
1697  * This function manages the navigation through the folders using the
1698  * keyboard or the hardware keys in the device
1699  */
1700 static gboolean
1701 on_key_pressed (GtkWidget *self,
1702                 GdkEventKey *event,
1703                 gpointer user_data)
1704 {
1705         GtkTreeSelection *selection;
1706         GtkTreeIter iter;
1707         GtkTreeModel *model;
1708         gboolean retval = FALSE;
1709
1710         /* Up and Down are automatically managed by the treeview */
1711         if (event->keyval == GDK_Return) {
1712                 /* Expand/Collapse the selected row */
1713                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1714                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1715                         GtkTreePath *path;
1716
1717                         path = gtk_tree_model_get_path (model, &iter);
1718
1719                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
1720                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
1721                         else
1722                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
1723                         gtk_tree_path_free (path);
1724                 }
1725                 /* No further processing */
1726                 retval = TRUE;
1727         }
1728
1729         return retval;
1730 }
1731
1732 /*
1733  * We listen to the changes in the local folder account name key,
1734  * because we want to show the right name in the view. The local
1735  * folder account name corresponds to the device name in the Maemo
1736  * version. We do this because we do not want to query gconf on each
1737  * tree view refresh. It's better to cache it and change whenever
1738  * necessary.
1739  */
1740 static void 
1741 on_configuration_key_changed (ModestConf* conf, 
1742                               const gchar *key, 
1743                               ModestConfEvent event, 
1744                               ModestFolderView *self)
1745 {
1746         ModestFolderViewPrivate *priv;
1747
1748         if (!key)
1749                 return;
1750
1751         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1752         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1753
1754         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
1755                 g_free (priv->local_account_name);
1756
1757                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
1758                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
1759                 else
1760                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
1761                                                                            MODEST_CONF_DEVICE_NAME, NULL);
1762
1763                 /* Force a redraw */
1764 #if GTK_CHECK_VERSION(2, 8, 0) /* gtk_tree_view_column_queue_resize is only available in GTK+ 2.8 */
1765                 GtkTreeViewColumn * tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
1766                                                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
1767                 gtk_tree_view_column_queue_resize (tree_column);
1768 #endif
1769         }
1770 }
1771
1772 void 
1773 modest_folder_view_set_style (ModestFolderView *self,
1774                               ModestFolderViewStyle style)
1775 {
1776         ModestFolderViewPrivate *priv;
1777
1778         g_return_if_fail (self);
1779         
1780         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1781
1782         priv->style = style;
1783 }
1784
1785 void
1786 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
1787                                                              const gchar *account_id)
1788 {
1789         ModestFolderViewPrivate *priv;
1790         GtkTreeModel *model;
1791
1792         g_return_if_fail (self);
1793         
1794         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1795
1796         /* This will be used by the filter_row callback,
1797          * to decided which rows to show: */
1798         if (priv->visible_account_id) {
1799                 g_free (priv->visible_account_id);
1800                 priv->visible_account_id = NULL;
1801         }
1802         if (account_id)
1803                 priv->visible_account_id = g_strdup (account_id);
1804
1805         /* Refilter */
1806         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1807         if (GTK_IS_TREE_MODEL_FILTER (model))
1808                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1809
1810         /* Save settings to gconf */
1811         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
1812                                    MODEST_CONF_FOLDER_VIEW_KEY);
1813 }
1814
1815 const gchar *
1816 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
1817 {
1818         ModestFolderViewPrivate *priv;
1819
1820         g_return_val_if_fail (self, NULL);
1821         
1822         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1823
1824         return (const gchar *) priv->visible_account_id;
1825 }
1826
1827 static gboolean
1828 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
1829 {
1830         do {
1831                 GtkTreeIter child;
1832                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1833                 gchar *name = NULL;
1834
1835                 gtk_tree_model_get (model, iter, 
1836                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
1837                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
1838                                     &type, -1);
1839
1840                 /*
1841                 printf ("DEBUG: %s: name=%s, type=%d, TNY_FOLDER_TYPE_INBOX=%d\n", 
1842                         __FUNCTION__, name, type, TNY_FOLDER_TYPE_INBOX);
1843                 */
1844                         
1845                 gboolean result = FALSE;
1846                 if (type == TNY_FOLDER_TYPE_INBOX) {
1847                         result = TRUE;
1848                 } else if (type == TNY_FOLDER_TYPE_NORMAL) {
1849                         /* tinymail's camel implementation only provides TNY_FOLDER_TYPE_NORMAL
1850                          * when getting folders from the cache, before connectin, so we do 
1851                          * an extra check. We could fix this in tinymail, but it's easier 
1852                          * to do here.
1853                          */
1854                          if (strcmp (name, "Inbox") == 0)
1855                                 result = TRUE;
1856                 }
1857                 
1858                 g_free (name);
1859                 
1860                 if (result) {
1861                         *inbox_iter = *iter;
1862                         return TRUE;
1863                 }
1864
1865                 if (gtk_tree_model_iter_children (model, &child, iter)) {
1866                         if (find_inbox_iter (model, &child, inbox_iter))
1867                                 return TRUE;
1868                 }
1869
1870         } while (gtk_tree_model_iter_next (model, iter));
1871
1872         return FALSE;
1873 }
1874
1875 void 
1876 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
1877 {
1878         GtkTreeModel *model;
1879         GtkTreeIter iter, inbox_iter;
1880         GtkTreeSelection *sel;
1881
1882 /*      /\* Do not set it if the folder view was not painted *\/ */
1883 /*      if (!GTK_WIDGET_MAPPED (self)) */
1884 /*              return; */
1885
1886         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1887         if (!model)
1888                 return;
1889
1890         expand_root_items (self);
1891         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1892
1893         gtk_tree_model_get_iter_first (model, &iter);
1894         if (find_inbox_iter (model, &iter, &inbox_iter)) {
1895                 gtk_tree_selection_select_iter (sel, &inbox_iter);
1896         }
1897         else {
1898                 gtk_tree_model_get_iter_first (model, &iter);
1899                 gtk_tree_selection_select_iter (sel, &iter);
1900         }
1901 }
1902
1903 void 
1904 modest_folder_view_copy_selection (ModestFolderView *folder_view)
1905 {
1906         /* Copy selection */
1907         _clipboard_set_selected_data (folder_view, FALSE);
1908 }
1909
1910 void 
1911 modest_folder_view_cut_selection (ModestFolderView *folder_view)
1912 {
1913         ModestFolderViewPrivate *priv = NULL;
1914         GtkTreeModel *model = NULL;
1915         const gchar **hidding = NULL;
1916         guint i, n_selected;
1917
1918         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1919         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1920
1921         /* Copy selection */
1922         _clipboard_set_selected_data (folder_view, TRUE);
1923
1924         /* Get hidding ids */
1925         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1926         
1927         /* Clear hidding array created by previous cut operation */
1928         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
1929
1930         /* Copy hidding array */
1931         priv->n_selected = n_selected;
1932         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1933         for (i=0; i < n_selected; i++) 
1934                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1935
1936         /* Hide cut folders */
1937         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
1938         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1939 }
1940
1941 static void
1942 _clipboard_set_selected_data (ModestFolderView *folder_view,
1943                               gboolean delete)
1944 {
1945         ModestFolderViewPrivate *priv = NULL;
1946         TnyFolderStore *folder = NULL;
1947
1948         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
1949         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
1950                 
1951         /* Set selected data on clipboard   */
1952         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1953         folder = modest_folder_view_get_selected (folder_view);
1954         modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
1955
1956         /* Free */
1957         g_object_unref (folder);
1958 }
1959
1960 static void
1961 _clear_hidding_filter (ModestFolderView *folder_view) 
1962 {
1963         ModestFolderViewPrivate *priv;
1964         guint i;
1965         
1966         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
1967         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
1968
1969         if (priv->hidding_ids != NULL) {
1970                 for (i=0; i < priv->n_selected; i++) 
1971                         g_free (priv->hidding_ids[i]);
1972                 g_free(priv->hidding_ids);
1973         }       
1974 }