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