* Partially fixes NB#83671, fixed a reference leak
[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 /*      modest_folder_view_disable_next_folder_selection (MODEST_FOLDER_VIEW(obj)); */
1115         if (priv->folder_to_select) {
1116                 g_object_unref (G_OBJECT(priv->folder_to_select));
1117                 priv->folder_to_select = NULL;
1118         }
1119    
1120         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1121         if (sel)
1122                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1123
1124         g_free (priv->local_account_name);
1125         g_free (priv->visible_account_id);
1126         
1127         if (priv->conf_key_signal) {
1128                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1129                                              priv->conf_key_signal);
1130                 priv->conf_key_signal = 0;
1131         }
1132
1133         if (priv->cur_folder_store) {
1134                 if (TNY_IS_FOLDER(priv->cur_folder_store)) {
1135                         ModestMailOperation *mail_op;
1136
1137                         mail_op = modest_mail_operation_new (NULL);
1138                         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1139                                                          mail_op);
1140                         modest_mail_operation_sync_folder (mail_op, TNY_FOLDER (priv->cur_folder_store), FALSE);
1141                         g_object_unref (mail_op);
1142                 }
1143
1144                 g_object_unref (priv->cur_folder_store);
1145                 priv->cur_folder_store = NULL;
1146         }
1147
1148         /* Clear hidding array created by cut operation */
1149         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1150
1151         G_OBJECT_CLASS(parent_class)->finalize (obj);
1152 }
1153
1154
1155 static void
1156 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1157 {
1158         ModestFolderViewPrivate *priv;
1159         TnyDevice *device;
1160
1161         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1162         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1163
1164         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1165         device = tny_account_store_get_device (account_store);
1166
1167         if (G_UNLIKELY (priv->account_store)) {
1168
1169                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1170                                                    priv->account_inserted_signal))
1171                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1172                                                      priv->account_inserted_signal);
1173                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
1174                                                    priv->account_removed_signal))
1175                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
1176                                                      priv->account_removed_signal);
1177                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store), 
1178                                                    priv->account_changed_signal))
1179                         g_signal_handler_disconnect (G_OBJECT (priv->account_store), 
1180                                                      priv->account_changed_signal);
1181                 g_object_unref (G_OBJECT (priv->account_store));
1182         }
1183
1184         priv->account_store = g_object_ref (G_OBJECT (account_store));
1185
1186         priv->account_removed_signal = 
1187                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1188                                   G_CALLBACK (on_account_removed), self);
1189
1190         priv->account_inserted_signal =
1191                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1192                                   G_CALLBACK (on_account_inserted), self);
1193
1194         priv->account_changed_signal =
1195                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1196                                   G_CALLBACK (on_account_changed), self);
1197
1198         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1199         
1200         g_object_unref (G_OBJECT (device));
1201 }
1202
1203 static void
1204 on_account_inserted (TnyAccountStore *account_store, 
1205                      TnyAccount *account,
1206                      gpointer user_data)
1207 {
1208         ModestFolderViewPrivate *priv;
1209         GtkTreeModel *sort_model, *filter_model;
1210
1211         /* Ignore transport account insertions, we're not showing them
1212            in the folder view */
1213         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1214                 return;
1215
1216         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1217
1218         /* If we're adding a new account, and there is no previous
1219            one, we need to select the visible server account */
1220         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1221             !priv->visible_account_id)
1222                 modest_widget_memory_restore (modest_runtime_get_conf(), 
1223                                               G_OBJECT (user_data),
1224                                               MODEST_CONF_FOLDER_VIEW_KEY);
1225
1226         /* Get the inner model */
1227         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1228         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1229
1230         /* Insert the account in the model */
1231         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1232                          G_OBJECT (account));
1233
1234         /* Refilter the model */
1235         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1236 }
1237
1238
1239 static void
1240 on_account_changed (TnyAccountStore *account_store, 
1241                     TnyAccount *tny_account,
1242                     gpointer user_data)
1243 {
1244         ModestFolderViewPrivate *priv;
1245         GtkTreeModel *sort_model, *filter_model;
1246
1247         /* Ignore transport account insertions, we're not showing them
1248            in the folder view */
1249         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1250                 return;
1251
1252         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1253
1254         /* Get the inner model */
1255         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1256         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1257
1258         /* Remove the account from the model */
1259         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1260                          G_OBJECT (tny_account));
1261
1262         /* Insert the account in the model */
1263         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1264                          G_OBJECT (tny_account));
1265
1266         /* Refilter the model */
1267         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1268 }
1269
1270 /**
1271  *
1272  * Selects the first inbox or the local account in an idle
1273  */
1274 static gboolean
1275 on_idle_select_first_inbox_or_local (gpointer user_data)
1276 {
1277         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1278
1279         gdk_threads_enter ();
1280         modest_folder_view_select_first_inbox_or_local (self);
1281         gdk_threads_leave ();
1282
1283         return FALSE;
1284 }
1285
1286
1287 static void
1288 on_account_removed (TnyAccountStore *account_store, 
1289                     TnyAccount *account,
1290                     gpointer user_data)
1291 {
1292         ModestFolderView *self = NULL;
1293         ModestFolderViewPrivate *priv;
1294         GtkTreeModel *sort_model, *filter_model;
1295         GtkTreeSelection *sel = NULL;
1296         gboolean same_account_selected = FALSE;
1297
1298         /* Ignore transport account removals, we're not showing them
1299            in the folder view */
1300         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1301                 return;
1302
1303         self = MODEST_FOLDER_VIEW (user_data);
1304         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1305
1306         /* Invalidate the cur_folder_store only if the selected folder
1307            belongs to the account that is being removed */
1308         if (priv->cur_folder_store) {
1309                 TnyAccount *selected_folder_account = NULL;
1310
1311                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1312                         selected_folder_account = 
1313                                 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1314                 } else {
1315                         selected_folder_account = 
1316                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1317                 }
1318
1319                 if (selected_folder_account == account) {
1320                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1321                         gtk_tree_selection_unselect_all (sel);
1322                         same_account_selected = TRUE;
1323                 }
1324                 g_object_unref (selected_folder_account);
1325         }
1326
1327         /* Invalidate row to select only if the folder to select
1328            belongs to the account that is being removed*/
1329         if (priv->folder_to_select) {
1330                 TnyAccount *folder_to_select_account = NULL;
1331
1332                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1333                 if (folder_to_select_account == account) {
1334                         modest_folder_view_disable_next_folder_selection (self);
1335                         g_object_unref (priv->folder_to_select);
1336                         priv->folder_to_select = NULL;
1337                 }
1338                 g_object_unref (folder_to_select_account);
1339         }
1340
1341         /* Remove the account from the model */
1342         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1343         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1344         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1345                          G_OBJECT (account));
1346
1347         /* If the removed account is the currently viewed one then
1348            clear the configuration value. The new visible account will be the default account */
1349         if (priv->visible_account_id &&
1350             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1351
1352                 /* Clear the current visible account_id */
1353                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1354
1355                 /* Call the restore method, this will set the new visible account */
1356                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1357                                               MODEST_CONF_FOLDER_VIEW_KEY);
1358         }
1359
1360         /* Refilter the model */
1361         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1362
1363         /* Select the first INBOX if the currently selected folder
1364            belongs to the account that is being deleted */
1365         if (same_account_selected)
1366                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1367 }
1368
1369 void
1370 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1371 {
1372         GtkTreeViewColumn *col;
1373         
1374         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1375
1376         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1377         if (!col) {
1378                 g_printerr ("modest: failed get column for title\n");
1379                 return;
1380         }
1381
1382         gtk_tree_view_column_set_title (col, title);
1383         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1384                                            title != NULL);
1385 }
1386
1387 static gboolean
1388 modest_folder_view_on_map (ModestFolderView *self, 
1389                            GdkEventExpose *event,
1390                            gpointer data)
1391 {
1392         ModestFolderViewPrivate *priv;
1393
1394         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1395
1396         /* This won't happen often */
1397         if (G_UNLIKELY (priv->reselect)) {
1398                 /* Select the first inbox or the local account if not found */
1399
1400                 /* TODO: this could cause a lock at startup, so we
1401                    comment it for the moment. We know that this will
1402                    be a bug, because the INBOX is not selected, but we
1403                    need to rewrite some parts of Modest to avoid the
1404                    deathlock situation */
1405                 /* TODO: check if this is still the case */
1406                 priv->reselect = FALSE;
1407                 modest_folder_view_select_first_inbox_or_local (self);
1408                 /* Notify the display name observers */
1409                 g_signal_emit (G_OBJECT(self),
1410                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1411                                NULL);
1412         }
1413
1414         if (priv->reexpand) {
1415                 expand_root_items (self); 
1416                 priv->reexpand = FALSE;
1417         }
1418
1419         return FALSE;
1420 }
1421
1422 GtkWidget*
1423 modest_folder_view_new (TnyFolderStoreQuery *query)
1424 {
1425         GObject *self;
1426         ModestFolderViewPrivate *priv;
1427         GtkTreeSelection *sel;
1428         
1429         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1430         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1431
1432         if (query)
1433                 priv->query = g_object_ref (query);
1434         
1435         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1436         priv->changed_signal = g_signal_connect (sel, "changed",
1437                                                  G_CALLBACK (on_selection_changed), self);
1438
1439         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1440
1441         return GTK_WIDGET(self);
1442 }
1443
1444 /* this feels dirty; any other way to expand all the root items? */
1445 static void
1446 expand_root_items (ModestFolderView *self)
1447 {
1448         GtkTreePath *path;
1449         GtkTreeModel *model;
1450         GtkTreeIter iter;
1451
1452         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1453         path = gtk_tree_path_new_first ();
1454
1455         /* all folders should have child items, so.. */
1456         do {
1457                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1458                 gtk_tree_path_next (path);
1459         } while (gtk_tree_model_get_iter (model, &iter, path));
1460         
1461         gtk_tree_path_free (path);
1462 }
1463
1464 /*
1465  * We use this function to implement the
1466  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1467  * account in this case, and the local folders.
1468  */
1469 static gboolean 
1470 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1471 {
1472         ModestFolderViewPrivate *priv;
1473         gboolean retval = TRUE;
1474         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1475         GObject *instance = NULL;
1476         const gchar *id = NULL;
1477         guint i;
1478         gboolean found = FALSE;
1479         gboolean cleared = FALSE;
1480
1481         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1482         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1483
1484         gtk_tree_model_get (model, iter,
1485                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1486                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1487                             -1);
1488
1489         /* Do not show if there is no instance, this could indeed
1490            happen when the model is being modified while it's being
1491            drawn. This could occur for example when moving folders
1492            using drag&drop */
1493         if (!instance)
1494                 return FALSE;
1495
1496         if (type == TNY_FOLDER_TYPE_ROOT) {
1497                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1498                    account instead of a folder. */
1499                 if (TNY_IS_ACCOUNT (instance)) {
1500                         TnyAccount *acc = TNY_ACCOUNT (instance);
1501                         const gchar *account_id = tny_account_get_id (acc);
1502         
1503                         /* If it isn't a special folder, 
1504                          * don't show it unless it is the visible account: */
1505                         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1506                             !modest_tny_account_is_virtual_local_folders (acc) &&
1507                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1508                                 
1509                                 /* Show only the visible account id */
1510                                 if (priv->visible_account_id) {
1511                                         if (strcmp (account_id, priv->visible_account_id))
1512                                                 retval = FALSE;
1513                                 } else {
1514                                         retval = FALSE;
1515                                 }                               
1516                         }
1517                         
1518                         /* Never show these to the user. They are merged into one folder 
1519                          * in the local-folders account instead: */
1520                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1521                                 retval = FALSE;
1522                 }
1523         }
1524
1525         /* Check hiding (if necessary) */
1526         cleared = modest_email_clipboard_cleared (priv->clipboard);            
1527         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1528                 id = tny_folder_get_id (TNY_FOLDER(instance));
1529                 if (priv->hidding_ids != NULL)
1530                         for (i=0; i < priv->n_selected && !found; i++)
1531                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1532                                         found = (!strcmp (priv->hidding_ids[i], id));
1533                 
1534                 retval = !found;
1535         }
1536         
1537         
1538         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1539         folder as no message can be move there according to UI specs */
1540         if (!priv->show_non_move) {
1541                 switch (type) {
1542                         case TNY_FOLDER_TYPE_OUTBOX:
1543                         case TNY_FOLDER_TYPE_SENT:
1544                         case TNY_FOLDER_TYPE_DRAFTS:
1545                                 retval = FALSE;
1546                                 break;
1547                         case TNY_FOLDER_TYPE_UNKNOWN:
1548                         case TNY_FOLDER_TYPE_NORMAL:
1549                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1550                                 if (type == TNY_FOLDER_TYPE_INVALID)
1551                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1552                                 
1553                                 if (type == TNY_FOLDER_TYPE_OUTBOX || 
1554                                     type == TNY_FOLDER_TYPE_SENT
1555                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1556                                         retval = FALSE;
1557                                 break;
1558                         default:
1559                                 break;
1560                 }
1561         }
1562         
1563         /* Free */
1564         g_object_unref (instance);
1565
1566         return retval;
1567 }
1568
1569
1570 gboolean
1571 modest_folder_view_update_model (ModestFolderView *self,
1572                                  TnyAccountStore *account_store)
1573 {
1574         ModestFolderViewPrivate *priv;
1575         GtkTreeModel *model /* , *old_model */;                                                    
1576         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1577
1578         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1579         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1580                               FALSE);
1581         
1582         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1583         
1584         /* Notify that there is no folder selected */
1585         g_signal_emit (G_OBJECT(self),
1586                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1587                        NULL, FALSE);
1588         if (priv->cur_folder_store) {
1589                 g_object_unref (priv->cur_folder_store);
1590                 priv->cur_folder_store = NULL;
1591         }
1592
1593         /* FIXME: the local accounts are not shown when the query
1594            selects only the subscribed folders */
1595         model = tny_gtk_folder_store_tree_model_new (NULL);
1596
1597         /* Get the accounts: */
1598         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1599                                         TNY_LIST (model),
1600                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1601
1602         sortable = gtk_tree_model_sort_new_with_model (model);
1603         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1604                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
1605                                               GTK_SORT_ASCENDING);
1606         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1607                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1608                                          cmp_rows, NULL, NULL);
1609
1610         /* Create filter model */
1611         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1612         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1613                                                 filter_row,
1614                                                 self,
1615                                                 NULL);
1616
1617         /* Set new model */
1618         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1619         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1620                           (GCallback) on_row_inserted_maybe_select_folder, self);
1621
1622         g_object_unref (model);
1623         g_object_unref (filter_model);          
1624         g_object_unref (sortable);
1625         
1626         /* Force a reselection of the INBOX next time the widget is shown */
1627         priv->reselect = TRUE;
1628                         
1629         return TRUE;
1630 }
1631
1632
1633 static void
1634 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1635 {
1636         GtkTreeModel *model = NULL;
1637         TnyFolderStore *folder = NULL;
1638         GtkTreeIter iter;
1639         ModestFolderView *tree_view = NULL;
1640         ModestFolderViewPrivate *priv = NULL;
1641         gboolean selected = FALSE;
1642
1643         g_return_if_fail (sel);
1644         g_return_if_fail (user_data);
1645         
1646         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1647
1648         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1649
1650         tree_view = MODEST_FOLDER_VIEW (user_data);
1651
1652         if (selected) {
1653                 gtk_tree_model_get (model, &iter,
1654                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1655                                     -1);
1656
1657                 /* If the folder is the same do not notify */
1658                 if (folder && priv->cur_folder_store == folder) {
1659                         g_object_unref (folder);
1660                         return;
1661                 }
1662         }
1663         
1664         /* Current folder was unselected */
1665         if (priv->cur_folder_store) {
1666                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1667                        priv->cur_folder_store, FALSE);
1668
1669                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1670                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1671                                                FALSE, NULL, NULL, NULL);
1672
1673                 /* FALSE --> don't expunge the messages */
1674
1675                 g_object_unref (priv->cur_folder_store);
1676                 priv->cur_folder_store = NULL;
1677         }
1678
1679         /* New current references */
1680         priv->cur_folder_store = folder;
1681
1682         /* New folder has been selected. Do not notify if there is
1683            nothing new selected */
1684         if (selected) {
1685                 g_signal_emit (G_OBJECT(tree_view),
1686                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1687                                0, priv->cur_folder_store, TRUE);
1688         }
1689 }
1690
1691 TnyFolderStore *
1692 modest_folder_view_get_selected (ModestFolderView *self)
1693 {
1694         ModestFolderViewPrivate *priv;
1695         
1696         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1697         
1698         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1699         if (priv->cur_folder_store)
1700                 g_object_ref (priv->cur_folder_store);
1701
1702         return priv->cur_folder_store;
1703 }
1704
1705 static gint
1706 get_cmp_rows_type_pos (GObject *folder)
1707 {
1708         /* Remote accounts -> Local account -> MMC account .*/
1709         /* 0, 1, 2 */
1710         
1711         if (TNY_IS_ACCOUNT (folder) && 
1712                 modest_tny_account_is_virtual_local_folders (
1713                         TNY_ACCOUNT (folder))) {
1714                 return 1;
1715         } else if (TNY_IS_ACCOUNT (folder)) {
1716                 TnyAccount *account = TNY_ACCOUNT (folder);
1717                 const gchar *account_id = tny_account_get_id (account);
1718                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1719                         return 2;
1720                 else
1721                         return 0;
1722         }
1723         else {
1724                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1725                 return -1; /* Should never happen */
1726         }
1727 }
1728
1729 static gint
1730 get_cmp_subfolder_type_pos (TnyFolderType t)
1731 {
1732         /* Inbox, Outbox, Drafts, Sent, User */
1733         /* 0, 1, 2, 3, 4 */
1734
1735         switch (t) {
1736         case TNY_FOLDER_TYPE_INBOX:
1737                 return 0;
1738                 break;
1739         case TNY_FOLDER_TYPE_OUTBOX:
1740                 return 1;
1741                 break;
1742         case TNY_FOLDER_TYPE_DRAFTS:
1743                 return 2;
1744                 break;
1745         case TNY_FOLDER_TYPE_SENT:
1746                 return 3;
1747                 break;
1748         default:
1749                 return 4;
1750         }
1751 }
1752
1753 /*
1754  * This function orders the mail accounts according to these rules:
1755  * 1st - remote accounts
1756  * 2nd - local account
1757  * 3rd - MMC account
1758  */
1759 static gint
1760 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1761           gpointer user_data)
1762 {
1763         gint cmp = 0;
1764         gchar *name1 = NULL;
1765         gchar *name2 = NULL;
1766         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1767         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1768         GObject *folder1 = NULL;
1769         GObject *folder2 = NULL;
1770
1771         gtk_tree_model_get (tree_model, iter1,
1772                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1773                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1774                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1775                             -1);
1776         gtk_tree_model_get (tree_model, iter2,
1777                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1778                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1779                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1780                             -1);
1781
1782         /* Return if we get no folder. This could happen when folder
1783            operations are happening. The model is updated after the
1784            folder copy/move actually occurs, so there could be
1785            situations where the model to be drawn is not correct */
1786         if (!folder1 || !folder2)
1787                 goto finish;
1788
1789         if (type == TNY_FOLDER_TYPE_ROOT) {
1790                 /* Compare the types, so that 
1791                  * Remote accounts -> Local account -> MMC account .*/
1792                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1793                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1794                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1795                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1796                 if (pos1 <  pos2)
1797                         cmp = -1;
1798                 else if (pos1 > pos2)
1799                         cmp = 1;
1800                 else {
1801                         /* Compare items of the same type: */
1802                         
1803                         TnyAccount *account1 = NULL;
1804                         if (TNY_IS_ACCOUNT (folder1))
1805                                 account1 = TNY_ACCOUNT (folder1);
1806                                 
1807                         TnyAccount *account2 = NULL;
1808                         if (TNY_IS_ACCOUNT (folder2))
1809                                 account2 = TNY_ACCOUNT (folder2);
1810                                 
1811                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1812                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1813         
1814                         if (!account_id && !account_id2) {
1815                                 cmp = 0;
1816                         } else if (!account_id) {
1817                                 cmp = -1;
1818                         } else if (!account_id2) {
1819                                 cmp = +1;
1820                         } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1821                                 cmp = +1;
1822                         } else {
1823                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1824                         }
1825                 }
1826         } else {
1827                 gint cmp1 = 0, cmp2 = 0;
1828                 /* get the parent to know if it's a local folder */
1829
1830                 GtkTreeIter parent;
1831                 gboolean has_parent;
1832                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1833                 if (has_parent) {
1834                         GObject *parent_folder;
1835                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1836                         gtk_tree_model_get (tree_model, &parent, 
1837                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1838                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1839                                             -1);
1840                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1841                             TNY_IS_ACCOUNT (parent_folder)) {
1842                                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1843                                         cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1844                                                                            (TNY_FOLDER (folder1)));
1845                                         cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1846                                                                            (TNY_FOLDER (folder2)));
1847                                 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1848                                         if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1849                                                 cmp1 = 0;
1850                                                 cmp2 = 1;
1851                                                 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1852                                                 cmp1 = 1;
1853                                                 cmp2 = 0;
1854                                         }
1855                                 }
1856                         }
1857                         g_object_unref (parent_folder);
1858                 }
1859                 
1860                 /* if they are not local folders */
1861                 if (cmp1 == cmp2) {
1862                         cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1863                         cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1864                 }
1865
1866                 if (cmp1 == cmp2)
1867                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1868                 else 
1869                         cmp = (cmp1 - cmp2);
1870         }
1871
1872 finish: 
1873         if (folder1)
1874                 g_object_unref(G_OBJECT(folder1));
1875         if (folder2)
1876                 g_object_unref(G_OBJECT(folder2));
1877
1878         g_free (name1);
1879         g_free (name2);
1880
1881         return cmp;     
1882 }
1883
1884 /*****************************************************************************/
1885 /*                        DRAG and DROP stuff                                */
1886 /*****************************************************************************/
1887 /*
1888  * This function fills the #GtkSelectionData with the row and the
1889  * model that has been dragged. It's called when this widget is a
1890  * source for dnd after the event drop happened
1891  */
1892 static void
1893 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, 
1894                   guint info, guint time, gpointer data)
1895 {
1896         GtkTreeSelection *selection;
1897         GtkTreeModel *model;
1898         GtkTreeIter iter;
1899         GtkTreePath *source_row;
1900         
1901         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1902         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1903
1904                 source_row = gtk_tree_model_get_path (model, &iter);
1905                 gtk_tree_set_row_drag_data (selection_data,
1906                                             model,
1907                                             source_row);
1908                 
1909                 gtk_tree_path_free (source_row);
1910         }
1911 }
1912
1913 typedef struct _DndHelper {
1914         ModestFolderView *folder_view;
1915         gboolean delete_source;
1916         GtkTreePath *source_row;
1917         GdkDragContext *context;
1918         guint time;
1919 } DndHelper;
1920
1921 static void
1922 dnd_helper_destroyer (DndHelper *helper)
1923 {
1924         /* Free the helper */
1925         g_object_unref (helper->folder_view);
1926         gtk_tree_path_free (helper->source_row);
1927         g_slice_free (DndHelper, helper);
1928 }
1929
1930 static void
1931 xfer_cb (ModestMailOperation *mail_op, 
1932          gpointer user_data)
1933 {
1934         gboolean success;
1935         DndHelper *helper;
1936
1937         helper = (DndHelper *) user_data;
1938
1939         if (modest_mail_operation_get_status (mail_op) == 
1940             MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1941                 success = TRUE;
1942         } else {
1943                 success = FALSE;
1944         }
1945
1946         /* Notify the drag source. Never call delete, the monitor will
1947            do the job if needed */
1948         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1949
1950         /* Free the helper */
1951         dnd_helper_destroyer (helper);
1952 }
1953
1954 static void
1955 xfer_msgs_cb (ModestMailOperation *mail_op, 
1956               gpointer user_data)
1957 {
1958         /* Common part */
1959         xfer_cb (mail_op, user_data);
1960 }
1961
1962 static void
1963 xfer_folder_cb (ModestMailOperation *mail_op, 
1964                 TnyFolder *new_folder,
1965                 gpointer user_data)
1966 {
1967         DndHelper *helper;
1968         GtkWidget *folder_view;
1969
1970         helper = (DndHelper *) user_data;
1971         folder_view = g_object_ref (helper->folder_view);
1972
1973         /* Common part */
1974         xfer_cb (mail_op, user_data);
1975
1976         /* Select the folder */
1977         if (new_folder)
1978                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
1979                                                   new_folder, FALSE);
1980         g_object_unref (folder_view);
1981 }
1982
1983
1984 /* get the folder for the row the treepath refers to. */
1985 /* folder must be unref'd */
1986 static TnyFolderStore *
1987 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1988 {
1989         GtkTreeIter iter;
1990         TnyFolderStore *folder = NULL;
1991         
1992         if (gtk_tree_model_get_iter (model,&iter, path))
1993                 gtk_tree_model_get (model, &iter,
1994                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1995                                     -1);
1996         return folder;
1997 }
1998
1999 /*
2000  * This function is used by drag_data_received_cb to manage drag and
2001  * drop of a header, i.e, and drag from the header view to the folder
2002  * view.
2003  */
2004 static void
2005 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2006                                 GtkTreeModel *dest_model,
2007                                 GtkTreePath  *dest_row,
2008                                 GtkSelectionData *selection_data,
2009                                 DndHelper    *helper)
2010 {
2011         TnyList *headers = NULL;
2012         TnyFolder *folder = NULL;
2013         TnyFolderType folder_type;
2014         ModestMailOperation *mail_op = NULL;
2015         GtkTreeIter source_iter, dest_iter;
2016         ModestWindowMgr *mgr = NULL;
2017         ModestWindow *main_win = NULL;
2018         gchar **uris, **tmp;
2019         gint response;
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
2031                 /* Get header */
2032                 path = gtk_tree_path_new_from_string (*tmp);
2033                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2034                 gtk_tree_model_get (source_model, &source_iter, 
2035                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
2036                                     &header, -1);
2037
2038                 /* Do not enable d&d of headers already opened */
2039                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2040                         tny_list_append (headers, G_OBJECT (header));
2041
2042                 /* Free and go on */
2043                 gtk_tree_path_free (path);
2044                 g_object_unref (header);
2045                 tmp++;
2046         }
2047         g_strfreev (uris);
2048
2049         /* Get the target folder */
2050         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2051         gtk_tree_model_get (dest_model, &dest_iter, 
2052                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2053                             &folder, -1);
2054         
2055         if (!folder || !TNY_IS_FOLDER(folder)) {
2056 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2057                 goto cleanup;
2058         }
2059         
2060         folder_type = modest_tny_folder_guess_folder_type (folder);
2061         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2062 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2063                 goto cleanup;  /* cannot move messages there */
2064         }
2065         
2066         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2067 /*              g_warning ("folder not writable"); */
2068                 goto cleanup; /* verboten! */
2069         }
2070         
2071         /* Ask for confirmation to move */
2072         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2073         if (!main_win) {
2074                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2075                 goto cleanup;
2076         }
2077
2078         response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder, 
2079                                                                 TRUE, headers);
2080         if (response == GTK_RESPONSE_CANCEL)
2081                 goto cleanup;
2082
2083         /* Transfer messages */
2084         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2085                                                                  modest_ui_actions_move_folder_error_handler,
2086                                                                  NULL, NULL);
2087
2088         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2089                                          mail_op);
2090
2091         modest_mail_operation_xfer_msgs (mail_op,
2092                                          headers, 
2093                                          folder, 
2094                                          helper->delete_source, 
2095                                          xfer_msgs_cb, helper);
2096         
2097         /* Frees */
2098 cleanup:
2099         if (G_IS_OBJECT(mail_op))
2100                 g_object_unref (G_OBJECT (mail_op));
2101         if (G_IS_OBJECT(folder))
2102                 g_object_unref (G_OBJECT (folder));
2103         if (G_IS_OBJECT(headers))
2104                 g_object_unref (headers);
2105 }
2106
2107 typedef struct {
2108         TnyFolderStore *src_folder;
2109         TnyFolderStore *dst_folder;
2110         ModestFolderView *folder_view;
2111         DndHelper *helper; 
2112 } DndFolderInfo;
2113
2114 static void
2115 dnd_folder_info_destroyer (DndFolderInfo *info)
2116 {
2117         if (info->src_folder)
2118                 g_object_unref (info->src_folder);
2119         if (info->dst_folder)
2120                 g_object_unref (info->dst_folder);
2121         if (info->folder_view)
2122                 g_object_unref (info->folder_view);
2123         g_slice_free (DndFolderInfo, info);
2124 }
2125
2126 static void
2127 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2128                                     GtkWindow *parent_window,
2129                                     TnyAccount *account)
2130 {
2131         time_t dnd_time = info->helper->time;
2132         GdkDragContext *context = info->helper->context;
2133         
2134         /* Show error */
2135         modest_ui_actions_on_account_connection_error (parent_window, account);
2136
2137         /* Free the helper & info */
2138         dnd_helper_destroyer (info->helper);
2139         dnd_folder_info_destroyer (info);
2140         
2141         /* Notify the drag source. Never call delete, the monitor will
2142            do the job if needed */
2143         gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2144         return;
2145 }
2146
2147 static void
2148 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled, 
2149                                                      GError *err,
2150                                                      GtkWindow *parent_window, 
2151                                                      TnyAccount *account, 
2152                                                      gpointer user_data)
2153 {
2154         DndFolderInfo *info = NULL;
2155         ModestMailOperation *mail_op;
2156
2157         info = (DndFolderInfo *) user_data;
2158
2159         if (err || canceled) {
2160                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2161                 return;
2162         }
2163
2164         /* Do the mail operation */
2165         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2166                                                                  modest_ui_actions_move_folder_error_handler,
2167                                                                  info->src_folder, NULL);
2168
2169         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2170                                          mail_op);
2171
2172         /* Transfer the folder */
2173         modest_mail_operation_xfer_folder (mail_op,
2174                                            TNY_FOLDER (info->src_folder),
2175                                            info->dst_folder,
2176                                            info->helper->delete_source,
2177                                            xfer_folder_cb,
2178                                            info->helper);
2179         
2180 /*      modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2181 /*                                        TNY_FOLDER (info->dst_folder), TRUE); */
2182
2183         g_object_unref (G_OBJECT (mail_op));
2184 }
2185
2186
2187 static void
2188 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled, 
2189                                                      GError *err,
2190                                                      GtkWindow *parent_window, 
2191                                                      TnyAccount *account, 
2192                                                      gpointer user_data)
2193 {
2194         DndFolderInfo *info = NULL;
2195
2196         info = (DndFolderInfo *) user_data;
2197
2198         if (err || canceled) {
2199                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2200                 return;
2201         }
2202
2203         /* Connect to source folder and perform the copy/move */
2204         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2205                                                        info->src_folder,
2206                                                        drag_and_drop_from_folder_view_src_folder_performer,
2207                                                        info);
2208 }
2209
2210 /*
2211  * This function is used by drag_data_received_cb to manage drag and
2212  * drop of a folder, i.e, and drag from the folder view to the same
2213  * folder view.
2214  */
2215 static void
2216 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2217                                 GtkTreeModel     *dest_model,
2218                                 GtkTreePath      *dest_row,
2219                                 GtkSelectionData *selection_data,
2220                                 DndHelper        *helper)
2221 {
2222         GtkTreeIter dest_iter, iter;
2223         TnyFolderStore *dest_folder = NULL;
2224         TnyFolderStore *folder = NULL;
2225         gboolean forbidden = FALSE;
2226         ModestWindow *win;
2227         DndFolderInfo *info = NULL;
2228
2229         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2230         if (!win) {
2231                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2232                 return;
2233         }
2234         
2235         if (!forbidden) {
2236                 /* check the folder rules for the destination */
2237                 folder = tree_path_to_folder (dest_model, dest_row);
2238                 if (TNY_IS_FOLDER(folder)) {
2239                         ModestTnyFolderRules rules =
2240                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2241                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2242                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2243                         /* enable local root as destination for folders */
2244                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) && 
2245                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2246                                 forbidden = TRUE;
2247                 }
2248                 g_object_unref (folder);
2249         }
2250         if (!forbidden) {
2251                 /* check the folder rules for the source */
2252                 folder = tree_path_to_folder (source_model, helper->source_row);
2253                 if (TNY_IS_FOLDER(folder)) {
2254                         ModestTnyFolderRules rules =
2255                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2256                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2257                 } else
2258                         forbidden = TRUE;
2259                 g_object_unref (folder);
2260         }
2261
2262         
2263         /* Check if the drag is possible */
2264         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2265                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2266                 gtk_tree_path_free (helper->source_row);        
2267                 g_slice_free (DndHelper, helper);
2268                 return;
2269         }
2270
2271         /* Get data */
2272         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2273         gtk_tree_model_get (dest_model, &dest_iter, 
2274                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
2275                             &dest_folder, -1);
2276         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2277         gtk_tree_model_get (source_model, &iter,
2278                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2279                             &folder, -1);
2280
2281         /* Create the info for the performer */
2282         info = g_slice_new (DndFolderInfo);
2283         info->src_folder = g_object_ref (folder);
2284         info->dst_folder = g_object_ref (dest_folder);
2285         info->folder_view = g_object_ref (helper->folder_view);
2286         info->helper = helper;
2287
2288         /* Connect to the destination folder and perform the copy/move */
2289         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2290                                                        dest_folder,
2291                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2292                                                        info);
2293         
2294         /* Frees */
2295         g_object_unref (dest_folder);
2296         g_object_unref (folder);
2297 }
2298
2299 /*
2300  * This function receives the data set by the "drag-data-get" signal
2301  * handler. This information comes within the #GtkSelectionData. This
2302  * function will manage both the drags of folders of the treeview and
2303  * drags of headers of the header view widget.
2304  */
2305 static void 
2306 on_drag_data_received (GtkWidget *widget, 
2307                        GdkDragContext *context, 
2308                        gint x, 
2309                        gint y, 
2310                        GtkSelectionData *selection_data, 
2311                        guint target_type, 
2312                        guint time, 
2313                        gpointer data)
2314 {
2315         GtkWidget *source_widget;
2316         GtkTreeModel *dest_model, *source_model;
2317         GtkTreePath *source_row, *dest_row;
2318         GtkTreeViewDropPosition pos;
2319         gboolean success = FALSE, delete_source = FALSE;
2320         DndHelper *helper = NULL; 
2321
2322         /* Do not allow further process */
2323         g_signal_stop_emission_by_name (widget, "drag-data-received");
2324         source_widget = gtk_drag_get_source_widget (context);
2325
2326         /* Get the action */
2327         if (context->action == GDK_ACTION_MOVE) {
2328                 delete_source = TRUE;
2329
2330                 /* Notify that there is no folder selected. We need to
2331                    do this in order to update the headers view (and
2332                    its monitors, because when moving, the old folder
2333                    won't longer exist. We can not wait for the end of
2334                    the operation, because the operation won't start if
2335                    the folder is in use */
2336                 if (source_widget == widget) {
2337                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2338                         gtk_tree_selection_unselect_all (sel);
2339                 }
2340         }
2341
2342         /* Check if the get_data failed */
2343         if (selection_data == NULL || selection_data->length < 0)
2344                 gtk_drag_finish (context, success, FALSE, time);
2345
2346         /* Select the destination model */
2347         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));  
2348
2349         /* Get the path to the destination row. Can not call
2350            gtk_tree_view_get_drag_dest_row() because the source row
2351            is not selected anymore */
2352         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2353                                            &dest_row, &pos);
2354
2355         /* Only allow drops IN other rows */
2356         if (!dest_row || 
2357             pos == GTK_TREE_VIEW_DROP_BEFORE || 
2358             pos == GTK_TREE_VIEW_DROP_AFTER)
2359                 gtk_drag_finish (context, success, FALSE, time);
2360
2361         /* Create the helper */
2362         helper = g_slice_new0 (DndHelper);
2363         helper->delete_source = delete_source;
2364         helper->context = context;
2365         helper->time = time;
2366         helper->folder_view = g_object_ref (widget);
2367
2368         /* Drags from the header view */
2369         if (source_widget != widget) {
2370                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2371
2372                 drag_and_drop_from_header_view (source_model,
2373                                                 dest_model,
2374                                                 dest_row,
2375                                                 selection_data,
2376                                                 helper);
2377         } else {
2378                 /* Get the source model and row */
2379                 gtk_tree_get_row_drag_data (selection_data,
2380                                             &source_model,
2381                                             &source_row);
2382                 helper->source_row = gtk_tree_path_copy (source_row);
2383
2384                 drag_and_drop_from_folder_view (source_model,
2385                                                 dest_model,
2386                                                 dest_row,
2387                                                 selection_data, 
2388                                                 helper);
2389
2390                 gtk_tree_path_free (source_row);
2391         }
2392
2393         /* Frees */
2394         gtk_tree_path_free (dest_row);
2395 }
2396
2397 /*
2398  * We define a "drag-drop" signal handler because we do not want to
2399  * use the default one, because the default one always calls
2400  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2401  * signal handler, because there we have all the information available
2402  * to know if the dnd was a success or not.
2403  */
2404 static gboolean
2405 drag_drop_cb (GtkWidget      *widget,
2406               GdkDragContext *context,
2407               gint            x,
2408               gint            y,
2409               guint           time,
2410               gpointer        user_data) 
2411 {
2412         gpointer target;
2413
2414         if (!context->targets)
2415                 return FALSE;
2416
2417         /* Check if we're dragging a folder row */
2418         target = gtk_drag_dest_find_target (widget, context, NULL);
2419
2420         /* Request the data from the source. */
2421         gtk_drag_get_data(widget, context, target, time);
2422
2423     return TRUE;
2424 }
2425
2426 /*
2427  * This function expands a node of a tree view if it's not expanded
2428  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2429  * does that, so that's why they're here.
2430  */
2431 static gint
2432 expand_row_timeout (gpointer data)
2433 {
2434         GtkTreeView *tree_view = data;
2435         GtkTreePath *dest_path = NULL;
2436         GtkTreeViewDropPosition pos;
2437         gboolean result = FALSE;
2438         
2439         gdk_threads_enter ();
2440         
2441         gtk_tree_view_get_drag_dest_row (tree_view,
2442                                          &dest_path,
2443                                          &pos);
2444         
2445         if (dest_path &&
2446             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2447              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2448                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2449                 gtk_tree_path_free (dest_path);
2450         }
2451         else {
2452                 if (dest_path)
2453                         gtk_tree_path_free (dest_path);
2454                 
2455                 result = TRUE;
2456         }
2457         
2458         gdk_threads_leave ();
2459
2460         return result;
2461 }
2462
2463 /*
2464  * This function is called whenever the pointer is moved over a widget
2465  * while dragging some data. It installs a timeout that will expand a
2466  * node of the treeview if not expanded yet. This function also calls
2467  * gdk_drag_status in order to set the suggested action that will be
2468  * used by the "drag-data-received" signal handler to know if we
2469  * should do a move or just a copy of the data.
2470  */
2471 static gboolean
2472 on_drag_motion (GtkWidget      *widget,
2473                 GdkDragContext *context,
2474                 gint            x,
2475                 gint            y,
2476                 guint           time,
2477                 gpointer        user_data)  
2478 {
2479         GtkTreeViewDropPosition pos;
2480         GtkTreePath *dest_row;
2481         GtkTreeModel *dest_model;
2482         ModestFolderViewPrivate *priv;
2483         GdkDragAction suggested_action;
2484         gboolean valid_location = FALSE;
2485         TnyFolderStore *folder = NULL;
2486
2487         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2488
2489         if (priv->timer_expander != 0) {
2490                 g_source_remove (priv->timer_expander);
2491                 priv->timer_expander = 0;
2492         }
2493
2494         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2495                                            x, y,
2496                                            &dest_row,
2497                                            &pos);
2498
2499         /* Do not allow drops between folders */
2500         if (!dest_row ||
2501             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2502             pos == GTK_TREE_VIEW_DROP_AFTER) {
2503                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2504                 gdk_drag_status(context, 0, time);
2505                 valid_location = FALSE;
2506                 goto out;
2507         } else {
2508                 valid_location = TRUE;
2509         }
2510
2511         /* Check that the destination folder is writable */
2512         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2513         folder = tree_path_to_folder (dest_model, dest_row);
2514         if (folder && TNY_IS_FOLDER (folder)) {
2515                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2516
2517                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2518                         valid_location = FALSE;
2519                         goto out;
2520                 }
2521         }
2522
2523         /* Expand the selected row after 1/2 second */
2524         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2525                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2526         }
2527         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2528
2529         /* Select the desired action. By default we pick MOVE */
2530         suggested_action = GDK_ACTION_MOVE;
2531
2532         if (context->actions == GDK_ACTION_COPY)
2533             gdk_drag_status(context, GDK_ACTION_COPY, time);
2534         else if (context->actions == GDK_ACTION_MOVE)
2535             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2536         else if (context->actions & suggested_action)
2537             gdk_drag_status(context, suggested_action, time);
2538         else
2539             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2540
2541  out:
2542         if (folder)
2543                 g_object_unref (folder);
2544         if (dest_row) {
2545                 gtk_tree_path_free (dest_row);
2546         }
2547         g_signal_stop_emission_by_name (widget, "drag-motion");
2548
2549         return valid_location;
2550 }
2551
2552 /*
2553  * This function sets the treeview as a source and a target for dnd
2554  * events. It also connects all the requirede signals.
2555  */
2556 static void
2557 setup_drag_and_drop (GtkTreeView *self)
2558 {
2559         /* Set up the folder view as a dnd destination. Set only the
2560            highlight flag, otherwise gtk will have a different
2561            behaviour */
2562         gtk_drag_dest_set (GTK_WIDGET (self),
2563                            GTK_DEST_DEFAULT_HIGHLIGHT,
2564                            folder_view_drag_types,
2565                            G_N_ELEMENTS (folder_view_drag_types),
2566                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2567
2568         g_signal_connect (G_OBJECT (self),
2569                           "drag_data_received",
2570                           G_CALLBACK (on_drag_data_received),
2571                           NULL);
2572
2573
2574         /* Set up the treeview as a dnd source */
2575         gtk_drag_source_set (GTK_WIDGET (self),
2576                              GDK_BUTTON1_MASK,
2577                              folder_view_drag_types,
2578                              G_N_ELEMENTS (folder_view_drag_types),
2579                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2580
2581         g_signal_connect (G_OBJECT (self),
2582                           "drag_motion",
2583                           G_CALLBACK (on_drag_motion),
2584                           NULL);
2585         
2586         g_signal_connect (G_OBJECT (self),
2587                           "drag_data_get",
2588                           G_CALLBACK (on_drag_data_get),
2589                           NULL);
2590
2591         g_signal_connect (G_OBJECT (self),
2592                           "drag_drop",
2593                           G_CALLBACK (drag_drop_cb),
2594                           NULL);
2595 }
2596
2597 /*
2598  * This function manages the navigation through the folders using the
2599  * keyboard or the hardware keys in the device
2600  */
2601 static gboolean
2602 on_key_pressed (GtkWidget *self,
2603                 GdkEventKey *event,
2604                 gpointer user_data)
2605 {
2606         GtkTreeSelection *selection;
2607         GtkTreeIter iter;
2608         GtkTreeModel *model;
2609         gboolean retval = FALSE;
2610
2611         /* Up and Down are automatically managed by the treeview */
2612         if (event->keyval == GDK_Return) {
2613                 /* Expand/Collapse the selected row */
2614                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2615                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2616                         GtkTreePath *path;
2617
2618                         path = gtk_tree_model_get_path (model, &iter);
2619
2620                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2621                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2622                         else
2623                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2624                         gtk_tree_path_free (path);
2625                 }
2626                 /* No further processing */
2627                 retval = TRUE;
2628         }
2629
2630         return retval;
2631 }
2632
2633 /*
2634  * We listen to the changes in the local folder account name key,
2635  * because we want to show the right name in the view. The local
2636  * folder account name corresponds to the device name in the Maemo
2637  * version. We do this because we do not want to query gconf on each
2638  * tree view refresh. It's better to cache it and change whenever
2639  * necessary.
2640  */
2641 static void 
2642 on_configuration_key_changed (ModestConf* conf, 
2643                               const gchar *key, 
2644                               ModestConfEvent event,
2645                               ModestConfNotificationId id, 
2646                               ModestFolderView *self)
2647 {
2648         ModestFolderViewPrivate *priv;
2649
2650
2651         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2652         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2653
2654         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2655                 g_free (priv->local_account_name);
2656
2657                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2658                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2659                 else
2660                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2661                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2662
2663                 /* Force a redraw */
2664 #if GTK_CHECK_VERSION(2, 8, 0)
2665                 GtkTreeViewColumn * tree_column;
2666
2667                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2668                                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2669                 gtk_tree_view_column_queue_resize (tree_column);
2670 #else
2671                 gtk_widget_queue_draw (GTK_WIDGET (self));
2672 #endif
2673         }
2674 }
2675
2676 void
2677 modest_folder_view_set_style (ModestFolderView *self,
2678                               ModestFolderViewStyle style)
2679 {
2680         ModestFolderViewPrivate *priv;
2681
2682         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2683         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2684                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2685         
2686         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2687
2688         
2689         priv->style = style;
2690 }
2691
2692 void
2693 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2694                                                              const gchar *account_id)
2695 {
2696         ModestFolderViewPrivate *priv;
2697         GtkTreeModel *model;
2698
2699         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2700         
2701         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2702
2703         /* This will be used by the filter_row callback,
2704          * to decided which rows to show: */
2705         if (priv->visible_account_id) {
2706                 g_free (priv->visible_account_id);
2707                 priv->visible_account_id = NULL;
2708         }
2709         if (account_id)
2710                 priv->visible_account_id = g_strdup (account_id);
2711
2712         /* Refilter */
2713         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2714         if (GTK_IS_TREE_MODEL_FILTER (model))
2715                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2716
2717         /* Save settings to gconf */
2718         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2719                                    MODEST_CONF_FOLDER_VIEW_KEY);
2720 }
2721
2722 const gchar *
2723 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2724 {
2725         ModestFolderViewPrivate *priv;
2726
2727         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2728         
2729         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2730
2731         return (const gchar *) priv->visible_account_id;
2732 }
2733
2734 static gboolean
2735 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2736 {
2737         do {
2738                 GtkTreeIter child;
2739                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2740
2741                 gtk_tree_model_get (model, iter, 
2742                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
2743                                     &type, -1);
2744                         
2745                 gboolean result = FALSE;
2746                 if (type == TNY_FOLDER_TYPE_INBOX) {
2747                         result = TRUE;
2748                 }               
2749                 if (result) {
2750                         *inbox_iter = *iter;
2751                         return TRUE;
2752                 }
2753
2754                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2755                         if (find_inbox_iter (model, &child, inbox_iter))
2756                                 return TRUE;
2757                 }
2758
2759         } while (gtk_tree_model_iter_next (model, iter));
2760
2761         return FALSE;
2762 }
2763
2764
2765
2766
2767 void 
2768 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2769 {
2770         GtkTreeModel *model;
2771         GtkTreeIter iter, inbox_iter;
2772         GtkTreeSelection *sel;
2773         GtkTreePath *path = NULL;
2774
2775         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2776         
2777         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2778         if (!model)
2779                 return;
2780
2781         expand_root_items (self);
2782         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2783
2784         if (!gtk_tree_model_get_iter_first (model, &iter)) {
2785                 g_warning ("%s: model is empty", __FUNCTION__);
2786                 return;
2787         }
2788
2789         if (find_inbox_iter (model, &iter, &inbox_iter))
2790                 path = gtk_tree_model_get_path (model, &inbox_iter);
2791         else
2792                 path = gtk_tree_path_new_first ();
2793
2794         /* Select the row and free */
2795         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2796         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2797         gtk_tree_path_free (path);
2798
2799         /* set focus */
2800         gtk_widget_grab_focus (GTK_WIDGET(self));
2801 }
2802
2803
2804 /* recursive */
2805 static gboolean
2806 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, 
2807                   TnyFolder* folder)
2808 {
2809         do {
2810                 GtkTreeIter child;
2811                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2812                 TnyFolder* a_folder;
2813                 gchar *name = NULL;
2814                 
2815                 gtk_tree_model_get (model, iter, 
2816                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2817                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2818                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type, 
2819                                     -1);                
2820                 g_free (name);
2821
2822                 if (folder == a_folder) {
2823                         g_object_unref (a_folder);
2824                         *folder_iter = *iter;
2825                         return TRUE;
2826                 }
2827                 g_object_unref (a_folder);
2828                 
2829                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2830                         if (find_folder_iter (model, &child, folder_iter, folder)) 
2831                                 return TRUE;
2832                 }
2833
2834         } while (gtk_tree_model_iter_next (model, iter));
2835
2836         return FALSE;
2837 }
2838
2839
2840 static void
2841 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, 
2842                                      GtkTreePath *path, 
2843                                      GtkTreeIter *iter,
2844                                      ModestFolderView *self)
2845 {
2846         ModestFolderViewPrivate *priv = NULL;
2847         GtkTreeSelection *sel;
2848         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2849         GObject *instance = NULL;
2850
2851         if (!MODEST_IS_FOLDER_VIEW(self))
2852                 return;
2853         
2854         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2855
2856         priv->reexpand = TRUE;
2857
2858         gtk_tree_model_get (tree_model, iter, 
2859                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2860                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2861                             -1);
2862         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2863                 priv->folder_to_select = g_object_ref (instance);
2864         }
2865         g_object_unref (instance);
2866
2867         
2868         if (priv->folder_to_select) {
2869                 
2870                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2871                                                        FALSE)) {
2872                         GtkTreePath *path;
2873                         path = gtk_tree_model_get_path (tree_model, iter);
2874                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2875                         
2876                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2877
2878                         gtk_tree_selection_select_iter (sel, iter);
2879                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2880
2881                         gtk_tree_path_free (path);
2882                 
2883                 }
2884
2885                 /* Disable next */
2886                 modest_folder_view_disable_next_folder_selection (self);
2887                 
2888                 /* Refilter the model */
2889                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2890         }
2891 }
2892
2893
2894 void
2895 modest_folder_view_disable_next_folder_selection (ModestFolderView *self) 
2896 {
2897         ModestFolderViewPrivate *priv;
2898
2899         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2900
2901         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2902
2903         if (priv->folder_to_select)
2904                 g_object_unref(priv->folder_to_select);
2905         
2906         priv->folder_to_select = NULL;
2907 }
2908
2909 gboolean
2910 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder, 
2911                                   gboolean after_change)
2912 {
2913         GtkTreeModel *model;
2914         GtkTreeIter iter, folder_iter;
2915         GtkTreeSelection *sel;
2916         ModestFolderViewPrivate *priv = NULL;
2917         
2918         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);     
2919         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE); 
2920                 
2921         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2922
2923         if (after_change) {
2924                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2925                 gtk_tree_selection_unselect_all (sel);
2926
2927                 if (priv->folder_to_select)
2928                         g_object_unref(priv->folder_to_select);
2929                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2930                 return TRUE;
2931         }
2932                 
2933         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2934         if (!model)
2935                 return FALSE;
2936
2937
2938         /* Refilter the model, before selecting the folder */
2939         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2940
2941         if (!gtk_tree_model_get_iter_first (model, &iter)) {
2942                 g_warning ("%s: model is empty", __FUNCTION__);
2943                 return FALSE;
2944         }
2945         
2946         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2947                 GtkTreePath *path;
2948
2949                 path = gtk_tree_model_get_path (model, &folder_iter);
2950                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2951
2952                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2953                 gtk_tree_selection_select_iter (sel, &folder_iter);
2954                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2955
2956                 gtk_tree_path_free (path);
2957                 return TRUE;
2958         }
2959         return FALSE;
2960 }
2961
2962
2963 void 
2964 modest_folder_view_copy_selection (ModestFolderView *self)
2965 {
2966         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2967         
2968         /* Copy selection */
2969         _clipboard_set_selected_data (self, FALSE);
2970 }
2971
2972 void 
2973 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2974 {
2975         ModestFolderViewPrivate *priv = NULL;
2976         GtkTreeModel *model = NULL;
2977         const gchar **hidding = NULL;
2978         guint i, n_selected;
2979
2980         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
2981         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2982
2983         /* Copy selection */
2984         if (!_clipboard_set_selected_data (folder_view, TRUE))
2985                 return;
2986
2987         /* Get hidding ids */
2988         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
2989         
2990         /* Clear hidding array created by previous cut operation */
2991         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2992
2993         /* Copy hidding array */
2994         priv->n_selected = n_selected;
2995         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2996         for (i=0; i < n_selected; i++) 
2997                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2998
2999         /* Hide cut folders */
3000         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3001         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3002 }
3003
3004 void
3005 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3006                                ModestFolderView *folder_view_dst)
3007 {
3008         GtkTreeModel *filter_model = NULL;
3009         GtkTreeModel *model = NULL;
3010         GtkTreeModel *new_filter_model = NULL;
3011         
3012         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3013         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3014         
3015         /* Get src model*/
3016         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3017         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3018
3019         /* Build new filter model */
3020         new_filter_model = gtk_tree_model_filter_new (model, NULL);     
3021         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3022                                                 filter_row,
3023                                                 folder_view_dst,
3024                                                 NULL);
3025         /* Set copied model */
3026         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3027         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3028                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3029
3030         /* Free */
3031         g_object_unref (new_filter_model);
3032 }
3033
3034 void
3035 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3036                                           gboolean show)
3037 {
3038         GtkTreeModel *model = NULL;
3039         ModestFolderViewPrivate* priv;
3040
3041         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3042
3043         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);     
3044         priv->show_non_move = show;
3045 /*      modest_folder_view_update_model(folder_view, */
3046 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3047
3048         /* Hide special folders */
3049         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3050         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3051                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3052         }
3053 }
3054
3055 /* Returns FALSE if it did not selected anything */
3056 static gboolean
3057 _clipboard_set_selected_data (ModestFolderView *folder_view,
3058                               gboolean delete)
3059 {
3060         ModestFolderViewPrivate *priv = NULL;
3061         TnyFolderStore *folder = NULL;
3062         gboolean retval = FALSE;
3063
3064         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3065         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3066                 
3067         /* Set selected data on clipboard   */
3068         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3069         folder = modest_folder_view_get_selected (folder_view);
3070
3071         /* Do not allow to select an account */
3072         if (TNY_IS_FOLDER (folder)) {
3073                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3074                 retval = TRUE;
3075         }
3076
3077         /* Free */
3078         g_object_unref (folder);
3079
3080         return retval;
3081 }
3082
3083 static void
3084 _clear_hidding_filter (ModestFolderView *folder_view) 
3085 {
3086         ModestFolderViewPrivate *priv;
3087         guint i;
3088         
3089         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
3090         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3091
3092         if (priv->hidding_ids != NULL) {
3093                 for (i=0; i < priv->n_selected; i++) 
3094                         g_free (priv->hidding_ids[i]);
3095                 g_free(priv->hidding_ids);
3096         }       
3097 }
3098
3099
3100 static void 
3101 on_display_name_changed (ModestAccountMgr *mgr, 
3102                          const gchar *account,
3103                          gpointer user_data)
3104 {
3105         ModestFolderView *self;
3106
3107         self = MODEST_FOLDER_VIEW (user_data);
3108
3109         /* Force a redraw */
3110 #if GTK_CHECK_VERSION(2, 8, 0)
3111         GtkTreeViewColumn * tree_column;
3112         
3113         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
3114                                                 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3115         gtk_tree_view_column_queue_resize (tree_column);
3116 #else
3117         gtk_widget_queue_draw (GTK_WIDGET (self));
3118 #endif
3119 }