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