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