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