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