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