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