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