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