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