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