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