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