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