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