Set gtkhtml view background to white and text to black (fixes NB#103326)
[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 (!modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1965                         return FALSE;
1966                 }
1967         }
1968
1969         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1970                 if (TNY_IS_FOLDER (instance)) {
1971                         /* Check folder rules */
1972                         ModestTnyFolderRules rules;
1973
1974                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1975                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
1976                 } else if (TNY_IS_ACCOUNT (instance)) {
1977                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1978                                 retval = FALSE;
1979                         } else {
1980                                 retval = TRUE;
1981                         }
1982                 }
1983         }
1984
1985         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1986                 if (TNY_IS_FOLDER (instance)) {
1987                         TnyFolderType guess_type;
1988
1989                         if (TNY_FOLDER_TYPE_NORMAL) {
1990                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1991                         } else {
1992                                 guess_type = type;
1993                         }
1994
1995                         switch (type) {
1996                         case TNY_FOLDER_TYPE_OUTBOX:
1997                         case TNY_FOLDER_TYPE_SENT:
1998                         case TNY_FOLDER_TYPE_DRAFTS:
1999                         case TNY_FOLDER_TYPE_ARCHIVE:
2000                         case TNY_FOLDER_TYPE_INBOX:
2001                                 retval = FALSE;
2002                                 break;
2003                         case TNY_FOLDER_TYPE_UNKNOWN:
2004                         case TNY_FOLDER_TYPE_NORMAL:
2005                                 break;
2006                         default:
2007                                 break;
2008                         }
2009
2010                 } else if (TNY_IS_ACCOUNT (instance)) {
2011                         retval = FALSE;
2012                 }
2013         }
2014
2015         if (retval && TNY_IS_FOLDER (instance)) {
2016                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2017         }
2018
2019         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2020                 if (TNY_IS_FOLDER (instance)) {
2021                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2022                 } else if (TNY_IS_ACCOUNT (instance)) {
2023                         retval = FALSE;
2024                 }
2025         }
2026
2027         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2028                 if (TNY_IS_FOLDER (instance)) {
2029                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2030                 } else if (TNY_IS_ACCOUNT (instance)) {
2031                         retval = FALSE;
2032                 }
2033         }
2034
2035         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2036                 if (TNY_IS_FOLDER (instance)) {
2037                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2038                 } else if (TNY_IS_ACCOUNT (instance)) {
2039                         retval = FALSE;
2040                 }
2041         }
2042
2043         /* Free */
2044         g_object_unref (instance);
2045
2046         return retval;
2047 }
2048
2049
2050 gboolean
2051 modest_folder_view_update_model (ModestFolderView *self,
2052                                  TnyAccountStore *account_store)
2053 {
2054         ModestFolderViewPrivate *priv;
2055         GtkTreeModel *model /* , *old_model */;
2056         GtkTreeModel *filter_model = NULL, *sortable = NULL;
2057
2058         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2059         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2060                               FALSE);
2061
2062         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2063
2064         /* Notify that there is no folder selected */
2065         g_signal_emit (G_OBJECT(self),
2066                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2067                        NULL, FALSE);
2068         if (priv->cur_folder_store) {
2069                 g_object_unref (priv->cur_folder_store);
2070                 priv->cur_folder_store = NULL;
2071         }
2072
2073         /* FIXME: the local accounts are not shown when the query
2074            selects only the subscribed folders */
2075 #ifdef MODEST_TOOLKIT_HILDON2
2076         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
2077                                                           TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2078         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2079                                                       MODEST_FOLDER_PATH_SEPARATOR);
2080 #else
2081         model = tny_gtk_folder_store_tree_model_new (NULL);
2082 #endif
2083
2084         /* When the model is a list store (plain representation) the
2085            outbox is not a child of any account so we have to manually
2086            delete it because removing the local folders account won't
2087            delete it (because tny_folder_get_account() is not defined
2088            for a merge folder */
2089         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2090                 TnyAccount *account;
2091                 ModestTnyAccountStore *acc_store;
2092
2093                 acc_store = modest_runtime_get_account_store ();
2094                 account = modest_tny_account_store_get_local_folders_account (acc_store);
2095
2096                 if (g_signal_handler_is_connected (account,
2097                                                    priv->outbox_deleted_handler))
2098                         g_signal_handler_disconnect (account,
2099                                                      priv->outbox_deleted_handler);
2100
2101                 priv->outbox_deleted_handler =
2102                         g_signal_connect (account,
2103                                           "outbox-deleted",
2104                                           G_CALLBACK (on_outbox_deleted_cb),
2105                                           self);
2106                 g_object_unref (account);
2107         }
2108
2109         /* Get the accounts: */
2110         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2111                                         TNY_LIST (model),
2112                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2113
2114         sortable = gtk_tree_model_sort_new_with_model (model);
2115         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2116                                               NAME_COLUMN,
2117                                               GTK_SORT_ASCENDING);
2118         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2119                                          NAME_COLUMN,
2120                                          cmp_rows, NULL, NULL);
2121
2122         /* Create filter model */
2123         filter_model = gtk_tree_model_filter_new (sortable, NULL);
2124         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2125                                                 filter_row,
2126                                                 self,
2127                                                 NULL);
2128
2129         /* Set new model */
2130         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2131 #ifndef MODEST_TOOLKIT_HILDON2
2132         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2133                           (GCallback) on_row_inserted_maybe_select_folder, self);
2134 #endif
2135
2136         g_object_unref (model);
2137         g_object_unref (filter_model);
2138         g_object_unref (sortable);
2139
2140         /* Force a reselection of the INBOX next time the widget is shown */
2141         priv->reselect = TRUE;
2142
2143         return TRUE;
2144 }
2145
2146
2147 static void
2148 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2149 {
2150         GtkTreeModel *model = NULL;
2151         TnyFolderStore *folder = NULL;
2152         GtkTreeIter iter;
2153         ModestFolderView *tree_view = NULL;
2154         ModestFolderViewPrivate *priv = NULL;
2155         gboolean selected = FALSE;
2156
2157         g_return_if_fail (sel);
2158         g_return_if_fail (user_data);
2159
2160         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2161
2162         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2163
2164         tree_view = MODEST_FOLDER_VIEW (user_data);
2165
2166         if (selected) {
2167                 gtk_tree_model_get (model, &iter,
2168                                     INSTANCE_COLUMN, &folder,
2169                                     -1);
2170
2171                 /* If the folder is the same do not notify */
2172                 if (folder && priv->cur_folder_store == folder) {
2173                         g_object_unref (folder);
2174                         return;
2175                 }
2176         }
2177
2178         /* Current folder was unselected */
2179         if (priv->cur_folder_store) {
2180                 /* We must do this firstly because a libtinymail-camel
2181                    implementation detail. If we issue the signal
2182                    before doing the sync_async, then that signal could
2183                    cause (and it actually does it) a free of the
2184                    summary of the folder (because the main window will
2185                    clear the headers view */
2186                 if (TNY_IS_FOLDER(priv->cur_folder_store))
2187                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2188                                                FALSE, NULL, NULL, NULL);
2189
2190                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2191                        priv->cur_folder_store, FALSE);
2192
2193                 g_object_unref (priv->cur_folder_store);
2194                 priv->cur_folder_store = NULL;
2195         }
2196
2197         /* New current references */
2198         priv->cur_folder_store = folder;
2199
2200         /* New folder has been selected. Do not notify if there is
2201            nothing new selected */
2202         if (selected) {
2203                 g_signal_emit (G_OBJECT(tree_view),
2204                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2205                                0, priv->cur_folder_store, TRUE);
2206         }
2207 }
2208
2209 static void
2210 on_row_activated (GtkTreeView *treeview,
2211                   GtkTreePath *treepath,
2212                   GtkTreeViewColumn *column,
2213                   gpointer user_data)
2214 {
2215         GtkTreeModel *model = NULL;
2216         TnyFolderStore *folder = NULL;
2217         GtkTreeIter iter;
2218         ModestFolderView *self = NULL;
2219         ModestFolderViewPrivate *priv = NULL;
2220
2221         g_return_if_fail (treeview);
2222         g_return_if_fail (user_data);
2223
2224         self = MODEST_FOLDER_VIEW (user_data);
2225         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2226
2227         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2228
2229         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2230                 return;
2231
2232         gtk_tree_model_get (model, &iter,
2233                             INSTANCE_COLUMN, &folder,
2234                             -1);
2235
2236         g_signal_emit (G_OBJECT(self),
2237                        signals[FOLDER_ACTIVATED_SIGNAL],
2238                        0, folder);
2239
2240 #ifdef MODEST_TOOLKIT_HILDON2
2241         HildonUIMode ui_mode;
2242         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2243         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2244                 if (priv->cur_folder_store)
2245                         g_object_unref (priv->cur_folder_store);
2246                 priv->cur_folder_store = g_object_ref (folder);
2247         }
2248 #endif
2249
2250         g_object_unref (folder);
2251 }
2252
2253 TnyFolderStore *
2254 modest_folder_view_get_selected (ModestFolderView *self)
2255 {
2256         ModestFolderViewPrivate *priv;
2257
2258         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2259
2260         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2261         if (priv->cur_folder_store)
2262                 g_object_ref (priv->cur_folder_store);
2263
2264         return priv->cur_folder_store;
2265 }
2266
2267 static gint
2268 get_cmp_rows_type_pos (GObject *folder)
2269 {
2270         /* Remote accounts -> Local account -> MMC account .*/
2271         /* 0, 1, 2 */
2272
2273         if (TNY_IS_ACCOUNT (folder) &&
2274                 modest_tny_account_is_virtual_local_folders (
2275                         TNY_ACCOUNT (folder))) {
2276                 return 1;
2277         } else if (TNY_IS_ACCOUNT (folder)) {
2278                 TnyAccount *account = TNY_ACCOUNT (folder);
2279                 const gchar *account_id = tny_account_get_id (account);
2280                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2281                         return 2;
2282                 else
2283                         return 0;
2284         }
2285         else {
2286                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2287                 return -1; /* Should never happen */
2288         }
2289 }
2290
2291 static gboolean
2292 inbox_is_special (TnyFolderStore *folder_store)
2293 {
2294         gboolean is_special = TRUE;
2295
2296         if (TNY_IS_FOLDER (folder_store)) {
2297                 const gchar *id;
2298                 gchar *downcase;
2299                 gchar *last_bar;
2300                 gchar *last_inbox_bar;
2301
2302                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2303                 downcase = g_utf8_strdown (id, -1);
2304                 last_bar = g_strrstr (downcase, "/");
2305                 if (last_bar) {
2306                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2307                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2308                                 is_special = FALSE;
2309                 } else {
2310                         is_special = FALSE;
2311                 }
2312                 g_free (downcase);
2313         }
2314         return is_special;
2315 }
2316
2317 static gint
2318 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2319 {
2320         TnyAccount *account;
2321         gboolean is_special;
2322         /* Inbox, Outbox, Drafts, Sent, User */
2323         /* 0, 1, 2, 3, 4 */
2324
2325         if (!TNY_IS_FOLDER (folder_store))
2326                 return 4;
2327         switch (t) {
2328         case TNY_FOLDER_TYPE_INBOX:
2329         {
2330                 account = tny_folder_get_account (folder_store);
2331                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2332
2333                 /* In inbox case we need to know if the inbox is really the top
2334                  * inbox of the account, or if it's a submailbox inbox. To do
2335                  * this we'll apply an heuristic rule: Find last "/" and check
2336                  * if it's preceeded by another Inbox */
2337                 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2338                 g_object_unref (account);
2339                 return is_special?0:4;
2340         }
2341         break;
2342         case TNY_FOLDER_TYPE_OUTBOX:
2343                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2344                 break;
2345         case TNY_FOLDER_TYPE_DRAFTS:
2346         {
2347                 account = tny_folder_get_account (folder_store);
2348                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2349                 g_object_unref (account);
2350                 return is_special?1:4;
2351         }
2352         break;
2353         case TNY_FOLDER_TYPE_SENT:
2354         {
2355                 account = tny_folder_get_account (folder_store);
2356                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2357                 g_object_unref (account);
2358                 return is_special?3:4;
2359         }
2360         break;
2361         default:
2362                 return 4;
2363         }
2364 }
2365
2366 static gint
2367 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2368 {
2369         const gchar *a1_name, *a2_name;
2370
2371         a1_name = tny_account_get_name (a1);
2372         a2_name = tny_account_get_name (a2);
2373
2374         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2375 }
2376
2377 static gint
2378 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2379 {
2380         TnyAccount *a1 = NULL, *a2 = NULL;
2381         gint cmp;
2382
2383         if (TNY_IS_ACCOUNT (s1)) {
2384                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2385         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2386                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2387         }
2388
2389         if (TNY_IS_ACCOUNT (s2)) {
2390                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2391         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2392                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2393         }
2394
2395         if (!a1 || !a2) {
2396                 if (!a1 && !a2)
2397                         cmp = 0;
2398                 else if (!a1)
2399                         cmp = 1;
2400                 else
2401                         cmp = -1;
2402                 goto finish;
2403         }
2404
2405         if (a1 == a2) {
2406                 cmp = 0;
2407                 goto finish;
2408         }
2409         /* First we sort with the type of account */
2410         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2411         if (cmp != 0)
2412                 goto finish;
2413
2414         cmp = compare_account_names (a1, a2);
2415
2416 finish:
2417         if (a1)
2418                 g_object_unref (a1);
2419         if (a2)
2420                 g_object_unref (a2);
2421
2422         return cmp;
2423 }
2424
2425 static gint
2426 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2427 {
2428         gint is_account1, is_account2;
2429
2430         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2431         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2432
2433         return is_account2 - is_account1;
2434 }
2435
2436 /*
2437  * This function orders the mail accounts according to these rules:
2438  * 1st - remote accounts
2439  * 2nd - local account
2440  * 3rd - MMC account
2441  */
2442 static gint
2443 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2444           gpointer user_data)
2445 {
2446         gint cmp = 0;
2447         gchar *name1 = NULL;
2448         gchar *name2 = NULL;
2449         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2450         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2451         GObject *folder1 = NULL;
2452         GObject *folder2 = NULL;
2453
2454         gtk_tree_model_get (tree_model, iter1,
2455                             NAME_COLUMN, &name1,
2456                             TYPE_COLUMN, &type,
2457                             INSTANCE_COLUMN, &folder1,
2458                             -1);
2459         gtk_tree_model_get (tree_model, iter2,
2460                             NAME_COLUMN, &name2,
2461                             TYPE_COLUMN, &type2,
2462                             INSTANCE_COLUMN, &folder2,
2463                             -1);
2464
2465         /* Return if we get no folder. This could happen when folder
2466            operations are happening. The model is updated after the
2467            folder copy/move actually occurs, so there could be
2468            situations where the model to be drawn is not correct */
2469         if (!folder1 || !folder2)
2470                 goto finish;
2471
2472         /* Sort by type. First the special folders, then the archives */
2473         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2474         if (cmp != 0)
2475                 goto finish;
2476
2477         /* Now we sort using the account of each folder */
2478         if (TNY_IS_FOLDER_STORE (folder1) && 
2479             TNY_IS_FOLDER_STORE (folder2)) {
2480                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2481                 if (cmp != 0)
2482                         goto finish;
2483
2484                 /* Each group is preceeded by its account */
2485                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2486                 if (cmp != 0)
2487                         goto finish;
2488         }
2489
2490         /* Pure sort by name */
2491         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2492  finish:
2493         if (folder1)
2494                 g_object_unref(G_OBJECT(folder1));
2495         if (folder2)
2496                 g_object_unref(G_OBJECT(folder2));
2497
2498         g_free (name1);
2499         g_free (name2);
2500
2501         return cmp;
2502 }
2503
2504 /*****************************************************************************/
2505 /*                        DRAG and DROP stuff                                */
2506 /*****************************************************************************/
2507 /*
2508  * This function fills the #GtkSelectionData with the row and the
2509  * model that has been dragged. It's called when this widget is a
2510  * source for dnd after the event drop happened
2511  */
2512 static void
2513 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2514                   guint info, guint time, gpointer data)
2515 {
2516         GtkTreeSelection *selection;
2517         GtkTreeModel *model;
2518         GtkTreeIter iter;
2519         GtkTreePath *source_row;
2520
2521         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2522         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2523
2524                 source_row = gtk_tree_model_get_path (model, &iter);
2525                 gtk_tree_set_row_drag_data (selection_data,
2526                                             model,
2527                                             source_row);
2528
2529                 gtk_tree_path_free (source_row);
2530         }
2531 }
2532
2533 typedef struct _DndHelper {
2534         ModestFolderView *folder_view;
2535         gboolean delete_source;
2536         GtkTreePath *source_row;
2537 } DndHelper;
2538
2539 static void
2540 dnd_helper_destroyer (DndHelper *helper)
2541 {
2542         /* Free the helper */
2543         gtk_tree_path_free (helper->source_row);
2544         g_slice_free (DndHelper, helper);
2545 }
2546
2547 static void
2548 xfer_folder_cb (ModestMailOperation *mail_op,
2549                 TnyFolder *new_folder,
2550                 gpointer user_data)
2551 {
2552         if (new_folder) {
2553                 /* Select the folder */
2554                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2555                                                   new_folder, FALSE);
2556         }
2557 }
2558
2559
2560 /* get the folder for the row the treepath refers to. */
2561 /* folder must be unref'd */
2562 static TnyFolderStore *
2563 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2564 {
2565         GtkTreeIter iter;
2566         TnyFolderStore *folder = NULL;
2567
2568         if (gtk_tree_model_get_iter (model,&iter, path))
2569                 gtk_tree_model_get (model, &iter,
2570                                     INSTANCE_COLUMN, &folder,
2571                                     -1);
2572         return folder;
2573 }
2574
2575
2576 /*
2577  * This function is used by drag_data_received_cb to manage drag and
2578  * drop of a header, i.e, and drag from the header view to the folder
2579  * view.
2580  */
2581 static void
2582 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2583                                 GtkTreeModel *dest_model,
2584                                 GtkTreePath  *dest_row,
2585                                 GtkSelectionData *selection_data)
2586 {
2587         TnyList *headers = NULL;
2588         TnyFolder *folder = NULL, *src_folder = NULL;
2589         TnyFolderType folder_type;
2590         GtkTreeIter source_iter, dest_iter;
2591         ModestWindowMgr *mgr = NULL;
2592         ModestWindow *main_win = NULL;
2593         gchar **uris, **tmp;
2594
2595         /* Build the list of headers */
2596         mgr = modest_runtime_get_window_mgr ();
2597         headers = tny_simple_list_new ();
2598         uris = modest_dnd_selection_data_get_paths (selection_data);
2599         tmp = uris;
2600
2601         while (*tmp != NULL) {
2602                 TnyHeader *header;
2603                 GtkTreePath *path;
2604                 gboolean first = TRUE;
2605
2606                 /* Get header */
2607                 path = gtk_tree_path_new_from_string (*tmp);
2608                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2609                 gtk_tree_model_get (source_model, &source_iter,
2610                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2611                                     &header, -1);
2612
2613                 /* Do not enable d&d of headers already opened */
2614                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2615                         tny_list_append (headers, G_OBJECT (header));
2616
2617                 if (G_UNLIKELY (first)) {
2618                         src_folder = tny_header_get_folder (header);
2619                         first = FALSE;
2620                 }
2621
2622                 /* Free and go on */
2623                 gtk_tree_path_free (path);
2624                 g_object_unref (header);
2625                 tmp++;
2626         }
2627         g_strfreev (uris);
2628
2629         /* This could happen ig we perform a d&d very quickly over the
2630            same row that row could dissapear because message is
2631            transferred */
2632         if (!TNY_IS_FOLDER (src_folder))
2633                 goto cleanup;
2634
2635         /* Get the target folder */
2636         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2637         gtk_tree_model_get (dest_model, &dest_iter,
2638                             INSTANCE_COLUMN,
2639                             &folder, -1);
2640
2641         if (!folder || !TNY_IS_FOLDER(folder)) {
2642 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2643                 goto cleanup;
2644         }
2645
2646         folder_type = modest_tny_folder_guess_folder_type (folder);
2647         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2648 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2649                 goto cleanup;  /* cannot move messages there */
2650         }
2651
2652         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2653 /*              g_warning ("folder not writable"); */
2654                 goto cleanup; /* verboten! */
2655         }
2656
2657         /* Ask for confirmation to move */
2658         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2659         if (!main_win) {
2660                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2661                 goto cleanup;
2662         }
2663
2664         /* Transfer messages */
2665         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2666                                                     headers, folder);
2667
2668         /* Frees */
2669 cleanup:
2670         if (G_IS_OBJECT (src_folder))
2671                 g_object_unref (src_folder);
2672         if (G_IS_OBJECT(folder))
2673                 g_object_unref (G_OBJECT (folder));
2674         if (G_IS_OBJECT(headers))
2675                 g_object_unref (headers);
2676 }
2677
2678 typedef struct {
2679         TnyFolderStore *src_folder;
2680         TnyFolderStore *dst_folder;
2681         ModestFolderView *folder_view;
2682         DndHelper *helper;
2683 } DndFolderInfo;
2684
2685 static void
2686 dnd_folder_info_destroyer (DndFolderInfo *info)
2687 {
2688         if (info->src_folder)
2689                 g_object_unref (info->src_folder);
2690         if (info->dst_folder)
2691                 g_object_unref (info->dst_folder);
2692         g_slice_free (DndFolderInfo, info);
2693 }
2694
2695 static void
2696 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2697                                     GtkWindow *parent_window,
2698                                     TnyAccount *account)
2699 {
2700         /* Show error */
2701         modest_ui_actions_on_account_connection_error (parent_window, account);
2702
2703         /* Free the helper & info */
2704         dnd_helper_destroyer (info->helper);
2705         dnd_folder_info_destroyer (info);
2706 }
2707
2708 static void
2709 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2710                                                      GError *err,
2711                                                      GtkWindow *parent_window,
2712                                                      TnyAccount *account,
2713                                                      gpointer user_data)
2714 {
2715         DndFolderInfo *info = NULL;
2716         ModestMailOperation *mail_op;
2717
2718         info = (DndFolderInfo *) user_data;
2719
2720         if (err || canceled) {
2721                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2722                 return;
2723         }
2724
2725         /* Do the mail operation */
2726         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2727                                                                  modest_ui_actions_move_folder_error_handler,
2728                                                                  info->src_folder, NULL);
2729
2730         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2731                                          mail_op);
2732
2733         /* Transfer the folder */
2734         modest_mail_operation_xfer_folder (mail_op,
2735                                            TNY_FOLDER (info->src_folder),
2736                                            info->dst_folder,
2737                                            info->helper->delete_source,
2738                                            xfer_folder_cb,
2739                                            info->helper->folder_view);
2740
2741         /* Frees */
2742         g_object_unref (G_OBJECT (mail_op));
2743         dnd_helper_destroyer (info->helper);
2744         dnd_folder_info_destroyer (info);
2745 }
2746
2747
2748 static void
2749 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2750                                                      GError *err,
2751                                                      GtkWindow *parent_window,
2752                                                      TnyAccount *account,
2753                                                      gpointer user_data)
2754 {
2755         DndFolderInfo *info = NULL;
2756
2757         info = (DndFolderInfo *) user_data;
2758
2759         if (err || canceled) {
2760                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2761                 return;
2762         }
2763
2764         /* Connect to source folder and perform the copy/move */
2765         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2766                                                        info->src_folder,
2767                                                        drag_and_drop_from_folder_view_src_folder_performer,
2768                                                        info);
2769 }
2770
2771 /*
2772  * This function is used by drag_data_received_cb to manage drag and
2773  * drop of a folder, i.e, and drag from the folder view to the same
2774  * folder view.
2775  */
2776 static void
2777 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2778                                 GtkTreeModel     *dest_model,
2779                                 GtkTreePath      *dest_row,
2780                                 GtkSelectionData *selection_data,
2781                                 DndHelper        *helper)
2782 {
2783         GtkTreeIter dest_iter, iter;
2784         TnyFolderStore *dest_folder = NULL;
2785         TnyFolderStore *folder = NULL;
2786         gboolean forbidden = FALSE;
2787         ModestWindow *win;
2788         DndFolderInfo *info = NULL;
2789
2790         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2791         if (!win) {
2792                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2793                 dnd_helper_destroyer (helper);
2794                 return;
2795         }
2796
2797         if (!forbidden) {
2798                 /* check the folder rules for the destination */
2799                 folder = tree_path_to_folder (dest_model, dest_row);
2800                 if (TNY_IS_FOLDER(folder)) {
2801                         ModestTnyFolderRules rules =
2802                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2803                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2804                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2805                         /* enable local root as destination for folders */
2806                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2807                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2808                                 forbidden = TRUE;
2809                 }
2810                 g_object_unref (folder);
2811         }
2812         if (!forbidden) {
2813                 /* check the folder rules for the source */
2814                 folder = tree_path_to_folder (source_model, helper->source_row);
2815                 if (TNY_IS_FOLDER(folder)) {
2816                         ModestTnyFolderRules rules =
2817                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2818                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2819                 } else
2820                         forbidden = TRUE;
2821                 g_object_unref (folder);
2822         }
2823
2824
2825         /* Check if the drag is possible */
2826         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2827                 /* Show error */
2828                 modest_platform_run_information_dialog ((GtkWindow *) win, 
2829                                                         _("mail_in_ui_folder_move_target_error"), 
2830                                                         FALSE);
2831                 /* Restore the previous selection */
2832                 folder = tree_path_to_folder (source_model, helper->source_row);
2833                 if (folder) {
2834                         if (TNY_IS_FOLDER (folder))
2835                                 modest_folder_view_select_folder (helper->folder_view, 
2836                                                                   TNY_FOLDER (folder), FALSE);
2837                         g_object_unref (folder);
2838                 }
2839                 dnd_helper_destroyer (helper);
2840                 return;
2841         }
2842
2843         /* Get data */
2844         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2845         gtk_tree_model_get (dest_model, &dest_iter,
2846                             INSTANCE_COLUMN,
2847                             &dest_folder, -1);
2848         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2849         gtk_tree_model_get (source_model, &iter,
2850                             INSTANCE_COLUMN,
2851                             &folder, -1);
2852
2853         /* Create the info for the performer */
2854         info = g_slice_new0 (DndFolderInfo);
2855         info->src_folder = g_object_ref (folder);
2856         info->dst_folder = g_object_ref (dest_folder);
2857         info->helper = helper;
2858
2859         /* Connect to the destination folder and perform the copy/move */
2860         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2861                                                        dest_folder,
2862                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2863                                                        info);
2864
2865         /* Frees */
2866         g_object_unref (dest_folder);
2867         g_object_unref (folder);
2868 }
2869
2870 /*
2871  * This function receives the data set by the "drag-data-get" signal
2872  * handler. This information comes within the #GtkSelectionData. This
2873  * function will manage both the drags of folders of the treeview and
2874  * drags of headers of the header view widget.
2875  */
2876 static void
2877 on_drag_data_received (GtkWidget *widget,
2878                        GdkDragContext *context,
2879                        gint x,
2880                        gint y,
2881                        GtkSelectionData *selection_data,
2882                        guint target_type,
2883                        guint time,
2884                        gpointer data)
2885 {
2886         GtkWidget *source_widget;
2887         GtkTreeModel *dest_model, *source_model;
2888         GtkTreePath *source_row, *dest_row;
2889         GtkTreeViewDropPosition pos;
2890         gboolean delete_source = FALSE;
2891         gboolean success = FALSE;
2892
2893         /* Do not allow further process */
2894         g_signal_stop_emission_by_name (widget, "drag-data-received");
2895         source_widget = gtk_drag_get_source_widget (context);
2896
2897         /* Get the action */
2898         if (context->action == GDK_ACTION_MOVE) {
2899                 delete_source = TRUE;
2900
2901                 /* Notify that there is no folder selected. We need to
2902                    do this in order to update the headers view (and
2903                    its monitors, because when moving, the old folder
2904                    won't longer exist. We can not wait for the end of
2905                    the operation, because the operation won't start if
2906                    the folder is in use */
2907                 if (source_widget == widget) {
2908                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2909                         gtk_tree_selection_unselect_all (sel);
2910                 }
2911         }
2912
2913         /* Check if the get_data failed */
2914         if (selection_data == NULL || selection_data->length < 0)
2915                 goto end;
2916
2917         /* Select the destination model */
2918         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2919
2920         /* Get the path to the destination row. Can not call
2921            gtk_tree_view_get_drag_dest_row() because the source row
2922            is not selected anymore */
2923         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2924                                            &dest_row, &pos);
2925
2926         /* Only allow drops IN other rows */
2927         if (!dest_row ||
2928             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2929             pos == GTK_TREE_VIEW_DROP_AFTER)
2930                 goto end;
2931
2932         success = TRUE;
2933         /* Drags from the header view */
2934         if (source_widget != widget) {
2935                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2936
2937                 drag_and_drop_from_header_view (source_model,
2938                                                 dest_model,
2939                                                 dest_row,
2940                                                 selection_data);
2941         } else {
2942                 DndHelper *helper = NULL;
2943
2944                 /* Get the source model and row */
2945                 gtk_tree_get_row_drag_data (selection_data,
2946                                             &source_model,
2947                                             &source_row);
2948
2949                 /* Create the helper */
2950                 helper = g_slice_new0 (DndHelper);
2951                 helper->delete_source = delete_source;
2952                 helper->source_row = gtk_tree_path_copy (source_row);
2953                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2954
2955                 drag_and_drop_from_folder_view (source_model,
2956                                                 dest_model,
2957                                                 dest_row,
2958                                                 selection_data,
2959                                                 helper);
2960
2961                 gtk_tree_path_free (source_row);
2962         }
2963
2964         /* Frees */
2965         gtk_tree_path_free (dest_row);
2966
2967  end:
2968         /* Finish the drag and drop */
2969         gtk_drag_finish (context, success, FALSE, time);
2970 }
2971
2972 /*
2973  * We define a "drag-drop" signal handler because we do not want to
2974  * use the default one, because the default one always calls
2975  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2976  * signal handler, because there we have all the information available
2977  * to know if the dnd was a success or not.
2978  */
2979 static gboolean
2980 drag_drop_cb (GtkWidget      *widget,
2981               GdkDragContext *context,
2982               gint            x,
2983               gint            y,
2984               guint           time,
2985               gpointer        user_data)
2986 {
2987         gpointer target;
2988
2989         if (!context->targets)
2990                 return FALSE;
2991
2992         /* Check if we're dragging a folder row */
2993         target = gtk_drag_dest_find_target (widget, context, NULL);
2994
2995         /* Request the data from the source. */
2996         gtk_drag_get_data(widget, context, target, time);
2997
2998     return TRUE;
2999 }
3000
3001 /*
3002  * This function expands a node of a tree view if it's not expanded
3003  * yet. Not sure why it needs the threads stuff, but gtk+`example code
3004  * does that, so that's why they're here.
3005  */
3006 static gint
3007 expand_row_timeout (gpointer data)
3008 {
3009         GtkTreeView *tree_view = data;
3010         GtkTreePath *dest_path = NULL;
3011         GtkTreeViewDropPosition pos;
3012         gboolean result = FALSE;
3013
3014         gdk_threads_enter ();
3015
3016         gtk_tree_view_get_drag_dest_row (tree_view,
3017                                          &dest_path,
3018                                          &pos);
3019
3020         if (dest_path &&
3021             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3022              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3023                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3024                 gtk_tree_path_free (dest_path);
3025         }
3026         else {
3027                 if (dest_path)
3028                         gtk_tree_path_free (dest_path);
3029
3030                 result = TRUE;
3031         }
3032
3033         gdk_threads_leave ();
3034
3035         return result;
3036 }
3037
3038 /*
3039  * This function is called whenever the pointer is moved over a widget
3040  * while dragging some data. It installs a timeout that will expand a
3041  * node of the treeview if not expanded yet. This function also calls
3042  * gdk_drag_status in order to set the suggested action that will be
3043  * used by the "drag-data-received" signal handler to know if we
3044  * should do a move or just a copy of the data.
3045  */
3046 static gboolean
3047 on_drag_motion (GtkWidget      *widget,
3048                 GdkDragContext *context,
3049                 gint            x,
3050                 gint            y,
3051                 guint           time,
3052                 gpointer        user_data)
3053 {
3054         GtkTreeViewDropPosition pos;
3055         GtkTreePath *dest_row;
3056         GtkTreeModel *dest_model;
3057         ModestFolderViewPrivate *priv;
3058         GdkDragAction suggested_action;
3059         gboolean valid_location = FALSE;
3060         TnyFolderStore *folder = NULL;
3061
3062         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3063
3064         if (priv->timer_expander != 0) {
3065                 g_source_remove (priv->timer_expander);
3066                 priv->timer_expander = 0;
3067         }
3068
3069         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3070                                            x, y,
3071                                            &dest_row,
3072                                            &pos);
3073
3074         /* Do not allow drops between folders */
3075         if (!dest_row ||
3076             pos == GTK_TREE_VIEW_DROP_BEFORE ||
3077             pos == GTK_TREE_VIEW_DROP_AFTER) {
3078                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3079                 gdk_drag_status(context, 0, time);
3080                 valid_location = FALSE;
3081                 goto out;
3082         } else {
3083                 valid_location = TRUE;
3084         }
3085
3086         /* Check that the destination folder is writable */
3087         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3088         folder = tree_path_to_folder (dest_model, dest_row);
3089         if (folder && TNY_IS_FOLDER (folder)) {
3090                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3091
3092                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3093                         valid_location = FALSE;
3094                         goto out;
3095                 }
3096         }
3097
3098         /* Expand the selected row after 1/2 second */
3099         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3100                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3101         }
3102         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3103
3104         /* Select the desired action. By default we pick MOVE */
3105         suggested_action = GDK_ACTION_MOVE;
3106
3107         if (context->actions == GDK_ACTION_COPY)
3108             gdk_drag_status(context, GDK_ACTION_COPY, time);
3109         else if (context->actions == GDK_ACTION_MOVE)
3110             gdk_drag_status(context, GDK_ACTION_MOVE, time);
3111         else if (context->actions & suggested_action)
3112             gdk_drag_status(context, suggested_action, time);
3113         else
3114             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3115
3116  out:
3117         if (folder)
3118                 g_object_unref (folder);
3119         if (dest_row) {
3120                 gtk_tree_path_free (dest_row);
3121         }
3122         g_signal_stop_emission_by_name (widget, "drag-motion");
3123
3124         return valid_location;
3125 }
3126
3127 /*
3128  * This function sets the treeview as a source and a target for dnd
3129  * events. It also connects all the requirede signals.
3130  */
3131 static void
3132 setup_drag_and_drop (GtkTreeView *self)
3133 {
3134         /* Set up the folder view as a dnd destination. Set only the
3135            highlight flag, otherwise gtk will have a different
3136            behaviour */
3137 #ifdef MODEST_TOOLKIT_HILDON2
3138         return;
3139 #endif
3140         gtk_drag_dest_set (GTK_WIDGET (self),
3141                            GTK_DEST_DEFAULT_HIGHLIGHT,
3142                            folder_view_drag_types,
3143                            G_N_ELEMENTS (folder_view_drag_types),
3144                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
3145
3146         g_signal_connect (G_OBJECT (self),
3147                           "drag_data_received",
3148                           G_CALLBACK (on_drag_data_received),
3149                           NULL);
3150
3151
3152         /* Set up the treeview as a dnd source */
3153         gtk_drag_source_set (GTK_WIDGET (self),
3154                              GDK_BUTTON1_MASK,
3155                              folder_view_drag_types,
3156                              G_N_ELEMENTS (folder_view_drag_types),
3157                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
3158
3159         g_signal_connect (G_OBJECT (self),
3160                           "drag_motion",
3161                           G_CALLBACK (on_drag_motion),
3162                           NULL);
3163
3164         g_signal_connect (G_OBJECT (self),
3165                           "drag_data_get",
3166                           G_CALLBACK (on_drag_data_get),
3167                           NULL);
3168
3169         g_signal_connect (G_OBJECT (self),
3170                           "drag_drop",
3171                           G_CALLBACK (drag_drop_cb),
3172                           NULL);
3173 }
3174
3175 /*
3176  * This function manages the navigation through the folders using the
3177  * keyboard or the hardware keys in the device
3178  */
3179 static gboolean
3180 on_key_pressed (GtkWidget *self,
3181                 GdkEventKey *event,
3182                 gpointer user_data)
3183 {
3184         GtkTreeSelection *selection;
3185         GtkTreeIter iter;
3186         GtkTreeModel *model;
3187         gboolean retval = FALSE;
3188
3189         /* Up and Down are automatically managed by the treeview */
3190         if (event->keyval == GDK_Return) {
3191                 /* Expand/Collapse the selected row */
3192                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3193                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3194                         GtkTreePath *path;
3195
3196                         path = gtk_tree_model_get_path (model, &iter);
3197
3198                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3199                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3200                         else
3201                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3202                         gtk_tree_path_free (path);
3203                 }
3204                 /* No further processing */
3205                 retval = TRUE;
3206         }
3207
3208         return retval;
3209 }
3210
3211 /*
3212  * We listen to the changes in the local folder account name key,
3213  * because we want to show the right name in the view. The local
3214  * folder account name corresponds to the device name in the Maemo
3215  * version. We do this because we do not want to query gconf on each
3216  * tree view refresh. It's better to cache it and change whenever
3217  * necessary.
3218  */
3219 static void
3220 on_configuration_key_changed (ModestConf* conf,
3221                               const gchar *key,
3222                               ModestConfEvent event,
3223                               ModestConfNotificationId id,
3224                               ModestFolderView *self)
3225 {
3226         ModestFolderViewPrivate *priv;
3227
3228
3229         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3230         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3231
3232         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3233                 g_free (priv->local_account_name);
3234
3235                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3236                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3237                 else
3238                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3239                                                                            MODEST_CONF_DEVICE_NAME, NULL);
3240
3241                 /* Force a redraw */
3242 #if GTK_CHECK_VERSION(2, 8, 0)
3243                 GtkTreeViewColumn * tree_column;
3244
3245                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3246                                                         NAME_COLUMN);
3247                 gtk_tree_view_column_queue_resize (tree_column);
3248 #else
3249                 gtk_widget_queue_draw (GTK_WIDGET (self));
3250 #endif
3251         }
3252 }
3253
3254 void
3255 modest_folder_view_set_style (ModestFolderView *self,
3256                               ModestFolderViewStyle style)
3257 {
3258         ModestFolderViewPrivate *priv;
3259
3260         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3261         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3262                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3263
3264         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3265
3266
3267         priv->style = style;
3268 }
3269
3270 void
3271 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3272                                                              const gchar *account_id)
3273 {
3274         ModestFolderViewPrivate *priv;
3275         GtkTreeModel *model;
3276
3277         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3278
3279         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3280
3281         /* This will be used by the filter_row callback,
3282          * to decided which rows to show: */
3283         if (priv->visible_account_id) {
3284                 g_free (priv->visible_account_id);
3285                 priv->visible_account_id = NULL;
3286         }
3287         if (account_id)
3288                 priv->visible_account_id = g_strdup (account_id);
3289
3290         /* Refilter */
3291         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3292         if (GTK_IS_TREE_MODEL_FILTER (model))
3293                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3294
3295         /* Save settings to gconf */
3296         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3297                                    MODEST_CONF_FOLDER_VIEW_KEY);
3298
3299         /* Notify observers */
3300         g_signal_emit (G_OBJECT(self),
3301                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3302                        account_id);
3303 }
3304
3305 const gchar *
3306 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3307 {
3308         ModestFolderViewPrivate *priv;
3309
3310         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3311
3312         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3313
3314         return (const gchar *) priv->visible_account_id;
3315 }
3316
3317 static gboolean
3318 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3319 {
3320         do {
3321                 GtkTreeIter child;
3322                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3323
3324                 gtk_tree_model_get (model, iter,
3325                                     TYPE_COLUMN,
3326                                     &type, -1);
3327
3328                 gboolean result = FALSE;
3329                 if (type == TNY_FOLDER_TYPE_INBOX) {
3330                         result = TRUE;
3331                 }
3332                 if (result) {
3333                         *inbox_iter = *iter;
3334                         return TRUE;
3335                 }
3336
3337                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3338                         if (find_inbox_iter (model, &child, inbox_iter))
3339                                 return TRUE;
3340                 }
3341
3342         } while (gtk_tree_model_iter_next (model, iter));
3343
3344         return FALSE;
3345 }
3346
3347
3348
3349
3350 void
3351 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3352 {
3353         GtkTreeModel *model;
3354         GtkTreeIter iter, inbox_iter;
3355         GtkTreeSelection *sel;
3356         GtkTreePath *path = NULL;
3357
3358         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3359
3360         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3361         if (!model)
3362                 return;
3363
3364         expand_root_items (self);
3365         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3366
3367         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3368                 g_warning ("%s: model is empty", __FUNCTION__);
3369                 return;
3370         }
3371
3372         if (find_inbox_iter (model, &iter, &inbox_iter))
3373                 path = gtk_tree_model_get_path (model, &inbox_iter);
3374         else
3375                 path = gtk_tree_path_new_first ();
3376
3377         /* Select the row and free */
3378         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3379         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3380         gtk_tree_path_free (path);
3381
3382         /* set focus */
3383         gtk_widget_grab_focus (GTK_WIDGET(self));
3384 }
3385
3386
3387 /* recursive */
3388 static gboolean
3389 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3390                   TnyFolder* folder)
3391 {
3392         do {
3393                 GtkTreeIter child;
3394                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3395                 TnyFolder* a_folder;
3396                 gchar *name = NULL;
3397
3398                 gtk_tree_model_get (model, iter,
3399                                     INSTANCE_COLUMN, &a_folder,
3400                                     NAME_COLUMN, &name,
3401                                     TYPE_COLUMN, &type,
3402                                     -1);
3403                 g_free (name);
3404
3405                 if (folder == a_folder) {
3406                         g_object_unref (a_folder);
3407                         *folder_iter = *iter;
3408                         return TRUE;
3409                 }
3410                 g_object_unref (a_folder);
3411
3412                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3413                         if (find_folder_iter (model, &child, folder_iter, folder))
3414                                 return TRUE;
3415                 }
3416
3417         } while (gtk_tree_model_iter_next (model, iter));
3418
3419         return FALSE;
3420 }
3421
3422 #ifndef MODEST_TOOLKIT_HILDON2
3423 static void
3424 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3425                                      GtkTreePath *path,
3426                                      GtkTreeIter *iter,
3427                                      ModestFolderView *self)
3428 {
3429         ModestFolderViewPrivate *priv = NULL;
3430         GtkTreeSelection *sel;
3431         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3432         GObject *instance = NULL;
3433
3434         if (!MODEST_IS_FOLDER_VIEW(self))
3435                 return;
3436
3437         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3438
3439         priv->reexpand = TRUE;
3440
3441         gtk_tree_model_get (tree_model, iter,
3442                             TYPE_COLUMN, &type,
3443                             INSTANCE_COLUMN, &instance,
3444                             -1);
3445
3446         if (!instance)
3447                 return;
3448
3449         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3450                 priv->folder_to_select = g_object_ref (instance);
3451         }
3452         g_object_unref (instance);
3453
3454         if (priv->folder_to_select) {
3455
3456                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3457                                                        FALSE)) {
3458                         GtkTreePath *path;
3459                         path = gtk_tree_model_get_path (tree_model, iter);
3460                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3461
3462                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3463
3464                         gtk_tree_selection_select_iter (sel, iter);
3465                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3466
3467                         gtk_tree_path_free (path);
3468                 }
3469
3470                 /* Disable next */
3471                 modest_folder_view_disable_next_folder_selection (self);
3472
3473                 /* Refilter the model */
3474                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3475         }
3476 }
3477 #endif
3478
3479 void
3480 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3481 {
3482         ModestFolderViewPrivate *priv;
3483
3484         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3485
3486         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3487
3488         if (priv->folder_to_select)
3489                 g_object_unref(priv->folder_to_select);
3490
3491         priv->folder_to_select = NULL;
3492 }
3493
3494 gboolean
3495 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3496                                   gboolean after_change)
3497 {
3498         GtkTreeModel *model;
3499         GtkTreeIter iter, folder_iter;
3500         GtkTreeSelection *sel;
3501         ModestFolderViewPrivate *priv = NULL;
3502
3503         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3504         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3505
3506         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3507
3508         if (after_change) {
3509                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3510                 gtk_tree_selection_unselect_all (sel);
3511
3512                 if (priv->folder_to_select)
3513                         g_object_unref(priv->folder_to_select);
3514                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3515                 return TRUE;
3516         }
3517
3518         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3519         if (!model)
3520                 return FALSE;
3521
3522
3523         /* Refilter the model, before selecting the folder */
3524         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3525
3526         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3527                 g_warning ("%s: model is empty", __FUNCTION__);
3528                 return FALSE;
3529         }
3530
3531         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3532                 GtkTreePath *path;
3533
3534                 path = gtk_tree_model_get_path (model, &folder_iter);
3535                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3536
3537                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3538                 gtk_tree_selection_select_iter (sel, &folder_iter);
3539                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3540
3541                 gtk_tree_path_free (path);
3542                 return TRUE;
3543         }
3544         return FALSE;
3545 }
3546
3547
3548 void
3549 modest_folder_view_copy_selection (ModestFolderView *self)
3550 {
3551         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3552
3553         /* Copy selection */
3554         _clipboard_set_selected_data (self, FALSE);
3555 }
3556
3557 void
3558 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3559 {
3560         ModestFolderViewPrivate *priv = NULL;
3561         GtkTreeModel *model = NULL;
3562         const gchar **hidding = NULL;
3563         guint i, n_selected;
3564
3565         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3566         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3567
3568         /* Copy selection */
3569         if (!_clipboard_set_selected_data (folder_view, TRUE))
3570                 return;
3571
3572         /* Get hidding ids */
3573         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3574
3575         /* Clear hidding array created by previous cut operation */
3576         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3577
3578         /* Copy hidding array */
3579         priv->n_selected = n_selected;
3580         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3581         for (i=0; i < n_selected; i++)
3582                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3583
3584         /* Hide cut folders */
3585         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3586         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3587 }
3588
3589 void
3590 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3591                                ModestFolderView *folder_view_dst)
3592 {
3593         GtkTreeModel *filter_model = NULL;
3594         GtkTreeModel *model = NULL;
3595         GtkTreeModel *new_filter_model = NULL;
3596
3597         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3598         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3599
3600         /* Get src model*/
3601         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3602         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3603
3604         /* Build new filter model */
3605         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3606         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3607                                                 filter_row,
3608                                                 folder_view_dst,
3609                                                 NULL);
3610         /* Set copied model */
3611         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3612 #ifndef MODEST_TOOLKIT_HILDON2
3613         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3614                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3615 #endif
3616
3617         /* Free */
3618         g_object_unref (new_filter_model);
3619 }
3620
3621 void
3622 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3623                                           gboolean show)
3624 {
3625         GtkTreeModel *model = NULL;
3626         ModestFolderViewPrivate* priv;
3627
3628         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3629
3630         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3631         priv->show_non_move = show;
3632 /*      modest_folder_view_update_model(folder_view, */
3633 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3634
3635         /* Hide special folders */
3636         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3637         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3638                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3639         }
3640 }
3641
3642 /* Returns FALSE if it did not selected anything */
3643 static gboolean
3644 _clipboard_set_selected_data (ModestFolderView *folder_view,
3645                               gboolean delete)
3646 {
3647         ModestFolderViewPrivate *priv = NULL;
3648         TnyFolderStore *folder = NULL;
3649         gboolean retval = FALSE;
3650
3651         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3652         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3653
3654         /* Set selected data on clipboard   */
3655         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3656         folder = modest_folder_view_get_selected (folder_view);
3657
3658         /* Do not allow to select an account */
3659         if (TNY_IS_FOLDER (folder)) {
3660                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3661                 retval = TRUE;
3662         }
3663
3664         /* Free */
3665         g_object_unref (folder);
3666
3667         return retval;
3668 }
3669
3670 static void
3671 _clear_hidding_filter (ModestFolderView *folder_view)
3672 {
3673         ModestFolderViewPrivate *priv;
3674         guint i;
3675
3676         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3677         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3678
3679         if (priv->hidding_ids != NULL) {
3680                 for (i=0; i < priv->n_selected; i++)
3681                         g_free (priv->hidding_ids[i]);
3682                 g_free(priv->hidding_ids);
3683         }
3684 }
3685
3686
3687 static void
3688 on_display_name_changed (ModestAccountMgr *mgr,
3689                          const gchar *account,
3690                          gpointer user_data)
3691 {
3692         ModestFolderView *self;
3693
3694         self = MODEST_FOLDER_VIEW (user_data);
3695
3696         /* Force a redraw */
3697 #if GTK_CHECK_VERSION(2, 8, 0)
3698         GtkTreeViewColumn * tree_column;
3699
3700         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3701                                                 NAME_COLUMN);
3702         gtk_tree_view_column_queue_resize (tree_column);
3703 #else
3704         gtk_widget_queue_draw (GTK_WIDGET (self));
3705 #endif
3706 }
3707
3708 void 
3709 modest_folder_view_set_cell_style (ModestFolderView *self,
3710                                    ModestFolderViewCellStyle cell_style)
3711 {
3712         ModestFolderViewPrivate *priv = NULL;
3713
3714         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3715         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3716
3717         priv->cell_style = cell_style;
3718
3719         g_object_set (G_OBJECT (priv->messages_renderer),
3720                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3721                       NULL);
3722         
3723         gtk_widget_queue_draw (GTK_WIDGET (self));
3724 }
3725
3726 static void
3727 update_style (ModestFolderView *self)
3728 {
3729         ModestFolderViewPrivate *priv;
3730         GdkColor style_color;
3731         PangoAttrList *attr_list;
3732         GtkStyle *style;
3733         PangoAttribute *attr;
3734
3735         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3736         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3737
3738         /* Set color */
3739
3740         attr_list = pango_attr_list_new ();
3741         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3742                 gdk_color_parse ("grey", &style_color);
3743         }
3744         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3745         pango_attr_list_insert (attr_list, attr);
3746         
3747         /* set font */
3748         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3749                                            (GTK_WIDGET(self)),
3750                                            "SmallSystemFont", NULL,
3751                                            G_TYPE_NONE);
3752         if (style) {
3753                 attr = pango_attr_font_desc_new (pango_font_description_copy
3754                                                  (style->font_desc));
3755                 pango_attr_list_insert (attr_list, attr);
3756
3757                 g_object_set (G_OBJECT (priv->messages_renderer),
3758                               "foreground-gdk", &style_color,
3759                               "foreground-set", TRUE,
3760                               "attributes", attr_list,
3761                               NULL);
3762                 pango_attr_list_unref (attr_list);
3763         }
3764 }
3765
3766 static void 
3767 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3768 {
3769         if (strcmp ("style", spec->name) == 0) {
3770                 update_style (MODEST_FOLDER_VIEW (obj));
3771                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3772         } 
3773 }
3774
3775 void 
3776 modest_folder_view_set_filter (ModestFolderView *self,
3777                                ModestFolderViewFilter filter)
3778 {
3779         ModestFolderViewPrivate *priv;
3780         GtkTreeModel *filter_model;
3781
3782         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3783         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3784
3785         priv->filter |= filter;
3786
3787         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3788         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3789                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3790         }
3791 }
3792
3793 void 
3794 modest_folder_view_unset_filter (ModestFolderView *self,
3795                                  ModestFolderViewFilter filter)
3796 {
3797         ModestFolderViewPrivate *priv;
3798         GtkTreeModel *filter_model;
3799
3800         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3801         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3802
3803         priv->filter &= ~filter;
3804
3805         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3806         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3807                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3808         }
3809 }
3810
3811 gboolean
3812 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3813                                              ModestTnyFolderRules rules)
3814 {
3815         GtkTreeModel *filter_model;
3816         GtkTreeIter iter;
3817         gboolean fulfil = FALSE;
3818
3819         if (!get_inner_models (self, &filter_model, NULL, NULL))
3820                 return FALSE;
3821
3822         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3823                 return FALSE;
3824
3825         do {
3826                 TnyFolderStore *folder;
3827
3828                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3829                 if (folder) {
3830                         if (TNY_IS_FOLDER (folder)) {
3831                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3832                                 /* Folder rules are negative: non_writable, non_deletable... */
3833                                 if (!(folder_rules & rules))
3834                                         fulfil = TRUE;
3835                         }
3836                         g_object_unref (folder);
3837                 }
3838
3839         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3840
3841         return fulfil;
3842 }
3843
3844 void 
3845 modest_folder_view_set_list_to_move (ModestFolderView *self,
3846                                      TnyList *list)
3847 {
3848         ModestFolderViewPrivate *priv;
3849
3850         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3851         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3852
3853         if (priv->list_to_move)
3854                 g_object_unref (priv->list_to_move);
3855
3856         if (list)
3857                 g_object_ref (list);
3858
3859         priv->list_to_move = list;
3860 }