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