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