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