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