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