810976fede08bbb881d1363134cec55af56f73b4
[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 inline 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                 const gchar *icon_code;
823
824         case TNY_FOLDER_TYPE_INVALID:
825                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
826                 break;
827
828         case TNY_FOLDER_TYPE_ROOT:
829                 if (TNY_IS_ACCOUNT (instance)) {
830
831                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance))) {
832                                 retval = get_composite_icons (MODEST_FOLDER_ICON_LOCAL_FOLDERS,
833                                                               &avirt_pixbuf,
834                                                               &avirt_pixbuf_open,
835                                                               &avirt_pixbuf_close);
836                         } else {
837                                 const gchar *account_id = tny_account_get_id (TNY_ACCOUNT (instance));
838
839                                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
840                                         retval = get_composite_icons (MODEST_FOLDER_ICON_MMC,
841                                                                       &ammc_pixbuf,
842                                                                       &ammc_pixbuf_open,
843                                                                       &ammc_pixbuf_close);
844                                 } else {
845                                         retval = get_composite_icons (MODEST_FOLDER_ICON_ACCOUNT,
846                                                                       &anorm_pixbuf,
847                                                                       &anorm_pixbuf_open,
848                                                                       &anorm_pixbuf_close);
849                                 }
850                         }
851                 }
852                 break;
853         case TNY_FOLDER_TYPE_INBOX:
854                 retval = get_composite_icons (MODEST_FOLDER_ICON_INBOX,
855                                               &inbox_pixbuf,
856                                               &inbox_pixbuf_open,
857                                               &inbox_pixbuf_close);
858                 break;
859         case TNY_FOLDER_TYPE_OUTBOX:
860                 retval = get_composite_icons (MODEST_FOLDER_ICON_OUTBOX,
861                                               &outbox_pixbuf,
862                                               &outbox_pixbuf_open,
863                                               &outbox_pixbuf_close);
864                 break;
865         case TNY_FOLDER_TYPE_JUNK:
866                 retval = get_composite_icons (MODEST_FOLDER_ICON_JUNK,
867                                               &junk_pixbuf,
868                                               &junk_pixbuf_open,
869                                               &junk_pixbuf_close);
870                 break;
871         case TNY_FOLDER_TYPE_SENT:
872                 retval = get_composite_icons (MODEST_FOLDER_ICON_SENT,
873                                               &sent_pixbuf,
874                                               &sent_pixbuf_open,
875                                               &sent_pixbuf_close);
876                 break;
877         case TNY_FOLDER_TYPE_TRASH:
878                 retval = get_composite_icons (MODEST_FOLDER_ICON_TRASH,
879                                               &trash_pixbuf,
880                                               &trash_pixbuf_open,
881                                               &trash_pixbuf_close);
882                 break;
883         case TNY_FOLDER_TYPE_DRAFTS:
884                 retval = get_composite_icons (MODEST_FOLDER_ICON_DRAFTS,
885                                               &draft_pixbuf,
886                                               &draft_pixbuf_open,
887                                               &draft_pixbuf_close);
888                 break;
889         case TNY_FOLDER_TYPE_NORMAL:
890         default:
891                 /* Memory card folders could have an special icon */
892                 if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
893                         icon_code = MODEST_FOLDER_ICON_MMC_FOLDER;
894                 else
895                         icon_code = MODEST_FOLDER_ICON_NORMAL;
896
897                 retval = get_composite_icons (icon_code,
898                                               &normal_pixbuf,
899                                               &normal_pixbuf_open,
900                                               &normal_pixbuf_close);
901                 break;
902         }
903
904         return retval;
905 }
906
907 static void
908 free_pixbufs (ThreePixbufs *pixbufs)
909 {
910         if (pixbufs->pixbuf)
911                 g_object_unref (pixbufs->pixbuf);
912         if (pixbufs->pixbuf_open)
913                 g_object_unref (pixbufs->pixbuf_open);
914         if (pixbufs->pixbuf_close)
915                 g_object_unref (pixbufs->pixbuf_close);
916         g_slice_free (ThreePixbufs, pixbufs);
917 }
918
919 static void
920 icon_cell_data  (GtkTreeViewColumn *column,
921                  GtkCellRenderer *renderer,
922                  GtkTreeModel *tree_model,
923                  GtkTreeIter *iter,
924                  gpointer data)
925 {
926         GObject *rendobj = NULL, *instance = NULL;
927         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
928         gboolean has_children;
929         ThreePixbufs *pixbufs;
930
931         rendobj = (GObject *) renderer;
932
933         gtk_tree_model_get (tree_model, iter,
934                             TYPE_COLUMN, &type,
935                             INSTANCE_COLUMN, &instance,
936                             -1);
937
938         if (!instance)
939                 return;
940
941         has_children = gtk_tree_model_iter_has_child (tree_model, iter);
942         pixbufs = get_folder_icons (type, instance);
943         g_object_unref (instance);
944
945         /* Set pixbuf */
946         g_object_set (rendobj, "pixbuf", pixbufs->pixbuf, NULL);
947
948         if (has_children) {
949                 g_object_set (rendobj, "pixbuf-expander-open", pixbufs->pixbuf_open, NULL);
950                 g_object_set (rendobj, "pixbuf-expander-closed", pixbufs->pixbuf_close, NULL);
951         }
952
953         free_pixbufs (pixbufs);
954 }
955
956 static void
957 add_columns (GtkWidget *treeview)
958 {
959         GtkTreeViewColumn *column;
960         GtkCellRenderer *renderer;
961         GtkTreeSelection *sel;
962         ModestFolderViewPrivate *priv;
963
964         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(treeview);
965
966         /* Create column */
967         column = gtk_tree_view_column_new ();
968
969         /* Set icon and text render function */
970         renderer = gtk_cell_renderer_pixbuf_new();
971         gtk_tree_view_column_pack_start (column, renderer, FALSE);
972         gtk_tree_view_column_set_cell_data_func(column, renderer,
973                                                 icon_cell_data, treeview, NULL);
974
975         renderer = gtk_cell_renderer_text_new();
976         g_object_set (renderer, 
977 #ifdef MODEST_TOOLKIT_HILDON2
978                       "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
979 #else
980                       "ellipsize", PANGO_ELLIPSIZE_END,
981 #endif
982                       "ellipsize-set", TRUE, NULL);
983         gtk_tree_view_column_pack_start (column, renderer, TRUE);
984         gtk_tree_view_column_set_cell_data_func(column, renderer,
985                                                 text_cell_data, treeview, NULL);
986
987         priv->messages_renderer = gtk_cell_renderer_text_new ();
988         g_object_set (priv->messages_renderer, 
989                       "scale", PANGO_SCALE_X_SMALL,
990                       "scale-set", TRUE,
991                       "alignment", PANGO_ALIGN_RIGHT,
992                       "align-set", TRUE,
993                       "xalign", 1.0,
994                       NULL);
995         gtk_tree_view_column_pack_start (column, priv->messages_renderer, FALSE);
996         gtk_tree_view_column_set_cell_data_func(column, priv->messages_renderer,
997                                                 messages_cell_data, treeview, NULL);
998
999         /* Set selection mode */
1000         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview));
1001         gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1002
1003         /* Set treeview appearance */
1004         gtk_tree_view_column_set_spacing (column, 2);
1005         gtk_tree_view_column_set_resizable (column, TRUE);
1006         gtk_tree_view_column_set_fixed_width (column, TRUE);
1007         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(treeview), FALSE);
1008         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(treeview), FALSE);
1009
1010         /* Add column */
1011         gtk_tree_view_append_column (GTK_TREE_VIEW(treeview),column);
1012 }
1013
1014 static void
1015 modest_folder_view_init (ModestFolderView *obj)
1016 {
1017         ModestFolderViewPrivate *priv;
1018         ModestConf *conf;
1019
1020         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1021
1022         priv->timer_expander = 0;
1023         priv->account_store  = NULL;
1024         priv->query          = NULL;
1025         priv->style          = MODEST_FOLDER_VIEW_STYLE_SHOW_ALL;
1026         priv->cur_folder_store   = NULL;
1027         priv->visible_account_id = NULL;
1028         priv->folder_to_select = NULL;
1029
1030         priv->reexpand = TRUE;
1031
1032         /* Initialize the local account name */
1033         conf = modest_runtime_get_conf();
1034         priv->local_account_name = modest_conf_get_string (conf, MODEST_CONF_DEVICE_NAME, NULL);
1035
1036         /* Init email clipboard */
1037         priv->clipboard = modest_runtime_get_email_clipboard ();
1038         priv->hidding_ids = NULL;
1039         priv->n_selected = 0;
1040         priv->filter = MODEST_FOLDER_VIEW_FILTER_NONE;
1041         priv->reselect = FALSE;
1042         priv->show_non_move = TRUE;
1043
1044         /* Build treeview */
1045         add_columns (GTK_WIDGET (obj));
1046
1047         /* Setup drag and drop */
1048         setup_drag_and_drop (GTK_TREE_VIEW(obj));
1049
1050         /* Connect signals */
1051         g_signal_connect (G_OBJECT (obj),
1052                           "key-press-event",
1053                           G_CALLBACK (on_key_pressed), NULL);
1054
1055         priv->display_name_changed_signal =
1056                 g_signal_connect (modest_runtime_get_account_mgr (),
1057                                   "display_name_changed",
1058                                   G_CALLBACK (on_display_name_changed),
1059                                   obj);
1060
1061         /*
1062          * Track changes in the local account name (in the device it
1063          * will be the device name)
1064          */
1065         priv->conf_key_signal = g_signal_connect (G_OBJECT(conf),
1066                                                   "key_changed",
1067                                                   G_CALLBACK(on_configuration_key_changed),
1068                                                   obj);
1069
1070         update_style (obj);
1071         g_signal_connect (G_OBJECT (obj), "notify::style", G_CALLBACK (on_notify_style), (gpointer) obj);
1072
1073
1074 }
1075
1076 static void
1077 tny_account_store_view_init (gpointer g, gpointer iface_data)
1078 {
1079         TnyAccountStoreViewIface *klass = (TnyAccountStoreViewIface *)g;
1080
1081         klass->set_account_store = modest_folder_view_set_account_store;
1082 }
1083
1084 static void
1085 modest_folder_view_finalize (GObject *obj)
1086 {
1087         ModestFolderViewPrivate *priv;
1088         GtkTreeSelection    *sel;
1089
1090         g_return_if_fail (obj);
1091
1092         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(obj);
1093
1094         if (priv->timer_expander != 0) {
1095                 g_source_remove (priv->timer_expander);
1096                 priv->timer_expander = 0;
1097         }
1098
1099         if (priv->account_store) {
1100                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1101                                              priv->account_inserted_signal);
1102                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1103                                              priv->account_removed_signal);
1104                 g_signal_handler_disconnect (G_OBJECT(priv->account_store),
1105                                              priv->account_changed_signal);
1106                 g_object_unref (G_OBJECT(priv->account_store));
1107                 priv->account_store = NULL;
1108         }
1109
1110         if (g_signal_handler_is_connected (modest_runtime_get_account_mgr (), 
1111                                            priv->display_name_changed_signal)) {
1112                 g_signal_handler_disconnect (modest_runtime_get_account_mgr (),
1113                                              priv->display_name_changed_signal);
1114                 priv->display_name_changed_signal = 0;
1115         }
1116
1117         if (priv->query) {
1118                 g_object_unref (G_OBJECT (priv->query));
1119                 priv->query = NULL;
1120         }
1121
1122         if (priv->folder_to_select) {
1123                 g_object_unref (G_OBJECT(priv->folder_to_select));
1124                 priv->folder_to_select = NULL;
1125         }
1126
1127         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1128         if (sel)
1129                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1130
1131         g_free (priv->local_account_name);
1132         g_free (priv->visible_account_id);
1133
1134         if (priv->conf_key_signal) {
1135                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1136                                              priv->conf_key_signal);
1137                 priv->conf_key_signal = 0;
1138         }
1139
1140         if (priv->cur_folder_store) {
1141                 g_object_unref (priv->cur_folder_store);
1142                 priv->cur_folder_store = NULL;
1143         }
1144
1145         /* Clear hidding array created by cut operation */
1146         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1147
1148         G_OBJECT_CLASS(parent_class)->finalize (obj);
1149 }
1150
1151
1152 static void
1153 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1154 {
1155         ModestFolderViewPrivate *priv;
1156         TnyDevice *device;
1157
1158         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1159         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1160
1161         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1162         device = tny_account_store_get_device (account_store);
1163
1164         if (G_UNLIKELY (priv->account_store)) {
1165
1166                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1167                                                    priv->account_inserted_signal))
1168                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1169                                                      priv->account_inserted_signal);
1170                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1171                                                    priv->account_removed_signal))
1172                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1173                                                      priv->account_removed_signal);
1174                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1175                                                    priv->account_changed_signal))
1176                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1177                                                      priv->account_changed_signal);
1178                 g_object_unref (G_OBJECT (priv->account_store));
1179         }
1180
1181         priv->account_store = g_object_ref (G_OBJECT (account_store));
1182
1183         priv->account_removed_signal =
1184                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1185                                   G_CALLBACK (on_account_removed), self);
1186
1187         priv->account_inserted_signal =
1188                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1189                                   G_CALLBACK (on_account_inserted), self);
1190
1191         priv->account_changed_signal =
1192                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1193                                   G_CALLBACK (on_account_changed), self);
1194
1195         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1196         priv->reselect = FALSE;
1197         modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1198
1199         g_object_unref (G_OBJECT (device));
1200 }
1201
1202 static void
1203 on_account_inserted (TnyAccountStore *account_store,
1204                      TnyAccount *account,
1205                      gpointer user_data)
1206 {
1207         ModestFolderViewPrivate *priv;
1208         GtkTreeModel *sort_model, *filter_model;
1209
1210         /* Ignore transport account insertions, we're not showing them
1211            in the folder view */
1212         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1213                 return;
1214
1215         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1216
1217
1218         /* If we're adding a new account, and there is no previous
1219            one, we need to select the visible server account */
1220         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1221             !priv->visible_account_id)
1222                 modest_widget_memory_restore (modest_runtime_get_conf(),
1223                                               G_OBJECT (user_data),
1224                                               MODEST_CONF_FOLDER_VIEW_KEY);
1225
1226         if (!GTK_IS_TREE_VIEW(user_data)) {
1227                 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1228                 return;
1229         }
1230
1231         /* Get the inner model */
1232         /* check, is some rare cases, we did not get the right thing here,
1233          * NB#84097 */
1234         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1235         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1236                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1237                 return;
1238         }
1239
1240         /* check, is some rare cases, we did not get the right thing here,
1241          * NB#84097 */
1242         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1243         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1244                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1245                 return;
1246         }
1247
1248         /* Insert the account in the model */
1249         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1250                          G_OBJECT (account));
1251
1252         /* Refilter the model */
1253         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1254 }
1255
1256
1257 static gboolean
1258 same_account_selected (ModestFolderView *self,
1259                        TnyAccount *account)
1260 {
1261         ModestFolderViewPrivate *priv;
1262         gboolean same_account = FALSE;
1263
1264         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1265
1266         if (priv->cur_folder_store) {
1267                 TnyAccount *selected_folder_account = NULL;
1268
1269                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1270                         selected_folder_account =
1271                                 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1272                 } else {
1273                         selected_folder_account =
1274                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1275                 }
1276
1277                 if (selected_folder_account == account)
1278                         same_account = TRUE;
1279
1280                 g_object_unref (selected_folder_account);
1281         }
1282         return same_account;
1283 }
1284
1285 /**
1286  *
1287  * Selects the first inbox or the local account in an idle
1288  */
1289 static gboolean
1290 on_idle_select_first_inbox_or_local (gpointer user_data)
1291 {
1292         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1293
1294         gdk_threads_enter ();
1295         modest_folder_view_select_first_inbox_or_local (self);
1296         gdk_threads_leave ();
1297
1298         return FALSE;
1299 }
1300
1301 static void
1302 on_account_changed (TnyAccountStore *account_store,
1303                     TnyAccount *tny_account,
1304                     gpointer user_data)
1305 {
1306         ModestFolderView *self;
1307         ModestFolderViewPrivate *priv;
1308         GtkTreeModel *sort_model, *filter_model;
1309         GtkTreeSelection *sel;
1310         gboolean same_account;
1311
1312         /* Ignore transport account insertions, we're not showing them
1313            in the folder view */
1314         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1315                 return;
1316
1317         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1318                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1319                 return;
1320         }
1321
1322         self = MODEST_FOLDER_VIEW (user_data);
1323         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1324
1325         /* Get the inner model */
1326         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1327         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1328                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1329                 return;
1330         }
1331
1332         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1333         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1334                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1335                 return;
1336         }
1337
1338         /* Invalidate the cur_folder_store only if the selected folder
1339            belongs to the account that is being removed */
1340         same_account = same_account_selected (self, tny_account);
1341         if (same_account) {
1342                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1343                 gtk_tree_selection_unselect_all (sel);
1344         }
1345
1346         /* Remove the account from the model */
1347         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1348                          G_OBJECT (tny_account));
1349
1350         /* Insert the account in the model */
1351         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1352                          G_OBJECT (tny_account));
1353
1354         /* Refilter the model */
1355         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1356
1357         /* Select the first INBOX if the currently selected folder
1358            belongs to the account that is being deleted */
1359         if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1360                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1361 }
1362
1363 static void
1364 on_account_removed (TnyAccountStore *account_store,
1365                     TnyAccount *account,
1366                     gpointer user_data)
1367 {
1368         ModestFolderView *self = NULL;
1369         ModestFolderViewPrivate *priv;
1370         GtkTreeModel *sort_model, *filter_model;
1371         GtkTreeSelection *sel = NULL;
1372         gboolean same_account = FALSE;
1373
1374         /* Ignore transport account removals, we're not showing them
1375            in the folder view */
1376         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1377                 return;
1378
1379         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1380                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1381                 return;
1382         }
1383
1384         self = MODEST_FOLDER_VIEW (user_data);
1385         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1386
1387         /* Invalidate the cur_folder_store only if the selected folder
1388            belongs to the account that is being removed */
1389         same_account = same_account_selected (self, account);
1390         if (same_account) {
1391                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1392                 gtk_tree_selection_unselect_all (sel);
1393         }
1394
1395         /* Invalidate row to select only if the folder to select
1396            belongs to the account that is being removed*/
1397         if (priv->folder_to_select) {
1398                 TnyAccount *folder_to_select_account = NULL;
1399
1400                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1401                 if (folder_to_select_account == account) {
1402                         modest_folder_view_disable_next_folder_selection (self);
1403                         g_object_unref (priv->folder_to_select);
1404                         priv->folder_to_select = NULL;
1405                 }
1406                 g_object_unref (folder_to_select_account);
1407         }
1408
1409         /* Remove the account from the model */
1410         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1411         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1412                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1413                 return;
1414         }
1415
1416         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1417         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1418                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1419                 return;
1420         }
1421
1422         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1423                          G_OBJECT (account));
1424
1425         /* If the removed account is the currently viewed one then
1426            clear the configuration value. The new visible account will be the default account */
1427         if (priv->visible_account_id &&
1428             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1429
1430                 /* Clear the current visible account_id */
1431                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1432
1433                 /* Call the restore method, this will set the new visible account */
1434                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1435                                               MODEST_CONF_FOLDER_VIEW_KEY);
1436         }
1437
1438         /* Refilter the model */
1439         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1440
1441         /* Select the first INBOX if the currently selected folder
1442            belongs to the account that is being deleted */
1443         if (same_account)
1444                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1445 }
1446
1447 void
1448 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1449 {
1450         GtkTreeViewColumn *col;
1451
1452         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1453
1454         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1455         if (!col) {
1456                 g_printerr ("modest: failed get column for title\n");
1457                 return;
1458         }
1459
1460         gtk_tree_view_column_set_title (col, title);
1461         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1462                                            title != NULL);
1463 }
1464
1465 static gboolean
1466 modest_folder_view_on_map (ModestFolderView *self,
1467                            GdkEventExpose *event,
1468                            gpointer data)
1469 {
1470         ModestFolderViewPrivate *priv;
1471
1472         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1473
1474         /* This won't happen often */
1475         if (G_UNLIKELY (priv->reselect)) {
1476                 /* Select the first inbox or the local account if not found */
1477
1478                 /* TODO: this could cause a lock at startup, so we
1479                    comment it for the moment. We know that this will
1480                    be a bug, because the INBOX is not selected, but we
1481                    need to rewrite some parts of Modest to avoid the
1482                    deathlock situation */
1483                 /* TODO: check if this is still the case */
1484                 priv->reselect = FALSE;
1485                 modest_folder_view_select_first_inbox_or_local (self);
1486                 /* Notify the display name observers */
1487                 g_signal_emit (G_OBJECT(self),
1488                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1489                                NULL);
1490         }
1491
1492         if (priv->reexpand) {
1493                 expand_root_items (self);
1494                 priv->reexpand = FALSE;
1495         }
1496
1497         return FALSE;
1498 }
1499
1500 GtkWidget*
1501 modest_folder_view_new (TnyFolderStoreQuery *query)
1502 {
1503         GObject *self;
1504         ModestFolderViewPrivate *priv;
1505         GtkTreeSelection *sel;
1506
1507         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, 
1508 #ifdef MODEST_TOOLKIT_HILDON2
1509                                        "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1510 #endif
1511                                        NULL));
1512         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1513
1514         if (query)
1515                 priv->query = g_object_ref (query);
1516
1517         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1518         priv->changed_signal = g_signal_connect (sel, "changed",
1519                                                  G_CALLBACK (on_selection_changed), self);
1520
1521         g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1522
1523         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1524
1525         return GTK_WIDGET(self);
1526 }
1527
1528 /* this feels dirty; any other way to expand all the root items? */
1529 static void
1530 expand_root_items (ModestFolderView *self)
1531 {
1532         GtkTreePath *path;
1533         GtkTreeModel *model;
1534         GtkTreeIter iter;
1535
1536         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1537         path = gtk_tree_path_new_first ();
1538
1539         /* all folders should have child items, so.. */
1540         do {
1541                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1542                 gtk_tree_path_next (path);
1543         } while (gtk_tree_model_get_iter (model, &iter, path));
1544
1545         gtk_tree_path_free (path);
1546 }
1547
1548 /*
1549  * We use this function to implement the
1550  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1551  * account in this case, and the local folders.
1552  */
1553 static gboolean
1554 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1555 {
1556         ModestFolderViewPrivate *priv;
1557         gboolean retval = TRUE;
1558         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1559         GObject *instance = NULL;
1560         const gchar *id = NULL;
1561         guint i;
1562         gboolean found = FALSE;
1563         gboolean cleared = FALSE;
1564
1565         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1566         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1567
1568         gtk_tree_model_get (model, iter,
1569                             TYPE_COLUMN, &type,
1570                             INSTANCE_COLUMN, &instance,
1571                             -1);
1572
1573         /* Do not show if there is no instance, this could indeed
1574            happen when the model is being modified while it's being
1575            drawn. This could occur for example when moving folders
1576            using drag&drop */
1577         if (!instance)
1578                 return FALSE;
1579
1580         if (TNY_IS_ACCOUNT (instance)) {
1581 #ifdef MODEST_TOOLKIT_HILDON2
1582                 /* In hildon 2.2 we don't show the account rows */
1583                 return FALSE;
1584 #else
1585                 TnyAccount *acc = TNY_ACCOUNT (instance);
1586                 const gchar *account_id = tny_account_get_id (acc);
1587
1588                 /* If it isn't a special folder,
1589                  * don't show it unless it is the visible account: */
1590                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1591                     !modest_tny_account_is_virtual_local_folders (acc) &&
1592                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1593
1594                         /* Show only the visible account id */
1595                         if (priv->visible_account_id) {
1596                                 if (strcmp (account_id, priv->visible_account_id))
1597                                         retval = FALSE;
1598                         } else {
1599                                 retval = FALSE;
1600                         }
1601                 }
1602
1603                 /* Never show these to the user. They are merged into one folder
1604                  * in the local-folders account instead: */
1605                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1606                         retval = FALSE;
1607 #endif
1608         } else {
1609                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
1610                         /* Only show special folders for current account if needed */
1611                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
1612                                 TnyAccount *account;
1613
1614                                 account = tny_folder_get_account (TNY_FOLDER (instance));
1615
1616                                 if (TNY_IS_ACCOUNT (account)) {
1617                                         const gchar *account_id = tny_account_get_id (account);
1618
1619                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
1620                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1621                                                 /* Show only the visible account id */
1622                                                 if (priv->visible_account_id) {
1623                                                         if (strcmp (account_id, priv->visible_account_id))
1624                                                                 retval = FALSE;
1625                                                 }
1626                                         }
1627                                                 g_object_unref (account);
1628                                 }
1629                         }
1630
1631                 }
1632         }
1633
1634         /* Check hiding (if necessary) */
1635         cleared = modest_email_clipboard_cleared (priv->clipboard);
1636         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1637                 id = tny_folder_get_id (TNY_FOLDER(instance));
1638                 if (priv->hidding_ids != NULL)
1639                         for (i=0; i < priv->n_selected && !found; i++)
1640                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1641                                         found = (!strcmp (priv->hidding_ids[i], id));
1642
1643                 retval = !found;
1644         }
1645
1646         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1647         folder as no message can be move there according to UI specs */
1648         if (!priv->show_non_move) {
1649                 switch (type) {
1650                         case TNY_FOLDER_TYPE_OUTBOX:
1651                         case TNY_FOLDER_TYPE_SENT:
1652                         case TNY_FOLDER_TYPE_DRAFTS:
1653                                 retval = FALSE;
1654                                 break;
1655                         case TNY_FOLDER_TYPE_UNKNOWN:
1656                         case TNY_FOLDER_TYPE_NORMAL:
1657                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1658                                 if (type == TNY_FOLDER_TYPE_INVALID)
1659                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1660
1661                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
1662                                     type == TNY_FOLDER_TYPE_SENT
1663                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1664                                         retval = FALSE;
1665                                 break;
1666                         default:
1667                                 break;
1668                 }
1669         }
1670
1671         /* apply special filters */
1672         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1673                 if (TNY_IS_FOLDER (instance)) {
1674                         TnyFolderCaps capabilities;
1675
1676                         capabilities = tny_folder_get_caps (TNY_FOLDER (instance));
1677                         retval = !(capabilities & TNY_FOLDER_CAPS_NOCHILDREN);
1678
1679                         if (retval) {
1680                                 retval = ((type != TNY_FOLDER_TYPE_DRAFTS) &&
1681                                           (type != TNY_FOLDER_TYPE_OUTBOX) &&
1682                                           (type != TNY_FOLDER_TYPE_SENT));
1683                         }
1684                 } else if (TNY_IS_ACCOUNT (instance)) {
1685                         retval = FALSE;
1686                 }
1687         }
1688
1689         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1690                 if (TNY_IS_FOLDER (instance)) {
1691                         TnyFolderType guess_type;
1692                         
1693                         if (TNY_FOLDER_TYPE_NORMAL) {
1694                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1695                         } else {
1696                                 guess_type = type;
1697                         }
1698
1699                         switch (type) {
1700                         case TNY_FOLDER_TYPE_OUTBOX:
1701                         case TNY_FOLDER_TYPE_SENT:
1702                         case TNY_FOLDER_TYPE_DRAFTS:
1703                         case TNY_FOLDER_TYPE_ARCHIVE:
1704                         case TNY_FOLDER_TYPE_INBOX:
1705                                 retval = FALSE;
1706                                 break;
1707                         case TNY_FOLDER_TYPE_UNKNOWN:
1708                         case TNY_FOLDER_TYPE_NORMAL:
1709                                 break;
1710                         default:
1711                                 break;
1712                         }
1713                         
1714                 } else if (TNY_IS_ACCOUNT (instance)) {
1715                         retval = FALSE;
1716                 }
1717         }
1718
1719         /* Free */
1720         g_object_unref (instance);
1721
1722         return retval;
1723 }
1724
1725
1726 gboolean
1727 modest_folder_view_update_model (ModestFolderView *self,
1728                                  TnyAccountStore *account_store)
1729 {
1730         ModestFolderViewPrivate *priv;
1731         GtkTreeModel *model /* , *old_model */;
1732         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1733
1734         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1735         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1736                               FALSE);
1737
1738         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1739
1740         /* Notify that there is no folder selected */
1741         g_signal_emit (G_OBJECT(self),
1742                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1743                        NULL, FALSE);
1744         if (priv->cur_folder_store) {
1745                 g_object_unref (priv->cur_folder_store);
1746                 priv->cur_folder_store = NULL;
1747         }
1748
1749         /* FIXME: the local accounts are not shown when the query
1750            selects only the subscribed folders */
1751 #ifdef MODEST_TOOLKIT_HILDON2
1752         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
1753                                                           TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
1754         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
1755                                                       MODEST_FOLDER_PATH_SEPARATOR);
1756 #else
1757         model = tny_gtk_folder_store_tree_model_new (NULL);
1758 #endif
1759
1760         /* Get the accounts: */
1761         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1762                                         TNY_LIST (model),
1763                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1764
1765         sortable = gtk_tree_model_sort_new_with_model (model);
1766         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1767                                               NAME_COLUMN,
1768                                               GTK_SORT_ASCENDING);
1769         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1770                                          NAME_COLUMN,
1771                                          cmp_rows, NULL, NULL);
1772
1773         /* Create filter model */
1774         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1775         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1776                                                 filter_row,
1777                                                 self,
1778                                                 NULL);
1779
1780         /* Set new model */
1781         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1782 #ifndef MODEST_TOOLKIT_HILDON2
1783         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1784                           (GCallback) on_row_inserted_maybe_select_folder, self);
1785 #endif
1786
1787         g_object_unref (model);
1788         g_object_unref (filter_model);
1789         g_object_unref (sortable);
1790
1791         /* Force a reselection of the INBOX next time the widget is shown */
1792         priv->reselect = TRUE;
1793
1794         return TRUE;
1795 }
1796
1797
1798 static void
1799 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1800 {
1801         GtkTreeModel *model = NULL;
1802         TnyFolderStore *folder = NULL;
1803         GtkTreeIter iter;
1804         ModestFolderView *tree_view = NULL;
1805         ModestFolderViewPrivate *priv = NULL;
1806         gboolean selected = FALSE;
1807
1808         g_return_if_fail (sel);
1809         g_return_if_fail (user_data);
1810
1811         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1812
1813         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1814
1815         tree_view = MODEST_FOLDER_VIEW (user_data);
1816
1817         if (selected) {
1818                 gtk_tree_model_get (model, &iter,
1819                                     INSTANCE_COLUMN, &folder,
1820                                     -1);
1821
1822                 /* If the folder is the same do not notify */
1823                 if (folder && priv->cur_folder_store == folder) {
1824                         g_object_unref (folder);
1825                         return;
1826                 }
1827         }
1828
1829         /* Current folder was unselected */
1830         if (priv->cur_folder_store) {
1831                 /* We must do this firstly because a libtinymail-camel
1832                    implementation detail. If we issue the signal
1833                    before doing the sync_async, then that signal could
1834                    cause (and it actually does it) a free of the
1835                    summary of the folder (because the main window will
1836                    clear the headers view */
1837                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1838                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1839                                                FALSE, NULL, NULL, NULL);
1840
1841                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1842                        priv->cur_folder_store, FALSE);
1843
1844                 g_object_unref (priv->cur_folder_store);
1845                 priv->cur_folder_store = NULL;
1846         }
1847
1848         /* New current references */
1849         priv->cur_folder_store = folder;
1850
1851         /* New folder has been selected. Do not notify if there is
1852            nothing new selected */
1853         if (selected) {
1854                 g_signal_emit (G_OBJECT(tree_view),
1855                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1856                                0, priv->cur_folder_store, TRUE);
1857         }
1858 }
1859
1860 static void
1861 on_row_activated (GtkTreeView *treeview,
1862                   GtkTreePath *treepath,
1863                   GtkTreeViewColumn *column,
1864                   gpointer user_data)
1865 {
1866         GtkTreeModel *model = NULL;
1867         TnyFolderStore *folder = NULL;
1868         GtkTreeIter iter;
1869         ModestFolderView *self = NULL;
1870         ModestFolderViewPrivate *priv = NULL;
1871
1872         g_return_if_fail (treeview);
1873         g_return_if_fail (user_data);
1874
1875         self = MODEST_FOLDER_VIEW (user_data);
1876         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1877
1878         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1879
1880         if (!gtk_tree_model_get_iter (model, &iter, treepath))
1881                 return;
1882
1883         gtk_tree_model_get (model, &iter,
1884                             INSTANCE_COLUMN, &folder,
1885                             -1);
1886
1887         g_signal_emit (G_OBJECT(self),
1888                        signals[FOLDER_ACTIVATED_SIGNAL],
1889                        0, folder);
1890
1891 #ifdef MODEST_TOOLKIT_HILDON2
1892         HildonUIMode ui_mode;
1893         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
1894         if (ui_mode == HILDON_UI_MODE_NORMAL) {
1895                 if (priv->cur_folder_store)
1896                         g_object_unref (priv->cur_folder_store);
1897                 priv->cur_folder_store = g_object_ref (folder);
1898         }
1899 #endif
1900
1901         g_object_unref (folder);
1902 }
1903
1904 TnyFolderStore *
1905 modest_folder_view_get_selected (ModestFolderView *self)
1906 {
1907         ModestFolderViewPrivate *priv;
1908
1909         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1910
1911         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1912         if (priv->cur_folder_store)
1913                 g_object_ref (priv->cur_folder_store);
1914
1915         return priv->cur_folder_store;
1916 }
1917
1918 static gint
1919 get_cmp_rows_type_pos (GObject *folder)
1920 {
1921         /* Remote accounts -> Local account -> MMC account .*/
1922         /* 0, 1, 2 */
1923
1924         if (TNY_IS_ACCOUNT (folder) &&
1925                 modest_tny_account_is_virtual_local_folders (
1926                         TNY_ACCOUNT (folder))) {
1927                 return 1;
1928         } else if (TNY_IS_ACCOUNT (folder)) {
1929                 TnyAccount *account = TNY_ACCOUNT (folder);
1930                 const gchar *account_id = tny_account_get_id (account);
1931                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1932                         return 2;
1933                 else
1934                         return 0;
1935         }
1936         else {
1937                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1938                 return -1; /* Should never happen */
1939         }
1940 }
1941
1942 static gint
1943 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
1944 {
1945         TnyAccount *account;
1946         gboolean is_special;
1947         /* Inbox, Outbox, Drafts, Sent, User */
1948         /* 0, 1, 2, 3, 4 */
1949
1950         if (!TNY_IS_FOLDER (folder_store))
1951                 return 4;
1952         switch (t) {
1953         case TNY_FOLDER_TYPE_INBOX:
1954         {
1955                 account = tny_folder_get_account (folder_store);
1956                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
1957                 g_object_unref (account);
1958                 return is_special?0:4;
1959         }
1960         break;
1961         case TNY_FOLDER_TYPE_OUTBOX:
1962                 return 2;
1963                 break;
1964         case TNY_FOLDER_TYPE_DRAFTS:
1965         {
1966                 account = tny_folder_get_account (folder_store);
1967                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1968                 g_object_unref (account);
1969                 return is_special?1:4;
1970         }
1971         break;
1972         case TNY_FOLDER_TYPE_SENT:
1973         {
1974                 account = tny_folder_get_account (folder_store);
1975                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
1976                 g_object_unref (account);
1977                 return is_special?3:4;
1978         }
1979         break;
1980         default:
1981                 return 4;
1982         }
1983 }
1984
1985 static gint
1986 compare_account_names (TnyAccount *a1, TnyAccount *a2)
1987 {
1988         const gchar *a1_name, *a2_name;
1989
1990         a1_name = tny_account_get_name (a1);
1991         a2_name = tny_account_get_name (a2);
1992
1993         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
1994 }
1995
1996 static gint
1997 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
1998 {
1999         TnyAccount *a1, *a2;
2000         gint cmp;
2001
2002         if (TNY_IS_ACCOUNT (s1)) {
2003                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2004         } else {
2005                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2006         }
2007
2008         if (TNY_IS_ACCOUNT (s2)) {
2009                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2010         } else {
2011                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2012         }
2013
2014         if (a1 == a2) {
2015                 cmp = 0;
2016                 goto finish;
2017         }
2018         /* First we sort with the type of account */
2019         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2020         if (cmp != 0)
2021                 goto finish;
2022
2023         cmp = compare_account_names (a1, a2);
2024
2025 finish:
2026         g_object_unref (a1);
2027         g_object_unref (a2);
2028
2029         return cmp;
2030 }
2031
2032 static gint
2033 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2034 {
2035         gint is_account1, is_account2;
2036
2037         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2038         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2039
2040         return is_account2 - is_account1;
2041 }
2042
2043 /*
2044  * This function orders the mail accounts according to these rules:
2045  * 1st - remote accounts
2046  * 2nd - local account
2047  * 3rd - MMC account
2048  */
2049 static gint
2050 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2051           gpointer user_data)
2052 {
2053         gint cmp = 0;
2054         gchar *name1 = NULL;
2055         gchar *name2 = NULL;
2056         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2057         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2058         GObject *folder1 = NULL;
2059         GObject *folder2 = NULL;
2060
2061         gtk_tree_model_get (tree_model, iter1,
2062                             NAME_COLUMN, &name1,
2063                             TYPE_COLUMN, &type,
2064                             INSTANCE_COLUMN, &folder1,
2065                             -1);
2066         gtk_tree_model_get (tree_model, iter2,
2067                             NAME_COLUMN, &name2,
2068                             TYPE_COLUMN, &type2,
2069                             INSTANCE_COLUMN, &folder2,
2070                             -1);
2071
2072         /* Return if we get no folder. This could happen when folder
2073            operations are happening. The model is updated after the
2074            folder copy/move actually occurs, so there could be
2075            situations where the model to be drawn is not correct */
2076         if (!folder1 || !folder2)
2077                 goto finish;
2078
2079         /* Sort by type. First the special folders, then the archives */
2080         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2081         if (cmp != 0)
2082                 goto finish;
2083
2084         /* Now we sort using the account of each folder */
2085         cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2086         if (cmp != 0)
2087                 goto finish;
2088
2089         /* Each group is preceeded by its account */
2090         cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2091         if (cmp != 0)
2092                 goto finish;
2093
2094         /* Pure sort by name */
2095         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2096  finish:
2097         if (folder1)
2098                 g_object_unref(G_OBJECT(folder1));
2099         if (folder2)
2100                 g_object_unref(G_OBJECT(folder2));
2101
2102         g_free (name1);
2103         g_free (name2);
2104
2105         return cmp;
2106 }
2107
2108 /*****************************************************************************/
2109 /*                        DRAG and DROP stuff                                */
2110 /*****************************************************************************/
2111 /*
2112  * This function fills the #GtkSelectionData with the row and the
2113  * model that has been dragged. It's called when this widget is a
2114  * source for dnd after the event drop happened
2115  */
2116 static void
2117 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2118                   guint info, guint time, gpointer data)
2119 {
2120         GtkTreeSelection *selection;
2121         GtkTreeModel *model;
2122         GtkTreeIter iter;
2123         GtkTreePath *source_row;
2124
2125         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2126         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2127
2128                 source_row = gtk_tree_model_get_path (model, &iter);
2129                 gtk_tree_set_row_drag_data (selection_data,
2130                                             model,
2131                                             source_row);
2132
2133                 gtk_tree_path_free (source_row);
2134         }
2135 }
2136
2137 typedef struct _DndHelper {
2138         ModestFolderView *folder_view;
2139         gboolean delete_source;
2140         GtkTreePath *source_row;
2141 } DndHelper;
2142
2143 static void
2144 dnd_helper_destroyer (DndHelper *helper)
2145 {
2146         /* Free the helper */
2147         gtk_tree_path_free (helper->source_row);
2148         g_slice_free (DndHelper, helper);
2149 }
2150
2151 static void
2152 xfer_folder_cb (ModestMailOperation *mail_op,
2153                 TnyFolder *new_folder,
2154                 gpointer user_data)
2155 {
2156         if (new_folder) {
2157                 /* Select the folder */
2158                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2159                                                   new_folder, FALSE);
2160         }
2161 }
2162
2163
2164 /* get the folder for the row the treepath refers to. */
2165 /* folder must be unref'd */
2166 static TnyFolderStore *
2167 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2168 {
2169         GtkTreeIter iter;
2170         TnyFolderStore *folder = NULL;
2171
2172         if (gtk_tree_model_get_iter (model,&iter, path))
2173                 gtk_tree_model_get (model, &iter,
2174                                     INSTANCE_COLUMN, &folder,
2175                                     -1);
2176         return folder;
2177 }
2178
2179
2180 /*
2181  * This function is used by drag_data_received_cb to manage drag and
2182  * drop of a header, i.e, and drag from the header view to the folder
2183  * view.
2184  */
2185 static void
2186 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2187                                 GtkTreeModel *dest_model,
2188                                 GtkTreePath  *dest_row,
2189                                 GtkSelectionData *selection_data)
2190 {
2191         TnyList *headers = NULL;
2192         TnyFolder *folder = NULL, *src_folder = NULL;
2193         TnyFolderType folder_type;
2194         GtkTreeIter source_iter, dest_iter;
2195         ModestWindowMgr *mgr = NULL;
2196         ModestWindow *main_win = NULL;
2197         gchar **uris, **tmp;
2198
2199         /* Build the list of headers */
2200         mgr = modest_runtime_get_window_mgr ();
2201         headers = tny_simple_list_new ();
2202         uris = modest_dnd_selection_data_get_paths (selection_data);
2203         tmp = uris;
2204
2205         while (*tmp != NULL) {
2206                 TnyHeader *header;
2207                 GtkTreePath *path;
2208                 gboolean first = TRUE;
2209
2210                 /* Get header */
2211                 path = gtk_tree_path_new_from_string (*tmp);
2212                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2213                 gtk_tree_model_get (source_model, &source_iter,
2214                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2215                                     &header, -1);
2216
2217                 /* Do not enable d&d of headers already opened */
2218                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2219                         tny_list_append (headers, G_OBJECT (header));
2220
2221                 if (G_UNLIKELY (first)) {
2222                         src_folder = tny_header_get_folder (header);
2223                         first = FALSE;
2224                 }
2225
2226                 /* Free and go on */
2227                 gtk_tree_path_free (path);
2228                 g_object_unref (header);
2229                 tmp++;
2230         }
2231         g_strfreev (uris);
2232
2233         /* This could happen ig we perform a d&d very quickly over the
2234            same row that row could dissapear because message is
2235            transferred */
2236         if (!TNY_IS_FOLDER (src_folder))
2237                 goto cleanup;
2238
2239         /* Get the target folder */
2240         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2241         gtk_tree_model_get (dest_model, &dest_iter,
2242                             INSTANCE_COLUMN,
2243                             &folder, -1);
2244
2245         if (!folder || !TNY_IS_FOLDER(folder)) {
2246 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2247                 goto cleanup;
2248         }
2249
2250         folder_type = modest_tny_folder_guess_folder_type (folder);
2251         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2252 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2253                 goto cleanup;  /* cannot move messages there */
2254         }
2255
2256         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2257 /*              g_warning ("folder not writable"); */
2258                 goto cleanup; /* verboten! */
2259         }
2260
2261         /* Ask for confirmation to move */
2262         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2263         if (!main_win) {
2264                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2265                 goto cleanup;
2266         }
2267
2268         /* Transfer messages */
2269         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2270                                                     headers, folder);
2271
2272         /* Frees */
2273 cleanup:
2274         if (G_IS_OBJECT (src_folder))
2275                 g_object_unref (src_folder);
2276         if (G_IS_OBJECT(folder))
2277                 g_object_unref (G_OBJECT (folder));
2278         if (G_IS_OBJECT(headers))
2279                 g_object_unref (headers);
2280 }
2281
2282 typedef struct {
2283         TnyFolderStore *src_folder;
2284         TnyFolderStore *dst_folder;
2285         ModestFolderView *folder_view;
2286         DndHelper *helper;
2287 } DndFolderInfo;
2288
2289 static void
2290 dnd_folder_info_destroyer (DndFolderInfo *info)
2291 {
2292         if (info->src_folder)
2293                 g_object_unref (info->src_folder);
2294         if (info->dst_folder)
2295                 g_object_unref (info->dst_folder);
2296         g_slice_free (DndFolderInfo, info);
2297 }
2298
2299 static void
2300 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2301                                     GtkWindow *parent_window,
2302                                     TnyAccount *account)
2303 {
2304         /* Show error */
2305         modest_ui_actions_on_account_connection_error (parent_window, account);
2306
2307         /* Free the helper & info */
2308         dnd_helper_destroyer (info->helper);
2309         dnd_folder_info_destroyer (info);
2310 }
2311
2312 static void
2313 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2314                                                      GError *err,
2315                                                      GtkWindow *parent_window,
2316                                                      TnyAccount *account,
2317                                                      gpointer user_data)
2318 {
2319         DndFolderInfo *info = NULL;
2320         ModestMailOperation *mail_op;
2321
2322         info = (DndFolderInfo *) user_data;
2323
2324         if (err || canceled) {
2325                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2326                 return;
2327         }
2328
2329         /* Do the mail operation */
2330         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2331                                                                  modest_ui_actions_move_folder_error_handler,
2332                                                                  info->src_folder, NULL);
2333
2334         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2335                                          mail_op);
2336
2337         /* Transfer the folder */
2338         modest_mail_operation_xfer_folder (mail_op,
2339                                            TNY_FOLDER (info->src_folder),
2340                                            info->dst_folder,
2341                                            info->helper->delete_source,
2342                                            xfer_folder_cb,
2343                                            info->helper->folder_view);
2344
2345         /* Frees */
2346         g_object_unref (G_OBJECT (mail_op));
2347         dnd_helper_destroyer (info->helper);
2348         dnd_folder_info_destroyer (info);
2349 }
2350
2351
2352 static void
2353 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2354                                                      GError *err,
2355                                                      GtkWindow *parent_window,
2356                                                      TnyAccount *account,
2357                                                      gpointer user_data)
2358 {
2359         DndFolderInfo *info = NULL;
2360
2361         info = (DndFolderInfo *) user_data;
2362
2363         if (err || canceled) {
2364                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2365                 return;
2366         }
2367
2368         /* Connect to source folder and perform the copy/move */
2369         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2370                                                        info->src_folder,
2371                                                        drag_and_drop_from_folder_view_src_folder_performer,
2372                                                        info);
2373 }
2374
2375 /*
2376  * This function is used by drag_data_received_cb to manage drag and
2377  * drop of a folder, i.e, and drag from the folder view to the same
2378  * folder view.
2379  */
2380 static void
2381 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2382                                 GtkTreeModel     *dest_model,
2383                                 GtkTreePath      *dest_row,
2384                                 GtkSelectionData *selection_data,
2385                                 DndHelper        *helper)
2386 {
2387         GtkTreeIter dest_iter, iter;
2388         TnyFolderStore *dest_folder = NULL;
2389         TnyFolderStore *folder = NULL;
2390         gboolean forbidden = FALSE;
2391         ModestWindow *win;
2392         DndFolderInfo *info = NULL;
2393
2394         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2395         if (!win) {
2396                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2397                 dnd_helper_destroyer (helper);
2398                 return;
2399         }
2400
2401         if (!forbidden) {
2402                 /* check the folder rules for the destination */
2403                 folder = tree_path_to_folder (dest_model, dest_row);
2404                 if (TNY_IS_FOLDER(folder)) {
2405                         ModestTnyFolderRules rules =
2406                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2407                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2408                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2409                         /* enable local root as destination for folders */
2410                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2411                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2412                                 forbidden = TRUE;
2413                 }
2414                 g_object_unref (folder);
2415         }
2416         if (!forbidden) {
2417                 /* check the folder rules for the source */
2418                 folder = tree_path_to_folder (source_model, helper->source_row);
2419                 if (TNY_IS_FOLDER(folder)) {
2420                         ModestTnyFolderRules rules =
2421                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2422                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2423                 } else
2424                         forbidden = TRUE;
2425                 g_object_unref (folder);
2426         }
2427
2428
2429         /* Check if the drag is possible */
2430         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2431                 /* Show error */
2432                 modest_platform_run_information_dialog ((GtkWindow *) win, 
2433                                                         _("mail_in_ui_folder_move_target_error"), 
2434                                                         FALSE);
2435                 /* Restore the previous selection */
2436                 folder = tree_path_to_folder (source_model, helper->source_row);
2437                 if (folder) {
2438                         if (TNY_IS_FOLDER (folder))
2439                                 modest_folder_view_select_folder (helper->folder_view, 
2440                                                                   TNY_FOLDER (folder), FALSE);
2441                         g_object_unref (folder);
2442                 }
2443                 dnd_helper_destroyer (helper);
2444                 return;
2445         }
2446
2447         /* Get data */
2448         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2449         gtk_tree_model_get (dest_model, &dest_iter,
2450                             INSTANCE_COLUMN,
2451                             &dest_folder, -1);
2452         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2453         gtk_tree_model_get (source_model, &iter,
2454                             INSTANCE_COLUMN,
2455                             &folder, -1);
2456
2457         /* Create the info for the performer */
2458         info = g_slice_new0 (DndFolderInfo);
2459         info->src_folder = g_object_ref (folder);
2460         info->dst_folder = g_object_ref (dest_folder);
2461         info->helper = helper;
2462
2463         /* Connect to the destination folder and perform the copy/move */
2464         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2465                                                        dest_folder,
2466                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2467                                                        info);
2468
2469         /* Frees */
2470         g_object_unref (dest_folder);
2471         g_object_unref (folder);
2472 }
2473
2474 /*
2475  * This function receives the data set by the "drag-data-get" signal
2476  * handler. This information comes within the #GtkSelectionData. This
2477  * function will manage both the drags of folders of the treeview and
2478  * drags of headers of the header view widget.
2479  */
2480 static void
2481 on_drag_data_received (GtkWidget *widget,
2482                        GdkDragContext *context,
2483                        gint x,
2484                        gint y,
2485                        GtkSelectionData *selection_data,
2486                        guint target_type,
2487                        guint time,
2488                        gpointer data)
2489 {
2490         GtkWidget *source_widget;
2491         GtkTreeModel *dest_model, *source_model;
2492         GtkTreePath *source_row, *dest_row;
2493         GtkTreeViewDropPosition pos;
2494         gboolean delete_source = FALSE;
2495         gboolean success = FALSE;
2496
2497         /* Do not allow further process */
2498         g_signal_stop_emission_by_name (widget, "drag-data-received");
2499         source_widget = gtk_drag_get_source_widget (context);
2500
2501         /* Get the action */
2502         if (context->action == GDK_ACTION_MOVE) {
2503                 delete_source = TRUE;
2504
2505                 /* Notify that there is no folder selected. We need to
2506                    do this in order to update the headers view (and
2507                    its monitors, because when moving, the old folder
2508                    won't longer exist. We can not wait for the end of
2509                    the operation, because the operation won't start if
2510                    the folder is in use */
2511                 if (source_widget == widget) {
2512                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2513                         gtk_tree_selection_unselect_all (sel);
2514                 }
2515         }
2516
2517         /* Check if the get_data failed */
2518         if (selection_data == NULL || selection_data->length < 0)
2519                 goto end;
2520
2521         /* Select the destination model */
2522         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2523
2524         /* Get the path to the destination row. Can not call
2525            gtk_tree_view_get_drag_dest_row() because the source row
2526            is not selected anymore */
2527         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2528                                            &dest_row, &pos);
2529
2530         /* Only allow drops IN other rows */
2531         if (!dest_row ||
2532             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2533             pos == GTK_TREE_VIEW_DROP_AFTER)
2534                 goto end;
2535
2536         success = TRUE;
2537         /* Drags from the header view */
2538         if (source_widget != widget) {
2539                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2540
2541                 drag_and_drop_from_header_view (source_model,
2542                                                 dest_model,
2543                                                 dest_row,
2544                                                 selection_data);
2545         } else {
2546                 DndHelper *helper = NULL;
2547
2548                 /* Get the source model and row */
2549                 gtk_tree_get_row_drag_data (selection_data,
2550                                             &source_model,
2551                                             &source_row);
2552
2553                 /* Create the helper */
2554                 helper = g_slice_new0 (DndHelper);
2555                 helper->delete_source = delete_source;
2556                 helper->source_row = gtk_tree_path_copy (source_row);
2557                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2558
2559                 drag_and_drop_from_folder_view (source_model,
2560                                                 dest_model,
2561                                                 dest_row,
2562                                                 selection_data,
2563                                                 helper);
2564
2565                 gtk_tree_path_free (source_row);
2566         }
2567
2568         /* Frees */
2569         gtk_tree_path_free (dest_row);
2570
2571  end:
2572         /* Finish the drag and drop */
2573         gtk_drag_finish (context, success, FALSE, time);
2574 }
2575
2576 /*
2577  * We define a "drag-drop" signal handler because we do not want to
2578  * use the default one, because the default one always calls
2579  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2580  * signal handler, because there we have all the information available
2581  * to know if the dnd was a success or not.
2582  */
2583 static gboolean
2584 drag_drop_cb (GtkWidget      *widget,
2585               GdkDragContext *context,
2586               gint            x,
2587               gint            y,
2588               guint           time,
2589               gpointer        user_data)
2590 {
2591         gpointer target;
2592
2593         if (!context->targets)
2594                 return FALSE;
2595
2596         /* Check if we're dragging a folder row */
2597         target = gtk_drag_dest_find_target (widget, context, NULL);
2598
2599         /* Request the data from the source. */
2600         gtk_drag_get_data(widget, context, target, time);
2601
2602     return TRUE;
2603 }
2604
2605 /*
2606  * This function expands a node of a tree view if it's not expanded
2607  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2608  * does that, so that's why they're here.
2609  */
2610 static gint
2611 expand_row_timeout (gpointer data)
2612 {
2613         GtkTreeView *tree_view = data;
2614         GtkTreePath *dest_path = NULL;
2615         GtkTreeViewDropPosition pos;
2616         gboolean result = FALSE;
2617
2618         gdk_threads_enter ();
2619
2620         gtk_tree_view_get_drag_dest_row (tree_view,
2621                                          &dest_path,
2622                                          &pos);
2623
2624         if (dest_path &&
2625             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2626              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2627                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2628                 gtk_tree_path_free (dest_path);
2629         }
2630         else {
2631                 if (dest_path)
2632                         gtk_tree_path_free (dest_path);
2633
2634                 result = TRUE;
2635         }
2636
2637         gdk_threads_leave ();
2638
2639         return result;
2640 }
2641
2642 /*
2643  * This function is called whenever the pointer is moved over a widget
2644  * while dragging some data. It installs a timeout that will expand a
2645  * node of the treeview if not expanded yet. This function also calls
2646  * gdk_drag_status in order to set the suggested action that will be
2647  * used by the "drag-data-received" signal handler to know if we
2648  * should do a move or just a copy of the data.
2649  */
2650 static gboolean
2651 on_drag_motion (GtkWidget      *widget,
2652                 GdkDragContext *context,
2653                 gint            x,
2654                 gint            y,
2655                 guint           time,
2656                 gpointer        user_data)
2657 {
2658         GtkTreeViewDropPosition pos;
2659         GtkTreePath *dest_row;
2660         GtkTreeModel *dest_model;
2661         ModestFolderViewPrivate *priv;
2662         GdkDragAction suggested_action;
2663         gboolean valid_location = FALSE;
2664         TnyFolderStore *folder = NULL;
2665
2666         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2667
2668         if (priv->timer_expander != 0) {
2669                 g_source_remove (priv->timer_expander);
2670                 priv->timer_expander = 0;
2671         }
2672
2673         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2674                                            x, y,
2675                                            &dest_row,
2676                                            &pos);
2677
2678         /* Do not allow drops between folders */
2679         if (!dest_row ||
2680             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2681             pos == GTK_TREE_VIEW_DROP_AFTER) {
2682                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2683                 gdk_drag_status(context, 0, time);
2684                 valid_location = FALSE;
2685                 goto out;
2686         } else {
2687                 valid_location = TRUE;
2688         }
2689
2690         /* Check that the destination folder is writable */
2691         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2692         folder = tree_path_to_folder (dest_model, dest_row);
2693         if (folder && TNY_IS_FOLDER (folder)) {
2694                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2695
2696                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2697                         valid_location = FALSE;
2698                         goto out;
2699                 }
2700         }
2701
2702         /* Expand the selected row after 1/2 second */
2703         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2704                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2705         }
2706         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2707
2708         /* Select the desired action. By default we pick MOVE */
2709         suggested_action = GDK_ACTION_MOVE;
2710
2711         if (context->actions == GDK_ACTION_COPY)
2712             gdk_drag_status(context, GDK_ACTION_COPY, time);
2713         else if (context->actions == GDK_ACTION_MOVE)
2714             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2715         else if (context->actions & suggested_action)
2716             gdk_drag_status(context, suggested_action, time);
2717         else
2718             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2719
2720  out:
2721         if (folder)
2722                 g_object_unref (folder);
2723         if (dest_row) {
2724                 gtk_tree_path_free (dest_row);
2725         }
2726         g_signal_stop_emission_by_name (widget, "drag-motion");
2727
2728         return valid_location;
2729 }
2730
2731 /*
2732  * This function sets the treeview as a source and a target for dnd
2733  * events. It also connects all the requirede signals.
2734  */
2735 static void
2736 setup_drag_and_drop (GtkTreeView *self)
2737 {
2738         /* Set up the folder view as a dnd destination. Set only the
2739            highlight flag, otherwise gtk will have a different
2740            behaviour */
2741 #ifdef MODEST_TOOLKIT_HILDON2
2742         return;
2743 #endif
2744         gtk_drag_dest_set (GTK_WIDGET (self),
2745                            GTK_DEST_DEFAULT_HIGHLIGHT,
2746                            folder_view_drag_types,
2747                            G_N_ELEMENTS (folder_view_drag_types),
2748                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2749
2750         g_signal_connect (G_OBJECT (self),
2751                           "drag_data_received",
2752                           G_CALLBACK (on_drag_data_received),
2753                           NULL);
2754
2755
2756         /* Set up the treeview as a dnd source */
2757         gtk_drag_source_set (GTK_WIDGET (self),
2758                              GDK_BUTTON1_MASK,
2759                              folder_view_drag_types,
2760                              G_N_ELEMENTS (folder_view_drag_types),
2761                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2762
2763         g_signal_connect (G_OBJECT (self),
2764                           "drag_motion",
2765                           G_CALLBACK (on_drag_motion),
2766                           NULL);
2767
2768         g_signal_connect (G_OBJECT (self),
2769                           "drag_data_get",
2770                           G_CALLBACK (on_drag_data_get),
2771                           NULL);
2772
2773         g_signal_connect (G_OBJECT (self),
2774                           "drag_drop",
2775                           G_CALLBACK (drag_drop_cb),
2776                           NULL);
2777 }
2778
2779 /*
2780  * This function manages the navigation through the folders using the
2781  * keyboard or the hardware keys in the device
2782  */
2783 static gboolean
2784 on_key_pressed (GtkWidget *self,
2785                 GdkEventKey *event,
2786                 gpointer user_data)
2787 {
2788         GtkTreeSelection *selection;
2789         GtkTreeIter iter;
2790         GtkTreeModel *model;
2791         gboolean retval = FALSE;
2792
2793         /* Up and Down are automatically managed by the treeview */
2794         if (event->keyval == GDK_Return) {
2795                 /* Expand/Collapse the selected row */
2796                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2797                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2798                         GtkTreePath *path;
2799
2800                         path = gtk_tree_model_get_path (model, &iter);
2801
2802                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2803                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2804                         else
2805                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2806                         gtk_tree_path_free (path);
2807                 }
2808                 /* No further processing */
2809                 retval = TRUE;
2810         }
2811
2812         return retval;
2813 }
2814
2815 /*
2816  * We listen to the changes in the local folder account name key,
2817  * because we want to show the right name in the view. The local
2818  * folder account name corresponds to the device name in the Maemo
2819  * version. We do this because we do not want to query gconf on each
2820  * tree view refresh. It's better to cache it and change whenever
2821  * necessary.
2822  */
2823 static void
2824 on_configuration_key_changed (ModestConf* conf,
2825                               const gchar *key,
2826                               ModestConfEvent event,
2827                               ModestConfNotificationId id,
2828                               ModestFolderView *self)
2829 {
2830         ModestFolderViewPrivate *priv;
2831
2832
2833         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2834         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2835
2836         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2837                 g_free (priv->local_account_name);
2838
2839                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2840                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2841                 else
2842                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2843                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2844
2845                 /* Force a redraw */
2846 #if GTK_CHECK_VERSION(2, 8, 0)
2847                 GtkTreeViewColumn * tree_column;
2848
2849                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
2850                                                         NAME_COLUMN);
2851                 gtk_tree_view_column_queue_resize (tree_column);
2852 #else
2853                 gtk_widget_queue_draw (GTK_WIDGET (self));
2854 #endif
2855         }
2856 }
2857
2858 void
2859 modest_folder_view_set_style (ModestFolderView *self,
2860                               ModestFolderViewStyle style)
2861 {
2862         ModestFolderViewPrivate *priv;
2863
2864         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2865         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2866                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2867
2868         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2869
2870
2871         priv->style = style;
2872 }
2873
2874 void
2875 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2876                                                              const gchar *account_id)
2877 {
2878         ModestFolderViewPrivate *priv;
2879         GtkTreeModel *model;
2880
2881         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2882
2883         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2884
2885         /* This will be used by the filter_row callback,
2886          * to decided which rows to show: */
2887         if (priv->visible_account_id) {
2888                 g_free (priv->visible_account_id);
2889                 priv->visible_account_id = NULL;
2890         }
2891         if (account_id)
2892                 priv->visible_account_id = g_strdup (account_id);
2893
2894         /* Refilter */
2895         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2896         if (GTK_IS_TREE_MODEL_FILTER (model))
2897                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2898
2899         /* Save settings to gconf */
2900         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2901                                    MODEST_CONF_FOLDER_VIEW_KEY);
2902 }
2903
2904 const gchar *
2905 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2906 {
2907         ModestFolderViewPrivate *priv;
2908
2909         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2910
2911         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2912
2913         return (const gchar *) priv->visible_account_id;
2914 }
2915
2916 static gboolean
2917 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2918 {
2919         do {
2920                 GtkTreeIter child;
2921                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2922
2923                 gtk_tree_model_get (model, iter,
2924                                     TYPE_COLUMN,
2925                                     &type, -1);
2926
2927                 gboolean result = FALSE;
2928                 if (type == TNY_FOLDER_TYPE_INBOX) {
2929                         result = TRUE;
2930                 }
2931                 if (result) {
2932                         *inbox_iter = *iter;
2933                         return TRUE;
2934                 }
2935
2936                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2937                         if (find_inbox_iter (model, &child, inbox_iter))
2938                                 return TRUE;
2939                 }
2940
2941         } while (gtk_tree_model_iter_next (model, iter));
2942
2943         return FALSE;
2944 }
2945
2946
2947
2948
2949 void
2950 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2951 {
2952         GtkTreeModel *model;
2953         GtkTreeIter iter, inbox_iter;
2954         GtkTreeSelection *sel;
2955         GtkTreePath *path = NULL;
2956
2957         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2958
2959         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2960         if (!model)
2961                 return;
2962
2963         expand_root_items (self);
2964         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2965
2966         if (!gtk_tree_model_get_iter_first (model, &iter)) {
2967                 g_warning ("%s: model is empty", __FUNCTION__);
2968                 return;
2969         }
2970
2971         if (find_inbox_iter (model, &iter, &inbox_iter))
2972                 path = gtk_tree_model_get_path (model, &inbox_iter);
2973         else
2974                 path = gtk_tree_path_new_first ();
2975
2976         /* Select the row and free */
2977         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2978         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2979         gtk_tree_path_free (path);
2980
2981         /* set focus */
2982         gtk_widget_grab_focus (GTK_WIDGET(self));
2983 }
2984
2985
2986 /* recursive */
2987 static gboolean
2988 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
2989                   TnyFolder* folder)
2990 {
2991         do {
2992                 GtkTreeIter child;
2993                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2994                 TnyFolder* a_folder;
2995                 gchar *name = NULL;
2996
2997                 gtk_tree_model_get (model, iter,
2998                                     INSTANCE_COLUMN, &a_folder,
2999                                     NAME_COLUMN, &name,
3000                                     TYPE_COLUMN, &type,
3001                                     -1);
3002                 g_free (name);
3003
3004                 if (folder == a_folder) {
3005                         g_object_unref (a_folder);
3006                         *folder_iter = *iter;
3007                         return TRUE;
3008                 }
3009                 g_object_unref (a_folder);
3010
3011                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3012                         if (find_folder_iter (model, &child, folder_iter, folder))
3013                                 return TRUE;
3014                 }
3015
3016         } while (gtk_tree_model_iter_next (model, iter));
3017
3018         return FALSE;
3019 }
3020
3021 #ifndef MODEST_TOOLKIT_HILDON2
3022 static void
3023 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3024                                      GtkTreePath *path,
3025                                      GtkTreeIter *iter,
3026                                      ModestFolderView *self)
3027 {
3028         ModestFolderViewPrivate *priv = NULL;
3029         GtkTreeSelection *sel;
3030         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3031         GObject *instance = NULL;
3032
3033         if (!MODEST_IS_FOLDER_VIEW(self))
3034                 return;
3035
3036         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3037
3038         priv->reexpand = TRUE;
3039
3040         gtk_tree_model_get (tree_model, iter,
3041                             TYPE_COLUMN, &type,
3042                             INSTANCE_COLUMN, &instance,
3043                             -1);
3044
3045         if (!instance)
3046                 return;
3047
3048         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3049                 priv->folder_to_select = g_object_ref (instance);
3050         }
3051         g_object_unref (instance);
3052
3053         if (priv->folder_to_select) {
3054
3055                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3056                                                        FALSE)) {
3057                         GtkTreePath *path;
3058                         path = gtk_tree_model_get_path (tree_model, iter);
3059                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3060
3061                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3062
3063                         gtk_tree_selection_select_iter (sel, iter);
3064                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3065
3066                         gtk_tree_path_free (path);
3067                 }
3068
3069                 /* Disable next */
3070                 modest_folder_view_disable_next_folder_selection (self);
3071
3072                 /* Refilter the model */
3073                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3074         }
3075 }
3076 #endif
3077
3078 void
3079 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3080 {
3081         ModestFolderViewPrivate *priv;
3082
3083         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3084
3085         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3086
3087         if (priv->folder_to_select)
3088                 g_object_unref(priv->folder_to_select);
3089
3090         priv->folder_to_select = NULL;
3091 }
3092
3093 gboolean
3094 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3095                                   gboolean after_change)
3096 {
3097         GtkTreeModel *model;
3098         GtkTreeIter iter, folder_iter;
3099         GtkTreeSelection *sel;
3100         ModestFolderViewPrivate *priv = NULL;
3101
3102         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3103         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3104
3105         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3106
3107         if (after_change) {
3108                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3109                 gtk_tree_selection_unselect_all (sel);
3110
3111                 if (priv->folder_to_select)
3112                         g_object_unref(priv->folder_to_select);
3113                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3114                 return TRUE;
3115         }
3116
3117         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3118         if (!model)
3119                 return FALSE;
3120
3121
3122         /* Refilter the model, before selecting the folder */
3123         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3124
3125         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3126                 g_warning ("%s: model is empty", __FUNCTION__);
3127                 return FALSE;
3128         }
3129
3130         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3131                 GtkTreePath *path;
3132
3133                 path = gtk_tree_model_get_path (model, &folder_iter);
3134                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3135
3136                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3137                 gtk_tree_selection_select_iter (sel, &folder_iter);
3138                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3139
3140                 gtk_tree_path_free (path);
3141                 return TRUE;
3142         }
3143         return FALSE;
3144 }
3145
3146
3147 void
3148 modest_folder_view_copy_selection (ModestFolderView *self)
3149 {
3150         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3151
3152         /* Copy selection */
3153         _clipboard_set_selected_data (self, FALSE);
3154 }
3155
3156 void
3157 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3158 {
3159         ModestFolderViewPrivate *priv = NULL;
3160         GtkTreeModel *model = NULL;
3161         const gchar **hidding = NULL;
3162         guint i, n_selected;
3163
3164         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3165         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3166
3167         /* Copy selection */
3168         if (!_clipboard_set_selected_data (folder_view, TRUE))
3169                 return;
3170
3171         /* Get hidding ids */
3172         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3173
3174         /* Clear hidding array created by previous cut operation */
3175         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3176
3177         /* Copy hidding array */
3178         priv->n_selected = n_selected;
3179         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3180         for (i=0; i < n_selected; i++)
3181                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3182
3183         /* Hide cut folders */
3184         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3185         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3186 }
3187
3188 void
3189 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3190                                ModestFolderView *folder_view_dst)
3191 {
3192         GtkTreeModel *filter_model = NULL;
3193         GtkTreeModel *model = NULL;
3194         GtkTreeModel *new_filter_model = NULL;
3195
3196         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3197         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3198
3199         /* Get src model*/
3200         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3201         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3202
3203         /* Build new filter model */
3204         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3205         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3206                                                 filter_row,
3207                                                 folder_view_dst,
3208                                                 NULL);
3209         /* Set copied model */
3210         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3211 #ifndef MODEST_TOOLKIT_HILDON2
3212         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3213                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3214 #endif
3215
3216         /* Free */
3217         g_object_unref (new_filter_model);
3218 }
3219
3220 void
3221 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3222                                           gboolean show)
3223 {
3224         GtkTreeModel *model = NULL;
3225         ModestFolderViewPrivate* priv;
3226
3227         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3228
3229         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3230         priv->show_non_move = show;
3231 /*      modest_folder_view_update_model(folder_view, */
3232 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3233
3234         /* Hide special folders */
3235         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3236         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3237                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3238         }
3239 }
3240
3241 /* Returns FALSE if it did not selected anything */
3242 static gboolean
3243 _clipboard_set_selected_data (ModestFolderView *folder_view,
3244                               gboolean delete)
3245 {
3246         ModestFolderViewPrivate *priv = NULL;
3247         TnyFolderStore *folder = NULL;
3248         gboolean retval = FALSE;
3249
3250         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3251         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3252
3253         /* Set selected data on clipboard   */
3254         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3255         folder = modest_folder_view_get_selected (folder_view);
3256
3257         /* Do not allow to select an account */
3258         if (TNY_IS_FOLDER (folder)) {
3259                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3260                 retval = TRUE;
3261         }
3262
3263         /* Free */
3264         g_object_unref (folder);
3265
3266         return retval;
3267 }
3268
3269 static void
3270 _clear_hidding_filter (ModestFolderView *folder_view)
3271 {
3272         ModestFolderViewPrivate *priv;
3273         guint i;
3274
3275         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3276         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3277
3278         if (priv->hidding_ids != NULL) {
3279                 for (i=0; i < priv->n_selected; i++)
3280                         g_free (priv->hidding_ids[i]);
3281                 g_free(priv->hidding_ids);
3282         }
3283 }
3284
3285
3286 static void
3287 on_display_name_changed (ModestAccountMgr *mgr,
3288                          const gchar *account,
3289                          gpointer user_data)
3290 {
3291         ModestFolderView *self;
3292
3293         self = MODEST_FOLDER_VIEW (user_data);
3294
3295         /* Force a redraw */
3296 #if GTK_CHECK_VERSION(2, 8, 0)
3297         GtkTreeViewColumn * tree_column;
3298
3299         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3300                                                 NAME_COLUMN);
3301         gtk_tree_view_column_queue_resize (tree_column);
3302 #else
3303         gtk_widget_queue_draw (GTK_WIDGET (self));
3304 #endif
3305 }
3306
3307 void 
3308 modest_folder_view_set_cell_style (ModestFolderView *self,
3309                                    ModestFolderViewCellStyle cell_style)
3310 {
3311         ModestFolderViewPrivate *priv = NULL;
3312
3313         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3314         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3315
3316         priv->cell_style = cell_style;
3317
3318         g_object_set (G_OBJECT (priv->messages_renderer),
3319                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3320                       NULL);
3321         
3322         gtk_widget_queue_draw (GTK_WIDGET (self));
3323 }
3324
3325 static void
3326 update_style (ModestFolderView *self)
3327 {
3328         ModestFolderViewPrivate *priv;
3329         GdkColor style_color;
3330
3331         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3332         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3333
3334         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3335                 gdk_color_parse ("grey", &style_color);
3336         }
3337
3338         g_object_set (G_OBJECT (priv->messages_renderer),
3339                       "foreground-gdk", &style_color,
3340                       "foreground-set", TRUE,
3341                       NULL);
3342 }
3343
3344 static void 
3345 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3346 {
3347         if (strcmp ("style", spec->name) == 0) {
3348                 update_style (MODEST_FOLDER_VIEW (obj));
3349                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3350         } 
3351 }
3352
3353 void 
3354 modest_folder_view_set_filter (ModestFolderView *self,
3355                                ModestFolderViewFilter filter)
3356 {
3357         ModestFolderViewPrivate *priv;
3358         GtkTreeModel *filter_model;
3359
3360         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3361         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3362
3363         priv->filter = filter;
3364
3365         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3366         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3367                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3368         }
3369 }