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