* Properly synchronizes the selected folder when closing the main window
[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 <tny-camel-account.h>
45 #include <modest-tny-account.h>
46 #include <modest-tny-folder.h>
47 #include <modest-tny-local-folders-account.h>
48 #include <modest-tny-outbox-account.h>
49 #include <modest-marshal.h>
50 #include <modest-icon-names.h>
51 #include <modest-tny-account-store.h>
52 #include <modest-text-utils.h>
53 #include <modest-runtime.h>
54 #include "modest-folder-view.h"
55 #include <modest-platform.h>
56 #include <modest-widget-memory.h>
57 #include <modest-ui-actions.h>
58 #include "modest-dnd.h"
59 #include "widgets/modest-window.h"
60
61 /* Folder view drag types */
62 const GtkTargetEntry folder_view_drag_types[] =
63 {
64         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, MODEST_FOLDER_ROW },
65         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
66 };
67
68 /* 'private'/'protected' functions */
69 static void modest_folder_view_class_init  (ModestFolderViewClass *klass);
70 static void modest_folder_view_init        (ModestFolderView *obj);
71 static void modest_folder_view_finalize    (GObject *obj);
72
73 static void         tny_account_store_view_init (gpointer g, 
74                                                  gpointer iface_data);
75
76 static void         modest_folder_view_set_account_store (TnyAccountStoreView *self, 
77                                                           TnyAccountStore     *account_store);
78
79 static void         on_selection_changed   (GtkTreeSelection *sel, 
80                                             gpointer data);
81
82 static void         on_account_removed     (TnyAccountStore *self, 
83                                             TnyAccount *account,
84                                             gpointer user_data);
85
86 static void         on_account_inserted    (TnyAccountStore *self, 
87                                             TnyAccount *account,
88                                             gpointer user_data);
89
90 static void         on_account_changed    (TnyAccountStore *self, 
91                                             TnyAccount *account,
92                                             gpointer user_data);
93
94 static gint         cmp_rows               (GtkTreeModel *tree_model, 
95                                             GtkTreeIter *iter1, 
96                                             GtkTreeIter *iter2,
97                                             gpointer user_data);
98
99 static gboolean     filter_row             (GtkTreeModel *model,
100                                             GtkTreeIter *iter,
101                                             gpointer data);
102
103 static gboolean     on_key_pressed         (GtkWidget *self,
104                                             GdkEventKey *event,
105                                             gpointer user_data);
106
107 static void         on_configuration_key_changed  (ModestConf* conf, 
108                                                    const gchar *key, 
109                                                    ModestConfEvent event,
110                                                    ModestConfNotificationId notification_id, 
111                                                    ModestFolderView *self);
112
113 /* DnD functions */
114 static void         on_drag_data_get       (GtkWidget *widget, 
115                                             GdkDragContext *context, 
116                                             GtkSelectionData *selection_data, 
117                                             guint info, 
118                                             guint time, 
119                                             gpointer data);
120
121 static void         on_drag_data_received  (GtkWidget *widget, 
122                                             GdkDragContext *context, 
123                                             gint x, 
124                                             gint y, 
125                                             GtkSelectionData *selection_data, 
126                                             guint info, 
127                                             guint time, 
128                                             gpointer data);
129
130 static gboolean     on_drag_motion         (GtkWidget      *widget,
131                                             GdkDragContext *context,
132                                             gint            x,
133                                             gint            y,
134                                             guint           time,
135                                             gpointer        user_data);
136
137 static void         expand_root_items (ModestFolderView *self);
138
139 static gint         expand_row_timeout     (gpointer data);
140
141 static void         setup_drag_and_drop    (GtkTreeView *self);
142
143 static gboolean     _clipboard_set_selected_data (ModestFolderView *folder_view, 
144                                                   gboolean delete);
145
146 static void         _clear_hidding_filter (ModestFolderView *folder_view);
147
148 static void         on_row_inserted_maybe_select_folder (GtkTreeModel     *tree_model, 
149                                                          GtkTreePath      *path, 
150                                                          GtkTreeIter      *iter,
151                                                          ModestFolderView *self);
152
153 static void         on_display_name_changed (ModestAccountMgr *self, 
154                                              const gchar *account,
155                                              gpointer user_data);
156
157 enum {
158         FOLDER_SELECTION_CHANGED_SIGNAL,
159         FOLDER_DISPLAY_NAME_CHANGED_SIGNAL,
160         LAST_SIGNAL
161 };
162
163 typedef struct _ModestFolderViewPrivate ModestFolderViewPrivate;
164 struct _ModestFolderViewPrivate {
165         TnyAccountStore      *account_store;
166         TnyFolderStore       *cur_folder_store;
167
168         TnyFolder            *folder_to_select; /* folder to select after the next update */
169
170         gulong                changed_signal;
171         gulong                account_inserted_signal;
172         gulong                account_removed_signal;
173         gulong                account_changed_signal;
174         gulong                conf_key_signal;
175         gulong                display_name_changed_signal;
176         
177         /* not unref this object, its a singlenton */
178         ModestEmailClipboard *clipboard;
179
180         /* Filter tree model */
181         gchar **hidding_ids;
182         guint n_selected;
183
184         TnyFolderStoreQuery  *query;
185         guint                 timer_expander;
186
187         gchar                *local_account_name;
188         gchar                *visible_account_id;
189         ModestFolderViewStyle style;
190
191         gboolean  reselect; /* we use this to force a reselection of the INBOX */
192         gboolean  show_non_move;
193         gboolean  reexpand; /* next time we expose, we'll expand all root folders */
194 };
195 #define MODEST_FOLDER_VIEW_GET_PRIVATE(o)                       \
196         (G_TYPE_INSTANCE_GET_PRIVATE((o),                       \
197                                      MODEST_TYPE_FOLDER_VIEW,   \
198                                      ModestFolderViewPrivate))
199 /* globals */
200 static GObjectClass *parent_class = NULL;
201
202 static guint signals[LAST_SIGNAL] = {0}; 
203
204 GType
205 modest_folder_view_get_type (void)
206 {
207         static GType my_type = 0;
208         if (!my_type) {
209                 static const GTypeInfo my_info = {
210                         sizeof(ModestFolderViewClass),
211                         NULL,           /* base init */
212                         NULL,           /* base finalize */
213                         (GClassInitFunc) modest_folder_view_class_init,
214                         NULL,           /* class finalize */
215                         NULL,           /* class data */
216                         sizeof(ModestFolderView),
217                         1,              /* n_preallocs */
218                         (GInstanceInitFunc) modest_folder_view_init,
219                         NULL
220                 };
221
222                 static const GInterfaceInfo tny_account_store_view_info = {
223                         (GInterfaceInitFunc) tny_account_store_view_init, /* interface_init */
224                         NULL,         /* interface_finalize */
225                         NULL          /* interface_data */
226                 };
227
228                                 
229                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
230                                                   "ModestFolderView",
231                                                   &my_info, 0);
232
233                 g_type_add_interface_static (my_type, 
234                                              TNY_TYPE_ACCOUNT_STORE_VIEW, 
235                                              &tny_account_store_view_info);
236         }
237         return my_type;
238 }
239
240 static void
241 modest_folder_view_class_init (ModestFolderViewClass *klass)
242 {
243         GObjectClass *gobject_class;
244         GtkTreeViewClass *treeview_class;
245         gobject_class = (GObjectClass*) klass;
246         treeview_class = (GtkTreeViewClass*) klass;
247
248         parent_class            = g_type_class_peek_parent (klass);
249         gobject_class->finalize = modest_folder_view_finalize;
250
251         g_type_class_add_private (gobject_class,
252                                   sizeof(ModestFolderViewPrivate));
253         
254         signals[FOLDER_SELECTION_CHANGED_SIGNAL] = 
255                 g_signal_new ("folder_selection_changed",
256                               G_TYPE_FROM_CLASS (gobject_class),
257                               G_SIGNAL_RUN_FIRST,
258                               G_STRUCT_OFFSET (ModestFolderViewClass,
259                                                folder_selection_changed),
260                               NULL, NULL,
261                               modest_marshal_VOID__POINTER_BOOLEAN,
262                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN);
263
264         /*
265          * This signal is emitted whenever the currently selected
266          * folder display name is computed. Note that the name could
267          * be different to the folder name, because we could append
268          * the unread messages count to the folder name to build the
269          * folder display name
270          */
271         signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL] = 
272                 g_signal_new ("folder-display-name-changed",
273                               G_TYPE_FROM_CLASS (gobject_class),
274                               G_SIGNAL_RUN_FIRST,
275                               G_STRUCT_OFFSET (ModestFolderViewClass,
276                                                folder_display_name_changed),
277                               NULL, NULL,
278                               g_cclosure_marshal_VOID__STRING,
279                               G_TYPE_NONE, 1, G_TYPE_STRING);
280
281         treeview_class->select_cursor_parent = NULL;
282
283 }
284
285 /* Simplify checks for NULLs: */
286 static gboolean
287 strings_are_equal (const gchar *a, const gchar *b)
288 {
289         if (!a && !b)
290                 return TRUE;
291         if (a && b)
292         {
293                 return (strcmp (a, b) == 0);
294         }
295         else
296                 return FALSE;
297 }
298
299 static gboolean
300 on_model_foreach_set_name(GtkTreeModel *model, GtkTreePath *path,  GtkTreeIter *iter, gpointer data)
301 {
302         GObject *instance = NULL;
303         
304         gtk_tree_model_get (model, iter,
305                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
306                             -1);
307                             
308         if (!instance)
309                 return FALSE; /* keep walking */
310                         
311         if (!TNY_IS_ACCOUNT (instance)) {
312                 g_object_unref (instance);
313                 return FALSE; /* keep walking */        
314         }    
315         
316         /* Check if this is the looked-for account: */
317         TnyAccount *this_account = TNY_ACCOUNT (instance);
318         TnyAccount *account = TNY_ACCOUNT (data);
319         
320         const gchar *this_account_id = tny_account_get_id(this_account);
321         const gchar *account_id = tny_account_get_id(account);
322         g_object_unref (instance);
323         instance = NULL;
324
325         /* printf ("DEBUG: %s: this_account_id=%s, account_id=%s\n", __FUNCTION__, this_account_id, account_id); */
326         if (strings_are_equal(this_account_id, account_id)) {
327                 /* Tell the model that the data has changed, so that
328                  * it calls the cell_data_func callbacks again: */
329                 /* TODO: This does not seem to actually cause the new string to be shown: */
330                 gtk_tree_model_row_changed (model, path, iter);
331                 
332                 return TRUE; /* stop walking */
333         }
334         
335         return FALSE; /* keep walking */
336 }
337
338 typedef struct 
339 {
340         ModestFolderView *self;
341         gchar *previous_name;
342 } GetMmcAccountNameData;
343
344 static void
345 on_get_mmc_account_name (TnyStoreAccount* account, gpointer user_data)
346 {
347         /* printf ("DEBU1G: %s: account name=%s\n", __FUNCTION__, tny_account_get_name (TNY_ACCOUNT(account))); */
348
349         GetMmcAccountNameData *data = (GetMmcAccountNameData*)user_data;
350         
351         if (!strings_are_equal (
352                 tny_account_get_name(TNY_ACCOUNT(account)), 
353                 data->previous_name)) {
354         
355                 /* Tell the model that the data has changed, so that 
356                  * it calls the cell_data_func callbacks again: */
357                 ModestFolderView *self = data->self;
358                 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
359                 if (model)
360                         gtk_tree_model_foreach(model, on_model_foreach_set_name, account);
361         }
362
363         g_free (data->previous_name);
364         g_slice_free (GetMmcAccountNameData, data);
365 }
366
367 static void
368 text_cell_data  (GtkTreeViewColumn *column,
369                  GtkCellRenderer *renderer,
370                  GtkTreeModel *tree_model,
371                  GtkTreeIter *iter,
372                  gpointer data)
373 {
374         ModestFolderViewPrivate *priv;
375         GObject *rendobj = (GObject *) renderer;
376         gchar *fname = NULL;
377         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
378         GObject *instance = NULL;
379
380         gtk_tree_model_get (tree_model, iter,
381                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &fname,
382                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
383                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
384                             -1);
385         if (!fname || !instance)
386                 goto end;
387
388         ModestFolderView *self = MODEST_FOLDER_VIEW (data);
389         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE (self);
390         
391         gchar *item_name = NULL;
392         gint item_weight = 400;
393         
394         if (type != TNY_FOLDER_TYPE_ROOT) {
395                 gint number = 0;
396                 
397                 if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)) ||
398                     modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance))) {
399                         type = modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (instance));
400                         if (type != TNY_FOLDER_TYPE_UNKNOWN) {
401                                 g_free (fname);
402                                 fname = g_strdup (modest_local_folder_info_get_type_display_name (type));
403                         }
404                 }
405
406                 /* note: we cannot reliably get the counts from the tree model, we need
407                  * to use explicit calls on tny_folder for some reason.
408                  */
409                 /* Select the number to show: the unread or unsent messages. in case of outbox/drafts, show all */
410                 if ((type == TNY_FOLDER_TYPE_DRAFTS) ||
411                     (type == TNY_FOLDER_TYPE_OUTBOX) ||
412                     (type == TNY_FOLDER_TYPE_MERGE)) /* _OUTBOX actually returns _MERGE... */
413                         number = tny_folder_get_all_count (TNY_FOLDER(instance));
414                 else
415                         number = tny_folder_get_unread_count (TNY_FOLDER(instance));
416                                                                 
417                 /* Use bold font style if there are unread or unset messages */
418                 if (number > 0) {
419                         if (type == TNY_FOLDER_TYPE_INBOX)
420                                 item_name = g_strdup_printf ("%s (%d)", _("mcen_me_folder_inbox"), number);
421                         else
422                                 item_name = g_strdup_printf ("%s (%d)", fname, number);
423                         item_weight = 800;
424                 } else {
425                         if (type == TNY_FOLDER_TYPE_INBOX)
426                                 item_name = g_strdup (_("mcen_me_folder_inbox"));
427                         else
428                                 item_name = g_strdup (fname);
429                         item_weight = 400;
430                 }
431                 
432         } else if (TNY_IS_ACCOUNT (instance)) {
433                 /* If it's a server account */
434                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
435                         item_name = g_strdup (priv->local_account_name);
436                         item_weight = 800;
437                 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
438                         /* fname is only correct when the items are first 
439                          * added to the model, not when the account is 
440                          * changed later, so get the name from the account
441                          * instance: */
442                         item_name = g_strdup (tny_account_get_name (TNY_ACCOUNT (instance)));
443                         item_weight = 800;
444                 } else {
445                         item_name = g_strdup (fname);
446                         item_weight = 800;
447                 }
448         }
449         
450         if (!item_name)
451                 item_name = g_strdup ("unknown");
452                         
453         if (item_name && item_weight) {
454                 /* Set the name in the treeview cell: */
455                 g_object_set (rendobj,"text", item_name, "weight", item_weight, NULL);
456                 
457                 /* Notify display name observers */
458                 /* TODO: What listens for this signal, and how can it use only the new name? */
459                 if (((GObject *) priv->cur_folder_store) == instance) {
460                         g_signal_emit (G_OBJECT(self),
461                                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
462                                                item_name);
463                 }
464                 g_free (item_name);
465                 
466         }
467         
468         /* If it is a Memory card account, make sure that we have the correct name.
469          * This function will be trigerred again when the name has been retrieved: */
470         if (TNY_IS_STORE_ACCOUNT (instance) && 
471                 modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance))) {
472
473                 /* Get the account name asynchronously: */
474                 GetMmcAccountNameData *callback_data = 
475                         g_slice_new0(GetMmcAccountNameData);
476                 callback_data->self = self;
477
478                 const gchar *name = tny_account_get_name (TNY_ACCOUNT(instance));
479                 if (name)
480                         callback_data->previous_name = g_strdup (name); 
481
482                 modest_tny_account_get_mmc_account_name (TNY_STORE_ACCOUNT (instance), 
483                                                          on_get_mmc_account_name, callback_data);
484         }
485  end:                   
486         if (instance)
487                 g_object_unref (G_OBJECT (instance));
488         if (fname)
489                 g_free (fname);
490 }
491
492
493 typedef struct {
494         GdkPixbuf *pixbuf;
495         GdkPixbuf *pixbuf_open;
496         GdkPixbuf *pixbuf_close;
497 } ThreePixbufs;
498
499
500 static ThreePixbufs*
501 get_folder_icons (TnyFolderType type, GObject *instance)
502 {
503         GdkPixbuf *pixbuf = NULL;
504         GdkPixbuf *pixbuf_open = NULL;
505         GdkPixbuf *pixbuf_close = NULL;
506         ThreePixbufs *retval = g_slice_new (ThreePixbufs);
507
508         static GdkPixbuf *inbox_pixbuf = NULL, *outbox_pixbuf = NULL,
509                 *junk_pixbuf = NULL, *sent_pixbuf = NULL,
510                 *trash_pixbuf = NULL, *draft_pixbuf = NULL,
511                 *normal_pixbuf = NULL, *anorm_pixbuf = NULL, 
512                 *ammc_pixbuf = NULL, *avirt_pixbuf = NULL;
513
514         static GdkPixbuf *inbox_pixbuf_open = NULL, *outbox_pixbuf_open = NULL,
515                 *junk_pixbuf_open = NULL, *sent_pixbuf_open = NULL,
516                 *trash_pixbuf_open = NULL, *draft_pixbuf_open = NULL,
517                 *normal_pixbuf_open = NULL, *anorm_pixbuf_open = NULL, 
518                 *ammc_pixbuf_open = NULL, *avirt_pixbuf_open = NULL;
519
520         static GdkPixbuf *inbox_pixbuf_close = NULL, *outbox_pixbuf_close = NULL,
521                 *junk_pixbuf_close = NULL, *sent_pixbuf_close = NULL,
522                 *trash_pixbuf_close = NULL, *draft_pixbuf_close = NULL,
523                 *normal_pixbuf_close = NULL, *anorm_pixbuf_close = NULL, 
524                 *ammc_pixbuf_close = NULL, *avirt_pixbuf_close = NULL;
525
526
527         /* MERGE is not needed anymore as the folder now has the correct type jschmid */
528         /* We include the MERGE type here because it's used to create
529            the local OUTBOX folder */
530         if (type == TNY_FOLDER_TYPE_NORMAL || 
531             type == TNY_FOLDER_TYPE_UNKNOWN) {
532                 type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));             
533         }
534
535         switch (type) {
536         case TNY_FOLDER_TYPE_INVALID:
537                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
538                 break;
539                 
540         case TNY_FOLDER_TYPE_ROOT:
541                 if (TNY_IS_ACCOUNT (instance)) {
542                         
543                         if (modest_tny_account_is_virtual_local_folders (
544                                 TNY_ACCOUNT (instance))) {
545
546                             if (!avirt_pixbuf)
547                                     avirt_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
548                                                                                               MODEST_ICON_SIZE_SMALL));
549                             
550                             if (!avirt_pixbuf_open) {
551                                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
552                                                                                   MODEST_ICON_SIZE_SMALL);
553                                     avirt_pixbuf_open = gdk_pixbuf_copy (avirt_pixbuf);
554                                     gdk_pixbuf_composite (emblem, avirt_pixbuf_open, 0, 0, 
555                                                           MIN (gdk_pixbuf_get_width (emblem), 
556                                                                gdk_pixbuf_get_width (avirt_pixbuf_open)),
557                                                           MIN (gdk_pixbuf_get_height (emblem), 
558                                                                gdk_pixbuf_get_height (avirt_pixbuf_open)),
559                                                           0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
560                                     g_object_unref (emblem);
561                             }
562
563                             if (!avirt_pixbuf_close) {
564                                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
565                                                                                   MODEST_ICON_SIZE_SMALL);
566                                 avirt_pixbuf_close = gdk_pixbuf_copy (avirt_pixbuf);
567                                 gdk_pixbuf_composite (emblem, avirt_pixbuf_close, 0, 0, 
568                                                       MIN (gdk_pixbuf_get_width (emblem), 
569                                                            gdk_pixbuf_get_width (avirt_pixbuf_close)),
570                                                       MIN (gdk_pixbuf_get_height (emblem), 
571                                                            gdk_pixbuf_get_height (avirt_pixbuf_close)),
572                                                       0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
573                                 g_object_unref (emblem);
574                             }
575
576
577                             pixbuf = g_object_ref (avirt_pixbuf);
578                             pixbuf_open = g_object_ref (avirt_pixbuf_open);
579                             pixbuf_close = g_object_ref (avirt_pixbuf_close);
580
581                         }
582                         else {
583                                 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
584                                 
585                                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {                                 
586                                     if (!ammc_pixbuf)
587                                             ammc_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_MMC,
588                                                                                                      MODEST_ICON_SIZE_SMALL));
589                                     
590                                     if (!ammc_pixbuf_open) {
591                                             GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
592                                                                                           MODEST_ICON_SIZE_SMALL);
593                                         ammc_pixbuf_open = gdk_pixbuf_copy (ammc_pixbuf);
594                                         gdk_pixbuf_composite (emblem, ammc_pixbuf_open, 0, 0, 
595                                                               MIN (gdk_pixbuf_get_width (emblem), 
596                                                                    gdk_pixbuf_get_width (ammc_pixbuf_open)),
597                                                               MIN (gdk_pixbuf_get_height (emblem), 
598                                                                    gdk_pixbuf_get_height (ammc_pixbuf_open)),
599                                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
600                                         g_object_unref (emblem);
601                                     }
602
603                                     if (!ammc_pixbuf_close) {
604                                             GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
605                                                                                           MODEST_ICON_SIZE_SMALL);
606                                         ammc_pixbuf_close = gdk_pixbuf_copy (ammc_pixbuf);
607                                         gdk_pixbuf_composite (emblem, ammc_pixbuf_close, 0, 0, 
608                                                               MIN (gdk_pixbuf_get_width (emblem), 
609                                                                    gdk_pixbuf_get_width (ammc_pixbuf_close)),
610                                                               MIN (gdk_pixbuf_get_height (emblem), 
611                                                                    gdk_pixbuf_get_height (ammc_pixbuf_close)),
612                                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
613                                         g_object_unref (emblem);
614                                     }
615
616
617                                     pixbuf = g_object_ref (ammc_pixbuf);
618                                     pixbuf_open = g_object_ref (ammc_pixbuf_open);
619                                     pixbuf_close = g_object_ref (ammc_pixbuf_close);
620
621                                 } else {
622
623                                     if (!anorm_pixbuf)
624                                             anorm_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_ACCOUNT,
625                                                                                                       MODEST_ICON_SIZE_SMALL));
626                                     if (!anorm_pixbuf_open) {
627                                             GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
628                                                                                           MODEST_ICON_SIZE_SMALL);
629                                         anorm_pixbuf_open = gdk_pixbuf_copy (anorm_pixbuf);
630                                         gdk_pixbuf_composite (emblem, anorm_pixbuf_open, 0, 0, 
631                                                               MIN (gdk_pixbuf_get_width (emblem), 
632                                                                    gdk_pixbuf_get_width (anorm_pixbuf_open)),
633                                                               MIN (gdk_pixbuf_get_height (emblem), 
634                                                                    gdk_pixbuf_get_height (anorm_pixbuf_open)),
635                                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
636                                         g_object_unref (emblem);
637                                     }
638
639                                     if (!anorm_pixbuf_close) {
640                                             GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
641                                                                                           MODEST_ICON_SIZE_SMALL);
642                                         anorm_pixbuf_close = gdk_pixbuf_copy (anorm_pixbuf);
643                                         gdk_pixbuf_composite (emblem, anorm_pixbuf_close, 0, 0, 
644                                                               MIN (gdk_pixbuf_get_width (emblem), 
645                                                                    gdk_pixbuf_get_width (anorm_pixbuf_close)),
646                                                               MIN (gdk_pixbuf_get_height (emblem), 
647                                                                    gdk_pixbuf_get_height (anorm_pixbuf_close)),
648                                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
649                                         g_object_unref (emblem);
650                                     }
651
652
653                                     pixbuf = g_object_ref (anorm_pixbuf);
654                                     pixbuf_open = g_object_ref (anorm_pixbuf_open);
655                                     pixbuf_close = g_object_ref (anorm_pixbuf_close);                             
656
657                                 }
658                         }
659                 }
660                 break;
661         case TNY_FOLDER_TYPE_INBOX:
662
663             if (!inbox_pixbuf)
664                     inbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_INBOX,
665                                                                               MODEST_ICON_SIZE_SMALL));
666
667             if (!inbox_pixbuf_open) {
668                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
669                                                                   MODEST_ICON_SIZE_SMALL);
670                     inbox_pixbuf_open = gdk_pixbuf_copy (inbox_pixbuf);
671                     gdk_pixbuf_composite (emblem, inbox_pixbuf_open, 0, 0, 
672                                           MIN (gdk_pixbuf_get_width (emblem), 
673                                                gdk_pixbuf_get_width (inbox_pixbuf_open)),
674                                           MIN (gdk_pixbuf_get_height (emblem), 
675                                                gdk_pixbuf_get_height (inbox_pixbuf_open)),
676                                           0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
677                     g_object_unref (emblem);
678             }
679             
680             if (!inbox_pixbuf_close) {
681                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
682                                                                   MODEST_ICON_SIZE_SMALL);
683                     inbox_pixbuf_close = gdk_pixbuf_copy (inbox_pixbuf);
684                     gdk_pixbuf_composite (emblem, inbox_pixbuf_close, 0, 0, 
685                                           MIN (gdk_pixbuf_get_width (emblem), 
686                                                gdk_pixbuf_get_width (inbox_pixbuf_close)),
687                                           MIN (gdk_pixbuf_get_height (emblem), 
688                                                gdk_pixbuf_get_height (inbox_pixbuf_close)),
689                                           0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
690                     g_object_unref (emblem);
691             }
692             
693
694             pixbuf = g_object_ref (inbox_pixbuf);
695             pixbuf_open = g_object_ref (inbox_pixbuf_open);
696             pixbuf_close = g_object_ref (inbox_pixbuf_close);
697             
698             break;
699         case TNY_FOLDER_TYPE_OUTBOX:
700                 if (!outbox_pixbuf)
701                         outbox_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_OUTBOX,
702                                                                                    MODEST_ICON_SIZE_SMALL));
703
704                 if (!outbox_pixbuf_open) {
705                         GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
706                                                                       MODEST_ICON_SIZE_SMALL);
707                         outbox_pixbuf_open = gdk_pixbuf_copy (outbox_pixbuf);
708                         gdk_pixbuf_composite (emblem, outbox_pixbuf_open, 0, 0, 
709                                               MIN (gdk_pixbuf_get_width (emblem), 
710                                                    gdk_pixbuf_get_width (outbox_pixbuf_open)),
711                                               MIN (gdk_pixbuf_get_height (emblem), 
712                                                    gdk_pixbuf_get_height (outbox_pixbuf_open)),
713                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
714                         g_object_unref (emblem);
715             }
716
717             if (!outbox_pixbuf_close) {
718                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
719                                                                   MODEST_ICON_SIZE_SMALL);
720                 outbox_pixbuf_close = gdk_pixbuf_copy (outbox_pixbuf);
721                 gdk_pixbuf_composite (emblem, outbox_pixbuf_close, 0, 0, 
722                                       MIN (gdk_pixbuf_get_width (emblem), 
723                                            gdk_pixbuf_get_width (outbox_pixbuf_close)),
724                                       MIN (gdk_pixbuf_get_height (emblem), 
725                                            gdk_pixbuf_get_height (outbox_pixbuf_close)),
726                                       0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
727                 g_object_unref (emblem);
728             }
729
730
731             pixbuf = g_object_ref (outbox_pixbuf);
732             pixbuf_open = g_object_ref (outbox_pixbuf_open);
733             pixbuf_close = g_object_ref (outbox_pixbuf_close);
734
735             break;
736         case TNY_FOLDER_TYPE_JUNK:
737             if (!junk_pixbuf)
738                     junk_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_JUNK,
739                                                                              MODEST_ICON_SIZE_SMALL));
740             if (!junk_pixbuf_open) {
741                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
742                                                                   MODEST_ICON_SIZE_SMALL);
743                     junk_pixbuf_open = gdk_pixbuf_copy (junk_pixbuf);
744                     gdk_pixbuf_composite (emblem, junk_pixbuf_open, 0, 0, 
745                                           MIN (gdk_pixbuf_get_width (emblem), 
746                                                gdk_pixbuf_get_width (junk_pixbuf_open)),
747                                           MIN (gdk_pixbuf_get_height (emblem), 
748                                                gdk_pixbuf_get_height (junk_pixbuf_open)),
749                                           0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
750                     g_object_unref (emblem);
751             }
752             
753             if (!junk_pixbuf_close) {
754                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
755                                                                   MODEST_ICON_SIZE_SMALL);
756                     junk_pixbuf_close = gdk_pixbuf_copy (junk_pixbuf);
757                     gdk_pixbuf_composite (emblem, junk_pixbuf_close, 0, 0, 
758                                           MIN (gdk_pixbuf_get_width (emblem), 
759                                                gdk_pixbuf_get_width (junk_pixbuf_close)),
760                                           MIN (gdk_pixbuf_get_height (emblem), 
761                                                gdk_pixbuf_get_height (junk_pixbuf_close)),
762                                           0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
763                     g_object_unref (emblem);
764             }
765             
766             
767             pixbuf = g_object_ref (junk_pixbuf);
768             pixbuf_open = g_object_ref (junk_pixbuf_open);
769             pixbuf_close = g_object_ref (junk_pixbuf_close);
770             
771             break;
772
773         case TNY_FOLDER_TYPE_SENT:
774                 if (!sent_pixbuf)
775                         sent_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_SENT,
776                                                                                  MODEST_ICON_SIZE_SMALL));
777                 
778                 if (!sent_pixbuf_open) {
779                         GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
780                                                                       MODEST_ICON_SIZE_SMALL);
781                         sent_pixbuf_open = gdk_pixbuf_copy (sent_pixbuf);
782                         gdk_pixbuf_composite (emblem, sent_pixbuf_open, 0, 0, 
783                                               MIN (gdk_pixbuf_get_width (emblem), 
784                                                    gdk_pixbuf_get_width (sent_pixbuf_open)),
785                                               MIN (gdk_pixbuf_get_height (emblem), 
786                                                    gdk_pixbuf_get_height (sent_pixbuf_open)),
787                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
788                         g_object_unref (emblem);
789                 }
790                 
791                 if (!sent_pixbuf_close) {
792                         GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
793                                                                       MODEST_ICON_SIZE_SMALL);
794                         sent_pixbuf_close = gdk_pixbuf_copy (sent_pixbuf);
795                         gdk_pixbuf_composite (emblem, sent_pixbuf_close, 0, 0, 
796                                               MIN (gdk_pixbuf_get_width (emblem), 
797                                                    gdk_pixbuf_get_width (sent_pixbuf_close)),
798                                               MIN (gdk_pixbuf_get_height (emblem), 
799                                                    gdk_pixbuf_get_height (sent_pixbuf_close)),
800                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
801                         g_object_unref (emblem);
802                 }
803                 
804
805             pixbuf = g_object_ref (sent_pixbuf);
806             pixbuf_open = g_object_ref (sent_pixbuf_open);
807             pixbuf_close = g_object_ref (sent_pixbuf_close);
808             
809             break;
810
811         case TNY_FOLDER_TYPE_TRASH:
812             if (!trash_pixbuf)
813                     trash_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_TRASH,
814                                                                               MODEST_ICON_SIZE_SMALL));
815             if (!trash_pixbuf_open) {
816                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
817                                                                   MODEST_ICON_SIZE_SMALL);
818                     trash_pixbuf_open = gdk_pixbuf_copy (trash_pixbuf);
819                     gdk_pixbuf_composite (emblem, trash_pixbuf_open, 0, 0, 
820                                           MIN (gdk_pixbuf_get_width (emblem), 
821                                                gdk_pixbuf_get_width (trash_pixbuf_open)),
822                                           MIN (gdk_pixbuf_get_height (emblem), 
823                                                gdk_pixbuf_get_height (trash_pixbuf_open)),
824                                           0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
825                     g_object_unref (emblem);
826             }
827             
828             if (!trash_pixbuf_close) {
829                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
830                                                                   MODEST_ICON_SIZE_SMALL);
831                     trash_pixbuf_close = gdk_pixbuf_copy (trash_pixbuf);
832                     gdk_pixbuf_composite (emblem, trash_pixbuf_close, 0, 0, 
833                                           MIN (gdk_pixbuf_get_width (emblem), 
834                                                gdk_pixbuf_get_width (trash_pixbuf_close)),
835                                           MIN (gdk_pixbuf_get_height (emblem), 
836                                                gdk_pixbuf_get_height (trash_pixbuf_close)),
837                                           0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
838                     g_object_unref (emblem);
839             }
840             
841             
842             pixbuf = g_object_ref (trash_pixbuf);
843             pixbuf_open = g_object_ref (trash_pixbuf_open);
844             pixbuf_close = g_object_ref (trash_pixbuf_close);
845
846             break;
847         case TNY_FOLDER_TYPE_DRAFTS:
848            if (!draft_pixbuf)
849                    draft_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_DRAFTS,
850                                                                              MODEST_ICON_SIZE_SMALL));
851            
852             if (!draft_pixbuf_open) {
853                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
854                                                                   MODEST_ICON_SIZE_SMALL);
855                 draft_pixbuf_open = gdk_pixbuf_copy (draft_pixbuf);
856                 gdk_pixbuf_composite (emblem, draft_pixbuf_open, 0, 0, 
857                                       MIN (gdk_pixbuf_get_width (emblem), 
858                                            gdk_pixbuf_get_width (draft_pixbuf_open)),
859                                       MIN (gdk_pixbuf_get_height (emblem), 
860                                            gdk_pixbuf_get_height (draft_pixbuf_open)),
861                                       0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
862                 g_object_unref (emblem);
863             }
864
865             if (!draft_pixbuf_close) {
866                     GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
867                                                                   MODEST_ICON_SIZE_SMALL);
868                 draft_pixbuf_close = gdk_pixbuf_copy (draft_pixbuf);
869                 gdk_pixbuf_composite (emblem, draft_pixbuf_close, 0, 0, 
870                                       MIN (gdk_pixbuf_get_width (emblem), 
871                                            gdk_pixbuf_get_width (draft_pixbuf_close)),
872                                       MIN (gdk_pixbuf_get_height (emblem), 
873                                            gdk_pixbuf_get_height (draft_pixbuf_close)),
874                                       0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
875                 g_object_unref (emblem);
876             }
877
878
879             pixbuf = g_object_ref (draft_pixbuf);
880             pixbuf_open = g_object_ref (draft_pixbuf_open);
881             pixbuf_close = g_object_ref (draft_pixbuf_close);
882
883             break;      
884         case TNY_FOLDER_TYPE_NORMAL:
885         default:
886                 if (!normal_pixbuf)
887                         normal_pixbuf = gdk_pixbuf_copy (modest_platform_get_icon (MODEST_FOLDER_ICON_NORMAL,
888                                                                                    MODEST_ICON_SIZE_SMALL));
889                 
890                 if (!normal_pixbuf_open) {
891                         GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_exp",
892                                                                       MODEST_ICON_SIZE_SMALL);
893                         normal_pixbuf_open = gdk_pixbuf_copy (normal_pixbuf);
894                         gdk_pixbuf_composite (emblem, normal_pixbuf_open, 0, 0, 
895                                               MIN (gdk_pixbuf_get_width (emblem), 
896                                                    gdk_pixbuf_get_width (normal_pixbuf_open)),
897                                               MIN (gdk_pixbuf_get_height (emblem), 
898                                                    gdk_pixbuf_get_height (normal_pixbuf_open)),
899                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
900                         g_object_unref (emblem);
901                 }
902                 
903                 if (!normal_pixbuf_close) {
904                         GdkPixbuf *emblem = modest_platform_get_icon ("qgn_list_gene_fldr_clp",
905                                                                       MODEST_ICON_SIZE_SMALL);
906                         normal_pixbuf_close = gdk_pixbuf_copy (normal_pixbuf);
907                         gdk_pixbuf_composite (emblem, normal_pixbuf_close, 0, 0, 
908                                               MIN (gdk_pixbuf_get_width (emblem), 
909                                                    gdk_pixbuf_get_width (normal_pixbuf_close)),
910                                               MIN (gdk_pixbuf_get_height (emblem), 
911                                                    gdk_pixbuf_get_height (normal_pixbuf_close)),
912                                               0, 0, 1, 1, GDK_INTERP_NEAREST, 255);
913                         g_object_unref (emblem);
914                 }
915                 
916                 
917             pixbuf = g_object_ref (normal_pixbuf);
918             pixbuf_open = g_object_ref (normal_pixbuf_open);
919             pixbuf_close = g_object_ref (normal_pixbuf_close);
920
921             break;      
922
923         }
924
925         retval->pixbuf = pixbuf;
926         retval->pixbuf_open = pixbuf_open;
927         retval->pixbuf_close = pixbuf_close;
928
929         return retval;
930 }
931
932
933 static void
934 free_pixbufs (ThreePixbufs *pixbufs)
935 {
936         g_object_unref (pixbufs->pixbuf);
937         g_object_unref (pixbufs->pixbuf_open);
938         g_object_unref (pixbufs->pixbuf_close);
939         g_slice_free (ThreePixbufs, pixbufs);
940 }
941
942 static void
943 icon_cell_data  (GtkTreeViewColumn *column,  
944                  GtkCellRenderer *renderer,
945                  GtkTreeModel *tree_model,  
946                  GtkTreeIter *iter, 
947                  gpointer data)
948 {
949         GObject *rendobj = NULL, *instance = NULL;
950         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
951         gboolean has_children;
952         ThreePixbufs *pixbufs;
953
954         rendobj = (GObject *) renderer;
955
956         gtk_tree_model_get (tree_model, iter,
957                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
958                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
959                             -1);
960
961         if (!instance) 
962                 return;
963
964         has_children = gtk_tree_model_iter_has_child (tree_model, iter);
965         pixbufs = get_folder_icons (type, instance);    
966         g_object_unref (instance);
967
968         /* Set pixbuf */
969         g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
970
971         if (has_children) {
972                 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
973                 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
974         }
975
976         free_pixbufs (pixbufs);
977
978         return;
979 }
980
981 static void
982 add_columns (GtkWidget *treeview)
983 {
984         GtkTreeViewColumn *column;
985         GtkCellRenderer *renderer;
986         GtkTreeSelection *sel;
987
988         /* Create column */
989         column = gtk_tree_view_column_new ();   
990         
991         /* Set icon and text render function */
992         renderer = gtk_cell_renderer_pixbuf_new();
993         gtk_tree_view_column_pack_start (column, renderer, FALSE);
994         gtk_tree_view_column_set_cell_data_func(column, renderer,
995                                                 icon_cell_data, treeview, NULL);
996         
997         renderer = gtk_cell_renderer_text_new();
998         g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, 
999                         "ellipsize-set", TRUE, NULL);
1000         gtk_tree_view_column_pack_start (column, renderer, TRUE);
1001         gtk_tree_view_column_set_cell_data_func(column, renderer,
1002                                                 text_cell_data, treeview, NULL);
1003         
1004         /* Set selection mode */
1005         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1006         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1007
1008         /* Set treeview appearance */
1009         gtk_tree_view_column_set_spacing (column, 2);
1010         gtk_tree_view_column_set_resizable (column, TRUE);
1011         gtk_tree_view_column_set_fixed_width (column, TRUE);            
1012         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1013         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1014
1015         /* Add column */
1016         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1017 }
1018
1019 static void
1020 modest_folder_view_init (ModestFolderView *obj)
1021 {
1022         ModestFolderViewPrivate *priv;
1023         ModestConf *conf;
1024         
1025         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1026         
1027         priv->timer_expander = 0;
1028         priv->account_store  = NULL;
1029         priv->query          = NULL;
1030         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1031         priv->cur_folder_store   = NULL;
1032         priv->visible_account_id = NULL;
1033         priv->folder_to_select = NULL;
1034
1035         priv->reexpand = TRUE;
1036
1037         /* Initialize the local account name */
1038         conf = modest_runtime_get_conf();
1039         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1040
1041         /* Init email clipboard */
1042         priv->clipboard = modest_runtime_get_email_clipboard ();
1043         priv->hidding_ids = NULL;
1044         priv->n_selected = 0;
1045         priv->reselect = FALSE;
1046         priv->show_non_move = TRUE;
1047
1048         /* Build treeview */
1049         add_columns (GTK_WIDGET (obj));
1050
1051         /* Setup drag and drop */
1052         setup_drag_and_drop (GTK_TREE_VIEW(obj));
1053
1054         /* Connect signals */
1055         g_signal_connect (G_OBJECT (obj), 
1056                           "key-press-event", 
1057                           G_CALLBACK (on_key_pressed), NULL);
1058
1059         priv->display_name_changed_signal = 
1060                 g_signal_connect (modest_runtime_get_account_mgr (),
1061                                   "display_name_changed",
1062                                   G_CALLBACK (on_display_name_changed),
1063                                   obj);
1064
1065         /*
1066          * Track changes in the local account name (in the device it
1067          * will be the device name)
1068          */
1069         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf), 
1070                                                   "key_changed",
1071                                                   G_CALLBACK(on_configuration_key_changed), 
1072                                                   obj);
1073 }
1074
1075 static void
1076 tny_account_store_view_init (gpointer g, gpointer iface_data)
1077 {
1078         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1079
1080         klass->set_account_store = modest_folder_view_set_account_store;
1081 }
1082
1083 static void
1084 modest_folder_view_finalize (GObject *obj)
1085 {
1086         ModestFolderViewPrivate *priv;
1087         GtkTreeSelection    *sel;
1088         
1089         g_return_if_fail (obj);
1090         
1091         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1092
1093         if (priv->timer_expander != 0) {
1094                 g_source_remove (priv->timer_expander);
1095                 priv->timer_expander = 0;
1096         }
1097
1098         if (priv->account_store) {
1099                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1100                                              priv->account_inserted_signal);
1101                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1102                                              priv->account_removed_signal);
1103                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1104                                              priv->account_changed_signal);
1105                 g_object_unref (G_OBJECT(priv->account_store));
1106                 priv->account_store = NULL;
1107         }
1108
1109         if (priv->query) {
1110                 g_object_unref (G_OBJECT (priv->query));
1111                 priv->query = NULL;
1112         }
1113
1114         if (priv->folder_to_select) {
1115                 g_object_unref (G_OBJECT(priv->folder_to_select));
1116                 priv->folder_to_select = NULL;
1117         }
1118    
1119         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1120         if (sel)
1121                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1122
1123         g_free (priv->local_account_name);
1124         g_free (priv->visible_account_id);
1125         
1126         if (priv->conf_key_signal) {
1127                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1128                                              priv->conf_key_signal);
1129                 priv->conf_key_signal = 0;
1130         }
1131
1132         if (priv->cur_folder_store) {
1133                 g_object_unref (priv->cur_folder_store);
1134                 priv->cur_folder_store = NULL;
1135         }
1136
1137         /* Clear hidding array created by cut operation */
1138         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1139
1140         G_OBJECT_CLASS(parent_class)->finalize (obj);
1141 }
1142
1143
1144 static void
1145 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1146 {
1147         ModestFolderViewPrivate *priv;
1148         TnyDevice *device;
1149
1150         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1151         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1152
1153         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1154         device = tny_account_store_get_device (account_store);
1155
1156         if (G_UNLIKELY (priv->account_store)) {
1157
1158                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1159                                                    priv->account_inserted_signal))
1160                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1161                                                      priv->account_inserted_signal);
1162                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
1163                                                    priv->account_removed_signal))
1164                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
1165                                                      priv->account_removed_signal);
1166                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
1167                                                    priv->account_changed_signal))
1168                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
1169                                                      priv->account_changed_signal);
1170                 g_object_unref (G_OBJECT (priv->account_store));
1171         }
1172
1173         priv->account_store = g_object_ref (G_OBJECT (account_store));
1174
1175         priv->account_removed_signal = 
1176                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1177                                   G_CALLBACK (on_account_removed), self);
1178
1179         priv->account_inserted_signal =
1180                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1181                                   G_CALLBACK (on_account_inserted), self);
1182
1183         priv->account_changed_signal =
1184                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1185                                   G_CALLBACK (on_account_changed), self);
1186
1187         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1188         priv->reselect = FALSE;
1189         modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1190         
1191         g_object_unref (G_OBJECT (device));
1192 }
1193
1194 static void
1195 on_account_inserted (TnyAccountStore *account_store, 
1196                      TnyAccount *account,
1197                      gpointer user_data)
1198 {
1199         ModestFolderViewPrivate *priv;
1200         GtkTreeModel *sort_model, *filter_model;
1201
1202         /* Ignore transport account insertions, we're not showing them
1203            in the folder view */
1204         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1205                 return;
1206
1207         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1208
1209
1210         /* If we're adding a new account, and there is no previous
1211            one, we need to select the visible server account */
1212         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1213             !priv->visible_account_id)
1214                 modest_widget_memory_restore (modest_runtime_get_conf(), 
1215                                               G_OBJECT (user_data),
1216                                               MODEST_CONF_FOLDER_VIEW_KEY);
1217
1218         if (!GTK_IS_TREE_VIEW(user_data)) {
1219                 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1220                 return;
1221         }
1222         
1223         /* Get the inner model */
1224         /* check, is some rare cases, we did not get the right thing here,
1225          * NB#84097 */
1226         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1227         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1228                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1229                 return;
1230         }
1231
1232         /* check, is some rare cases, we did not get the right thing here,
1233          * NB#84097 */
1234         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1235         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1236                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1237                 return;
1238         }
1239
1240         /* Insert the account in the model */
1241         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1242                          G_OBJECT (account));
1243         
1244         /* Refilter the model */
1245         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1246 }
1247
1248
1249 static void
1250 on_account_changed (TnyAccountStore *account_store, 
1251                     TnyAccount *tny_account,
1252                     gpointer user_data)
1253 {
1254         ModestFolderViewPrivate *priv;
1255         GtkTreeModel *sort_model, *filter_model;
1256         GtkTreeSelection *sel;
1257
1258         /* Ignore transport account insertions, we're not showing them
1259            in the folder view */
1260         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1261                 return;
1262
1263         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1264                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1265                 return;
1266         }
1267
1268
1269         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1270
1271         /* Get the inner model */
1272         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1273         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1274                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1275                 return;
1276         }
1277
1278
1279         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1280         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1281                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1282                 return;
1283         }
1284
1285         /* Unselect the folder, clear the header list */
1286         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (user_data));
1287         gtk_tree_selection_unselect_all (sel);
1288
1289         /* Remove the account from the model */
1290         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1291                          G_OBJECT (tny_account));
1292         
1293         /* Insert the account in the model */
1294         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1295                          G_OBJECT (tny_account));
1296
1297         /* Refilter the model */
1298         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1299 }
1300
1301 /**
1302  *
1303  * Selects the first inbox or the local account in an idle
1304  */
1305 static gboolean
1306 on_idle_select_first_inbox_or_local (gpointer user_data)
1307 {
1308         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1309
1310         gdk_threads_enter ();
1311         modest_folder_view_select_first_inbox_or_local (self);
1312         gdk_threads_leave ();
1313
1314         return FALSE;
1315 }
1316
1317
1318 static void
1319 on_account_removed (TnyAccountStore *account_store, 
1320                     TnyAccount *account,
1321                     gpointer user_data)
1322 {
1323         ModestFolderView *self = NULL;
1324         ModestFolderViewPrivate *priv;
1325         GtkTreeModel *sort_model, *filter_model;
1326         GtkTreeSelection *sel = NULL;
1327         gboolean same_account_selected = FALSE;
1328
1329         /* Ignore transport account removals, we're not showing them
1330            in the folder view */
1331         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1332                 return;
1333
1334         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1335                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1336                 return;
1337         }
1338
1339         self = MODEST_FOLDER_VIEW (user_data);
1340         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1341
1342         /* Invalidate the cur_folder_store only if the selected folder
1343            belongs to the account that is being removed */
1344         if (priv->cur_folder_store) {
1345                 TnyAccount *selected_folder_account = NULL;
1346
1347                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1348                         selected_folder_account = 
1349                                 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1350                 } else {
1351                         selected_folder_account = 
1352                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1353                 }
1354
1355                 if (selected_folder_account == account) {
1356                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1357                         gtk_tree_selection_unselect_all (sel);
1358                         same_account_selected = TRUE;
1359                 }
1360                 g_object_unref (selected_folder_account);
1361         }
1362
1363         /* Invalidate row to select only if the folder to select
1364            belongs to the account that is being removed*/
1365         if (priv->folder_to_select) {
1366                 TnyAccount *folder_to_select_account = NULL;
1367
1368                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1369                 if (folder_to_select_account == account) {
1370                         modest_folder_view_disable_next_folder_selection (self);
1371                         g_object_unref (priv->folder_to_select);
1372                         priv->folder_to_select = NULL;
1373                 }
1374                 g_object_unref (folder_to_select_account);
1375         }
1376
1377         /* Remove the account from the model */
1378         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1379         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1380                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1381                 return;
1382         }
1383
1384         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1385         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1386                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1387                 return;
1388         }
1389         
1390         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1391                          G_OBJECT (account));
1392
1393         /* If the removed account is the currently viewed one then
1394            clear the configuration value. The new visible account will be the default account */
1395         if (priv->visible_account_id &&
1396             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1397
1398                 /* Clear the current visible account_id */
1399                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1400
1401                 /* Call the restore method, this will set the new visible account */
1402                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1403                                               MODEST_CONF_FOLDER_VIEW_KEY);
1404         }
1405
1406         /* Refilter the model */
1407         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1408
1409         /* Select the first INBOX if the currently selected folder
1410            belongs to the account that is being deleted */
1411         if (same_account_selected)
1412                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1413 }
1414
1415 void
1416 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1417 {
1418         GtkTreeViewColumn *col;
1419         
1420         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1421
1422         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1423         if (!col) {
1424                 g_printerr ("modest: failed get column for title\n");
1425                 return;
1426         }
1427
1428         gtk_tree_view_column_set_title (col, title);
1429         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1430                                            title != NULL);
1431 }
1432
1433 static gboolean
1434 modest_folder_view_on_map (ModestFolderView *self, 
1435                            GdkEventExpose *event,
1436                            gpointer data)
1437 {
1438         ModestFolderViewPrivate *priv;
1439
1440         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1441
1442         /* This won't happen often */
1443         if (G_UNLIKELY (priv->reselect)) {
1444                 /* Select the first inbox or the local account if not found */
1445
1446                 /* TODO: this could cause a lock at startup, so we
1447                    comment it for the moment. We know that this will
1448                    be a bug, because the INBOX is not selected, but we
1449                    need to rewrite some parts of Modest to avoid the
1450                    deathlock situation */
1451                 /* TODO: check if this is still the case */
1452                 priv->reselect = FALSE;
1453                 modest_folder_view_select_first_inbox_or_local (self);
1454                 /* Notify the display name observers */
1455                 g_signal_emit (G_OBJECT(self),
1456                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1457                                NULL);
1458         }
1459
1460         if (priv->reexpand) {
1461                 expand_root_items (self); 
1462                 priv->reexpand = FALSE;
1463         }
1464
1465         return FALSE;
1466 }
1467
1468 GtkWidget*
1469 modest_folder_view_new (TnyFolderStoreQuery *query)
1470 {
1471         GObject *self;
1472         ModestFolderViewPrivate *priv;
1473         GtkTreeSelection *sel;
1474         
1475         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1476         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1477
1478         if (query)
1479                 priv->query = g_object_ref (query);
1480         
1481         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1482         priv->changed_signal = g_signal_connect (sel, "changed",
1483                                                  G_CALLBACK (on_selection_changed), self);
1484
1485         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1486
1487         return GTK_WIDGET(self);
1488 }
1489
1490 /* this feels dirty; any other way to expand all the root items? */
1491 static void
1492 expand_root_items (ModestFolderView *self)
1493 {
1494         GtkTreePath *path;
1495         GtkTreeModel *model;
1496         GtkTreeIter iter;
1497
1498         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1499         path = gtk_tree_path_new_first ();
1500
1501         /* all folders should have child items, so.. */
1502         do {
1503                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1504                 gtk_tree_path_next (path);
1505         } while (gtk_tree_model_get_iter (model, &iter, path));
1506         
1507         gtk_tree_path_free (path);
1508 }
1509
1510 /*
1511  * We use this function to implement the
1512  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1513  * account in this case, and the local folders.
1514  */
1515 static gboolean 
1516 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1517 {
1518         ModestFolderViewPrivate *priv;
1519         gboolean retval = TRUE;
1520         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1521         GObject *instance = NULL;
1522         const gchar *id = NULL;
1523         guint i;
1524         gboolean found = FALSE;
1525         gboolean cleared = FALSE;
1526
1527         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1528         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1529
1530         gtk_tree_model_get (model, iter,
1531                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1532                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1533                             -1);
1534
1535         /* Do not show if there is no instance, this could indeed
1536            happen when the model is being modified while it's being
1537            drawn. This could occur for example when moving folders
1538            using drag&drop */
1539         if (!instance)
1540                 return FALSE;
1541
1542         if (type == TNY_FOLDER_TYPE_ROOT) {
1543                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1544                    account instead of a folder. */
1545                 if (TNY_IS_ACCOUNT (instance)) {
1546                         TnyAccount *acc = TNY_ACCOUNT (instance);
1547                         const gchar *account_id = tny_account_get_id (acc);
1548         
1549                         /* If it isn't a special folder, 
1550                          * don't show it unless it is the visible account: */
1551                         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1552                             !modest_tny_account_is_virtual_local_folders (acc) &&
1553                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1554                                 
1555                                 /* Show only the visible account id */
1556                                 if (priv->visible_account_id) {
1557                                         if (strcmp (account_id, priv->visible_account_id))
1558                                                 retval = FALSE;
1559                                 } else {
1560                                         retval = FALSE;
1561                                 }                               
1562                         }
1563                         
1564                         /* Never show these to the user. They are merged into one folder 
1565                          * in the local-folders account instead: */
1566                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1567                                 retval = FALSE;
1568                 }
1569         }
1570
1571         /* Check hiding (if necessary) */
1572         cleared = modest_email_clipboard_cleared (priv->clipboard);            
1573         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1574                 id = tny_folder_get_id (TNY_FOLDER(instance));
1575                 if (priv->hidding_ids != NULL)
1576                         for (i=0; i < priv->n_selected && !found; i++)
1577                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1578                                         found = (!strcmp (priv->hidding_ids[i], id));
1579                 
1580                 retval = !found;
1581         }
1582         
1583         
1584         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1585         folder as no message can be move there according to UI specs */
1586         if (!priv->show_non_move) {
1587                 switch (type) {
1588                         case TNY_FOLDER_TYPE_OUTBOX:
1589                         case TNY_FOLDER_TYPE_SENT:
1590                         case TNY_FOLDER_TYPE_DRAFTS:
1591                                 retval = FALSE;
1592                                 break;
1593                         case TNY_FOLDER_TYPE_UNKNOWN:
1594                         case TNY_FOLDER_TYPE_NORMAL:
1595                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1596                                 if (type == TNY_FOLDER_TYPE_INVALID)
1597                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1598                                 
1599                                 if (type == TNY_FOLDER_TYPE_OUTBOX || 
1600                                     type == TNY_FOLDER_TYPE_SENT
1601                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1602                                         retval = FALSE;
1603                                 break;
1604                         default:
1605                                 break;
1606                 }
1607         }
1608         
1609         /* Free */
1610         g_object_unref (instance);
1611
1612         return retval;
1613 }
1614
1615
1616 gboolean
1617 modest_folder_view_update_model (ModestFolderView *self,
1618                                  TnyAccountStore *account_store)
1619 {
1620         ModestFolderViewPrivate *priv;
1621         GtkTreeModel *model /* , *old_model */;                                                    
1622         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1623
1624         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1625         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1626                               FALSE);
1627         
1628         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1629         
1630         /* Notify that there is no folder selected */
1631         g_signal_emit (G_OBJECT(self),
1632                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1633                        NULL, FALSE);
1634         if (priv->cur_folder_store) {
1635                 g_object_unref (priv->cur_folder_store);
1636                 priv->cur_folder_store = NULL;
1637         }
1638
1639         /* FIXME: the local accounts are not shown when the query
1640            selects only the subscribed folders */
1641         model = tny_gtk_folder_store_tree_model_new (NULL);
1642
1643         /* Get the accounts: */
1644         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1645                                         TNY_LIST (model),
1646                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1647
1648         sortable = gtk_tree_model_sort_new_with_model (model);
1649         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1650                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
1651                                               GTK_SORT_ASCENDING);
1652         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1653                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1654                                          cmp_rows, NULL, NULL);
1655
1656         /* Create filter model */
1657         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1658         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1659                                                 filter_row,
1660                                                 self,
1661                                                 NULL);
1662
1663         /* Set new model */
1664         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1665         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1666                           (GCallback) on_row_inserted_maybe_select_folder, self);
1667
1668         g_object_unref (model);
1669         g_object_unref (filter_model);          
1670         g_object_unref (sortable);
1671         
1672         /* Force a reselection of the INBOX next time the widget is shown */
1673         priv->reselect = TRUE;
1674                         
1675         return TRUE;
1676 }
1677
1678
1679 static void
1680 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1681 {
1682         GtkTreeModel *model = NULL;
1683         TnyFolderStore *folder = NULL;
1684         GtkTreeIter iter;
1685         ModestFolderView *tree_view = NULL;
1686         ModestFolderViewPrivate *priv = NULL;
1687         gboolean selected = FALSE;
1688
1689         g_return_if_fail (sel);
1690         g_return_if_fail (user_data);
1691         
1692         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1693
1694         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1695
1696         tree_view = MODEST_FOLDER_VIEW (user_data);
1697
1698         if (selected) {
1699                 gtk_tree_model_get (model, &iter,
1700                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1701                                     -1);
1702
1703                 /* If the folder is the same do not notify */
1704                 if (folder && priv->cur_folder_store == folder) {
1705                         g_object_unref (folder);
1706                         return;
1707                 }
1708         }
1709         
1710         /* Current folder was unselected */
1711         if (priv->cur_folder_store) {
1712                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1713                        priv->cur_folder_store, FALSE);
1714
1715                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1716                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1717                                                FALSE, NULL, NULL, NULL);
1718
1719                 /* FALSE --> don't expunge the messages */
1720
1721                 g_object_unref (priv->cur_folder_store);
1722                 priv->cur_folder_store = NULL;
1723         }
1724
1725         /* New current references */
1726         priv->cur_folder_store = folder;
1727
1728         /* New folder has been selected. Do not notify if there is
1729            nothing new selected */
1730         if (selected) {
1731                 g_signal_emit (G_OBJECT(tree_view),
1732                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1733                                0, priv->cur_folder_store, TRUE);
1734         }
1735 }
1736
1737 TnyFolderStore *
1738 modest_folder_view_get_selected (ModestFolderView *self)
1739 {
1740         ModestFolderViewPrivate *priv;
1741         
1742         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1743         
1744         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1745         if (priv->cur_folder_store)
1746                 g_object_ref (priv->cur_folder_store);
1747
1748         return priv->cur_folder_store;
1749 }
1750
1751 static gint
1752 get_cmp_rows_type_pos (GObject *folder)
1753 {
1754         /* Remote accounts -> Local account -> MMC account .*/
1755         /* 0, 1, 2 */
1756         
1757         if (TNY_IS_ACCOUNT (folder) && 
1758                 modest_tny_account_is_virtual_local_folders (
1759                         TNY_ACCOUNT (folder))) {
1760                 return 1;
1761         } else if (TNY_IS_ACCOUNT (folder)) {
1762                 TnyAccount *account = TNY_ACCOUNT (folder);
1763                 const gchar *account_id = tny_account_get_id (account);
1764                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1765                         return 2;
1766                 else
1767                         return 0;
1768         }
1769         else {
1770                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1771                 return -1; /* Should never happen */
1772         }
1773 }
1774
1775 static gint
1776 get_cmp_subfolder_type_pos (TnyFolderType t)
1777 {
1778         /* Inbox, Outbox, Drafts, Sent, User */
1779         /* 0, 1, 2, 3, 4 */
1780
1781         switch (t) {
1782         case TNY_FOLDER_TYPE_INBOX:
1783                 return 0;
1784                 break;
1785         case TNY_FOLDER_TYPE_OUTBOX:
1786                 return 1;
1787                 break;
1788         case TNY_FOLDER_TYPE_DRAFTS:
1789                 return 2;
1790                 break;
1791         case TNY_FOLDER_TYPE_SENT:
1792                 return 3;
1793                 break;
1794         default:
1795                 return 4;
1796         }
1797 }
1798
1799 /*
1800  * This function orders the mail accounts according to these rules:
1801  * 1st - remote accounts
1802  * 2nd - local account
1803  * 3rd - MMC account
1804  */
1805 static gint
1806 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1807           gpointer user_data)
1808 {
1809         gint cmp = 0;
1810         gchar *name1 = NULL;
1811         gchar *name2 = NULL;
1812         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1813         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1814         GObject *folder1 = NULL;
1815         GObject *folder2 = NULL;
1816
1817         gtk_tree_model_get (tree_model, iter1,
1818                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1819                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1820                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1821                             -1);
1822         gtk_tree_model_get (tree_model, iter2,
1823                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1824                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1825                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1826                             -1);
1827
1828         /* Return if we get no folder. This could happen when folder
1829            operations are happening. The model is updated after the
1830            folder copy/move actually occurs, so there could be
1831            situations where the model to be drawn is not correct */
1832         if (!folder1 || !folder2)
1833                 goto finish;
1834
1835         if (type == TNY_FOLDER_TYPE_ROOT) {
1836                 /* Compare the types, so that 
1837                  * Remote accounts -> Local account -> MMC account .*/
1838                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1839                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1840                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1841                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1842                 if (pos1 <  pos2)
1843                         cmp = -1;
1844                 else if (pos1 > pos2)
1845                         cmp = 1;
1846                 else {
1847                         /* Compare items of the same type: */
1848                         
1849                         TnyAccount *account1 = NULL;
1850                         if (TNY_IS_ACCOUNT (folder1))
1851                                 account1 = TNY_ACCOUNT (folder1);
1852                                 
1853                         TnyAccount *account2 = NULL;
1854                         if (TNY_IS_ACCOUNT (folder2))
1855                                 account2 = TNY_ACCOUNT (folder2);
1856                                 
1857                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1858                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1859         
1860                         if (!account_id && !account_id2) {
1861                                 cmp = 0;
1862                         } else if (!account_id) {
1863                                 cmp = -1;
1864                         } else if (!account_id2) {
1865                                 cmp = +1;
1866                         } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1867                                 cmp = +1;
1868                         } else {
1869                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1870                         }
1871                 }
1872         } else {
1873                 gint cmp1 = 0, cmp2 = 0;
1874                 /* get the parent to know if it's a local folder */
1875
1876                 GtkTreeIter parent;
1877                 gboolean has_parent;
1878                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1879                 if (has_parent) {
1880                         GObject *parent_folder;
1881                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1882                         gtk_tree_model_get (tree_model, &parent, 
1883                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1884                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1885                                             -1);
1886                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1887                             TNY_IS_ACCOUNT (parent_folder)) {
1888                                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1889                                         cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1890                                                                            (TNY_FOLDER (folder1)));
1891                                         cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1892                                                                            (TNY_FOLDER (folder2)));
1893                                 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1894                                         if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1895                                                 cmp1 = 0;
1896                                                 cmp2 = 1;
1897                                                 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1898                                                 cmp1 = 1;
1899                                                 cmp2 = 0;
1900                                         }
1901                                 }
1902                         }
1903                         g_object_unref (parent_folder);
1904                 }
1905                 
1906                 /* if they are not local folders */
1907                 if (cmp1 == cmp2) {
1908                         cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1909                         cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1910                 }
1911
1912                 if (cmp1 == cmp2)
1913                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1914                 else 
1915                         cmp = (cmp1 - cmp2);
1916         }
1917
1918 finish: 
1919         if (folder1)
1920                 g_object_unref(G_OBJECT(folder1));
1921         if (folder2)
1922                 g_object_unref(G_OBJECT(folder2));
1923
1924         g_free (name1);
1925         g_free (name2);
1926
1927         return cmp;     
1928 }
1929
1930 /*****************************************************************************/
1931 /*                        DRAG and DROP stuff                                */
1932 /*****************************************************************************/
1933 /*
1934  * This function fills the #GtkSelectionData with the row and the
1935  * model that has been dragged. It's called when this widget is a
1936  * source for dnd after the event drop happened
1937  */
1938 static void
1939 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, 
1940                   guint info, guint time, gpointer data)
1941 {
1942         GtkTreeSelection *selection;
1943         GtkTreeModel *model;
1944         GtkTreeIter iter;
1945         GtkTreePath *source_row;
1946         
1947         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1948         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1949
1950                 source_row = gtk_tree_model_get_path (model, &iter);
1951                 gtk_tree_set_row_drag_data (selection_data,
1952                                             model,
1953                                             source_row);
1954                 
1955                 gtk_tree_path_free (source_row);
1956         }
1957 }
1958
1959 typedef struct _DndHelper {
1960         ModestFolderView *folder_view;
1961         gboolean delete_source;
1962         GtkTreePath *source_row;
1963 } DndHelper;
1964
1965 static void
1966 dnd_helper_destroyer (DndHelper *helper)
1967 {
1968         /* Free the helper */
1969         gtk_tree_path_free (helper->source_row);
1970         g_slice_free (DndHelper, helper);
1971 }
1972
1973 static void
1974 xfer_folder_cb (ModestMailOperation *mail_op, 
1975                 TnyFolder *new_folder,
1976                 gpointer user_data)
1977 {
1978         if (new_folder) {       
1979                 /* Select the folder */
1980                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data), 
1981                                                   new_folder, TRUE);
1982         }
1983 }
1984
1985
1986 /* get the folder for the row the treepath refers to. */
1987 /* folder must be unref'd */
1988 static TnyFolderStore *
1989 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1990 {
1991         GtkTreeIter iter;
1992         TnyFolderStore *folder = NULL;
1993         
1994         if (gtk_tree_model_get_iter (model,&iter, path))
1995                 gtk_tree_model_get (model, &iter,
1996                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1997                                     -1);
1998         return folder;
1999 }
2000
2001
2002 /*
2003  * This function is used by drag_data_received_cb to manage drag and
2004  * drop of a header, i.e, and drag from the header view to the folder
2005  * view.
2006  */
2007 static void
2008 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2009                                 GtkTreeModel *dest_model,
2010                                 GtkTreePath  *dest_row,
2011                                 GtkSelectionData *selection_data)
2012 {
2013         TnyList *headers = NULL;
2014         TnyFolder *folder = NULL, *src_folder = NULL;
2015         TnyFolderType folder_type;
2016         GtkTreeIter source_iter, dest_iter;
2017         ModestWindowMgr *mgr = NULL;
2018         ModestWindow *main_win = NULL;
2019         gchar **uris, **tmp;
2020         
2021         /* Build the list of headers */
2022         mgr = modest_runtime_get_window_mgr ();
2023         headers = tny_simple_list_new ();
2024         uris = modest_dnd_selection_data_get_paths (selection_data);
2025         tmp = uris;
2026
2027         while (*tmp != NULL) {
2028                 TnyHeader *header;
2029                 GtkTreePath *path;
2030                 gboolean first = TRUE;
2031
2032                 /* Get header */
2033                 path = gtk_tree_path_new_from_string (*tmp);
2034                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2035                 gtk_tree_model_get (source_model, &source_iter, 
2036                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
2037                                     &header, -1);
2038
2039                 /* Do not enable d&d of headers already opened */
2040                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2041                         tny_list_append (headers, G_OBJECT (header));
2042
2043                 if (G_UNLIKELY (first)) {
2044                         src_folder = tny_header_get_folder (header);
2045                         first = FALSE;
2046                 }
2047
2048                 /* Free and go on */
2049                 gtk_tree_path_free (path);
2050                 g_object_unref (header);
2051                 tmp++;
2052         }
2053         g_strfreev (uris);
2054
2055         /* Get the target folder */
2056         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2057         gtk_tree_model_get (dest_model, &dest_iter, 
2058                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2059                             &folder, -1);
2060         
2061         if (!folder || !TNY_IS_FOLDER(folder)) {
2062 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2063                 goto cleanup;
2064         }
2065         
2066         folder_type = modest_tny_folder_guess_folder_type (folder);
2067         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2068 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2069                 goto cleanup;  /* cannot move messages there */
2070         }
2071         
2072         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2073 /*              g_warning ("folder not writable"); */
2074                 goto cleanup; /* verboten! */
2075         }
2076         
2077         /* Ask for confirmation to move */
2078         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2079         if (!main_win) {
2080                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2081                 goto cleanup;
2082         }
2083
2084         /* Transfer messages */
2085         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder, 
2086                                                     headers, folder);
2087         
2088         /* Frees */
2089 cleanup:
2090         if (G_IS_OBJECT (src_folder))
2091                 g_object_unref (src_folder);
2092         if (G_IS_OBJECT(folder))
2093                 g_object_unref (G_OBJECT (folder));
2094         if (G_IS_OBJECT(headers))
2095                 g_object_unref (headers);
2096 }
2097
2098 typedef struct {
2099         TnyFolderStore *src_folder;
2100         TnyFolderStore *dst_folder;
2101         ModestFolderView *folder_view;
2102         DndHelper *helper; 
2103 } DndFolderInfo;
2104
2105 static void
2106 dnd_folder_info_destroyer (DndFolderInfo *info)
2107 {
2108         if (info->src_folder)
2109                 g_object_unref (info->src_folder);
2110         if (info->dst_folder)
2111                 g_object_unref (info->dst_folder);
2112         g_slice_free (DndFolderInfo, info);
2113 }
2114
2115 static void
2116 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2117                                     GtkWindow *parent_window,
2118                                     TnyAccount *account)
2119 {
2120         /* Show error */
2121         modest_ui_actions_on_account_connection_error (parent_window, account);
2122
2123         /* Free the helper & info */
2124         dnd_helper_destroyer (info->helper);
2125         dnd_folder_info_destroyer (info);
2126 }
2127
2128 static void
2129 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled, 
2130                                                      GError *err,
2131                                                      GtkWindow *parent_window, 
2132                                                      TnyAccount *account, 
2133                                                      gpointer user_data)
2134 {
2135         DndFolderInfo *info = NULL;
2136         ModestMailOperation *mail_op;
2137
2138         info = (DndFolderInfo *) user_data;
2139
2140         if (err || canceled) {
2141                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2142                 return;
2143         }
2144
2145         /* Do the mail operation */
2146         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2147                                                                  modest_ui_actions_move_folder_error_handler,
2148                                                                  info->src_folder, NULL);
2149
2150         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2151                                          mail_op);
2152
2153         /* Transfer the folder */
2154         modest_mail_operation_xfer_folder (mail_op,
2155                                            TNY_FOLDER (info->src_folder),
2156                                            info->dst_folder,
2157                                            info->helper->delete_source,
2158                                            xfer_folder_cb,
2159                                            info->helper->folder_view);
2160
2161         /* Frees */     
2162         g_object_unref (G_OBJECT (mail_op));
2163         dnd_helper_destroyer (info->helper);
2164         dnd_folder_info_destroyer (info);
2165 }
2166
2167
2168 static void
2169 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled, 
2170                                                      GError *err,
2171                                                      GtkWindow *parent_window, 
2172                                                      TnyAccount *account, 
2173                                                      gpointer user_data)
2174 {
2175         DndFolderInfo *info = NULL;
2176
2177         info = (DndFolderInfo *) user_data;
2178
2179         if (err || canceled) {
2180                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2181                 return;
2182         }
2183
2184         /* Connect to source folder and perform the copy/move */
2185         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2186                                                        info->src_folder,
2187                                                        drag_and_drop_from_folder_view_src_folder_performer,
2188                                                        info);
2189 }
2190
2191 /*
2192  * This function is used by drag_data_received_cb to manage drag and
2193  * drop of a folder, i.e, and drag from the folder view to the same
2194  * folder view.
2195  */
2196 static void
2197 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2198                                 GtkTreeModel     *dest_model,
2199                                 GtkTreePath      *dest_row,
2200                                 GtkSelectionData *selection_data,
2201                                 DndHelper        *helper)
2202 {
2203         GtkTreeIter dest_iter, iter;
2204         TnyFolderStore *dest_folder = NULL;
2205         TnyFolderStore *folder = NULL;
2206         gboolean forbidden = FALSE;
2207         ModestWindow *win;
2208         DndFolderInfo *info = NULL;
2209
2210         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2211         if (!win) {
2212                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2213                 dnd_helper_destroyer (helper);
2214                 return;
2215         }
2216         
2217         if (!forbidden) {
2218                 /* check the folder rules for the destination */
2219                 folder = tree_path_to_folder (dest_model, dest_row);
2220                 if (TNY_IS_FOLDER(folder)) {
2221                         ModestTnyFolderRules rules =
2222                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2223                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2224                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2225                         /* enable local root as destination for folders */
2226                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) && 
2227                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2228                                 forbidden = TRUE;
2229                 }
2230                 g_object_unref (folder);
2231         }
2232         if (!forbidden) {
2233                 /* check the folder rules for the source */
2234                 folder = tree_path_to_folder (source_model, helper->source_row);
2235                 if (TNY_IS_FOLDER(folder)) {
2236                         ModestTnyFolderRules rules =
2237                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2238                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2239                 } else
2240                         forbidden = TRUE;
2241                 g_object_unref (folder);
2242         }
2243
2244         
2245         /* Check if the drag is possible */
2246         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2247                 dnd_helper_destroyer (helper);
2248                 return;
2249         }
2250
2251         /* Get data */
2252         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2253         gtk_tree_model_get (dest_model, &dest_iter, 
2254                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
2255                             &dest_folder, -1);
2256         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2257         gtk_tree_model_get (source_model, &iter,
2258                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2259                             &folder, -1);
2260
2261         /* Create the info for the performer */
2262         info = g_slice_new (DndFolderInfo);
2263         info->src_folder = g_object_ref (folder);
2264         info->dst_folder = g_object_ref (dest_folder);
2265         info->helper = helper;
2266
2267         /* Connect to the destination folder and perform the copy/move */
2268         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2269                                                        dest_folder,
2270                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2271                                                        info);
2272         
2273         /* Frees */
2274         g_object_unref (dest_folder);
2275         g_object_unref (folder);
2276 }
2277
2278 /*
2279  * This function receives the data set by the "drag-data-get" signal
2280  * handler. This information comes within the #GtkSelectionData. This
2281  * function will manage both the drags of folders of the treeview and
2282  * drags of headers of the header view widget.
2283  */
2284 static void 
2285 on_drag_data_received (GtkWidget *widget, 
2286                        GdkDragContext *context, 
2287                        gint x, 
2288                        gint y, 
2289                        GtkSelectionData *selection_data, 
2290                        guint target_type, 
2291                        guint time, 
2292                        gpointer data)
2293 {
2294         GtkWidget *source_widget;
2295         GtkTreeModel *dest_model, *source_model;
2296         GtkTreePath *source_row, *dest_row;
2297         GtkTreeViewDropPosition pos;
2298         gboolean delete_source = FALSE;
2299         gboolean success = FALSE;
2300
2301         /* Do not allow further process */
2302         g_signal_stop_emission_by_name (widget, "drag-data-received");
2303         source_widget = gtk_drag_get_source_widget (context);
2304
2305         /* Get the action */
2306         if (context->action == GDK_ACTION_MOVE) {
2307                 delete_source = TRUE;
2308
2309                 /* Notify that there is no folder selected. We need to
2310                    do this in order to update the headers view (and
2311                    its monitors, because when moving, the old folder
2312                    won't longer exist. We can not wait for the end of
2313                    the operation, because the operation won't start if
2314                    the folder is in use */
2315                 if (source_widget == widget) {
2316                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2317                         gtk_tree_selection_unselect_all (sel);
2318                 }
2319         }
2320
2321         /* Check if the get_data failed */
2322         if (selection_data == NULL || selection_data->length < 0)
2323                 goto end;
2324
2325         /* Select the destination model */
2326         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2327
2328         /* Get the path to the destination row. Can not call
2329            gtk_tree_view_get_drag_dest_row() because the source row
2330            is not selected anymore */
2331         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2332                                            &dest_row, &pos);
2333
2334         /* Only allow drops IN other rows */
2335         if (!dest_row || 
2336             pos == GTK_TREE_VIEW_DROP_BEFORE || 
2337             pos == GTK_TREE_VIEW_DROP_AFTER)
2338                 goto end;
2339
2340         success = TRUE;
2341         /* Drags from the header view */
2342         if (source_widget != widget) {
2343                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2344
2345                 drag_and_drop_from_header_view (source_model,
2346                                                 dest_model,
2347                                                 dest_row,
2348                                                 selection_data);
2349         } else {
2350                 DndHelper *helper = NULL; 
2351
2352                 /* Get the source model and row */
2353                 gtk_tree_get_row_drag_data (selection_data,
2354                                             &source_model,
2355                                             &source_row);
2356
2357                 /* Create the helper */
2358                 helper = g_slice_new0 (DndHelper);
2359                 helper->delete_source = delete_source;
2360                 helper->source_row = gtk_tree_path_copy (source_row);
2361                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2362
2363                 drag_and_drop_from_folder_view (source_model,
2364                                                 dest_model,
2365                                                 dest_row,
2366                                                 selection_data, 
2367                                                 helper);
2368
2369                 gtk_tree_path_free (source_row);
2370         }
2371
2372         /* Frees */
2373         gtk_tree_path_free (dest_row);
2374
2375  end:
2376         /* Finish the drag and drop */
2377         gtk_drag_finish (context, success, FALSE, time);
2378 }
2379
2380 /*
2381  * We define a "drag-drop" signal handler because we do not want to
2382  * use the default one, because the default one always calls
2383  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2384  * signal handler, because there we have all the information available
2385  * to know if the dnd was a success or not.
2386  */
2387 static gboolean
2388 drag_drop_cb (GtkWidget      *widget,
2389               GdkDragContext *context,
2390               gint            x,
2391               gint            y,
2392               guint           time,
2393               gpointer        user_data) 
2394 {
2395         gpointer target;
2396
2397         if (!context->targets)
2398                 return FALSE;
2399
2400         /* Check if we're dragging a folder row */
2401         target = gtk_drag_dest_find_target (widget, context, NULL);
2402
2403         /* Request the data from the source. */
2404         gtk_drag_get_data(widget, context, target, time);
2405
2406     return TRUE;
2407 }
2408
2409 /*
2410  * This function expands a node of a tree view if it's not expanded
2411  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2412  * does that, so that's why they're here.
2413  */
2414 static gint
2415 expand_row_timeout (gpointer data)
2416 {
2417         GtkTreeView *tree_view = data;
2418         GtkTreePath *dest_path = NULL;
2419         GtkTreeViewDropPosition pos;
2420         gboolean result = FALSE;
2421         
2422         gdk_threads_enter ();
2423         
2424         gtk_tree_view_get_drag_dest_row (tree_view,
2425                                          &dest_path,
2426                                          &pos);
2427         
2428         if (dest_path &&
2429             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2430              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2431                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2432                 gtk_tree_path_free (dest_path);
2433         }
2434         else {
2435                 if (dest_path)
2436                         gtk_tree_path_free (dest_path);
2437                 
2438                 result = TRUE;
2439         }
2440         
2441         gdk_threads_leave ();
2442
2443         return result;
2444 }
2445
2446 /*
2447  * This function is called whenever the pointer is moved over a widget
2448  * while dragging some data. It installs a timeout that will expand a
2449  * node of the treeview if not expanded yet. This function also calls
2450  * gdk_drag_status in order to set the suggested action that will be
2451  * used by the "drag-data-received" signal handler to know if we
2452  * should do a move or just a copy of the data.
2453  */
2454 static gboolean
2455 on_drag_motion (GtkWidget      *widget,
2456                 GdkDragContext *context,
2457                 gint            x,
2458                 gint            y,
2459                 guint           time,
2460                 gpointer        user_data)  
2461 {
2462         GtkTreeViewDropPosition pos;
2463         GtkTreePath *dest_row;
2464         GtkTreeModel *dest_model;
2465         ModestFolderViewPrivate *priv;
2466         GdkDragAction suggested_action;
2467         gboolean valid_location = FALSE;
2468         TnyFolderStore *folder = NULL;
2469
2470         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2471
2472         if (priv->timer_expander != 0) {
2473                 g_source_remove (priv->timer_expander);
2474                 priv->timer_expander = 0;
2475         }
2476
2477         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2478                                            x, y,
2479                                            &dest_row,
2480                                            &pos);
2481
2482         /* Do not allow drops between folders */
2483         if (!dest_row ||
2484             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2485             pos == GTK_TREE_VIEW_DROP_AFTER) {
2486                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2487                 gdk_drag_status(context, 0, time);
2488                 valid_location = FALSE;
2489                 goto out;
2490         } else {
2491                 valid_location = TRUE;
2492         }
2493
2494         /* Check that the destination folder is writable */
2495         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2496         folder = tree_path_to_folder (dest_model, dest_row);
2497         if (folder && TNY_IS_FOLDER (folder)) {
2498                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2499
2500                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2501                         valid_location = FALSE;
2502                         goto out;
2503                 }
2504         }
2505
2506         /* Expand the selected row after 1/2 second */
2507         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2508                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2509         }
2510         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2511
2512         /* Select the desired action. By default we pick MOVE */
2513         suggested_action = GDK_ACTION_MOVE;
2514
2515         if (context->actions == GDK_ACTION_COPY)
2516             gdk_drag_status(context, GDK_ACTION_COPY, time);
2517         else if (context->actions == GDK_ACTION_MOVE)
2518             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2519         else if (context->actions & suggested_action)
2520             gdk_drag_status(context, suggested_action, time);
2521         else
2522             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2523
2524  out:
2525         if (folder)
2526                 g_object_unref (folder);
2527         if (dest_row) {
2528                 gtk_tree_path_free (dest_row);
2529         }
2530         g_signal_stop_emission_by_name (widget, "drag-motion");
2531
2532         return valid_location;
2533 }
2534
2535 /*
2536  * This function sets the treeview as a source and a target for dnd
2537  * events. It also connects all the requirede signals.
2538  */
2539 static void
2540 setup_drag_and_drop (GtkTreeView *self)
2541 {
2542         /* Set up the folder view as a dnd destination. Set only the
2543            highlight flag, otherwise gtk will have a different
2544            behaviour */
2545         gtk_drag_dest_set (GTK_WIDGET (self),
2546                            GTK_DEST_DEFAULT_HIGHLIGHT,
2547                            folder_view_drag_types,
2548                            G_N_ELEMENTS (folder_view_drag_types),
2549                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2550
2551         g_signal_connect (G_OBJECT (self),
2552                           "drag_data_received",
2553                           G_CALLBACK (on_drag_data_received),
2554                           NULL);
2555
2556
2557         /* Set up the treeview as a dnd source */
2558         gtk_drag_source_set (GTK_WIDGET (self),
2559                              GDK_BUTTON1_MASK,
2560                              folder_view_drag_types,
2561                              G_N_ELEMENTS (folder_view_drag_types),
2562                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2563
2564         g_signal_connect (G_OBJECT (self),
2565                           "drag_motion",
2566                           G_CALLBACK (on_drag_motion),
2567                           NULL);
2568         
2569         g_signal_connect (G_OBJECT (self),
2570                           "drag_data_get",
2571                           G_CALLBACK (on_drag_data_get),
2572                           NULL);
2573
2574         g_signal_connect (G_OBJECT (self),
2575                           "drag_drop",
2576                           G_CALLBACK (drag_drop_cb),
2577                           NULL);
2578 }
2579
2580 /*
2581  * This function manages the navigation through the folders using the
2582  * keyboard or the hardware keys in the device
2583  */
2584 static gboolean
2585 on_key_pressed (GtkWidget *self,
2586                 GdkEventKey *event,
2587                 gpointer user_data)
2588 {
2589         GtkTreeSelection *selection;
2590         GtkTreeIter iter;
2591         GtkTreeModel *model;
2592         gboolean retval = FALSE;
2593
2594         /* Up and Down are automatically managed by the treeview */
2595         if (event->keyval == GDK_Return) {
2596                 /* Expand/Collapse the selected row */
2597                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2598                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2599                         GtkTreePath *path;
2600
2601                         path = gtk_tree_model_get_path (model, &iter);
2602
2603                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2604                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2605                         else
2606                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2607                         gtk_tree_path_free (path);
2608                 }
2609                 /* No further processing */
2610                 retval = TRUE;
2611         }
2612
2613         return retval;
2614 }
2615
2616 /*
2617  * We listen to the changes in the local folder account name key,
2618  * because we want to show the right name in the view. The local
2619  * folder account name corresponds to the device name in the Maemo
2620  * version. We do this because we do not want to query gconf on each
2621  * tree view refresh. It's better to cache it and change whenever
2622  * necessary.
2623  */
2624 static void 
2625 on_configuration_key_changed (ModestConf* conf, 
2626                               const gchar *key, 
2627                               ModestConfEvent event,
2628                               ModestConfNotificationId id, 
2629                               ModestFolderView *self)
2630 {
2631         ModestFolderViewPrivate *priv;
2632
2633
2634         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2635         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2636
2637         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2638                 g_free (priv->local_account_name);
2639
2640                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2641                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2642                 else
2643                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2644                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2645
2646                 /* Force a redraw */
2647 #if GTK_CHECK_VERSION(2, 8, 0)
2648                 GtkTreeViewColumn * tree_column;
2649
2650                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2651                                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2652                 gtk_tree_view_column_queue_resize (tree_column);
2653 #else
2654                 gtk_widget_queue_draw (GTK_WIDGET (self));
2655 #endif
2656         }
2657 }
2658
2659 void
2660 modest_folder_view_set_style (ModestFolderView *self,
2661                               ModestFolderViewStyle style)
2662 {
2663         ModestFolderViewPrivate *priv;
2664
2665         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2666         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2667                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2668         
2669         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2670
2671         
2672         priv->style = style;
2673 }
2674
2675 void
2676 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2677                                                              const gchar *account_id)
2678 {
2679         ModestFolderViewPrivate *priv;
2680         GtkTreeModel *model;
2681
2682         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2683         
2684         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2685
2686         /* This will be used by the filter_row callback,
2687          * to decided which rows to show: */
2688         if (priv->visible_account_id) {
2689                 g_free (priv->visible_account_id);
2690                 priv->visible_account_id = NULL;
2691         }
2692         if (account_id)
2693                 priv->visible_account_id = g_strdup (account_id);
2694
2695         /* Refilter */
2696         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2697         if (GTK_IS_TREE_MODEL_FILTER (model))
2698                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2699
2700         /* Save settings to gconf */
2701         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2702                                    MODEST_CONF_FOLDER_VIEW_KEY);
2703 }
2704
2705 const gchar *
2706 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2707 {
2708         ModestFolderViewPrivate *priv;
2709
2710         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2711         
2712         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2713
2714         return (const gchar *) priv->visible_account_id;
2715 }
2716
2717 static gboolean
2718 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2719 {
2720         do {
2721                 GtkTreeIter child;
2722                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2723
2724                 gtk_tree_model_get (model, iter, 
2725                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
2726                                     &type, -1);
2727                         
2728                 gboolean result = FALSE;
2729                 if (type == TNY_FOLDER_TYPE_INBOX) {
2730                         result = TRUE;
2731                 }               
2732                 if (result) {
2733                         *inbox_iter = *iter;
2734                         return TRUE;
2735                 }
2736
2737                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2738                         if (find_inbox_iter (model, &child, inbox_iter))
2739                                 return TRUE;
2740                 }
2741
2742         } while (gtk_tree_model_iter_next (model, iter));
2743
2744         return FALSE;
2745 }
2746
2747
2748
2749
2750 void 
2751 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2752 {
2753         GtkTreeModel *model;
2754         GtkTreeIter iter, inbox_iter;
2755         GtkTreeSelection *sel;
2756         GtkTreePath *path = NULL;
2757
2758         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2759         
2760         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2761         if (!model)
2762                 return;
2763
2764         expand_root_items (self);
2765         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2766
2767         if (!gtk_tree_model_get_iter_first (model, &iter)) {
2768                 g_warning ("%s: model is empty", __FUNCTION__);
2769                 return;
2770         }
2771
2772         if (find_inbox_iter (model, &iter, &inbox_iter))
2773                 path = gtk_tree_model_get_path (model, &inbox_iter);
2774         else
2775                 path = gtk_tree_path_new_first ();
2776
2777         /* Select the row and free */
2778         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2779         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2780         gtk_tree_path_free (path);
2781
2782         /* set focus */
2783         gtk_widget_grab_focus (GTK_WIDGET(self));
2784 }
2785
2786
2787 /* recursive */
2788 static gboolean
2789 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, 
2790                   TnyFolder* folder)
2791 {
2792         do {
2793                 GtkTreeIter child;
2794                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2795                 TnyFolder* a_folder;
2796                 gchar *name = NULL;
2797                 
2798                 gtk_tree_model_get (model, iter, 
2799                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2800                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2801                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type, 
2802                                     -1);                
2803                 g_free (name);
2804
2805                 if (folder == a_folder) {
2806                         g_object_unref (a_folder);
2807                         *folder_iter = *iter;
2808                         return TRUE;
2809                 }
2810                 g_object_unref (a_folder);
2811                 
2812                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2813                         if (find_folder_iter (model, &child, folder_iter, folder)) 
2814                                 return TRUE;
2815                 }
2816
2817         } while (gtk_tree_model_iter_next (model, iter));
2818
2819         return FALSE;
2820 }
2821
2822
2823 static void
2824 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, 
2825                                      GtkTreePath *path, 
2826                                      GtkTreeIter *iter,
2827                                      ModestFolderView *self)
2828 {
2829         ModestFolderViewPrivate *priv = NULL;
2830         GtkTreeSelection *sel;
2831         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2832         GObject *instance = NULL;
2833
2834         if (!MODEST_IS_FOLDER_VIEW(self))
2835                 return;
2836         
2837         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2838
2839         priv->reexpand = TRUE;
2840
2841         gtk_tree_model_get (tree_model, iter, 
2842                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2843                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2844                             -1);
2845         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2846                 priv->folder_to_select = g_object_ref (instance);
2847         }
2848         g_object_unref (instance);
2849         
2850         if (priv->folder_to_select) {
2851                 
2852                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2853                                                        FALSE)) {
2854                         GtkTreePath *path;
2855                         path = gtk_tree_model_get_path (tree_model, iter);
2856                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2857                         
2858                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2859
2860                         gtk_tree_selection_select_iter (sel, iter);
2861                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2862
2863                         gtk_tree_path_free (path);              
2864                 }
2865
2866                 /* Disable next */
2867                 modest_folder_view_disable_next_folder_selection (self);
2868                 
2869                 /* Refilter the model */
2870                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2871         }
2872 }
2873
2874
2875 void
2876 modest_folder_view_disable_next_folder_selection (ModestFolderView *self) 
2877 {
2878         ModestFolderViewPrivate *priv;
2879
2880         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2881
2882         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2883
2884         if (priv->folder_to_select)
2885                 g_object_unref(priv->folder_to_select);
2886         
2887         priv->folder_to_select = NULL;
2888 }
2889
2890 gboolean
2891 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder, 
2892                                   gboolean after_change)
2893 {
2894         GtkTreeModel *model;
2895         GtkTreeIter iter, folder_iter;
2896         GtkTreeSelection *sel;
2897         ModestFolderViewPrivate *priv = NULL;
2898         
2899         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);     
2900         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE); 
2901                 
2902         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2903
2904         if (after_change) {
2905                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2906                 gtk_tree_selection_unselect_all (sel);
2907
2908                 if (priv->folder_to_select)
2909                         g_object_unref(priv->folder_to_select);
2910                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2911                 return TRUE;
2912         }
2913                 
2914         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2915         if (!model)
2916                 return FALSE;
2917
2918
2919         /* Refilter the model, before selecting the folder */
2920         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2921
2922         if (!gtk_tree_model_get_iter_first (model, &iter)) {
2923                 g_warning ("%s: model is empty", __FUNCTION__);
2924                 return FALSE;
2925         }
2926         
2927         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2928                 GtkTreePath *path;
2929
2930                 path = gtk_tree_model_get_path (model, &folder_iter);
2931                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2932
2933                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2934                 gtk_tree_selection_select_iter (sel, &folder_iter);
2935                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2936
2937                 gtk_tree_path_free (path);
2938                 return TRUE;
2939         }
2940         return FALSE;
2941 }
2942
2943
2944 void 
2945 modest_folder_view_copy_selection (ModestFolderView *self)
2946 {
2947         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2948         
2949         /* Copy selection */
2950         _clipboard_set_selected_data (self, FALSE);
2951 }
2952
2953 void 
2954 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2955 {
2956         ModestFolderViewPrivate *priv = NULL;
2957         GtkTreeModel *model = NULL;
2958         const gchar **hidding = NULL;
2959         guint i, n_selected;
2960
2961         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2962         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2963
2964         /* Copy selection */
2965         if (!_clipboard_set_selected_data (folder_view, TRUE))
2966                 return;
2967
2968         /* Get hidding ids */
2969         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
2970         
2971         /* Clear hidding array created by previous cut operation */
2972         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2973
2974         /* Copy hidding array */
2975         priv->n_selected = n_selected;
2976         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2977         for (i=0; i < n_selected; i++) 
2978                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2979
2980         /* Hide cut folders */
2981         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2982         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2983 }
2984
2985 void
2986 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2987                                ModestFolderView *folder_view_dst)
2988 {
2989         GtkTreeModel *filter_model = NULL;
2990         GtkTreeModel *model = NULL;
2991         GtkTreeModel *new_filter_model = NULL;
2992         
2993         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
2994         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
2995         
2996         /* Get src model*/
2997         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2998         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2999
3000         /* Build new filter model */
3001         new_filter_model = gtk_tree_model_filter_new (model, NULL);     
3002         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3003                                                 filter_row,
3004                                                 folder_view_dst,
3005                                                 NULL);
3006         /* Set copied model */
3007         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3008         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3009                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3010
3011         /* Free */
3012         g_object_unref (new_filter_model);
3013 }
3014
3015 void
3016 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3017                                           gboolean show)
3018 {
3019         GtkTreeModel *model = NULL;
3020         ModestFolderViewPrivate* priv;
3021
3022         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3023
3024         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);     
3025         priv->show_non_move = show;
3026 /*      modest_folder_view_update_model(folder_view, */
3027 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3028
3029         /* Hide special folders */
3030         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3031         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3032                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3033         }
3034 }
3035
3036 /* Returns FALSE if it did not selected anything */
3037 static gboolean
3038 _clipboard_set_selected_data (ModestFolderView *folder_view,
3039                               gboolean delete)
3040 {
3041         ModestFolderViewPrivate *priv = NULL;
3042         TnyFolderStore *folder = NULL;
3043         gboolean retval = FALSE;
3044
3045         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3046         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3047                 
3048         /* Set selected data on clipboard   */
3049         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3050         folder = modest_folder_view_get_selected (folder_view);
3051
3052         /* Do not allow to select an account */
3053         if (TNY_IS_FOLDER (folder)) {
3054                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3055                 retval = TRUE;
3056         }
3057
3058         /* Free */
3059         g_object_unref (folder);
3060
3061         return retval;
3062 }
3063
3064 static void
3065 _clear_hidding_filter (ModestFolderView *folder_view) 
3066 {
3067         ModestFolderViewPrivate *priv;
3068         guint i;
3069         
3070         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
3071         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3072
3073         if (priv->hidding_ids != NULL) {
3074                 for (i=0; i < priv->n_selected; i++) 
3075                         g_free (priv->hidding_ids[i]);
3076                 g_free(priv->hidding_ids);
3077         }       
3078 }
3079
3080
3081 static void 
3082 on_display_name_changed (ModestAccountMgr *mgr, 
3083                          const gchar *account,
3084                          gpointer user_data)
3085 {
3086         ModestFolderView *self;
3087
3088         self = MODEST_FOLDER_VIEW (user_data);
3089
3090         /* Force a redraw */
3091 #if GTK_CHECK_VERSION(2, 8, 0)
3092         GtkTreeViewColumn * tree_column;
3093         
3094         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
3095                                                 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3096         gtk_tree_view_column_queue_resize (tree_column);
3097 #else
3098         gtk_widget_queue_draw (GTK_WIDGET (self));
3099 #endif
3100 }