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