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