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