* Fixed an invalid g_object_ref/unref
[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         /* Refilter the model */
871         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
872 }
873
874
875 static void
876 on_account_changed (TnyAccountStore *account_store, 
877                     TnyAccount *tny_account,
878                     gpointer user_data)
879 {
880         /* do nothing */
881         ModestFolderViewPrivate *priv;
882         GtkTreeModel *sort_model, *filter_model;
883
884         /* Ignore transport account insertions, we're not showing them
885            in the folder view */
886         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
887                 return;
888
889         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
890
891         /* Get the inner model */
892         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
893         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
894         
895         /* Remove the account from the model */
896         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
897                          G_OBJECT (tny_account));
898
899         /* Insert the account in the model */
900         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
901                          G_OBJECT (tny_account));
902
903         /* Refilter the model */
904         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
905 }
906
907
908
909 static void
910 on_account_removed (TnyAccountStore *account_store, 
911                     TnyAccount *account,
912                     gpointer user_data)
913 {
914         ModestFolderView *self = NULL;
915         ModestFolderViewPrivate *priv;
916         GtkTreeModel *sort_model, *filter_model;
917         GtkTreeSelection *sel = NULL;
918
919         /* Ignore transport account removals, we're not showing them
920            in the folder view */
921         if (TNY_IS_TRANSPORT_ACCOUNT (account))
922                 return;
923
924         self = MODEST_FOLDER_VIEW (user_data);
925         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
926
927         /* Invalidate the cur_folder_store only if the selected folder
928            belongs to the account that is being removed */
929         if (priv->cur_folder_store) {
930                 TnyAccount *selected_folder_account = NULL;
931
932                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
933                         selected_folder_account = 
934                                 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
935                 } else {
936                         selected_folder_account = 
937                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
938                 }
939
940                 if (selected_folder_account == account) {
941                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
942                         gtk_tree_selection_unselect_all (sel);
943                 }
944                 g_object_unref (selected_folder_account);
945         }
946
947         /* Invalidate row to select only if the folder to select
948            belongs to the account that is being removed*/
949         if (priv->folder_to_select) {
950                 TnyAccount *folder_to_select_account = NULL;
951
952                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
953                 if (folder_to_select_account == account) {
954 /*                      modest_folder_view_disable_next_folder_selection (self); */
955                         g_object_unref (priv->folder_to_select);
956                         priv->folder_to_select = NULL;
957                 }
958                 g_object_unref (folder_to_select_account);
959         }
960
961         /* Remove the account from the model */
962         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
963         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
964         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
965                          G_OBJECT (account));
966
967         /* If the removed account is the currently viewed one then
968            clear the configuration value. The new visible account will be the default account */
969         if (priv->visible_account_id &&
970             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
971
972                 /* Clear the current visible account_id */
973                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
974
975                 /* Call the restore method, this will set the new visible account */
976                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
977                                               MODEST_CONF_FOLDER_VIEW_KEY);
978         }
979
980         /* Refilter the model */
981         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
982
983         /* Select the first INBOX if the currently selected folder
984            belongs to the account that is being deleted */
985         if (priv->cur_folder_store) {
986                 TnyAccount *folder_selected_account;
987
988                 folder_selected_account = (TNY_IS_FOLDER (priv->cur_folder_store)) ?
989                         modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store)) :
990                         TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
991                 if (account == folder_selected_account)
992                         modest_folder_view_select_first_inbox_or_local (self);
993                 g_object_unref (folder_selected_account);
994         }
995 }
996
997 void
998 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
999 {
1000         GtkTreeViewColumn *col;
1001         
1002         g_return_if_fail (self);
1003
1004         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1005         if (!col) {
1006                 g_printerr ("modest: failed get column for title\n");
1007                 return;
1008         }
1009
1010         gtk_tree_view_column_set_title (col, title);
1011         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1012                                            title != NULL);
1013 }
1014
1015 static gboolean
1016 modest_folder_view_on_map (ModestFolderView *self, 
1017                            GdkEventExpose *event,
1018                            gpointer data)
1019 {
1020         ModestFolderViewPrivate *priv;
1021
1022         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1023
1024         /* This won't happen often */
1025         if (G_UNLIKELY (priv->reselect)) {
1026                 /* Select the first inbox or the local account if not found */
1027
1028                 /* TODO: this could cause a lock at startup, so we
1029                    comment it for the moment. We know that this will
1030                    be a bug, because the INBOX is not selected, but we
1031                    need to rewrite some parts of Modest to avoid the
1032                    deathlock situation */
1033                 /* TODO: check if this is still the case */
1034                 priv->reselect = FALSE;
1035                 modest_folder_view_select_first_inbox_or_local (self);
1036                 /* Notify the display name observers */
1037                 g_signal_emit (G_OBJECT(self),
1038                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1039                                NULL);
1040         }
1041
1042         if (priv->reexpand) {
1043                 expand_root_items (self); 
1044                 priv->reexpand = FALSE;
1045         }
1046
1047         return FALSE;
1048 }
1049
1050 GtkWidget*
1051 modest_folder_view_new (TnyFolderStoreQuery *query)
1052 {
1053         GObject *self;
1054         ModestFolderViewPrivate *priv;
1055         GtkTreeSelection *sel;
1056         
1057         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1058         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1059
1060         if (query)
1061                 priv->query = g_object_ref (query);
1062         
1063         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1064         priv->changed_signal = g_signal_connect (sel, "changed",
1065                                                  G_CALLBACK (on_selection_changed), self);
1066
1067         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1068
1069         return GTK_WIDGET(self);
1070 }
1071
1072 /* this feels dirty; any other way to expand all the root items? */
1073 static void
1074 expand_root_items (ModestFolderView *self)
1075 {
1076         GtkTreePath *path;
1077         GtkTreeModel *model;
1078         GtkTreeIter iter;
1079
1080         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1081         path = gtk_tree_path_new_first ();
1082
1083         /* all folders should have child items, so.. */
1084         do {
1085                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1086                 gtk_tree_path_next (path);
1087         } while (gtk_tree_model_get_iter (model, &iter, path));
1088         
1089         gtk_tree_path_free (path);
1090 }
1091
1092 /*
1093  * We use this function to implement the
1094  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1095  * account in this case, and the local folders.
1096  */
1097 static gboolean 
1098 filter_row (GtkTreeModel *model,
1099             GtkTreeIter *iter,
1100             gpointer data)
1101 {
1102         ModestFolderViewPrivate *priv;
1103         gboolean retval = TRUE;
1104         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1105         GObject *instance = NULL;
1106         const gchar *id = NULL;
1107         guint i;
1108         gboolean found = FALSE;
1109         gboolean cleared = FALSE;
1110
1111         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1112         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1113
1114         gtk_tree_model_get (model, iter,
1115                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1116                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1117                             -1);
1118
1119         /* Do not show if there is no instance, this could indeed
1120            happen when the model is being modified while it's being
1121            drawn. This could occur for example when moving folders
1122            using drag&drop */
1123         if (!instance)
1124                 return FALSE;
1125
1126         if (type == TNY_FOLDER_TYPE_ROOT) {
1127                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1128                    account instead of a folder. */
1129                 if (TNY_IS_ACCOUNT (instance)) {
1130                         TnyAccount *acc = TNY_ACCOUNT (instance);
1131                         const gchar *account_id = tny_account_get_id (acc);
1132         
1133                         /* If it isn't a special folder, 
1134                          * don't show it unless it is the visible account: */
1135                         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1136                             !modest_tny_account_is_virtual_local_folders (acc) &&
1137                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1138                                 
1139                                 /* Show only the visible account id */
1140                                 if (priv->visible_account_id) {
1141                                         if (strcmp (account_id, priv->visible_account_id))
1142                                                 retval = FALSE;
1143                                 } else {
1144                                         retval = FALSE;
1145                                 }                               
1146                         }
1147                         
1148                         /* Never show these to the user. They are merged into one folder 
1149                          * in the local-folders account instead: */
1150                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1151                                 retval = FALSE;
1152                 }
1153         }
1154
1155         /* Check hiding (if necessary) */
1156         cleared = modest_email_clipboard_cleared (priv->clipboard);            
1157         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1158                 id = tny_folder_get_id (TNY_FOLDER(instance));
1159                 if (priv->hidding_ids != NULL)
1160                         for (i=0; i < priv->n_selected && !found; i++)
1161                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1162                                         found = (!strcmp (priv->hidding_ids[i], id));
1163                 
1164                 retval = !found;
1165         }
1166         
1167         
1168         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1169         folder as no message can be move there according to UI specs */
1170         if (!priv->show_non_move) {
1171                 switch (type) {
1172                         case TNY_FOLDER_TYPE_OUTBOX:
1173                         case TNY_FOLDER_TYPE_SENT:
1174                         case TNY_FOLDER_TYPE_DRAFTS:
1175                                 retval = FALSE;
1176                                 break;
1177                         case TNY_FOLDER_TYPE_UNKNOWN:
1178                         case TNY_FOLDER_TYPE_NORMAL:
1179                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1180                                 if (type == TNY_FOLDER_TYPE_OUTBOX || 
1181                                     type == TNY_FOLDER_TYPE_SENT
1182                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1183                                         retval = FALSE;
1184                                 break;
1185                         default:
1186                                 break;
1187                 }
1188         }
1189         
1190         /* Free */
1191         g_object_unref (instance);
1192
1193         return retval;
1194 }
1195
1196
1197 gboolean
1198 modest_folder_view_update_model (ModestFolderView *self,
1199                                  TnyAccountStore *account_store)
1200 {
1201         ModestFolderViewPrivate *priv;
1202         GtkTreeModel *model /* , *old_model */;                                                    
1203         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1204
1205         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1206         g_return_val_if_fail (account_store, FALSE);
1207
1208         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1209         
1210         /* Notify that there is no folder selected */
1211         g_signal_emit (G_OBJECT(self), 
1212                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1213                        NULL, FALSE);
1214         if (priv->cur_folder_store) {
1215                 g_object_unref (priv->cur_folder_store);
1216                 priv->cur_folder_store = NULL;
1217         }
1218
1219         /* FIXME: the local accounts are not shown when the query
1220            selects only the subscribed folders */
1221         model        = tny_gtk_folder_store_tree_model_new (NULL);
1222
1223         /* Get the accounts: */
1224         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1225                                         TNY_LIST (model),
1226                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1227
1228         sortable = gtk_tree_model_sort_new_with_model (model);
1229         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1230                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
1231                                               GTK_SORT_ASCENDING);
1232         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1233                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1234                                          cmp_rows, NULL, NULL);
1235
1236         /* Create filter model */
1237         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1238         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1239                                                 filter_row,
1240                                                 self,
1241                                                 NULL);
1242
1243         /* Set new model */
1244         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1245         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1246                           (GCallback) on_row_inserted_maybe_select_folder, self);
1247
1248
1249         g_object_unref (model);
1250         g_object_unref (filter_model);          
1251         g_object_unref (sortable);
1252         
1253         /* Force a reselection of the INBOX next time the widget is shown */
1254         priv->reselect = TRUE;
1255                         
1256         return TRUE;
1257 }
1258
1259
1260 static void
1261 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1262 {
1263         GtkTreeModel *model = NULL;
1264         TnyFolderStore *folder = NULL;
1265         GtkTreeIter iter;
1266         ModestFolderView *tree_view = NULL;
1267         ModestFolderViewPrivate *priv = NULL;
1268         gboolean selected = FALSE;
1269
1270         g_return_if_fail (sel);
1271         g_return_if_fail (user_data);
1272         
1273         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1274
1275         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1276
1277         /* Notify the display name observers */
1278         g_signal_emit (G_OBJECT(user_data),
1279                        signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1280                        NULL);
1281
1282         tree_view = MODEST_FOLDER_VIEW (user_data);
1283
1284         if (selected) {
1285                 gtk_tree_model_get (model, &iter,
1286                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1287                                     -1);
1288
1289                 /* If the folder is the same do not notify */
1290                 if (folder && priv->cur_folder_store == folder) {
1291                         g_object_unref (folder);
1292                         return;
1293                 }
1294         }
1295         
1296         /* Current folder was unselected */
1297         if (priv->cur_folder_store) {
1298                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1299                        priv->cur_folder_store, FALSE);
1300
1301                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1302                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1303                                                FALSE, NULL, NULL, NULL);
1304
1305                 /* FALSE --> don't expunge the messages */
1306
1307                 g_object_unref (priv->cur_folder_store);
1308                 priv->cur_folder_store = NULL;
1309         }
1310
1311         /* New current references */
1312         priv->cur_folder_store = folder;
1313
1314         /* New folder has been selected */
1315         g_signal_emit (G_OBJECT(tree_view),
1316                        signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1317                        0, priv->cur_folder_store, TRUE);
1318 }
1319
1320 TnyFolderStore *
1321 modest_folder_view_get_selected (ModestFolderView *self)
1322 {
1323         ModestFolderViewPrivate *priv;
1324
1325         g_return_val_if_fail (self, NULL);
1326         
1327         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1328         if (priv->cur_folder_store)
1329                 g_object_ref (priv->cur_folder_store);
1330
1331         return priv->cur_folder_store;
1332 }
1333
1334 static gint
1335 get_cmp_rows_type_pos (GObject *folder)
1336 {
1337         /* Remote accounts -> Local account -> MMC account .*/
1338         /* 0, 1, 2 */
1339         
1340         if (TNY_IS_ACCOUNT (folder) && 
1341                 modest_tny_account_is_virtual_local_folders (
1342                         TNY_ACCOUNT (folder))) {
1343                 return 1;
1344         } else if (TNY_IS_ACCOUNT (folder)) {
1345                 TnyAccount *account = TNY_ACCOUNT (folder);
1346                 const gchar *account_id = tny_account_get_id (account);
1347                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1348                         return 2;
1349                 else
1350                         return 0;
1351         }
1352         else {
1353                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1354                 return -1; /* Should never happen */
1355         }
1356 }
1357
1358 static gint
1359 get_cmp_subfolder_type_pos (TnyFolderType t)
1360 {
1361         /* Inbox, Outbox, Drafts, Sent, User */
1362         /* 0, 1, 2, 3, 4 */
1363
1364         switch (t) {
1365         case TNY_FOLDER_TYPE_INBOX:
1366                 return 0;
1367                 break;
1368         case TNY_FOLDER_TYPE_OUTBOX:
1369                 return 1;
1370                 break;
1371         case TNY_FOLDER_TYPE_DRAFTS:
1372                 return 2;
1373                 break;
1374         case TNY_FOLDER_TYPE_SENT:
1375                 return 3;
1376                 break;
1377         default:
1378                 return 4;
1379         }
1380 }
1381
1382 /*
1383  * This function orders the mail accounts according to these rules:
1384  * 1st - remote accounts
1385  * 2nd - local account
1386  * 3rd - MMC account
1387  */
1388 static gint
1389 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1390           gpointer user_data)
1391 {
1392         gint cmp = 0;
1393         gchar *name1 = NULL;
1394         gchar *name2 = NULL;
1395         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1396         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1397         GObject *folder1 = NULL;
1398         GObject *folder2 = NULL;
1399
1400         gtk_tree_model_get (tree_model, iter1,
1401                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1402                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1403                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1404                             -1);
1405         gtk_tree_model_get (tree_model, iter2,
1406                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1407                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1408                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1409                             -1);
1410
1411         /* Return if we get no folder. This could happen when folder
1412            operations are happening. The model is updated after the
1413            folder copy/move actually occurs, so there could be
1414            situations where the model to be drawn is not correct */
1415         if (!folder1 || !folder2)
1416                 goto finish;
1417
1418         if (type == TNY_FOLDER_TYPE_ROOT) {
1419                 /* Compare the types, so that 
1420                  * Remote accounts -> Local account -> MMC account .*/
1421                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1422                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1423                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1424                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1425                 if (pos1 <  pos2)
1426                         cmp = -1;
1427                 else if (pos1 > pos2)
1428                         cmp = 1;
1429                 else {
1430                         /* Compare items of the same type: */
1431                         
1432                         TnyAccount *account1 = NULL;
1433                         if (TNY_IS_ACCOUNT (folder1))
1434                                 account1 = TNY_ACCOUNT (folder1);
1435                                 
1436                         TnyAccount *account2 = NULL;
1437                         if (TNY_IS_ACCOUNT (folder2))
1438                                 account2 = TNY_ACCOUNT (folder2);
1439                                 
1440                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1441                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1442         
1443                         if (!account_id && !account_id2) {
1444                                 cmp = 0;
1445                         } else if (!account_id) {
1446                                 cmp = -1;
1447                         } else if (!account_id2) {
1448                                 cmp = +1;
1449                         } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1450                                 cmp = +1;
1451                         } else {
1452                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1453                         }
1454                 }
1455         } else {
1456                 gint cmp1 = 0, cmp2 = 0;
1457                 /* get the parent to know if it's a local folder */
1458
1459                 GtkTreeIter parent;
1460                 gboolean has_parent;
1461                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1462                 if (has_parent) {
1463                         GObject *parent_folder;
1464                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1465                         gtk_tree_model_get (tree_model, &parent, 
1466                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1467                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1468                                             -1);
1469                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1470                             TNY_IS_ACCOUNT (parent_folder) &&
1471                             modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1472                                 cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder1)));
1473                                 cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type (TNY_FOLDER (folder2)));
1474                         }
1475                         g_object_unref (parent_folder);
1476                 }
1477
1478                 /* if they are not local folders */
1479                 if (cmp1 == cmp2) {
1480                         cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1481                         cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1482                 }
1483
1484                 if (cmp1 == cmp2)
1485                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1486                 else 
1487                         cmp = (cmp1 - cmp2);
1488         }
1489
1490 finish: 
1491         if (folder1)
1492                 g_object_unref(G_OBJECT(folder1));
1493         if (folder2)
1494                 g_object_unref(G_OBJECT(folder2));
1495
1496         g_free (name1);
1497         g_free (name2);
1498
1499         return cmp;     
1500 }
1501
1502 /*****************************************************************************/
1503 /*                        DRAG and DROP stuff                                */
1504 /*****************************************************************************/
1505 /*
1506  * This function fills the #GtkSelectionData with the row and the
1507  * model that has been dragged. It's called when this widget is a
1508  * source for dnd after the event drop happened
1509  */
1510 static void
1511 on_drag_data_get (GtkWidget *widget, 
1512                   GdkDragContext *context, 
1513                   GtkSelectionData *selection_data, 
1514                   guint info, 
1515                   guint time, 
1516                   gpointer data)
1517 {
1518         GtkTreeSelection *selection;
1519         GtkTreeModel *model;
1520         GtkTreeIter iter;
1521         GtkTreePath *source_row;
1522
1523         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1524         gtk_tree_selection_get_selected (selection, &model, &iter);
1525         source_row = gtk_tree_model_get_path (model, &iter);
1526
1527         gtk_tree_set_row_drag_data (selection_data,
1528                                     model,
1529                                     source_row);
1530
1531         gtk_tree_path_free (source_row);
1532 }
1533
1534 typedef struct _DndHelper {
1535         gboolean delete_source;
1536         GtkTreePath *source_row;
1537         GdkDragContext *context;
1538         guint time;
1539 } DndHelper;
1540
1541
1542 /*
1543  * This function is the callback of the
1544  * modest_mail_operation_xfer_msgs () and
1545  * modest_mail_operation_xfer_folder() calls. We check here if the
1546  * message/folder was correctly asynchronously transferred. The reason
1547  * to use the same callback is that the code is the same, it only has
1548  * to check that the operation went fine and then finalize the drag
1549  * and drop action
1550  */
1551 static void
1552 xfer_cb (ModestMailOperation *mail_op, 
1553          gpointer user_data)
1554 {
1555         gboolean success;
1556         DndHelper *helper;
1557
1558         helper = (DndHelper *) user_data;
1559
1560         if (modest_mail_operation_get_status (mail_op) == 
1561             MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1562                 success = TRUE;
1563         } else {
1564                 success = FALSE;
1565         }
1566
1567         /* Notify the drag source. Never call delete, the monitor will
1568            do the job if needed */
1569         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1570
1571         /* Free the helper */
1572         gtk_tree_path_free (helper->source_row);
1573         g_slice_free (DndHelper, helper);
1574 }
1575
1576 /* get the folder for the row the treepath refers to. */
1577 /* folder must be unref'd */
1578 static TnyFolderStore *
1579 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
1580 {
1581         GtkTreeIter iter;
1582         TnyFolderStore *folder = NULL;
1583         
1584         if (gtk_tree_model_get_iter (model,&iter, path))
1585                 gtk_tree_model_get (model, &iter,
1586                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1587                                     -1);
1588         return folder;
1589 }
1590
1591 /*
1592  * This function is used by drag_data_received_cb to manage drag and
1593  * drop of a header, i.e, and drag from the header view to the folder
1594  * view.
1595  */
1596 static void
1597 drag_and_drop_from_header_view (GtkTreeModel *source_model,
1598                                 GtkTreeModel *dest_model,
1599                                 GtkTreePath  *dest_row,
1600                                 GtkSelectionData *selection_data,
1601                                 DndHelper    *helper)
1602 {
1603         TnyList *headers = NULL;
1604         TnyFolder *folder = NULL;
1605         ModestMailOperation *mail_op = NULL;
1606         GtkTreeIter source_iter, dest_iter;
1607         ModestWindowMgr *mgr = NULL;
1608         ModestWindow *main_win = NULL;
1609         gchar **uris, **tmp;
1610         gint response;
1611
1612         /* Build the list of headers */
1613         mgr = modest_runtime_get_window_mgr ();
1614         headers = tny_simple_list_new ();
1615         uris = modest_dnd_selection_data_get_paths (selection_data);
1616         tmp = uris;
1617
1618         while (*tmp != NULL) {
1619                 TnyHeader *header;
1620                 GtkTreePath *path;
1621
1622                 /* Get header */
1623                 path = gtk_tree_path_new_from_string (*tmp);
1624                 gtk_tree_model_get_iter (source_model, &source_iter, path);
1625                 gtk_tree_model_get (source_model, &source_iter, 
1626                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1627                                     &header, -1);
1628
1629                 /* Do not enable d&d of headers already opened */
1630                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
1631                         tny_list_append (headers, G_OBJECT (header));
1632
1633                 /* Free and go on */
1634                 gtk_tree_path_free (path);
1635                 g_object_unref (header);
1636                 tmp++;
1637         }
1638         g_strfreev (uris);
1639
1640         /* Get the target folder */
1641         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1642         gtk_tree_model_get (dest_model, &dest_iter, 
1643                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1644                             &folder, -1);
1645
1646         /* Ask for confirmation to move */
1647         main_win = modest_window_mgr_get_main_window (mgr);
1648         response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder, 
1649                                                                 TRUE, headers);
1650         if (response == GTK_RESPONSE_CANCEL)
1651                 goto cleanup;
1652
1653         /* Transfer messages */
1654         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
1655                                                                  modest_ui_actions_move_folder_error_handler,
1656                                                                  NULL, NULL);
1657
1658         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1659                                          mail_op);
1660
1661         modest_mail_operation_xfer_msgs (mail_op,
1662                                          headers, 
1663                                          folder, 
1664                                          helper->delete_source, 
1665                                          xfer_cb, helper);
1666         
1667         /* Frees */
1668 cleanup:
1669         if (G_IS_OBJECT(mail_op))
1670                 g_object_unref (G_OBJECT (mail_op));
1671         if (G_IS_OBJECT(folder))
1672                 g_object_unref (G_OBJECT (folder));
1673         if (G_IS_OBJECT(headers))
1674                 g_object_unref (headers);
1675 }
1676
1677 /*
1678  * This function is used by drag_data_received_cb to manage drag and
1679  * drop of a folder, i.e, and drag from the folder view to the same
1680  * folder view.
1681  */
1682 static void
1683 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
1684                                 GtkTreeModel     *dest_model,
1685                                 GtkTreePath      *dest_row,
1686                                 GtkSelectionData *selection_data,
1687                                 DndHelper        *helper)
1688 {
1689         ModestMailOperation *mail_op = NULL;
1690         GtkTreeIter dest_iter, iter;
1691         TnyFolderStore *dest_folder = NULL;
1692         TnyFolderStore *folder = NULL;
1693         gboolean forbidden = FALSE;
1694
1695         if (!forbidden) {
1696                 /* check the folder rules for the destination */
1697                 folder = tree_path_to_folder (dest_model, dest_row);
1698                 if (TNY_IS_FOLDER(folder)) {
1699                         ModestTnyFolderRules rules =
1700                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
1701                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
1702                 } else if (TNY_IS_FOLDER_STORE(folder)) {
1703                         /* enable local root as destination for folders */
1704                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder)
1705                                         && TNY_IS_ACCOUNT (folder))
1706                                 forbidden = TRUE;
1707                 }
1708                 g_object_unref (folder);
1709         }
1710         if (!forbidden) {
1711                 /* check the folder rules for the source */
1712                 folder = tree_path_to_folder (source_model, helper->source_row);
1713                 if (TNY_IS_FOLDER(folder)) {
1714                         ModestTnyFolderRules rules =
1715                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
1716                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
1717                 } else
1718                         forbidden = TRUE;
1719                 g_object_unref (folder);
1720         }
1721
1722         
1723         /* Check if the drag is possible */
1724         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
1725                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
1726                 gtk_tree_path_free (helper->source_row);        
1727                 g_slice_free (DndHelper, helper);
1728                 return;
1729         }
1730
1731         /* Get data */
1732         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
1733         gtk_tree_model_get (dest_model, &dest_iter, 
1734                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
1735                             &dest_folder, -1);
1736         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
1737         gtk_tree_model_get (source_model, &iter,
1738                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
1739                             &folder, -1);
1740
1741         /* Offer the connection dialog if necessary, for the destination parent folder and source folder: */
1742         if (modest_platform_connect_and_wait_if_network_folderstore (NULL, dest_folder) && 
1743             modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) {
1744                 ModestWindowMgr *mgr = modest_runtime_get_window_mgr ();
1745
1746                 /* Do the mail operation */
1747                 mail_op = 
1748                         modest_mail_operation_new_with_error_handling ((GObject *) modest_window_mgr_get_main_window (mgr),
1749                                                                        modest_ui_actions_move_folder_error_handler,
1750                                                                        folder, NULL);
1751
1752                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), 
1753                                                  mail_op);
1754
1755                 modest_mail_operation_xfer_folder (mail_op, 
1756                                                    TNY_FOLDER (folder), 
1757                                                    dest_folder,
1758                                                    helper->delete_source,
1759                                                    xfer_cb,
1760                                                    helper);
1761
1762                 g_object_unref (G_OBJECT (mail_op));
1763         }
1764         
1765         /* Frees */
1766         g_object_unref (G_OBJECT (dest_folder));
1767         g_object_unref (G_OBJECT (folder));
1768 }
1769
1770 /*
1771  * This function receives the data set by the "drag-data-get" signal
1772  * handler. This information comes within the #GtkSelectionData. This
1773  * function will manage both the drags of folders of the treeview and
1774  * drags of headers of the header view widget.
1775  */
1776 static void 
1777 on_drag_data_received (GtkWidget *widget, 
1778                        GdkDragContext *context, 
1779                        gint x, 
1780                        gint y, 
1781                        GtkSelectionData *selection_data, 
1782                        guint target_type, 
1783                        guint time, 
1784                        gpointer data)
1785 {
1786         GtkWidget *source_widget;
1787         GtkTreeModel *dest_model, *source_model;
1788         GtkTreePath *source_row, *dest_row;
1789         GtkTreeViewDropPosition pos;
1790         gboolean success = FALSE, delete_source = FALSE;
1791         DndHelper *helper = NULL; 
1792
1793         /* Do not allow further process */
1794         g_signal_stop_emission_by_name (widget, "drag-data-received");
1795         source_widget = gtk_drag_get_source_widget (context);
1796
1797         /* Get the action */
1798         if (context->action == GDK_ACTION_MOVE) {
1799                 delete_source = TRUE;
1800
1801                 /* Notify that there is no folder selected. We need to
1802                    do this in order to update the headers view (and
1803                    its monitors, because when moving, the old folder
1804                    won't longer exist. We can not wait for the end of
1805                    the operation, because the operation won't start if
1806                    the folder is in use */
1807                 if (source_widget == widget) {
1808                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1809                         gtk_tree_selection_unselect_all (sel);
1810                 }
1811         }
1812
1813         /* Check if the get_data failed */
1814         if (selection_data == NULL || selection_data->length < 0)
1815                 gtk_drag_finish (context, success, FALSE, time);
1816
1817         /* Select the destination model */
1818         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));  
1819
1820         /* Get the path to the destination row. Can not call
1821            gtk_tree_view_get_drag_dest_row() because the source row
1822            is not selected anymore */
1823         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
1824                                            &dest_row, &pos);
1825
1826         /* Only allow drops IN other rows */
1827         if (!dest_row || 
1828             pos == GTK_TREE_VIEW_DROP_BEFORE || 
1829             pos == GTK_TREE_VIEW_DROP_AFTER)
1830                 gtk_drag_finish (context, success, FALSE, time);
1831
1832         /* Create the helper */
1833         helper = g_slice_new0 (DndHelper);
1834         helper->delete_source = delete_source;
1835         helper->context = context;
1836         helper->time = time;
1837
1838         /* Drags from the header view */
1839         if (source_widget != widget) {
1840                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
1841
1842                 drag_and_drop_from_header_view (source_model,
1843                                                 dest_model,
1844                                                 dest_row,
1845                                                 selection_data,
1846                                                 helper);
1847         } else {
1848                 /* Get the source model and row */
1849                 gtk_tree_get_row_drag_data (selection_data,
1850                                             &source_model,
1851                                             &source_row);
1852                 helper->source_row = gtk_tree_path_copy (source_row);
1853
1854                 drag_and_drop_from_folder_view (source_model,
1855                                                 dest_model,
1856                                                 dest_row,
1857                                                 selection_data, 
1858                                                 helper);
1859
1860                 gtk_tree_path_free (source_row);
1861         }
1862
1863         /* Frees */
1864         gtk_tree_path_free (dest_row);
1865 }
1866
1867 /*
1868  * We define a "drag-drop" signal handler because we do not want to
1869  * use the default one, because the default one always calls
1870  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
1871  * signal handler, because there we have all the information available
1872  * to know if the dnd was a success or not.
1873  */
1874 static gboolean
1875 drag_drop_cb (GtkWidget      *widget,
1876               GdkDragContext *context,
1877               gint            x,
1878               gint            y,
1879               guint           time,
1880               gpointer        user_data) 
1881 {
1882         gpointer target;
1883
1884         if (!context->targets)
1885                 return FALSE;
1886
1887         /* Check if we're dragging a folder row */
1888         target = gtk_drag_dest_find_target (widget, context, NULL);
1889
1890         /* Request the data from the source. */
1891         gtk_drag_get_data(widget, context, target, time);
1892
1893     return TRUE;
1894 }
1895
1896 /*
1897  * This function expands a node of a tree view if it's not expanded
1898  * yet. Not sure why it needs the threads stuff, but gtk+`example code
1899  * does that, so that's why they're here.
1900  */
1901 static gint
1902 expand_row_timeout (gpointer data)
1903 {
1904         GtkTreeView *tree_view = data;
1905         GtkTreePath *dest_path = NULL;
1906         GtkTreeViewDropPosition pos;
1907         gboolean result = FALSE;
1908         
1909         GDK_THREADS_ENTER ();
1910         
1911         gtk_tree_view_get_drag_dest_row (tree_view,
1912                                          &dest_path,
1913                                          &pos);
1914         
1915         if (dest_path &&
1916             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
1917              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
1918                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
1919                 gtk_tree_path_free (dest_path);
1920         }
1921         else {
1922                 if (dest_path)
1923                         gtk_tree_path_free (dest_path);
1924                 
1925                 result = TRUE;
1926         }
1927         
1928         GDK_THREADS_LEAVE ();
1929
1930         return result;
1931 }
1932
1933 /*
1934  * This function is called whenever the pointer is moved over a widget
1935  * while dragging some data. It installs a timeout that will expand a
1936  * node of the treeview if not expanded yet. This function also calls
1937  * gdk_drag_status in order to set the suggested action that will be
1938  * used by the "drag-data-received" signal handler to know if we
1939  * should do a move or just a copy of the data.
1940  */
1941 static gboolean
1942 on_drag_motion (GtkWidget      *widget,
1943                 GdkDragContext *context,
1944                 gint            x,
1945                 gint            y,
1946                 guint           time,
1947                 gpointer        user_data)  
1948 {
1949         GtkTreeViewDropPosition pos;
1950         GtkTreePath *dest_row;
1951         GtkTreeModel *dest_model;
1952         ModestFolderViewPrivate *priv;
1953         GdkDragAction suggested_action;
1954         gboolean valid_location = FALSE;
1955         TnyFolderStore *folder;
1956
1957         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
1958
1959         if (priv->timer_expander != 0) {
1960                 g_source_remove (priv->timer_expander);
1961                 priv->timer_expander = 0;
1962         }
1963
1964         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
1965                                            x, y,
1966                                            &dest_row,
1967                                            &pos);
1968
1969         /* Do not allow drops between folders */
1970         if (!dest_row ||
1971             pos == GTK_TREE_VIEW_DROP_BEFORE ||
1972             pos == GTK_TREE_VIEW_DROP_AFTER) {
1973                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
1974                 gdk_drag_status(context, 0, time);
1975                 valid_location = FALSE;
1976                 goto out;
1977         } else {
1978                 valid_location = TRUE;
1979         }
1980
1981         /* Check that the destination folder is writable */
1982         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1983         folder = tree_path_to_folder (dest_model, dest_row);
1984         if (folder && TNY_IS_FOLDER (folder)) {
1985                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
1986
1987                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1988                         valid_location = FALSE;
1989                         goto out;
1990                 }
1991         }
1992         g_object_unref (folder);
1993
1994         /* Expand the selected row after 1/2 second */
1995         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
1996                 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
1997                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
1998         }
1999
2000         /* Select the desired action. By default we pick MOVE */
2001         suggested_action = GDK_ACTION_MOVE;
2002
2003         if (context->actions == GDK_ACTION_COPY)
2004             gdk_drag_status(context, GDK_ACTION_COPY, time);
2005         else if (context->actions == GDK_ACTION_MOVE)
2006             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2007         else if (context->actions & suggested_action)
2008             gdk_drag_status(context, suggested_action, time);
2009         else
2010             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2011
2012  out:
2013         if (dest_row)
2014                 gtk_tree_path_free (dest_row);
2015         g_signal_stop_emission_by_name (widget, "drag-motion");
2016
2017         return valid_location;
2018 }
2019
2020 /*
2021  * This function sets the treeview as a source and a target for dnd
2022  * events. It also connects all the requirede signals.
2023  */
2024 static void
2025 setup_drag_and_drop (GtkTreeView *self)
2026 {
2027         /* Set up the folder view as a dnd destination. Set only the
2028            highlight flag, otherwise gtk will have a different
2029            behaviour */
2030         gtk_drag_dest_set (GTK_WIDGET (self),
2031                            GTK_DEST_DEFAULT_HIGHLIGHT,
2032                            folder_view_drag_types,
2033                            G_N_ELEMENTS (folder_view_drag_types),
2034                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2035
2036         g_signal_connect (G_OBJECT (self),
2037                           "drag_data_received",
2038                           G_CALLBACK (on_drag_data_received),
2039                           NULL);
2040
2041
2042         /* Set up the treeview as a dnd source */
2043         gtk_drag_source_set (GTK_WIDGET (self),
2044                              GDK_BUTTON1_MASK,
2045                              folder_view_drag_types,
2046                              G_N_ELEMENTS (folder_view_drag_types),
2047                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2048
2049         g_signal_connect (G_OBJECT (self),
2050                           "drag_motion",
2051                           G_CALLBACK (on_drag_motion),
2052                           NULL);
2053         
2054         g_signal_connect (G_OBJECT (self),
2055                           "drag_data_get",
2056                           G_CALLBACK (on_drag_data_get),
2057                           NULL);
2058
2059         g_signal_connect (G_OBJECT (self),
2060                           "drag_drop",
2061                           G_CALLBACK (drag_drop_cb),
2062                           NULL);
2063 }
2064
2065 /*
2066  * This function manages the navigation through the folders using the
2067  * keyboard or the hardware keys in the device
2068  */
2069 static gboolean
2070 on_key_pressed (GtkWidget *self,
2071                 GdkEventKey *event,
2072                 gpointer user_data)
2073 {
2074         GtkTreeSelection *selection;
2075         GtkTreeIter iter;
2076         GtkTreeModel *model;
2077         gboolean retval = FALSE;
2078
2079         /* Up and Down are automatically managed by the treeview */
2080         if (event->keyval == GDK_Return) {
2081                 /* Expand/Collapse the selected row */
2082                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2083                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2084                         GtkTreePath *path;
2085
2086                         path = gtk_tree_model_get_path (model, &iter);
2087
2088                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2089                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2090                         else
2091                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2092                         gtk_tree_path_free (path);
2093                 }
2094                 /* No further processing */
2095                 retval = TRUE;
2096         }
2097
2098         return retval;
2099 }
2100
2101 /*
2102  * We listen to the changes in the local folder account name key,
2103  * because we want to show the right name in the view. The local
2104  * folder account name corresponds to the device name in the Maemo
2105  * version. We do this because we do not want to query gconf on each
2106  * tree view refresh. It's better to cache it and change whenever
2107  * necessary.
2108  */
2109 static void 
2110 on_configuration_key_changed (ModestConf* conf, 
2111                               const gchar *key, 
2112                               ModestConfEvent event,
2113                               ModestConfNotificationId id, 
2114                               ModestFolderView *self)
2115 {
2116         ModestFolderViewPrivate *priv;
2117
2118
2119         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2120         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2121
2122         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2123                 g_free (priv->local_account_name);
2124
2125                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2126                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2127                 else
2128                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2129                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2130
2131                 /* Force a redraw */
2132 #if GTK_CHECK_VERSION(2, 8, 0)
2133                 GtkTreeViewColumn * tree_column;
2134
2135                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2136                                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2137                 gtk_tree_view_column_queue_resize (tree_column);
2138 #else
2139                 gtk_widget_queue_draw (GTK_WIDGET (self));
2140 #endif
2141         }
2142 }
2143
2144 void
2145 modest_folder_view_set_style (ModestFolderView *self,
2146                               ModestFolderViewStyle style)
2147 {
2148         ModestFolderViewPrivate *priv;
2149
2150         g_return_if_fail (self);
2151         
2152         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2153
2154         priv->style = style;
2155 }
2156
2157 void
2158 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2159                                                              const gchar *account_id)
2160 {
2161         ModestFolderViewPrivate *priv;
2162         GtkTreeModel *model;
2163
2164         g_return_if_fail (self);
2165         
2166         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2167
2168         /* This will be used by the filter_row callback,
2169          * to decided which rows to show: */
2170         if (priv->visible_account_id) {
2171                 g_free (priv->visible_account_id);
2172                 priv->visible_account_id = NULL;
2173         }
2174         if (account_id)
2175                 priv->visible_account_id = g_strdup (account_id);
2176
2177         /* Refilter */
2178         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2179         if (GTK_IS_TREE_MODEL_FILTER (model))
2180                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2181
2182         /* Save settings to gconf */
2183         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2184                                    MODEST_CONF_FOLDER_VIEW_KEY);
2185 }
2186
2187 const gchar *
2188 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2189 {
2190         ModestFolderViewPrivate *priv;
2191
2192         g_return_val_if_fail (self, NULL);
2193         
2194         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2195
2196         return (const gchar *) priv->visible_account_id;
2197 }
2198
2199 static gboolean
2200 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2201 {
2202         do {
2203                 GtkTreeIter child;
2204                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2205
2206                 gtk_tree_model_get (model, iter, 
2207                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
2208                                     &type, -1);
2209                         
2210                 gboolean result = FALSE;
2211                 if (type == TNY_FOLDER_TYPE_INBOX) {
2212                         result = TRUE;
2213                 }               
2214                 if (result) {
2215                         *inbox_iter = *iter;
2216                         return TRUE;
2217                 }
2218
2219                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2220                         if (find_inbox_iter (model, &child, inbox_iter))
2221                                 return TRUE;
2222                 }
2223
2224         } while (gtk_tree_model_iter_next (model, iter));
2225
2226         return FALSE;
2227 }
2228
2229
2230
2231
2232 void 
2233 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2234 {
2235         GtkTreeModel *model;
2236         GtkTreeIter iter, inbox_iter;
2237         GtkTreeSelection *sel;
2238         GtkTreePath *path = NULL;
2239
2240         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2241         if (!model)
2242                 return;
2243
2244         expand_root_items (self);
2245         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2246
2247         gtk_tree_model_get_iter_first (model, &iter);
2248
2249         if (find_inbox_iter (model, &iter, &inbox_iter))
2250                 path = gtk_tree_model_get_path (model, &inbox_iter);
2251         else
2252                 path = gtk_tree_path_new_first ();
2253
2254         /* Select the row and free */
2255         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2256         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2257         gtk_tree_path_free (path);
2258
2259         /* set focus */
2260         gtk_widget_grab_focus (GTK_WIDGET(self));
2261 }
2262
2263
2264 /* recursive */
2265 static gboolean
2266 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, 
2267                   TnyFolder* folder)
2268 {
2269         do {
2270                 GtkTreeIter child;
2271                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2272                 TnyFolder* a_folder;
2273                 gchar *name = NULL;
2274                 
2275                 gtk_tree_model_get (model, iter, 
2276                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2277                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2278                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type, 
2279                                     -1);                
2280                 g_free (name);
2281
2282                 if (folder == a_folder) {
2283                         g_object_unref (a_folder);
2284                         *folder_iter = *iter;
2285                         return TRUE;
2286                 }
2287                 g_object_unref (a_folder);
2288                 
2289                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2290                         if (find_folder_iter (model, &child, folder_iter, folder)) 
2291                                 return TRUE;
2292                 }
2293
2294         } while (gtk_tree_model_iter_next (model, iter));
2295
2296         return FALSE;
2297 }
2298
2299
2300 static void
2301 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, GtkTreePath  *path, GtkTreeIter *iter,
2302                                      ModestFolderView *self)
2303 {
2304         ModestFolderViewPrivate *priv = NULL;
2305         GtkTreeSelection *sel;
2306         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2307         GObject *instance = NULL;
2308
2309         if (!MODEST_IS_FOLDER_VIEW(self))
2310                 return;
2311         
2312         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2313
2314         priv->reexpand = TRUE;
2315
2316         gtk_tree_model_get (tree_model, iter, 
2317                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2318                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2319                             -1);
2320         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2321                 priv->folder_to_select = g_object_ref (instance);
2322         }
2323         g_object_unref (instance);
2324
2325         
2326         if (priv->folder_to_select) {
2327                 
2328                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2329                                                        FALSE)) {
2330                         GtkTreePath *path;
2331                         path = gtk_tree_model_get_path (tree_model, iter);
2332                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2333                         
2334                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2335
2336                         gtk_tree_selection_select_iter (sel, iter);
2337                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2338
2339                         gtk_tree_path_free (path);
2340                 
2341                 }
2342
2343                 /* Disable next */
2344                 modest_folder_view_disable_next_folder_selection (self);
2345 /*              g_object_unref (priv->folder_to_select); */
2346 /*              priv->folder_to_select = NULL; */
2347         }
2348 }
2349
2350
2351 void
2352 modest_folder_view_disable_next_folder_selection (ModestFolderView *self) 
2353 {
2354         ModestFolderViewPrivate *priv = NULL;
2355
2356         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));        
2357         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2358
2359         if (priv->folder_to_select)
2360                 g_object_unref(priv->folder_to_select);
2361         
2362         priv->folder_to_select = NULL;
2363 }
2364
2365 gboolean
2366 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder, 
2367                                   gboolean after_change)
2368 {
2369         GtkTreeModel *model;
2370         GtkTreeIter iter, folder_iter;
2371         GtkTreeSelection *sel;
2372         ModestFolderViewPrivate *priv = NULL;
2373         
2374         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);     
2375         g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE);   
2376                 
2377         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2378
2379         if (after_change) {
2380
2381                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2382                 gtk_tree_selection_unselect_all (sel);
2383
2384                 if (priv->folder_to_select)
2385                         g_object_unref(priv->folder_to_select);
2386                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2387                 return TRUE;
2388         }
2389                 
2390         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2391         if (!model)
2392                 return FALSE;
2393
2394                 
2395         gtk_tree_model_get_iter_first (model, &iter);
2396         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2397                 GtkTreePath *path;
2398
2399                 path = gtk_tree_model_get_path (model, &folder_iter);
2400                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2401
2402                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2403                 gtk_tree_selection_select_iter (sel, &folder_iter);
2404                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2405
2406                 gtk_tree_path_free (path);
2407                 return TRUE;
2408         }
2409         return FALSE;
2410 }
2411
2412
2413 void 
2414 modest_folder_view_copy_selection (ModestFolderView *folder_view)
2415 {
2416         /* Copy selection */
2417         _clipboard_set_selected_data (folder_view, FALSE);
2418 }
2419
2420 void 
2421 modest_folder_view_cut_selection (ModestFolderView *folder_view)
2422 {
2423         ModestFolderViewPrivate *priv = NULL;
2424         GtkTreeModel *model = NULL;
2425         const gchar **hidding = NULL;
2426         guint i, n_selected;
2427
2428         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
2429         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2430
2431         /* Copy selection */
2432         if (!_clipboard_set_selected_data (folder_view, TRUE))
2433                 return;
2434
2435         /* Get hidding ids */
2436         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
2437         
2438         /* Clear hidding array created by previous cut operation */
2439         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
2440
2441         /* Copy hidding array */
2442         priv->n_selected = n_selected;
2443         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2444         for (i=0; i < n_selected; i++) 
2445                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2446
2447         /* Hide cut folders */
2448         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2449         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2450 }
2451
2452 void
2453 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
2454                                ModestFolderView *folder_view_dst)
2455 {
2456         GtkTreeModel *filter_model = NULL;
2457         GtkTreeModel *model = NULL;
2458         GtkTreeModel *new_filter_model = NULL;
2459         
2460         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_src));
2461         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view_dst));
2462
2463         /* Get src model*/
2464         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
2465         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
2466
2467         /* Build new filter model */
2468         new_filter_model = gtk_tree_model_filter_new (model, NULL);     
2469         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
2470                                                 filter_row,
2471                                                 folder_view_dst,
2472                                                 NULL);
2473         /* Set copied model */
2474         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
2475         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
2476                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
2477
2478         /* Free */
2479         g_object_unref (new_filter_model);
2480 }
2481
2482 void
2483 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
2484                                           gboolean show)
2485 {
2486         GtkTreeModel *model = NULL;
2487         ModestFolderViewPrivate* priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2488         priv->show_non_move = show;
2489 /*      modest_folder_view_update_model(folder_view, */
2490 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
2491
2492         /* Hide special folders */
2493         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
2494         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2495                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2496         }
2497 }
2498
2499 /* Returns FALSE if it did not selected anything */
2500 static gboolean
2501 _clipboard_set_selected_data (ModestFolderView *folder_view,
2502                               gboolean delete)
2503 {
2504         ModestFolderViewPrivate *priv = NULL;
2505         TnyFolderStore *folder = NULL;
2506         gboolean retval = FALSE;
2507
2508         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
2509         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
2510                 
2511         /* Set selected data on clipboard   */
2512         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
2513         folder = modest_folder_view_get_selected (folder_view);
2514
2515         /* Do not allow to select an account */
2516         if (TNY_IS_FOLDER (folder)) {
2517                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
2518                 retval = TRUE;
2519         }
2520
2521         /* Free */
2522         g_object_unref (folder);
2523
2524         return retval;
2525 }
2526
2527 static void
2528 _clear_hidding_filter (ModestFolderView *folder_view) 
2529 {
2530         ModestFolderViewPrivate *priv;
2531         guint i;
2532         
2533         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
2534         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
2535
2536         if (priv->hidding_ids != NULL) {
2537                 for (i=0; i < priv->n_selected; i++) 
2538                         g_free (priv->hidding_ids[i]);
2539                 g_free(priv->hidding_ids);
2540         }       
2541 }
2542
2543
2544 static void 
2545 on_display_name_changed (ModestAccountMgr *mgr, 
2546                          const gchar *account,
2547                          gpointer user_data)
2548 {
2549         ModestFolderView *self;
2550
2551         self = MODEST_FOLDER_VIEW (user_data);
2552
2553         /* Force a redraw */
2554 #if GTK_CHECK_VERSION(2, 8, 0)
2555         GtkTreeViewColumn * tree_column;
2556         
2557         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2558                                                 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2559         gtk_tree_view_column_queue_resize (tree_column);
2560 #else
2561         gtk_widget_queue_draw (GTK_WIDGET (self));
2562 #endif
2563 }