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