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