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