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