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