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