* Fixed NB#62792, the editor is closed if the account is removed
[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 (MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1624                                                                  NULL,
1625                                                                  modest_ui_actions_move_folder_error_handler,
1626                                                                  NULL);
1627
1628         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1629                                          mail_op);
1630
1631         modest_mail_operation_xfer_msgs (mail_op,
1632                                          headers, 
1633                                          folder, 
1634                                          helper->delete_source, 
1635                                          xfer_cb, helper);
1636         
1637         /* Frees */
1638 cleanup:
1639         if (G_IS_OBJECT(mail_op))
1640                 g_object_unref (G_OBJECT (mail_op));
1641         if (G_IS_OBJECT(folder))
1642                 g_object_unref (G_OBJECT (folder));
1643         if (G_IS_OBJECT(headers))
1644                 g_object_unref (headers);
1645 }
1646
1647 /*
1648  * This function is used by drag_data_received_cb to manage drag and
1649  * drop of a folder, i.e, and drag from the folder view to the same
1650  * folder view.
1651  */
1652 static void
1653 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
1654                                 GtkTreeModel     *dest_model,
1655                                 GtkTreePath      *dest_row,
1656                                 GtkSelectionData *selection_data,
1657                                 DndHelper        *helper)
1658 {
1659         ModestMailOperation *mail_op = NULL;
1660         GtkTreeIter dest_iter, iter;
1661         TnyFolderStore *dest_folder = NULL;
1662         TnyFolder *folder = NULL;
1663         gboolean forbidden = FALSE;
1664
1665         if (!forbidden) {
1666                 /* check the folder rules for the destination */
1667                 folder = tree_path_to_folder (dest_model, dest_row);
1668                 if (TNY_IS_FOLDER(folder)) {
1669                         ModestTnyFolderRules rules =
1670                                         modest_tny_folder_get_rules (folder);
1671                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1672                 } else if (TNY_IS_FOLDER_STORE(folder)) {
1673                         /* enable local root as destination for folders */
1674                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1675                                         && TNY_IS_ACCOUNT (folder))
1676                                 forbidden = TRUE;
1677                 }
1678                 g_object_unref (folder);
1679         }
1680         if (!forbidden) {
1681                 /* check the folder rules for the source */
1682                 folder = tree_path_to_folder (source_model, helper->source_row);
1683                 if (TNY_IS_FOLDER(folder)) {
1684                         ModestTnyFolderRules rules =
1685                                         modest_tny_folder_get_rules (folder);
1686                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1687                 } else
1688                         forbidden = TRUE;
1689                 g_object_unref (folder);
1690         }
1691
1692         
1693         /* Check if the drag is possible */
1694         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1695                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1696                 gtk_tree_path_free (helper->source_row);        
1697                 g_slice_free (DndHelper, helper);
1698                 return;
1699         }
1700
1701         /* Get data */
1702         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1703         gtk_tree_model_get (dest_model, &dest_iter, 
1704                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1705                             &dest_folder, -1);
1706         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1707         gtk_tree_model_get (source_model, &iter,
1708                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1709                             &folder, -1);
1710
1711         /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1712         if (modest_platform_connect_and_wait_if_network_folderstore (NULL, dest_folder) && 
1713             modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1714                 ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1715
1716                 /* Do the mail operation */
1717                 mail_op = 
1718                         modest_mail_operation_new_with_error_handling (MODEST_MAIL_OPERATION_TYPE_RECEIVE, 
1719                                                                        G_OBJECT (modest_window_mgr_get_main_window (mgr)),
1720                                                                        modest_ui_actions_move_folder_error_handler,
1721                                                                        folder);
1722
1723                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), 
1724                                                  mail_op);
1725
1726                 modest_mail_operation_xfer_folder (mail_op, 
1727                                                    folder, 
1728                                                    dest_folder,
1729                                                    helper->delete_source,
1730                                                    xfer_cb,
1731                                                    helper);
1732
1733                 g_object_unref (G_OBJECT (mail_op));
1734         }
1735         
1736         /* Frees */
1737         g_object_unref (G_OBJECT (dest_folder));
1738         g_object_unref (G_OBJECT (folder));
1739 }
1740
1741 /*
1742  * This function receives the data set by the "drag-data-get" signal
1743  * handler. This information comes within the #GtkSelectionData. This
1744  * function will manage both the drags of folders of the treeview and
1745  * drags of headers of the header view widget.
1746  */
1747 static void 
1748 on_drag_data_received (GtkWidget *widget, 
1749                        GdkDragContext *context, 
1750                        gint x, 
1751                        gint y, 
1752                        GtkSelectionData *selection_data, 
1753                        guint target_type, 
1754                        guint time, 
1755                        gpointer data)
1756 {
1757         GtkWidget *source_widget;
1758         GtkTreeModel *dest_model, *source_model;
1759         GtkTreePath *source_row, *dest_row;
1760         GtkTreeViewDropPosition pos;
1761         gboolean success = FALSE, delete_source = FALSE;
1762         DndHelper *helper = NULL; 
1763
1764         /* Do not allow further process */
1765         g_signal_stop_emission_by_name (widget, "drag-data-received");
1766         source_widget = gtk_drag_get_source_widget (context);
1767
1768         /* Get the action */
1769         if (context->action == GDK_ACTION_MOVE) {
1770                 delete_source = TRUE;
1771
1772                 /* Notify that there is no folder selected. We need to
1773                    do this in order to update the headers view (and
1774                    its monitors, because when moving, the old folder
1775                    won't longer exist. We can not wait for the end of
1776                    the operation, because the operation won't start if
1777                    the folder is in use */
1778                 if (source_widget == widget) {
1779                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1780                         gtk_tree_selection_unselect_all (sel);
1781                 }
1782         }
1783
1784         /* Check if the get_data failed */
1785         if (selection_data == NULL || selection_data->length < 0)
1786                 gtk_drag_finish (context, success, FALSE, time);
1787
1788         /* Select the destination model */
1789         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));  
1790
1791         /* Get the path to the destination row. Can not call
1792            gtk_tree_view_get_drag_dest_row() because the source row
1793            is not selected anymore */
1794         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1795                                            &dest_row, &pos);
1796
1797         /* Only allow drops IN other rows */
1798         if (!dest_row || 
1799             pos == GTK_TREE_VIEW_DROP_BEFORE || 
1800             pos == GTK_TREE_VIEW_DROP_AFTER)
1801                 gtk_drag_finish (context, success, FALSE, time);
1802
1803         /* Create the helper */
1804         helper = g_slice_new0 (DndHelper);
1805         helper->delete_source = delete_source;
1806         helper->context = context;
1807         helper->time = time;
1808
1809         /* Drags from the header view */
1810         if (source_widget != widget) {
1811                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
1812
1813                 drag_and_drop_from_header_view (source_model,
1814                                                 dest_model,
1815                                                 dest_row,
1816                                                 selection_data,
1817                                                 helper);
1818         } else {
1819                 /* Get the source model and row */
1820                 gtk_tree_get_row_drag_data (selection_data,
1821                                             &source_model,
1822                                             &source_row);
1823                 helper->source_row = gtk_tree_path_copy (source_row);
1824
1825                 drag_and_drop_from_folder_view (source_model,
1826                                                 dest_model,
1827                                                 dest_row,
1828                                                 selection_data, 
1829                                                 helper);
1830
1831                 gtk_tree_path_free (source_row);
1832         }
1833
1834         /* Frees */
1835         gtk_tree_path_free (dest_row);
1836 }
1837
1838 /*
1839  * We define a "drag-drop" signal handler because we do not want to
1840  * use the default one, because the default one always calls
1841  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1842  * signal handler, because there we have all the information available
1843  * to know if the dnd was a success or not.
1844  */
1845 static gboolean
1846 drag_drop_cb (GtkWidget      *widget,
1847               GdkDragContext *context,
1848               gint            x,
1849               gint            y,
1850               guint           time,
1851               gpointer        user_data) 
1852 {
1853         gpointer target;
1854
1855         if (!context->targets)
1856                 return FALSE;
1857
1858         /* Check if we're dragging a folder row */
1859         target = gtk_drag_dest_find_target (widget, context, NULL);
1860
1861         /* Request the data from the source. */
1862         gtk_drag_get_data(widget, context, target, time);
1863
1864     return TRUE;
1865 }
1866
1867 /*
1868  * This function expands a node of a tree view if it's not expanded
1869  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1870  * does that, so that's why they're here.
1871  */
1872 static gint
1873 expand_row_timeout (gpointer data)
1874 {
1875         GtkTreeView *tree_view = data;
1876         GtkTreePath *dest_path = NULL;
1877         GtkTreeViewDropPosition pos;
1878         gboolean result = FALSE;
1879         
1880         GDK_THREADS_ENTER ();
1881         
1882         gtk_tree_view_get_drag_dest_row (tree_view,
1883                                          &dest_path,
1884                                          &pos);
1885         
1886         if (dest_path &&
1887             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1888              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1889                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1890                 gtk_tree_path_free (dest_path);
1891         }
1892         else {
1893                 if (dest_path)
1894                         gtk_tree_path_free (dest_path);
1895                 
1896                 result = TRUE;
1897         }
1898         
1899         GDK_THREADS_LEAVE ();
1900
1901         return result;
1902 }
1903
1904 /*
1905  * This function is called whenever the pointer is moved over a widget
1906  * while dragging some data. It installs a timeout that will expand a
1907  * node of the treeview if not expanded yet. This function also calls
1908  * gdk_drag_status in order to set the suggested action that will be
1909  * used by the "drag-data-received" signal handler to know if we
1910  * should do a move or just a copy of the data.
1911  */
1912 static gboolean
1913 on_drag_motion (GtkWidget      *widget,
1914                 GdkDragContext *context,
1915                 gint            x,
1916                 gint            y,
1917                 guint           time,
1918                 gpointer        user_data)  
1919 {
1920         GtkTreeViewDropPosition pos;
1921         GtkTreePath *dest_row;
1922         GtkTreeModel *dest_model;
1923         ModestFolderViewPrivate *priv;
1924         GdkDragAction suggested_action;
1925         gboolean valid_location = FALSE;
1926         TnyFolder *folder;
1927
1928         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1929
1930         if (priv->timer_expander != 0) {
1931                 g_source_remove (priv->timer_expander);
1932                 priv->timer_expander = 0;
1933         }
1934
1935         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1936                                            x, y,
1937                                            &dest_row,
1938                                            &pos);
1939
1940         /* Do not allow drops between folders */
1941         if (!dest_row ||
1942             pos == GTK_TREE_VIEW_DROP_BEFORE ||
1943             pos == GTK_TREE_VIEW_DROP_AFTER) {
1944                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1945                 gdk_drag_status(context, 0, time);
1946                 valid_location = FALSE;
1947                 goto out;
1948         } else {
1949                 valid_location = TRUE;
1950         }
1951
1952         /* Check that the destination folder is writable */
1953         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1954         folder = tree_path_to_folder (dest_model, dest_row);
1955         if (folder) {
1956                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(folder);
1957
1958                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1959                         valid_location = FALSE;
1960                         goto out;
1961                 }
1962                 g_object_unref (folder);
1963         }
1964
1965         /* Expand the selected row after 1/2 second */
1966         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1967                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1968                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1969         }
1970
1971         /* Select the desired action. By default we pick MOVE */
1972         suggested_action = GDK_ACTION_MOVE;
1973
1974         if (context->actions == GDK_ACTION_COPY)
1975             gdk_drag_status(context, GDK_ACTION_COPY, time);
1976         else if (context->actions == GDK_ACTION_MOVE)
1977             gdk_drag_status(context, GDK_ACTION_MOVE, time);
1978         else if (context->actions & suggested_action)
1979             gdk_drag_status(context, suggested_action, time);
1980         else
1981             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
1982
1983  out:
1984         if (dest_row)
1985                 gtk_tree_path_free (dest_row);
1986         g_signal_stop_emission_by_name (widget, "drag-motion");
1987
1988         return valid_location;
1989 }
1990
1991 /*
1992  * This function sets the treeview as a source and a target for dnd
1993  * events. It also connects all the requirede signals.
1994  */
1995 static void
1996 setup_drag_and_drop (GtkTreeView *self)
1997 {
1998         /* Set up the folder view as a dnd destination. Set only the
1999            highlight flag, otherwise gtk will have a different
2000            behaviour */
2001         gtk_drag_dest_set (GTK_WIDGET (self),
2002                            GTK_DEST_DEFAULT_HIGHLIGHT,
2003                            folder_view_drag_types,
2004                            G_N_ELEMENTS (folder_view_drag_types),
2005                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2006
2007         g_signal_connect (G_OBJECT (self),
2008                           "drag_data_received",
2009                           G_CALLBACK (on_drag_data_received),
2010                           NULL);
2011
2012
2013         /* Set up the treeview as a dnd source */
2014         gtk_drag_source_set (GTK_WIDGET (self),
2015                              GDK_BUTTON1_MASK,
2016                              folder_view_drag_types,
2017                              G_N_ELEMENTS (folder_view_drag_types),
2018                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2019
2020         g_signal_connect (G_OBJECT (self),
2021                           "drag_motion",
2022                           G_CALLBACK (on_drag_motion),
2023                           NULL);
2024         
2025         g_signal_connect (G_OBJECT (self),
2026                           "drag_data_get",
2027                           G_CALLBACK (on_drag_data_get),
2028                           NULL);
2029
2030         g_signal_connect (G_OBJECT (self),
2031                           "drag_drop",
2032                           G_CALLBACK (drag_drop_cb),
2033                           NULL);
2034 }
2035
2036 /*
2037  * This function manages the navigation through the folders using the
2038  * keyboard or the hardware keys in the device
2039  */
2040 static gboolean
2041 on_key_pressed (GtkWidget *self,
2042                 GdkEventKey *event,
2043                 gpointer user_data)
2044 {
2045         GtkTreeSelection *selection;
2046         GtkTreeIter iter;
2047         GtkTreeModel *model;
2048         gboolean retval = FALSE;
2049
2050         /* Up and Down are automatically managed by the treeview */
2051         if (event->keyval == GDK_Return) {
2052                 /* Expand/Collapse the selected row */
2053                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2054                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2055                         GtkTreePath *path;
2056
2057                         path = gtk_tree_model_get_path (model, &iter);
2058
2059                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2060                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2061                         else
2062                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2063                         gtk_tree_path_free (path);
2064                 }
2065                 /* No further processing */
2066                 retval = TRUE;
2067         }
2068
2069         return retval;
2070 }
2071
2072 /*
2073  * We listen to the changes in the local folder account name key,
2074  * because we want to show the right name in the view. The local
2075  * folder account name corresponds to the device name in the Maemo
2076  * version. We do this because we do not want to query gconf on each
2077  * tree view refresh. It's better to cache it and change whenever
2078  * necessary.
2079  */
2080 static void 
2081 on_configuration_key_changed (ModestConf* conf, 
2082                               const gchar *key, 
2083                               ModestConfEvent event,
2084                               ModestConfNotificationId id, 
2085                               ModestFolderView *self)
2086 {
2087         ModestFolderViewPrivate *priv;
2088
2089
2090         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2091         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2092
2093         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2094                 g_free (priv->local_account_name);
2095
2096                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2097                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2098                 else
2099                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2100                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2101
2102                 /* Force a redraw */
2103 #if GTK_CHECK_VERSION(2, 8, 0)
2104                 GtkTreeViewColumn * tree_column;
2105
2106                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2107                                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2108                 gtk_tree_view_column_queue_resize (tree_column);
2109 #else
2110                 gtk_widget_queue_draw (GTK_WIDGET (self));
2111 #endif
2112         }
2113 }
2114
2115 void
2116 modest_folder_view_set_style (ModestFolderView *self,
2117                               ModestFolderViewStyle style)
2118 {
2119         ModestFolderViewPrivate *priv;
2120
2121         g_return_if_fail (self);
2122         
2123         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2124
2125         priv->style = style;
2126 }
2127
2128 void
2129 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2130                                                              const gchar *account_id)
2131 {
2132         ModestFolderViewPrivate *priv;
2133         GtkTreeModel *model;
2134
2135         g_return_if_fail (self);
2136         
2137         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2138
2139         /* This will be used by the filter_row callback,
2140          * to decided which rows to show: */
2141         if (priv->visible_account_id) {
2142                 g_free (priv->visible_account_id);
2143                 priv->visible_account_id = NULL;
2144         }
2145         if (account_id)
2146                 priv->visible_account_id = g_strdup (account_id);
2147
2148         /* Refilter */
2149         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2150         if (GTK_IS_TREE_MODEL_FILTER (model))
2151                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2152
2153         /* Save settings to gconf */
2154         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2155                                    MODEST_CONF_FOLDER_VIEW_KEY);
2156 }
2157
2158 const gchar *
2159 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2160 {
2161         ModestFolderViewPrivate *priv;
2162
2163         g_return_val_if_fail (self, NULL);
2164         
2165         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2166
2167         return (const gchar *) priv->visible_account_id;
2168 }
2169
2170 static gboolean
2171 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2172 {
2173         do {
2174                 GtkTreeIter child;
2175                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2176
2177                 gtk_tree_model_get (model, iter, 
2178                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
2179                                     &type, -1);
2180                         
2181                 gboolean result = FALSE;
2182                 if (type == TNY_FOLDER_TYPE_INBOX) {
2183                         result = TRUE;
2184                 }               
2185                 if (result) {
2186                         *inbox_iter = *iter;
2187                         return TRUE;
2188                 }
2189
2190                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2191                         if (find_inbox_iter (model, &child, inbox_iter))
2192                                 return TRUE;
2193                 }
2194
2195         } while (gtk_tree_model_iter_next (model, iter));
2196
2197         return FALSE;
2198 }
2199
2200
2201
2202
2203 void 
2204 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2205 {
2206         GtkTreeModel *model;
2207         GtkTreeIter iter, inbox_iter;
2208         GtkTreeSelection *sel;
2209         GtkTreePath *path = NULL;
2210
2211         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2212         if (!model)
2213                 return;
2214
2215         expand_root_items (self);
2216         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2217
2218         gtk_tree_model_get_iter_first (model, &iter);
2219
2220         if (find_inbox_iter (model, &iter, &inbox_iter))
2221                 path = gtk_tree_model_get_path (model, &inbox_iter);
2222         else
2223                 path = gtk_tree_path_new_first ();
2224
2225         /* Select the row and free */
2226         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2227         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2228         gtk_tree_path_free (path);
2229
2230         /* set focus */
2231         gtk_widget_grab_focus (GTK_WIDGET(self));
2232 }
2233
2234
2235 /* recursive */
2236 static gboolean
2237 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, 
2238                   TnyFolder* folder)
2239 {
2240         do {
2241                 GtkTreeIter child;
2242                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2243                 TnyFolder* a_folder;
2244                 gchar *name = NULL;
2245                 
2246                 gtk_tree_model_get (model, iter, 
2247                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2248                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2249                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type, 
2250                                     -1);                
2251                 g_free (name);
2252
2253                 if (folder == a_folder) {
2254                         g_object_unref (a_folder);
2255                         *folder_iter = *iter;
2256                         return TRUE;
2257                 }
2258                 g_object_unref (a_folder);
2259                 
2260                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2261                         if (find_folder_iter (model, &child, folder_iter, folder)) 
2262                                 return TRUE;
2263                 }
2264
2265         } while (gtk_tree_model_iter_next (model, iter));
2266
2267         return FALSE;
2268 }
2269
2270
2271 static void
2272 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath  *path, GtkTreeIter *iter,
2273                                      ModestFolderView *self)
2274 {
2275         ModestFolderViewPrivate *priv = NULL;
2276         GtkTreeSelection *sel;
2277         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2278         GObject *instance = NULL;
2279
2280         if (!MODEST_IS_FOLDER_VIEW(self))
2281                 return;
2282         
2283         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2284
2285         priv->reexpand = TRUE;
2286
2287         gtk_tree_model_get (tree_model, iter, 
2288                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2289                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2290                             -1);
2291         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2292                 priv->folder_to_select = g_object_ref (instance);
2293         }
2294         g_object_unref (instance);
2295
2296         
2297         if (priv->folder_to_select) {
2298                 
2299                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2300                                                        FALSE)) {
2301                         GtkTreePath *path;
2302                         path = gtk_tree_model_get_path (tree_model, iter);
2303                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2304                         
2305                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2306
2307                         gtk_tree_selection_select_iter (sel, iter);
2308                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2309
2310                         gtk_tree_path_free (path);
2311                 
2312                 }
2313
2314                 /* Disable next */
2315                 modest_folder_view_disable_next_folder_selection (self);
2316 /*              g_object_unref (priv->folder_to_select); */
2317 /*              priv->folder_to_select = NULL; */
2318         }
2319 }
2320
2321
2322 void
2323 modest_folder_view_disable_next_folder_selection (ModestFolderView *self) 
2324 {
2325         ModestFolderViewPrivate *priv = NULL;
2326
2327         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));        
2328         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2329
2330         if (priv->folder_to_select)
2331                 g_object_unref(priv->folder_to_select);
2332         
2333         priv->folder_to_select = NULL;
2334 }
2335
2336 gboolean
2337 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder, 
2338                                   gboolean after_change)
2339 {
2340         GtkTreeModel *model;
2341         GtkTreeIter iter, folder_iter;
2342         GtkTreeSelection *sel;
2343         ModestFolderViewPrivate *priv = NULL;
2344         
2345         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);     
2346         g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);   
2347                 
2348         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2349
2350         if (after_change) {
2351
2352                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2353                 gtk_tree_selection_unselect_all (sel);
2354
2355                 if (priv->folder_to_select)
2356                         g_object_unref(priv->folder_to_select);
2357                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2358                 return TRUE;
2359         }
2360                 
2361         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2362         if (!model)
2363                 return FALSE;
2364
2365                 
2366         gtk_tree_model_get_iter_first (model, &iter);
2367         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2368                 GtkTreePath *path;
2369
2370                 path = gtk_tree_model_get_path (model, &folder_iter);
2371                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2372
2373                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2374                 gtk_tree_selection_select_iter (sel, &folder_iter);
2375                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2376
2377                 gtk_tree_path_free (path);
2378                 return TRUE;
2379         }
2380         return FALSE;
2381 }
2382
2383
2384 void 
2385 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2386 {
2387         /* Copy selection */
2388         _clipboard_set_selected_data (folder_view, FALSE);
2389 }
2390
2391 void 
2392 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2393 {
2394         ModestFolderViewPrivate *priv = NULL;
2395         GtkTreeModel *model = NULL;
2396         const gchar **hidding = NULL;
2397         guint i, n_selected;
2398
2399         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2400         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2401
2402         /* Copy selection */
2403         if (!_clipboard_set_selected_data (folder_view, TRUE))
2404                 return;
2405
2406         /* Get hidding ids */
2407         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
2408         
2409         /* Clear hidding array created by previous cut operation */
2410         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2411
2412         /* Copy hidding array */
2413         priv->n_selected = n_selected;
2414         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2415         for (i=0; i < n_selected; i++) 
2416                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2417
2418         /* Hide cut folders */
2419         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2420         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2421 }
2422
2423 void
2424 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2425                                ModestFolderView *folder_view_dst)
2426 {
2427         GtkTreeModel *filter_model = NULL;
2428         GtkTreeModel *model = NULL;
2429         GtkTreeModel *new_filter_model = NULL;
2430         
2431         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2432         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2433
2434         /* Get src model*/
2435         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2436         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2437
2438         /* Build new filter model */
2439         new_filter_model = gtk_tree_model_filter_new (model, NULL);     
2440         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2441                                                 filter_row,
2442                                                 folder_view_dst,
2443                                                 NULL);
2444         /* Set copied model */
2445         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2446         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
2447                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
2448
2449         /* Free */
2450         g_object_unref (new_filter_model);
2451 }
2452
2453 void
2454 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2455                                           gboolean show)
2456 {
2457         GtkTreeModel *model = NULL;
2458         ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2459         priv->show_non_move = show;
2460 /*      modest_folder_view_update_model(folder_view, */
2461 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2462
2463         /* Hide special folders */
2464         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2465         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2466                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2467         }
2468 }
2469
2470 /* Returns FALSE if it did not selected anything */
2471 static gboolean
2472 _clipboard_set_selected_data (ModestFolderView *folder_view,
2473                               gboolean delete)
2474 {
2475         ModestFolderViewPrivate *priv = NULL;
2476         TnyFolderStore *folder = NULL;
2477         gboolean retval = FALSE;
2478
2479         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2480         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2481                 
2482         /* Set selected data on clipboard   */
2483         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2484         folder = modest_folder_view_get_selected (folder_view);
2485
2486         /* Do not allow to select an account */
2487         if (TNY_IS_FOLDER (folder)) {
2488                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2489                 retval = TRUE;
2490         }
2491
2492         /* Free */
2493         g_object_unref (folder);
2494
2495         return retval;
2496 }
2497
2498 static void
2499 _clear_hidding_filter (ModestFolderView *folder_view) 
2500 {
2501         ModestFolderViewPrivate *priv;
2502         guint i;
2503         
2504         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
2505         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2506
2507         if (priv->hidding_ids != NULL) {
2508                 for (i=0; i < priv->n_selected; i++) 
2509                         g_free (priv->hidding_ids[i]);
2510                 g_free(priv->hidding_ids);
2511         }       
2512 }
2513
2514
2515 static void 
2516 on_display_name_changed (ModestAccountMgr *mgr, 
2517                          const gchar *account,
2518                          gpointer user_data)
2519 {
2520         ModestFolderView *self;
2521
2522         self = MODEST_FOLDER_VIEW (user_data);
2523
2524         /* Force a redraw */
2525 #if GTK_CHECK_VERSION(2, 8, 0)
2526         GtkTreeViewColumn * tree_column;
2527         
2528         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2529                                                 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2530         gtk_tree_view_column_queue_resize (tree_column);
2531 #else
2532         gtk_widget_queue_draw (GTK_WIDGET (self));
2533 #endif
2534 }