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