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