Show folders with non read messages with active text color
[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"), 
854                                                              number);
855                         } else if (number == 1) {
856                                 item_name = g_strdup_printf (drafts?_("mcen_ti_message"):_("mcen_ti_new_message"), 
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         if (priv->query) {
1389                 g_object_unref (G_OBJECT (priv->query));
1390                 priv->query = NULL;
1391         }
1392
1393         if (priv->folder_to_select) {
1394                 g_object_unref (G_OBJECT(priv->folder_to_select));
1395                 priv->folder_to_select = NULL;
1396         }
1397
1398         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(obj));
1399         if (sel)
1400                 g_signal_handler_disconnect (G_OBJECT(sel), priv->changed_signal);
1401
1402         g_free (priv->local_account_name);
1403         g_free (priv->visible_account_id);
1404         g_free (priv->mailbox);
1405
1406         if (priv->conf_key_signal) {
1407                 g_signal_handler_disconnect (modest_runtime_get_conf (),
1408                                              priv->conf_key_signal);
1409                 priv->conf_key_signal = 0;
1410         }
1411
1412         if (priv->cur_folder_store) {
1413                 g_object_unref (priv->cur_folder_store);
1414                 priv->cur_folder_store = NULL;
1415         }
1416
1417         if (priv->list_to_move) {
1418                 g_object_unref (priv->list_to_move);
1419                 priv->list_to_move = NULL;
1420         }
1421
1422         /* Clear hidding array created by cut operation */
1423         _clear_hidding_filter (MODEST_FOLDER_VIEW (obj));
1424
1425         G_OBJECT_CLASS(parent_class)->finalize (obj);
1426 }
1427
1428
1429 static void
1430 modest_folder_view_set_account_store (TnyAccountStoreView *self, TnyAccountStore *account_store)
1431 {
1432         ModestFolderViewPrivate *priv;
1433         TnyDevice *device;
1434
1435         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
1436         g_return_if_fail (TNY_IS_ACCOUNT_STORE (account_store));
1437
1438         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1439         device = tny_account_store_get_device (account_store);
1440
1441         if (G_UNLIKELY (priv->account_store)) {
1442
1443                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1444                                                    priv->account_inserted_signal))
1445                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1446                                                      priv->account_inserted_signal);
1447                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1448                                                    priv->account_removed_signal))
1449                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1450                                                      priv->account_removed_signal);
1451                 if (g_signal_handler_is_connected (G_OBJECT (priv->account_store),
1452                                                    priv->account_changed_signal))
1453                         g_signal_handler_disconnect (G_OBJECT (priv->account_store),
1454                                                      priv->account_changed_signal);
1455                 g_object_unref (G_OBJECT (priv->account_store));
1456         }
1457
1458         priv->account_store = g_object_ref (G_OBJECT (account_store));
1459
1460         priv->account_removed_signal =
1461                 g_signal_connect (G_OBJECT(account_store), "account_removed",
1462                                   G_CALLBACK (on_account_removed), self);
1463
1464         priv->account_inserted_signal =
1465                 g_signal_connect (G_OBJECT(account_store), "account_inserted",
1466                                   G_CALLBACK (on_account_inserted), self);
1467
1468         priv->account_changed_signal =
1469                 g_signal_connect (G_OBJECT(account_store), "account_changed",
1470                                   G_CALLBACK (on_account_changed), self);
1471
1472         modest_folder_view_update_model (MODEST_FOLDER_VIEW (self), account_store);
1473         priv->reselect = FALSE;
1474         modest_folder_view_select_first_inbox_or_local (MODEST_FOLDER_VIEW (self));
1475
1476         g_object_unref (G_OBJECT (device));
1477 }
1478
1479 static void
1480 on_outbox_deleted_cb (ModestTnyLocalFoldersAccount *local_account,
1481                       gpointer user_data)
1482 {
1483         ModestFolderView *self;
1484         GtkTreeModel *model, *filter_model;
1485         TnyFolder *outbox;
1486
1487         self = MODEST_FOLDER_VIEW (user_data);
1488
1489         if (!get_inner_models (self, &filter_model, NULL, &model))
1490                 return;
1491
1492         /* Remove outbox from model */
1493         outbox = modest_tny_local_folders_account_get_merged_outbox (local_account);
1494         tny_list_remove (TNY_LIST (model), G_OBJECT (outbox));
1495         g_object_unref (outbox);
1496
1497         /* Refilter view */
1498         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1499 }
1500
1501 static void
1502 on_account_inserted (TnyAccountStore *account_store,
1503                      TnyAccount *account,
1504                      gpointer user_data)
1505 {
1506         ModestFolderViewPrivate *priv;
1507         GtkTreeModel *model, *filter_model;
1508
1509         /* Ignore transport account insertions, we're not showing them
1510            in the folder view */
1511         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1512                 return;
1513
1514         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1515
1516
1517         /* If we're adding a new account, and there is no previous
1518            one, we need to select the visible server account */
1519         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1520             !priv->visible_account_id)
1521                 modest_widget_memory_restore (modest_runtime_get_conf(),
1522                                               G_OBJECT (user_data),
1523                                               MODEST_CONF_FOLDER_VIEW_KEY);
1524
1525
1526         /* Get models */
1527         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1528                                &filter_model, NULL, &model))
1529                 return;
1530
1531         /* Insert the account in the model */
1532         tny_list_append (TNY_LIST (model), G_OBJECT (account));
1533
1534         /* When the model is a list store (plain representation) the
1535            outbox is not a child of any account so we have to manually
1536            delete it because removing the local folders account won't
1537            delete it (because tny_folder_get_account() is not defined
1538            for a merge folder */
1539         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1540             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1541
1542                 priv->outbox_deleted_handler =
1543                         g_signal_connect (account,
1544                                           "outbox-deleted",
1545                                           G_CALLBACK (on_outbox_deleted_cb),
1546                                           user_data);
1547         }
1548
1549         /* Refilter the model */
1550         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1551 }
1552
1553
1554 static gboolean
1555 same_account_selected (ModestFolderView *self,
1556                        TnyAccount *account)
1557 {
1558         ModestFolderViewPrivate *priv;
1559         gboolean same_account = FALSE;
1560
1561         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1562
1563         if (priv->cur_folder_store) {
1564                 TnyAccount *selected_folder_account = NULL;
1565
1566                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1567                         selected_folder_account =
1568                                 modest_tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1569                 } else {
1570                         selected_folder_account =
1571                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1572                 }
1573
1574                 if (selected_folder_account == account)
1575                         same_account = TRUE;
1576
1577                 g_object_unref (selected_folder_account);
1578         }
1579         return same_account;
1580 }
1581
1582 /**
1583  *
1584  * Selects the first inbox or the local account in an idle
1585  */
1586 static gboolean
1587 on_idle_select_first_inbox_or_local (gpointer user_data)
1588 {
1589         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1590
1591         gdk_threads_enter ();
1592         modest_folder_view_select_first_inbox_or_local (self);
1593         gdk_threads_leave ();
1594
1595         return FALSE;
1596 }
1597
1598 static void
1599 on_account_changed (TnyAccountStore *account_store,
1600                     TnyAccount *tny_account,
1601                     gpointer user_data)
1602 {
1603         ModestFolderView *self;
1604         ModestFolderViewPrivate *priv;
1605         GtkTreeModel *model, *filter_model;
1606         GtkTreeSelection *sel;
1607         gboolean same_account;
1608
1609         /* Ignore transport account insertions, we're not showing them
1610            in the folder view */
1611         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1612                 return;
1613
1614         self = MODEST_FOLDER_VIEW (user_data);
1615         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1616
1617         /* Get the inner model */
1618         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1619                                &filter_model, NULL, &model))
1620                 return;
1621
1622         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1623
1624         /* Invalidate the cur_folder_store only if the selected folder
1625            belongs to the account that is being removed */
1626         same_account = same_account_selected (self, tny_account);
1627         if (same_account) {
1628                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1629                 gtk_tree_selection_unselect_all (sel);
1630         }
1631
1632         /* Remove the account from the model */
1633         tny_list_remove (TNY_LIST (model), G_OBJECT (tny_account));
1634
1635         /* Insert the account in the model */
1636         tny_list_append (TNY_LIST (model), G_OBJECT (tny_account));
1637
1638         /* Refilter the model */
1639         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1640
1641         /* Select the first INBOX if the currently selected folder
1642            belongs to the account that is being deleted */
1643         if (same_account && !MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (tny_account))
1644                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1645 }
1646
1647 static void
1648 on_account_removed (TnyAccountStore *account_store,
1649                     TnyAccount *account,
1650                     gpointer user_data)
1651 {
1652         ModestFolderView *self = NULL;
1653         ModestFolderViewPrivate *priv;
1654         GtkTreeModel *model, *filter_model;
1655         GtkTreeSelection *sel = NULL;
1656         gboolean same_account = FALSE;
1657
1658         /* Ignore transport account removals, we're not showing them
1659            in the folder view */
1660         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1661                 return;
1662
1663         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1664                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1665                 return;
1666         }
1667
1668         self = MODEST_FOLDER_VIEW (user_data);
1669         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1670
1671         /* Invalidate the cur_folder_store only if the selected folder
1672            belongs to the account that is being removed */
1673         same_account = same_account_selected (self, account);
1674         if (same_account) {
1675                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1676                 gtk_tree_selection_unselect_all (sel);
1677         }
1678
1679         /* Invalidate row to select only if the folder to select
1680            belongs to the account that is being removed*/
1681         if (priv->folder_to_select) {
1682                 TnyAccount *folder_to_select_account = NULL;
1683
1684                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1685                 if (folder_to_select_account == account) {
1686                         modest_folder_view_disable_next_folder_selection (self);
1687                         g_object_unref (priv->folder_to_select);
1688                         priv->folder_to_select = NULL;
1689                 }
1690                 g_object_unref (folder_to_select_account);
1691         }
1692
1693         if (!get_inner_models (MODEST_FOLDER_VIEW (user_data),
1694                                &filter_model, NULL, &model))
1695                 return;
1696
1697         /* Disconnect the signal handler */
1698         if (TNY_IS_GTK_FOLDER_LIST_STORE (model) &&
1699             MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (account)) {
1700                 if (g_signal_handler_is_connected (account,
1701                                                    priv->outbox_deleted_handler))
1702                         g_signal_handler_disconnect (account,
1703                                                      priv->outbox_deleted_handler);
1704         }
1705
1706         /* Remove the account from the model */
1707         tny_list_remove (TNY_LIST (model), G_OBJECT (account));
1708
1709         /* If the removed account is the currently viewed one then
1710            clear the configuration value. The new visible account will be the default account */
1711         if (priv->visible_account_id &&
1712             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1713
1714                 /* Clear the current visible account_id */
1715                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1716                 modest_folder_view_set_mailbox (self, NULL);
1717
1718                 /* Call the restore method, this will set the new visible account */
1719                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1720                                               MODEST_CONF_FOLDER_VIEW_KEY);
1721         }
1722
1723         /* Refilter the model */
1724         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1725
1726         /* Select the first INBOX if the currently selected folder
1727            belongs to the account that is being deleted */
1728         if (same_account)
1729                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1730 }
1731
1732 void
1733 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1734 {
1735         GtkTreeViewColumn *col;
1736
1737         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1738
1739         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1740         if (!col) {
1741                 g_printerr ("modest: failed get column for title\n");
1742                 return;
1743         }
1744
1745         gtk_tree_view_column_set_title (col, title);
1746         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1747                                            title != NULL);
1748 }
1749
1750 static gboolean
1751 modest_folder_view_on_map (ModestFolderView *self,
1752                            GdkEventExpose *event,
1753                            gpointer data)
1754 {
1755         ModestFolderViewPrivate *priv;
1756
1757         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1758
1759         /* This won't happen often */
1760         if (G_UNLIKELY (priv->reselect)) {
1761                 /* Select the first inbox or the local account if not found */
1762
1763                 /* TODO: this could cause a lock at startup, so we
1764                    comment it for the moment. We know that this will
1765                    be a bug, because the INBOX is not selected, but we
1766                    need to rewrite some parts of Modest to avoid the
1767                    deathlock situation */
1768                 /* TODO: check if this is still the case */
1769                 priv->reselect = FALSE;
1770                 modest_folder_view_select_first_inbox_or_local (self);
1771                 /* Notify the display name observers */
1772                 g_signal_emit (G_OBJECT(self),
1773                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1774                                NULL);
1775         }
1776
1777         if (priv->reexpand) {
1778                 expand_root_items (self);
1779                 priv->reexpand = FALSE;
1780         }
1781
1782         return FALSE;
1783 }
1784
1785 GtkWidget*
1786 modest_folder_view_new (TnyFolderStoreQuery *query)
1787 {
1788         return modest_folder_view_new_full (query, TRUE);
1789 }
1790
1791 GtkWidget*
1792 modest_folder_view_new_full (TnyFolderStoreQuery *query, gboolean do_refresh)
1793 {
1794         GObject *self;
1795         ModestFolderViewPrivate *priv;
1796         GtkTreeSelection *sel;
1797
1798         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, 
1799 #ifdef MODEST_TOOLKIT_HILDON2
1800                                        "hildon-ui-mode", HILDON_UI_MODE_NORMAL,
1801 #endif
1802                                        NULL));
1803         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1804
1805         if (query)
1806                 priv->query = g_object_ref (query);
1807
1808         priv->do_refresh = do_refresh;
1809
1810         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1811         priv->changed_signal = g_signal_connect (sel, "changed",
1812                                                  G_CALLBACK (on_selection_changed), self);
1813
1814         g_signal_connect (self, "row-activated", G_CALLBACK (on_row_activated), self);
1815
1816         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1817
1818         return GTK_WIDGET(self);
1819 }
1820
1821 /* this feels dirty; any other way to expand all the root items? */
1822 static void
1823 expand_root_items (ModestFolderView *self)
1824 {
1825         GtkTreePath *path;
1826         GtkTreeModel *model;
1827         GtkTreeIter iter;
1828
1829         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1830         path = gtk_tree_path_new_first ();
1831
1832         /* all folders should have child items, so.. */
1833         do {
1834                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1835                 gtk_tree_path_next (path);
1836         } while (gtk_tree_model_get_iter (model, &iter, path));
1837
1838         gtk_tree_path_free (path);
1839 }
1840
1841 static gboolean
1842 is_parent_of (TnyFolder *a, TnyFolder *b)
1843 {
1844         const gchar *a_id;
1845         gboolean retval = FALSE;
1846
1847         a_id = tny_folder_get_id (a);
1848         if (a_id) {
1849                 gchar *string_to_match;
1850                 const gchar *b_id;
1851
1852                 string_to_match = g_strconcat (a_id, "/", NULL);
1853                 b_id = tny_folder_get_id (b);
1854                 retval = g_str_has_prefix (b_id, string_to_match);
1855                 g_free (string_to_match);
1856         }
1857         
1858         return retval;
1859 }
1860
1861 typedef struct _ForeachFolderInfo {
1862         gchar *needle;
1863         gboolean found;
1864 } ForeachFolderInfo;
1865
1866 static gboolean 
1867 foreach_folder_with_id (GtkTreeModel *model,
1868                         GtkTreePath *path,
1869                         GtkTreeIter *iter,
1870                         gpointer data)
1871 {
1872         ForeachFolderInfo *info;
1873         GObject *instance;
1874
1875         info = (ForeachFolderInfo *) data;
1876         gtk_tree_model_get (model, iter,
1877                             INSTANCE_COLUMN, &instance,
1878                             -1);
1879
1880         if (TNY_IS_FOLDER (instance)) {
1881                 const gchar *id;
1882                 gchar *collate;
1883                 id = tny_folder_get_id (TNY_FOLDER (instance));
1884                 if (id) {
1885                         collate = g_utf8_collate_key (id, -1);
1886                         info->found = !strcmp (info->needle, collate);
1887                         g_free (collate);
1888                 }
1889         }
1890
1891         if (instance)
1892                 g_object_unref (instance);
1893
1894         return info->found;
1895         
1896 }
1897
1898
1899 static gboolean
1900 has_folder_with_id (ModestFolderView *self, const gchar *id)
1901 {
1902         GtkTreeModel *model;
1903         ForeachFolderInfo info = {NULL, FALSE};
1904
1905         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1906         info.needle = g_utf8_collate_key (id, -1);
1907         
1908         gtk_tree_model_foreach (model, foreach_folder_with_id, &info);
1909         g_free (info.needle);
1910
1911         return info.found;
1912 }
1913
1914 static gboolean
1915 has_child_with_name_of (ModestFolderView *self, TnyFolder *a, TnyFolder *b)
1916 {
1917         const gchar *a_id;
1918         gboolean retval = FALSE;
1919
1920         a_id = tny_folder_get_id (a);
1921         if (a_id) {
1922                 const gchar *b_id;
1923                 b_id = tny_folder_get_id (b);
1924                 
1925                 if (b_id) {
1926                         const gchar *last_bar;
1927                         gchar *string_to_match;
1928                         last_bar = g_strrstr (b_id, "/");
1929                         if (last_bar)
1930                                 last_bar++;
1931                         else
1932                                 last_bar = b_id;
1933                         string_to_match = g_strconcat (a_id, "/", last_bar, NULL);
1934                         retval = has_folder_with_id (self, string_to_match);
1935                         g_free (string_to_match);
1936                 }
1937         }
1938
1939         return retval;
1940 }
1941
1942 static gboolean
1943 check_move_to_this_folder_valid (ModestFolderView *self, TnyFolder *folder)
1944 {
1945         ModestFolderViewPrivate *priv;
1946         TnyIterator *iterator;
1947         gboolean retval = TRUE;
1948
1949         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
1950         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1951
1952         for (iterator = tny_list_create_iterator (priv->list_to_move);
1953              retval && !tny_iterator_is_done (iterator);
1954              tny_iterator_next (iterator)) {
1955                 GObject *instance;
1956                 instance = tny_iterator_get_current (iterator);
1957                 if (instance == (GObject *) folder) {
1958                         retval = FALSE;
1959                 } else if (TNY_IS_FOLDER (instance)) {
1960                         retval = !is_parent_of (TNY_FOLDER (instance), folder);
1961                         if (retval) {
1962                                 retval = !has_child_with_name_of (self, folder, TNY_FOLDER (instance));
1963                         }
1964                 }
1965                 g_object_unref (instance);
1966         }
1967         g_object_unref (iterator);
1968
1969         return retval;
1970 }
1971
1972
1973 /*
1974  * We use this function to implement the
1975  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1976  * account in this case, and the local folders.
1977  */
1978 static gboolean
1979 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1980 {
1981         ModestFolderViewPrivate *priv;
1982         gboolean retval = TRUE;
1983         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1984         GObject *instance = NULL;
1985         const gchar *id = NULL;
1986         guint i;
1987         gboolean found = FALSE;
1988         gboolean cleared = FALSE;
1989         ModestTnyFolderRules rules = 0;
1990         gchar *fname;
1991
1992         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1993         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1994
1995         gtk_tree_model_get (model, iter,
1996                             NAME_COLUMN, &fname,
1997                             TYPE_COLUMN, &type,
1998                             INSTANCE_COLUMN, &instance,
1999                             -1);
2000
2001         /* Do not show if there is no instance, this could indeed
2002            happen when the model is being modified while it's being
2003            drawn. This could occur for example when moving folders
2004            using drag&drop */
2005         if (!instance) {
2006                 g_free (fname);
2007                 return FALSE;
2008         }
2009
2010         if (TNY_IS_ACCOUNT (instance)) {
2011                 TnyAccount *acc = TNY_ACCOUNT (instance);
2012                 const gchar *account_id = tny_account_get_id (acc);
2013
2014                 /* If it isn't a special folder,
2015                  * don't show it unless it is the visible account: */
2016                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
2017                     !modest_tny_account_is_virtual_local_folders (acc) &&
2018                     strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2019
2020                         /* Show only the visible account id */
2021                         if (priv->visible_account_id) {
2022                                 if (strcmp (account_id, priv->visible_account_id))
2023                                         retval = FALSE;
2024                         } else {
2025                                 retval = FALSE;
2026                         }
2027                 }
2028
2029                 /* Never show these to the user. They are merged into one folder
2030                  * in the local-folders account instead: */
2031                 if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
2032                         retval = FALSE;
2033         } else {
2034                 if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE) {
2035                         /* Only show special folders for current account if needed */
2036                         if (TNY_IS_FOLDER (instance) && !TNY_IS_MERGE_FOLDER (instance)) {
2037                                 TnyAccount *account;
2038
2039                                 account = tny_folder_get_account (TNY_FOLDER (instance));
2040
2041                                 if (TNY_IS_ACCOUNT (account)) {
2042                                         const gchar *account_id = tny_account_get_id (account);
2043
2044                                         if (!modest_tny_account_is_virtual_local_folders (account) &&
2045                                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
2046                                                 /* Show only the visible account id */
2047                                                 if (priv->visible_account_id) {
2048                                                   if (strcmp (account_id, priv->visible_account_id)) {
2049                                                           retval = FALSE;
2050                                                   } else if (priv->mailbox) {
2051                                                           /* Filter mailboxes */
2052                                                           if (!g_str_has_prefix (fname, priv->mailbox)) {
2053                                                                   retval = FALSE;
2054                                                           } else if (!strcmp (fname, priv->mailbox)) {
2055                                                                   /* Hide mailbox parent */
2056                                                                   retval = FALSE;
2057                                                           }
2058                                                   }
2059                                                 }
2060                                         }
2061                                                 g_object_unref (account);
2062                                 }
2063                         }
2064
2065                 }
2066         }
2067
2068         /* Check hiding (if necessary) */
2069         cleared = modest_email_clipboard_cleared (priv->clipboard);
2070         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
2071                 id = tny_folder_get_id (TNY_FOLDER(instance));
2072                 if (priv->hidding_ids != NULL)
2073                         for (i=0; i < priv->n_selected && !found; i++)
2074                                 if (priv->hidding_ids[i] != NULL && id != NULL)
2075                                         found = (!strcmp (priv->hidding_ids[i], id));
2076
2077                 retval = !found;
2078         }
2079
2080         /* If this is a move to dialog, hide Sent, Outbox and Drafts
2081         folder as no message can be move there according to UI specs */
2082         if (retval && !priv->show_non_move) {
2083                 if (priv->list_to_move && 
2084                     tny_list_get_length (priv->list_to_move) > 0 &&
2085                     TNY_IS_FOLDER (instance)) {
2086                         retval = check_move_to_this_folder_valid (MODEST_FOLDER_VIEW (data), TNY_FOLDER (instance));
2087                 }
2088                 if (retval && TNY_IS_FOLDER (instance) && 
2089                     modest_tny_folder_is_local_folder (TNY_FOLDER (instance))) {
2090                         switch (type) {
2091                         case TNY_FOLDER_TYPE_OUTBOX:
2092                         case TNY_FOLDER_TYPE_SENT:
2093                         case TNY_FOLDER_TYPE_DRAFTS:
2094                                 retval = FALSE;
2095                                 break;
2096                         case TNY_FOLDER_TYPE_UNKNOWN:
2097                         case TNY_FOLDER_TYPE_NORMAL:
2098                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
2099                                 if (type == TNY_FOLDER_TYPE_INVALID)
2100                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
2101                                 
2102                                 if (type == TNY_FOLDER_TYPE_OUTBOX ||
2103                                     type == TNY_FOLDER_TYPE_SENT
2104                                     || type == TNY_FOLDER_TYPE_DRAFTS)
2105                                         retval = FALSE;
2106                                 break;
2107                         default:
2108                                 break;
2109                         }
2110                 }
2111                 if (retval && TNY_IS_ACCOUNT (instance) &&
2112                     modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2113                         ModestProtocolType protocol_type;
2114
2115                         protocol_type = modest_tny_account_get_protocol_type (TNY_ACCOUNT (instance));
2116                         retval  = !modest_protocol_registry_protocol_type_has_tag 
2117                                 (modest_runtime_get_protocol_registry (),
2118                                  protocol_type,
2119                                  MODEST_PROTOCOL_REGISTRY_STORE_FORBID_MESSAGE_ADD);
2120                 }
2121         }
2122
2123         /* apply special filters */
2124         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_ACCOUNTS)) {
2125                 if (TNY_IS_ACCOUNT (instance))
2126                         return FALSE;
2127         }
2128
2129         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_FOLDERS)) {
2130                 if (TNY_IS_FOLDER (instance))
2131                         return FALSE;
2132         }
2133
2134         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_LOCAL_FOLDERS)) {
2135                 if (TNY_IS_ACCOUNT (instance)) {
2136                         if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (instance)))
2137                                 return FALSE;
2138                 } else if (TNY_IS_FOLDER (instance)) {
2139                         if (modest_tny_folder_is_local_folder (TNY_FOLDER (instance)))
2140                                 return FALSE;
2141                 }
2142         }
2143
2144         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MCC_FOLDERS)) {
2145                 if (TNY_IS_ACCOUNT (instance)) {
2146                         if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (instance)))
2147                                 return FALSE;
2148                 } else if (TNY_IS_FOLDER (instance)) {
2149                         if (modest_tny_folder_is_memory_card_folder (TNY_FOLDER (instance)))
2150                                 return FALSE;
2151                 }
2152         }
2153
2154         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_SHOW_ONLY_MAILBOXES)) {
2155                 /* A mailbox is a fake folder with an @ in the middle of the name */
2156                 if (!TNY_IS_FOLDER (instance) ||
2157                     !(tny_folder_get_caps (TNY_FOLDER (instance)) & TNY_FOLDER_CAPS_NOSELECT)) {
2158                         return FALSE;
2159                 } else {
2160                         const gchar *folder_name;
2161                         folder_name = tny_folder_get_name (TNY_FOLDER (instance));
2162                         if (!folder_name || strchr (folder_name, '@') == NULL)
2163                                 return FALSE;
2164                 }
2165                 
2166         }
2167
2168         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
2169                 if (TNY_IS_FOLDER (instance)) {
2170                         /* Check folder rules */
2171                         ModestTnyFolderRules rules;
2172
2173                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2174                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
2175                 } else if (TNY_IS_ACCOUNT (instance)) {
2176                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
2177                                 retval = FALSE;
2178                         } else {
2179                                 retval = TRUE;
2180                         }
2181                 }
2182         }
2183
2184         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
2185                 if (TNY_IS_FOLDER (instance)) {
2186                         TnyFolderType guess_type;
2187
2188                         if (TNY_FOLDER_TYPE_NORMAL) {
2189                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
2190                         } else {
2191                                 guess_type = type;
2192                         }
2193
2194                         switch (type) {
2195                         case TNY_FOLDER_TYPE_OUTBOX:
2196                         case TNY_FOLDER_TYPE_SENT:
2197                         case TNY_FOLDER_TYPE_DRAFTS:
2198                         case TNY_FOLDER_TYPE_ARCHIVE:
2199                         case TNY_FOLDER_TYPE_INBOX:
2200                                 retval = FALSE;
2201                                 break;
2202                         case TNY_FOLDER_TYPE_UNKNOWN:
2203                         case TNY_FOLDER_TYPE_NORMAL:
2204                                 break;
2205                         default:
2206                                 break;
2207                         }
2208
2209                 } else if (TNY_IS_ACCOUNT (instance)) {
2210                         retval = FALSE;
2211                 }
2212         }
2213
2214         if (retval && TNY_IS_FOLDER (instance)) {
2215                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2216         }
2217
2218         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2219                 if (TNY_IS_FOLDER (instance)) {
2220                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2221                 } else if (TNY_IS_ACCOUNT (instance)) {
2222                         retval = FALSE;
2223                 }
2224         }
2225
2226         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2227                 if (TNY_IS_FOLDER (instance)) {
2228                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2229                 } else if (TNY_IS_ACCOUNT (instance)) {
2230                         retval = FALSE;
2231                 }
2232         }
2233
2234         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2235                 if (TNY_IS_FOLDER (instance)) {
2236                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2237                 } else if (TNY_IS_ACCOUNT (instance)) {
2238                         retval = FALSE;
2239                 }
2240         }
2241
2242         /* Free */
2243         g_object_unref (instance);
2244         g_free (fname);
2245
2246         return retval;
2247 }
2248
2249
2250 gboolean
2251 modest_folder_view_update_model (ModestFolderView *self,
2252                                  TnyAccountStore *account_store)
2253 {
2254         ModestFolderViewPrivate *priv;
2255         GtkTreeModel *model;
2256         GtkTreeModel *filter_model = NULL, *sortable = NULL;
2257
2258         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2259         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2260                               FALSE);
2261
2262         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2263
2264         /* Notify that there is no folder selected */
2265         g_signal_emit (G_OBJECT(self),
2266                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2267                        NULL, FALSE);
2268         if (priv->cur_folder_store) {
2269                 g_object_unref (priv->cur_folder_store);
2270                 priv->cur_folder_store = NULL;
2271         }
2272
2273         /* FIXME: the local accounts are not shown when the query
2274            selects only the subscribed folders */
2275 #ifdef MODEST_TOOLKIT_HILDON2
2276         TnyGtkFolderListStoreFlags flags;
2277         flags = TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH;
2278         if (!priv->do_refresh)
2279                 flags &= TNY_GTK_FOLDER_LIST_STORE_FLAG_NO_REFRESH;
2280         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
2281                                                           flags);
2282         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2283                                                       MODEST_FOLDER_PATH_SEPARATOR);
2284 #else
2285         model = tny_gtk_folder_store_tree_model_new (NULL);
2286 #endif
2287
2288         /* When the model is a list store (plain representation) the
2289            outbox is not a child of any account so we have to manually
2290            delete it because removing the local folders account won't
2291            delete it (because tny_folder_get_account() is not defined
2292            for a merge folder */
2293         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2294                 TnyAccount *account;
2295                 ModestTnyAccountStore *acc_store;
2296
2297                 acc_store = modest_runtime_get_account_store ();
2298                 account = modest_tny_account_store_get_local_folders_account (acc_store);
2299
2300                 if (g_signal_handler_is_connected (account,
2301                                                    priv->outbox_deleted_handler))
2302                         g_signal_handler_disconnect (account,
2303                                                      priv->outbox_deleted_handler);
2304
2305                 priv->outbox_deleted_handler =
2306                         g_signal_connect (account,
2307                                           "outbox-deleted",
2308                                           G_CALLBACK (on_outbox_deleted_cb),
2309                                           self);
2310                 g_object_unref (account);
2311         }
2312
2313         /* Get the accounts: */
2314         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2315                                         TNY_LIST (model),
2316                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2317
2318         sortable = gtk_tree_model_sort_new_with_model (model);
2319         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2320                                               NAME_COLUMN,
2321                                               GTK_SORT_ASCENDING);
2322         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2323                                          NAME_COLUMN,
2324                                          cmp_rows, NULL, NULL);
2325
2326         /* Create filter model */
2327         filter_model = gtk_tree_model_filter_new (sortable, NULL);
2328         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2329                                                 filter_row,
2330                                                 self,
2331                                                 NULL);
2332
2333         if (priv->activity_changed_handler > 0) {
2334                 GtkTreeModel *old_tny_model;
2335
2336                 if (get_inner_models (self, NULL, NULL, &old_tny_model)) {
2337                         g_signal_handler_disconnect (G_OBJECT (old_tny_model), priv->activity_changed_handler);
2338                 }
2339                 priv->activity_changed_handler = 0;
2340         }
2341
2342         /* Set new model */
2343         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2344 #ifndef MODEST_TOOLKIT_HILDON2
2345         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2346                           (GCallback) on_row_inserted_maybe_select_folder, self);
2347 #endif
2348
2349 #ifdef MODEST_TOOLKIT_HILDON2
2350         priv->activity_changed_handler = 
2351                 g_signal_connect (G_OBJECT (model), "activity-changed", G_CALLBACK (on_activity_changed), self);
2352 #endif
2353
2354         g_object_unref (model);
2355         g_object_unref (filter_model);
2356         g_object_unref (sortable);
2357
2358         /* Force a reselection of the INBOX next time the widget is shown */
2359         priv->reselect = TRUE;
2360
2361         return TRUE;
2362 }
2363
2364
2365 static void
2366 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2367 {
2368         GtkTreeModel *model = NULL;
2369         TnyFolderStore *folder = NULL;
2370         GtkTreeIter iter;
2371         ModestFolderView *tree_view = NULL;
2372         ModestFolderViewPrivate *priv = NULL;
2373         gboolean selected = FALSE;
2374
2375         g_return_if_fail (sel);
2376         g_return_if_fail (user_data);
2377
2378         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2379
2380         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2381
2382         tree_view = MODEST_FOLDER_VIEW (user_data);
2383
2384         if (selected) {
2385                 gtk_tree_model_get (model, &iter,
2386                                     INSTANCE_COLUMN, &folder,
2387                                     -1);
2388
2389                 /* If the folder is the same do not notify */
2390                 if (folder && priv->cur_folder_store == folder) {
2391                         g_object_unref (folder);
2392                         return;
2393                 }
2394         }
2395
2396         /* Current folder was unselected */
2397         if (priv->cur_folder_store) {
2398                 /* We must do this firstly because a libtinymail-camel
2399                    implementation detail. If we issue the signal
2400                    before doing the sync_async, then that signal could
2401                    cause (and it actually does it) a free of the
2402                    summary of the folder (because the main window will
2403                    clear the headers view */
2404                 if (TNY_IS_FOLDER(priv->cur_folder_store))
2405                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2406                                                FALSE, NULL, NULL, NULL);
2407
2408                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2409                        priv->cur_folder_store, FALSE);
2410
2411                 g_object_unref (priv->cur_folder_store);
2412                 priv->cur_folder_store = NULL;
2413         }
2414
2415         /* New current references */
2416         priv->cur_folder_store = folder;
2417
2418         /* New folder has been selected. Do not notify if there is
2419            nothing new selected */
2420         if (selected) {
2421                 g_signal_emit (G_OBJECT(tree_view),
2422                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2423                                0, priv->cur_folder_store, TRUE);
2424         }
2425 }
2426
2427 static void
2428 on_row_activated (GtkTreeView *treeview,
2429                   GtkTreePath *treepath,
2430                   GtkTreeViewColumn *column,
2431                   gpointer user_data)
2432 {
2433         GtkTreeModel *model = NULL;
2434         TnyFolderStore *folder = NULL;
2435         GtkTreeIter iter;
2436         ModestFolderView *self = NULL;
2437         ModestFolderViewPrivate *priv = NULL;
2438
2439         g_return_if_fail (treeview);
2440         g_return_if_fail (user_data);
2441
2442         self = MODEST_FOLDER_VIEW (user_data);
2443         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2444
2445         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2446
2447         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2448                 return;
2449
2450         gtk_tree_model_get (model, &iter,
2451                             INSTANCE_COLUMN, &folder,
2452                             -1);
2453
2454         g_signal_emit (G_OBJECT(self),
2455                        signals[FOLDER_ACTIVATED_SIGNAL],
2456                        0, folder);
2457
2458 #ifdef MODEST_TOOLKIT_HILDON2
2459         HildonUIMode ui_mode;
2460         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2461         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2462                 if (priv->cur_folder_store)
2463                         g_object_unref (priv->cur_folder_store);
2464                 priv->cur_folder_store = g_object_ref (folder);
2465         }
2466 #endif
2467
2468         g_object_unref (folder);
2469 }
2470
2471 TnyFolderStore *
2472 modest_folder_view_get_selected (ModestFolderView *self)
2473 {
2474         ModestFolderViewPrivate *priv;
2475
2476         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2477
2478         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2479         if (priv->cur_folder_store)
2480                 g_object_ref (priv->cur_folder_store);
2481
2482         return priv->cur_folder_store;
2483 }
2484
2485 static gint
2486 get_cmp_rows_type_pos (GObject *folder)
2487 {
2488         /* Remote accounts -> Local account -> MMC account .*/
2489         /* 0, 1, 2 */
2490
2491         if (TNY_IS_ACCOUNT (folder) &&
2492                 modest_tny_account_is_virtual_local_folders (
2493                         TNY_ACCOUNT (folder))) {
2494                 return 1;
2495         } else if (TNY_IS_ACCOUNT (folder)) {
2496                 TnyAccount *account = TNY_ACCOUNT (folder);
2497                 const gchar *account_id = tny_account_get_id (account);
2498                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2499                         return 2;
2500                 else
2501                         return 0;
2502         }
2503         else {
2504                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2505                 return -1; /* Should never happen */
2506         }
2507 }
2508
2509 static gboolean
2510 inbox_is_special (TnyFolderStore *folder_store)
2511 {
2512         gboolean is_special = TRUE;
2513
2514         if (TNY_IS_FOLDER (folder_store)) {
2515                 const gchar *id;
2516                 gchar *downcase;
2517                 gchar *last_bar;
2518                 gchar *last_inbox_bar;
2519
2520                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2521                 downcase = g_utf8_strdown (id, -1);
2522                 last_bar = g_strrstr (downcase, "/");
2523                 if (last_bar) {
2524                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2525                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2526                                 is_special = FALSE;
2527                 } else {
2528                         is_special = FALSE;
2529                 }
2530                 g_free (downcase);
2531         }
2532         return is_special;
2533 }
2534
2535 static gint
2536 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2537 {
2538         TnyAccount *account;
2539         gboolean is_special;
2540         /* Inbox, Outbox, Drafts, Sent, User */
2541         /* 0, 1, 2, 3, 4 */
2542
2543         if (!TNY_IS_FOLDER (folder_store))
2544                 return 4;
2545         switch (t) {
2546         case TNY_FOLDER_TYPE_INBOX:
2547         {
2548                 account = tny_folder_get_account (folder_store);
2549                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2550
2551                 /* In inbox case we need to know if the inbox is really the top
2552                  * inbox of the account, or if it's a submailbox inbox. To do
2553                  * this we'll apply an heuristic rule: Find last "/" and check
2554                  * if it's preceeded by another Inbox */
2555                 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2556                 g_object_unref (account);
2557                 return is_special?0:4;
2558         }
2559         break;
2560         case TNY_FOLDER_TYPE_OUTBOX:
2561                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2562                 break;
2563         case TNY_FOLDER_TYPE_DRAFTS:
2564         {
2565                 account = tny_folder_get_account (folder_store);
2566                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2567                 g_object_unref (account);
2568                 return is_special?1:4;
2569         }
2570         break;
2571         case TNY_FOLDER_TYPE_SENT:
2572         {
2573                 account = tny_folder_get_account (folder_store);
2574                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2575                 g_object_unref (account);
2576                 return is_special?3:4;
2577         }
2578         break;
2579         default:
2580                 return 4;
2581         }
2582 }
2583
2584 static gint
2585 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2586 {
2587         const gchar *a1_name, *a2_name;
2588
2589         a1_name = tny_account_get_name (a1);
2590         a2_name = tny_account_get_name (a2);
2591
2592         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2593 }
2594
2595 static gint
2596 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2597 {
2598         TnyAccount *a1 = NULL, *a2 = NULL;
2599         gint cmp;
2600
2601         if (TNY_IS_ACCOUNT (s1)) {
2602                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2603         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2604                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2605         }
2606
2607         if (TNY_IS_ACCOUNT (s2)) {
2608                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2609         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2610                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2611         }
2612
2613         if (!a1 || !a2) {
2614                 if (!a1 && !a2)
2615                         cmp = 0;
2616                 else if (!a1)
2617                         cmp = 1;
2618                 else
2619                         cmp = -1;
2620                 goto finish;
2621         }
2622
2623         if (a1 == a2) {
2624                 cmp = 0;
2625                 goto finish;
2626         }
2627         /* First we sort with the type of account */
2628         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2629         if (cmp != 0)
2630                 goto finish;
2631
2632         cmp = compare_account_names (a1, a2);
2633
2634 finish:
2635         if (a1)
2636                 g_object_unref (a1);
2637         if (a2)
2638                 g_object_unref (a2);
2639
2640         return cmp;
2641 }
2642
2643 static gint
2644 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2645 {
2646         gint is_account1, is_account2;
2647
2648         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2649         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2650
2651         return is_account2 - is_account1;
2652 }
2653
2654 /*
2655  * This function orders the mail accounts according to these rules:
2656  * 1st - remote accounts
2657  * 2nd - local account
2658  * 3rd - MMC account
2659  */
2660 static gint
2661 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2662           gpointer user_data)
2663 {
2664         gint cmp = 0;
2665         gchar *name1 = NULL;
2666         gchar *name2 = NULL;
2667         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2668         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2669         GObject *folder1 = NULL;
2670         GObject *folder2 = NULL;
2671
2672         gtk_tree_model_get (tree_model, iter1,
2673                             NAME_COLUMN, &name1,
2674                             TYPE_COLUMN, &type,
2675                             INSTANCE_COLUMN, &folder1,
2676                             -1);
2677         gtk_tree_model_get (tree_model, iter2,
2678                             NAME_COLUMN, &name2,
2679                             TYPE_COLUMN, &type2,
2680                             INSTANCE_COLUMN, &folder2,
2681                             -1);
2682
2683         /* Return if we get no folder. This could happen when folder
2684            operations are happening. The model is updated after the
2685            folder copy/move actually occurs, so there could be
2686            situations where the model to be drawn is not correct */
2687         if (!folder1 || !folder2)
2688                 goto finish;
2689
2690         /* Sort by type. First the special folders, then the archives */
2691         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2692         if (cmp != 0)
2693                 goto finish;
2694
2695         /* Now we sort using the account of each folder */
2696         if (TNY_IS_FOLDER_STORE (folder1) && 
2697             TNY_IS_FOLDER_STORE (folder2)) {
2698                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2699                 if (cmp != 0)
2700                         goto finish;
2701
2702                 /* Each group is preceeded by its account */
2703                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2704                 if (cmp != 0)
2705                         goto finish;
2706         }
2707
2708         /* Pure sort by name */
2709         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2710  finish:
2711         if (folder1)
2712                 g_object_unref(G_OBJECT(folder1));
2713         if (folder2)
2714                 g_object_unref(G_OBJECT(folder2));
2715
2716         g_free (name1);
2717         g_free (name2);
2718
2719         return cmp;
2720 }
2721
2722 /*****************************************************************************/
2723 /*                        DRAG and DROP stuff                                */
2724 /*****************************************************************************/
2725 /*
2726  * This function fills the #GtkSelectionData with the row and the
2727  * model that has been dragged. It's called when this widget is a
2728  * source for dnd after the event drop happened
2729  */
2730 static void
2731 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2732                   guint info, guint time, gpointer data)
2733 {
2734         GtkTreeSelection *selection;
2735         GtkTreeModel *model;
2736         GtkTreeIter iter;
2737         GtkTreePath *source_row;
2738
2739         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2740         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2741
2742                 source_row = gtk_tree_model_get_path (model, &iter);
2743                 gtk_tree_set_row_drag_data (selection_data,
2744                                             model,
2745                                             source_row);
2746
2747                 gtk_tree_path_free (source_row);
2748         }
2749 }
2750
2751 typedef struct _DndHelper {
2752         ModestFolderView *folder_view;
2753         gboolean delete_source;
2754         GtkTreePath *source_row;
2755 } DndHelper;
2756
2757 static void
2758 dnd_helper_destroyer (DndHelper *helper)
2759 {
2760         /* Free the helper */
2761         gtk_tree_path_free (helper->source_row);
2762         g_slice_free (DndHelper, helper);
2763 }
2764
2765 static void
2766 xfer_folder_cb (ModestMailOperation *mail_op,
2767                 TnyFolder *new_folder,
2768                 gpointer user_data)
2769 {
2770         if (new_folder) {
2771                 /* Select the folder */
2772                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2773                                                   new_folder, FALSE);
2774         }
2775 }
2776
2777
2778 /* get the folder for the row the treepath refers to. */
2779 /* folder must be unref'd */
2780 static TnyFolderStore *
2781 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2782 {
2783         GtkTreeIter iter;
2784         TnyFolderStore *folder = NULL;
2785
2786         if (gtk_tree_model_get_iter (model,&iter, path))
2787                 gtk_tree_model_get (model, &iter,
2788                                     INSTANCE_COLUMN, &folder,
2789                                     -1);
2790         return folder;
2791 }
2792
2793
2794 /*
2795  * This function is used by drag_data_received_cb to manage drag and
2796  * drop of a header, i.e, and drag from the header view to the folder
2797  * view.
2798  */
2799 static void
2800 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2801                                 GtkTreeModel *dest_model,
2802                                 GtkTreePath  *dest_row,
2803                                 GtkSelectionData *selection_data)
2804 {
2805         TnyList *headers = NULL;
2806         TnyFolder *folder = NULL, *src_folder = NULL;
2807         TnyFolderType folder_type;
2808         GtkTreeIter source_iter, dest_iter;
2809         ModestWindowMgr *mgr = NULL;
2810         ModestWindow *main_win = NULL;
2811         gchar **uris, **tmp;
2812
2813         /* Build the list of headers */
2814         mgr = modest_runtime_get_window_mgr ();
2815         headers = tny_simple_list_new ();
2816         uris = modest_dnd_selection_data_get_paths (selection_data);
2817         tmp = uris;
2818
2819         while (*tmp != NULL) {
2820                 TnyHeader *header;
2821                 GtkTreePath *path;
2822                 gboolean first = TRUE;
2823
2824                 /* Get header */
2825                 path = gtk_tree_path_new_from_string (*tmp);
2826                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2827                 gtk_tree_model_get (source_model, &source_iter,
2828                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2829                                     &header, -1);
2830
2831                 /* Do not enable d&d of headers already opened */
2832                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2833                         tny_list_append (headers, G_OBJECT (header));
2834
2835                 if (G_UNLIKELY (first)) {
2836                         src_folder = tny_header_get_folder (header);
2837                         first = FALSE;
2838                 }
2839
2840                 /* Free and go on */
2841                 gtk_tree_path_free (path);
2842                 g_object_unref (header);
2843                 tmp++;
2844         }
2845         g_strfreev (uris);
2846
2847         /* This could happen ig we perform a d&d very quickly over the
2848            same row that row could dissapear because message is
2849            transferred */
2850         if (!TNY_IS_FOLDER (src_folder))
2851                 goto cleanup;
2852
2853         /* Get the target folder */
2854         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2855         gtk_tree_model_get (dest_model, &dest_iter,
2856                             INSTANCE_COLUMN,
2857                             &folder, -1);
2858
2859         if (!folder || !TNY_IS_FOLDER(folder)) {
2860 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2861                 goto cleanup;
2862         }
2863
2864         folder_type = modest_tny_folder_guess_folder_type (folder);
2865         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2866 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2867                 goto cleanup;  /* cannot move messages there */
2868         }
2869
2870         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2871 /*              g_warning ("folder not writable"); */
2872                 goto cleanup; /* verboten! */
2873         }
2874
2875         /* Ask for confirmation to move */
2876         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2877         if (!main_win) {
2878                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2879                 goto cleanup;
2880         }
2881
2882         /* Transfer messages */
2883         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2884                                                     headers, folder);
2885
2886         /* Frees */
2887 cleanup:
2888         if (G_IS_OBJECT (src_folder))
2889                 g_object_unref (src_folder);
2890         if (G_IS_OBJECT(folder))
2891                 g_object_unref (G_OBJECT (folder));
2892         if (G_IS_OBJECT(headers))
2893                 g_object_unref (headers);
2894 }
2895
2896 typedef struct {
2897         TnyFolderStore *src_folder;
2898         TnyFolderStore *dst_folder;
2899         ModestFolderView *folder_view;
2900         DndHelper *helper;
2901 } DndFolderInfo;
2902
2903 static void
2904 dnd_folder_info_destroyer (DndFolderInfo *info)
2905 {
2906         if (info->src_folder)
2907                 g_object_unref (info->src_folder);
2908         if (info->dst_folder)
2909                 g_object_unref (info->dst_folder);
2910         g_slice_free (DndFolderInfo, info);
2911 }
2912
2913 static void
2914 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2915                                     GtkWindow *parent_window,
2916                                     TnyAccount *account)
2917 {
2918         /* Show error */
2919         modest_ui_actions_on_account_connection_error (parent_window, account);
2920
2921         /* Free the helper & info */
2922         dnd_helper_destroyer (info->helper);
2923         dnd_folder_info_destroyer (info);
2924 }
2925
2926 static void
2927 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2928                                                      GError *err,
2929                                                      GtkWindow *parent_window,
2930                                                      TnyAccount *account,
2931                                                      gpointer user_data)
2932 {
2933         DndFolderInfo *info = NULL;
2934         ModestMailOperation *mail_op;
2935
2936         info = (DndFolderInfo *) user_data;
2937
2938         if (err || canceled) {
2939                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2940                 return;
2941         }
2942
2943         /* Do the mail operation */
2944         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2945                                                                  modest_ui_actions_move_folder_error_handler,
2946                                                                  info->src_folder, NULL);
2947
2948         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2949                                          mail_op);
2950
2951         /* Transfer the folder */
2952         modest_mail_operation_xfer_folder (mail_op,
2953                                            TNY_FOLDER (info->src_folder),
2954                                            info->dst_folder,
2955                                            info->helper->delete_source,
2956                                            xfer_folder_cb,
2957                                            info->helper->folder_view);
2958
2959         /* Frees */
2960         g_object_unref (G_OBJECT (mail_op));
2961         dnd_helper_destroyer (info->helper);
2962         dnd_folder_info_destroyer (info);
2963 }
2964
2965
2966 static void
2967 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2968                                                      GError *err,
2969                                                      GtkWindow *parent_window,
2970                                                      TnyAccount *account,
2971                                                      gpointer user_data)
2972 {
2973         DndFolderInfo *info = NULL;
2974
2975         info = (DndFolderInfo *) user_data;
2976
2977         if (err || canceled) {
2978                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2979                 return;
2980         }
2981
2982         /* Connect to source folder and perform the copy/move */
2983         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2984                                                        info->src_folder,
2985                                                        drag_and_drop_from_folder_view_src_folder_performer,
2986                                                        info);
2987 }
2988
2989 /*
2990  * This function is used by drag_data_received_cb to manage drag and
2991  * drop of a folder, i.e, and drag from the folder view to the same
2992  * folder view.
2993  */
2994 static void
2995 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2996                                 GtkTreeModel     *dest_model,
2997                                 GtkTreePath      *dest_row,
2998                                 GtkSelectionData *selection_data,
2999                                 DndHelper        *helper)
3000 {
3001         GtkTreeIter dest_iter, iter;
3002         TnyFolderStore *dest_folder = NULL;
3003         TnyFolderStore *folder = NULL;
3004         gboolean forbidden = FALSE;
3005         ModestWindow *win;
3006         DndFolderInfo *info = NULL;
3007
3008         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
3009         if (!win) {
3010                 g_warning ("%s: BUG: no main window", __FUNCTION__);
3011                 dnd_helper_destroyer (helper);
3012                 return;
3013         }
3014
3015         if (!forbidden) {
3016                 /* check the folder rules for the destination */
3017                 folder = tree_path_to_folder (dest_model, dest_row);
3018                 if (TNY_IS_FOLDER(folder)) {
3019                         ModestTnyFolderRules rules =
3020                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
3021                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
3022                 } else if (TNY_IS_FOLDER_STORE(folder)) {
3023                         /* enable local root as destination for folders */
3024                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
3025                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
3026                                 forbidden = TRUE;
3027                 }
3028                 g_object_unref (folder);
3029         }
3030         if (!forbidden) {
3031                 /* check the folder rules for the source */
3032                 folder = tree_path_to_folder (source_model, helper->source_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_MOVEABLE;
3037                 } else
3038                         forbidden = TRUE;
3039                 g_object_unref (folder);
3040         }
3041
3042
3043         /* Check if the drag is possible */
3044         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
3045                 /* Show error */
3046                 modest_platform_run_information_dialog ((GtkWindow *) win, 
3047                                                         _("mail_in_ui_folder_move_target_error"), 
3048                                                         FALSE);
3049                 /* Restore the previous selection */
3050                 folder = tree_path_to_folder (source_model, helper->source_row);
3051                 if (folder) {
3052                         if (TNY_IS_FOLDER (folder))
3053                                 modest_folder_view_select_folder (helper->folder_view, 
3054                                                                   TNY_FOLDER (folder), FALSE);
3055                         g_object_unref (folder);
3056                 }
3057                 dnd_helper_destroyer (helper);
3058                 return;
3059         }
3060
3061         /* Get data */
3062         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
3063         gtk_tree_model_get (dest_model, &dest_iter,
3064                             INSTANCE_COLUMN,
3065                             &dest_folder, -1);
3066         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
3067         gtk_tree_model_get (source_model, &iter,
3068                             INSTANCE_COLUMN,
3069                             &folder, -1);
3070
3071         /* Create the info for the performer */
3072         info = g_slice_new0 (DndFolderInfo);
3073         info->src_folder = g_object_ref (folder);
3074         info->dst_folder = g_object_ref (dest_folder);
3075         info->helper = helper;
3076
3077         /* Connect to the destination folder and perform the copy/move */
3078         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
3079                                                        dest_folder,
3080                                                        drag_and_drop_from_folder_view_dst_folder_performer,
3081                                                        info);
3082
3083         /* Frees */
3084         g_object_unref (dest_folder);
3085         g_object_unref (folder);
3086 }
3087
3088 /*
3089  * This function receives the data set by the "drag-data-get" signal
3090  * handler. This information comes within the #GtkSelectionData. This
3091  * function will manage both the drags of folders of the treeview and
3092  * drags of headers of the header view widget.
3093  */
3094 static void
3095 on_drag_data_received (GtkWidget *widget,
3096                        GdkDragContext *context,
3097                        gint x,
3098                        gint y,
3099                        GtkSelectionData *selection_data,
3100                        guint target_type,
3101                        guint time,
3102                        gpointer data)
3103 {
3104         GtkWidget *source_widget;
3105         GtkTreeModel *dest_model, *source_model;
3106         GtkTreePath *source_row, *dest_row;
3107         GtkTreeViewDropPosition pos;
3108         gboolean delete_source = FALSE;
3109         gboolean success = FALSE;
3110
3111         /* Do not allow further process */
3112         g_signal_stop_emission_by_name (widget, "drag-data-received");
3113         source_widget = gtk_drag_get_source_widget (context);
3114
3115         /* Get the action */
3116         if (context->action == GDK_ACTION_MOVE) {
3117                 delete_source = TRUE;
3118
3119                 /* Notify that there is no folder selected. We need to
3120                    do this in order to update the headers view (and
3121                    its monitors, because when moving, the old folder
3122                    won't longer exist. We can not wait for the end of
3123                    the operation, because the operation won't start if
3124                    the folder is in use */
3125                 if (source_widget == widget) {
3126                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
3127                         gtk_tree_selection_unselect_all (sel);
3128                 }
3129         }
3130
3131         /* Check if the get_data failed */
3132         if (selection_data == NULL || selection_data->length < 0)
3133                 goto end;
3134
3135         /* Select the destination model */
3136         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3137
3138         /* Get the path to the destination row. Can not call
3139            gtk_tree_view_get_drag_dest_row() because the source row
3140            is not selected anymore */
3141         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
3142                                            &dest_row, &pos);
3143
3144         /* Only allow drops IN other rows */
3145         if (!dest_row ||
3146             pos == GTK_TREE_VIEW_DROP_BEFORE ||
3147             pos == GTK_TREE_VIEW_DROP_AFTER)
3148                 goto end;
3149
3150         success = TRUE;
3151         /* Drags from the header view */
3152         if (source_widget != widget) {
3153                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
3154
3155                 drag_and_drop_from_header_view (source_model,
3156                                                 dest_model,
3157                                                 dest_row,
3158                                                 selection_data);
3159         } else {
3160                 DndHelper *helper = NULL;
3161
3162                 /* Get the source model and row */
3163                 gtk_tree_get_row_drag_data (selection_data,
3164                                             &source_model,
3165                                             &source_row);
3166
3167                 /* Create the helper */
3168                 helper = g_slice_new0 (DndHelper);
3169                 helper->delete_source = delete_source;
3170                 helper->source_row = gtk_tree_path_copy (source_row);
3171                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
3172
3173                 drag_and_drop_from_folder_view (source_model,
3174                                                 dest_model,
3175                                                 dest_row,
3176                                                 selection_data,
3177                                                 helper);
3178
3179                 gtk_tree_path_free (source_row);
3180         }
3181
3182         /* Frees */
3183         gtk_tree_path_free (dest_row);
3184
3185  end:
3186         /* Finish the drag and drop */
3187         gtk_drag_finish (context, success, FALSE, time);
3188 }
3189
3190 /*
3191  * We define a "drag-drop" signal handler because we do not want to
3192  * use the default one, because the default one always calls
3193  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
3194  * signal handler, because there we have all the information available
3195  * to know if the dnd was a success or not.
3196  */
3197 static gboolean
3198 drag_drop_cb (GtkWidget      *widget,
3199               GdkDragContext *context,
3200               gint            x,
3201               gint            y,
3202               guint           time,
3203               gpointer        user_data)
3204 {
3205         gpointer target;
3206
3207         if (!context->targets)
3208                 return FALSE;
3209
3210         /* Check if we're dragging a folder row */
3211         target = gtk_drag_dest_find_target (widget, context, NULL);
3212
3213         /* Request the data from the source. */
3214         gtk_drag_get_data(widget, context, target, time);
3215
3216     return TRUE;
3217 }
3218
3219 /*
3220  * This function expands a node of a tree view if it's not expanded
3221  * yet. Not sure why it needs the threads stuff, but gtk+`example code
3222  * does that, so that's why they're here.
3223  */
3224 static gint
3225 expand_row_timeout (gpointer data)
3226 {
3227         GtkTreeView *tree_view = data;
3228         GtkTreePath *dest_path = NULL;
3229         GtkTreeViewDropPosition pos;
3230         gboolean result = FALSE;
3231
3232         gdk_threads_enter ();
3233
3234         gtk_tree_view_get_drag_dest_row (tree_view,
3235                                          &dest_path,
3236                                          &pos);
3237
3238         if (dest_path &&
3239             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3240              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3241                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3242                 gtk_tree_path_free (dest_path);
3243         }
3244         else {
3245                 if (dest_path)
3246                         gtk_tree_path_free (dest_path);
3247
3248                 result = TRUE;
3249         }
3250
3251         gdk_threads_leave ();
3252
3253         return result;
3254 }
3255
3256 /*
3257  * This function is called whenever the pointer is moved over a widget
3258  * while dragging some data. It installs a timeout that will expand a
3259  * node of the treeview if not expanded yet. This function also calls
3260  * gdk_drag_status in order to set the suggested action that will be
3261  * used by the "drag-data-received" signal handler to know if we
3262  * should do a move or just a copy of the data.
3263  */
3264 static gboolean
3265 on_drag_motion (GtkWidget      *widget,
3266                 GdkDragContext *context,
3267                 gint            x,
3268                 gint            y,
3269                 guint           time,
3270                 gpointer        user_data)
3271 {
3272         GtkTreeViewDropPosition pos;
3273         GtkTreePath *dest_row;
3274         GtkTreeModel *dest_model;
3275         ModestFolderViewPrivate *priv;
3276         GdkDragAction suggested_action;
3277         gboolean valid_location = FALSE;
3278         TnyFolderStore *folder = NULL;
3279
3280         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3281
3282         if (priv->timer_expander != 0) {
3283                 g_source_remove (priv->timer_expander);
3284                 priv->timer_expander = 0;
3285         }
3286
3287         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3288                                            x, y,
3289                                            &dest_row,
3290                                            &pos);
3291
3292         /* Do not allow drops between folders */
3293         if (!dest_row ||
3294             pos == GTK_TREE_VIEW_DROP_BEFORE ||
3295             pos == GTK_TREE_VIEW_DROP_AFTER) {
3296                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3297                 gdk_drag_status(context, 0, time);
3298                 valid_location = FALSE;
3299                 goto out;
3300         } else {
3301                 valid_location = TRUE;
3302         }
3303
3304         /* Check that the destination folder is writable */
3305         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3306         folder = tree_path_to_folder (dest_model, dest_row);
3307         if (folder && TNY_IS_FOLDER (folder)) {
3308                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3309
3310                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3311                         valid_location = FALSE;
3312                         goto out;
3313                 }
3314         }
3315
3316         /* Expand the selected row after 1/2 second */
3317         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3318                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3319         }
3320         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3321
3322         /* Select the desired action. By default we pick MOVE */
3323         suggested_action = GDK_ACTION_MOVE;
3324
3325         if (context->actions == GDK_ACTION_COPY)
3326             gdk_drag_status(context, GDK_ACTION_COPY, time);
3327         else if (context->actions == GDK_ACTION_MOVE)
3328             gdk_drag_status(context, GDK_ACTION_MOVE, time);
3329         else if (context->actions & suggested_action)
3330             gdk_drag_status(context, suggested_action, time);
3331         else
3332             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3333
3334  out:
3335         if (folder)
3336                 g_object_unref (folder);
3337         if (dest_row) {
3338                 gtk_tree_path_free (dest_row);
3339         }
3340         g_signal_stop_emission_by_name (widget, "drag-motion");
3341
3342         return valid_location;
3343 }
3344
3345 /*
3346  * This function sets the treeview as a source and a target for dnd
3347  * events. It also connects all the requirede signals.
3348  */
3349 static void
3350 setup_drag_and_drop (GtkTreeView *self)
3351 {
3352         /* Set up the folder view as a dnd destination. Set only the
3353            highlight flag, otherwise gtk will have a different
3354            behaviour */
3355 #ifdef MODEST_TOOLKIT_HILDON2
3356         return;
3357 #endif
3358         gtk_drag_dest_set (GTK_WIDGET (self),
3359                            GTK_DEST_DEFAULT_HIGHLIGHT,
3360                            folder_view_drag_types,
3361                            G_N_ELEMENTS (folder_view_drag_types),
3362                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
3363
3364         g_signal_connect (G_OBJECT (self),
3365                           "drag_data_received",
3366                           G_CALLBACK (on_drag_data_received),
3367                           NULL);
3368
3369
3370         /* Set up the treeview as a dnd source */
3371         gtk_drag_source_set (GTK_WIDGET (self),
3372                              GDK_BUTTON1_MASK,
3373                              folder_view_drag_types,
3374                              G_N_ELEMENTS (folder_view_drag_types),
3375                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
3376
3377         g_signal_connect (G_OBJECT (self),
3378                           "drag_motion",
3379                           G_CALLBACK (on_drag_motion),
3380                           NULL);
3381
3382         g_signal_connect (G_OBJECT (self),
3383                           "drag_data_get",
3384                           G_CALLBACK (on_drag_data_get),
3385                           NULL);
3386
3387         g_signal_connect (G_OBJECT (self),
3388                           "drag_drop",
3389                           G_CALLBACK (drag_drop_cb),
3390                           NULL);
3391 }
3392
3393 /*
3394  * This function manages the navigation through the folders using the
3395  * keyboard or the hardware keys in the device
3396  */
3397 static gboolean
3398 on_key_pressed (GtkWidget *self,
3399                 GdkEventKey *event,
3400                 gpointer user_data)
3401 {
3402         GtkTreeSelection *selection;
3403         GtkTreeIter iter;
3404         GtkTreeModel *model;
3405         gboolean retval = FALSE;
3406
3407         /* Up and Down are automatically managed by the treeview */
3408         if (event->keyval == GDK_Return) {
3409                 /* Expand/Collapse the selected row */
3410                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3411                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3412                         GtkTreePath *path;
3413
3414                         path = gtk_tree_model_get_path (model, &iter);
3415
3416                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3417                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3418                         else
3419                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3420                         gtk_tree_path_free (path);
3421                 }
3422                 /* No further processing */
3423                 retval = TRUE;
3424         }
3425
3426         return retval;
3427 }
3428
3429 /*
3430  * We listen to the changes in the local folder account name key,
3431  * because we want to show the right name in the view. The local
3432  * folder account name corresponds to the device name in the Maemo
3433  * version. We do this because we do not want to query gconf on each
3434  * tree view refresh. It's better to cache it and change whenever
3435  * necessary.
3436  */
3437 static void
3438 on_configuration_key_changed (ModestConf* conf,
3439                               const gchar *key,
3440                               ModestConfEvent event,
3441                               ModestConfNotificationId id,
3442                               ModestFolderView *self)
3443 {
3444         ModestFolderViewPrivate *priv;
3445
3446
3447         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3448         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3449
3450         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3451                 g_free (priv->local_account_name);
3452
3453                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3454                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3455                 else
3456                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3457                                                                            MODEST_CONF_DEVICE_NAME, NULL);
3458
3459                 /* Force a redraw */
3460 #if GTK_CHECK_VERSION(2, 8, 0)
3461                 GtkTreeViewColumn * tree_column;
3462
3463                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3464                                                         NAME_COLUMN);
3465                 gtk_tree_view_column_queue_resize (tree_column);
3466 #else
3467                 gtk_widget_queue_draw (GTK_WIDGET (self));
3468 #endif
3469         }
3470 }
3471
3472 void
3473 modest_folder_view_set_style (ModestFolderView *self,
3474                               ModestFolderViewStyle style)
3475 {
3476         ModestFolderViewPrivate *priv;
3477
3478         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3479         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3480                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3481
3482         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3483
3484
3485         priv->style = style;
3486 }
3487
3488 void
3489 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3490                                                              const gchar *account_id)
3491 {
3492         ModestFolderViewPrivate *priv;
3493         GtkTreeModel *model;
3494
3495         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3496
3497         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3498
3499         /* This will be used by the filter_row callback,
3500          * to decided which rows to show: */
3501         if (priv->visible_account_id) {
3502                 g_free (priv->visible_account_id);
3503                 priv->visible_account_id = NULL;
3504         }
3505         if (account_id)
3506                 priv->visible_account_id = g_strdup (account_id);
3507
3508         /* Refilter */
3509         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3510         if (GTK_IS_TREE_MODEL_FILTER (model))
3511                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3512
3513         /* Save settings to gconf */
3514         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3515                                    MODEST_CONF_FOLDER_VIEW_KEY);
3516
3517         /* Notify observers */
3518         g_signal_emit (G_OBJECT(self),
3519                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3520                        account_id);
3521 }
3522
3523 const gchar *
3524 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3525 {
3526         ModestFolderViewPrivate *priv;
3527
3528         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3529
3530         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3531
3532         return (const gchar *) priv->visible_account_id;
3533 }
3534
3535 static gboolean
3536 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3537 {
3538         do {
3539                 GtkTreeIter child;
3540                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3541
3542                 gtk_tree_model_get (model, iter,
3543                                     TYPE_COLUMN,
3544                                     &type, -1);
3545
3546                 gboolean result = FALSE;
3547                 if (type == TNY_FOLDER_TYPE_INBOX) {
3548                         result = TRUE;
3549                 }
3550                 if (result) {
3551                         *inbox_iter = *iter;
3552                         return TRUE;
3553                 }
3554
3555                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3556                         if (find_inbox_iter (model, &child, inbox_iter))
3557                                 return TRUE;
3558                 }
3559
3560         } while (gtk_tree_model_iter_next (model, iter));
3561
3562         return FALSE;
3563 }
3564
3565
3566
3567
3568 void
3569 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3570 {
3571 #ifndef MODEST_TOOLKIT_HILDON2
3572         GtkTreeModel *model;
3573         GtkTreeIter iter, inbox_iter;
3574         GtkTreeSelection *sel;
3575         GtkTreePath *path = NULL;
3576
3577         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3578
3579         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3580         if (!model)
3581                 return;
3582
3583         expand_root_items (self);
3584         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3585
3586         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3587                 g_warning ("%s: model is empty", __FUNCTION__);
3588                 return;
3589         }
3590
3591         if (find_inbox_iter (model, &iter, &inbox_iter))
3592                 path = gtk_tree_model_get_path (model, &inbox_iter);
3593         else
3594                 path = gtk_tree_path_new_first ();
3595
3596         /* Select the row and free */
3597         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3598         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3599         gtk_tree_path_free (path);
3600
3601         /* set focus */
3602         gtk_widget_grab_focus (GTK_WIDGET(self));
3603 #endif
3604 }
3605
3606
3607 /* recursive */
3608 static gboolean
3609 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3610                   TnyFolder* folder)
3611 {
3612         do {
3613                 GtkTreeIter child;
3614                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3615                 TnyFolder* a_folder;
3616                 gchar *name = NULL;
3617
3618                 gtk_tree_model_get (model, iter,
3619                                     INSTANCE_COLUMN, &a_folder,
3620                                     NAME_COLUMN, &name,
3621                                     TYPE_COLUMN, &type,
3622                                     -1);
3623                 g_free (name);
3624
3625                 if (folder == a_folder) {
3626                         g_object_unref (a_folder);
3627                         *folder_iter = *iter;
3628                         return TRUE;
3629                 }
3630                 g_object_unref (a_folder);
3631
3632                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3633                         if (find_folder_iter (model, &child, folder_iter, folder))
3634                                 return TRUE;
3635                 }
3636
3637         } while (gtk_tree_model_iter_next (model, iter));
3638
3639         return FALSE;
3640 }
3641
3642 #ifndef MODEST_TOOLKIT_HILDON2
3643 static void
3644 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3645                                      GtkTreePath *path,
3646                                      GtkTreeIter *iter,
3647                                      ModestFolderView *self)
3648 {
3649         ModestFolderViewPrivate *priv = NULL;
3650         GtkTreeSelection *sel;
3651         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3652         GObject *instance = NULL;
3653
3654         if (!MODEST_IS_FOLDER_VIEW(self))
3655                 return;
3656
3657         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3658
3659         priv->reexpand = TRUE;
3660
3661         gtk_tree_model_get (tree_model, iter,
3662                             TYPE_COLUMN, &type,
3663                             INSTANCE_COLUMN, &instance,
3664                             -1);
3665
3666         if (!instance)
3667                 return;
3668
3669         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3670                 priv->folder_to_select = g_object_ref (instance);
3671         }
3672         g_object_unref (instance);
3673
3674         if (priv->folder_to_select) {
3675
3676                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3677                                                        FALSE)) {
3678                         GtkTreePath *path;
3679                         path = gtk_tree_model_get_path (tree_model, iter);
3680                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3681
3682                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3683
3684                         gtk_tree_selection_select_iter (sel, iter);
3685                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3686
3687                         gtk_tree_path_free (path);
3688                 }
3689
3690                 /* Disable next */
3691                 modest_folder_view_disable_next_folder_selection (self);
3692
3693                 /* Refilter the model */
3694                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3695         }
3696 }
3697 #endif
3698
3699 void
3700 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3701 {
3702         ModestFolderViewPrivate *priv;
3703
3704         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3705
3706         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3707
3708         if (priv->folder_to_select)
3709                 g_object_unref(priv->folder_to_select);
3710
3711         priv->folder_to_select = NULL;
3712 }
3713
3714 gboolean
3715 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3716                                   gboolean after_change)
3717 {
3718         GtkTreeModel *model;
3719         GtkTreeIter iter, folder_iter;
3720         GtkTreeSelection *sel;
3721         ModestFolderViewPrivate *priv = NULL;
3722
3723         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3724         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3725
3726         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3727
3728         if (after_change) {
3729                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3730                 gtk_tree_selection_unselect_all (sel);
3731
3732                 if (priv->folder_to_select)
3733                         g_object_unref(priv->folder_to_select);
3734                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3735                 return TRUE;
3736         }
3737
3738         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3739         if (!model)
3740                 return FALSE;
3741
3742
3743         /* Refilter the model, before selecting the folder */
3744         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3745
3746         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3747                 g_warning ("%s: model is empty", __FUNCTION__);
3748                 return FALSE;
3749         }
3750
3751         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3752                 GtkTreePath *path;
3753
3754                 path = gtk_tree_model_get_path (model, &folder_iter);
3755                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3756
3757                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3758                 gtk_tree_selection_select_iter (sel, &folder_iter);
3759                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3760
3761                 gtk_tree_path_free (path);
3762                 return TRUE;
3763         }
3764         return FALSE;
3765 }
3766
3767
3768 void
3769 modest_folder_view_copy_selection (ModestFolderView *self)
3770 {
3771         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3772
3773         /* Copy selection */
3774         _clipboard_set_selected_data (self, FALSE);
3775 }
3776
3777 void
3778 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3779 {
3780         ModestFolderViewPrivate *priv = NULL;
3781         GtkTreeModel *model = NULL;
3782         const gchar **hidding = NULL;
3783         guint i, n_selected;
3784
3785         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3786         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3787
3788         /* Copy selection */
3789         if (!_clipboard_set_selected_data (folder_view, TRUE))
3790                 return;
3791
3792         /* Get hidding ids */
3793         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3794
3795         /* Clear hidding array created by previous cut operation */
3796         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3797
3798         /* Copy hidding array */
3799         priv->n_selected = n_selected;
3800         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3801         for (i=0; i < n_selected; i++)
3802                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3803
3804         /* Hide cut folders */
3805         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3806         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3807 }
3808
3809 void
3810 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3811                                ModestFolderView *folder_view_dst)
3812 {
3813         GtkTreeModel *filter_model = NULL;
3814         GtkTreeModel *model = NULL;
3815         GtkTreeModel *new_filter_model = NULL;
3816         GtkTreeModel *old_tny_model = NULL;
3817         GtkTreeModel *new_tny_model = NULL;
3818         ModestFolderViewPrivate *dst_priv;
3819
3820         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3821         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3822
3823         dst_priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view_dst);
3824         if (!get_inner_models (folder_view_src, NULL, NULL, &new_tny_model))
3825                 new_tny_model = NULL;
3826
3827         /* Get src model*/
3828         if (get_inner_models (folder_view_dst, NULL, NULL, &old_tny_model)) {
3829                 g_signal_handler_disconnect (G_OBJECT (old_tny_model), dst_priv->activity_changed_handler);
3830                 dst_priv->activity_changed_handler = 0;
3831         }
3832         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3833         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3834
3835         /* Build new filter model */
3836         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3837         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3838                                                 filter_row,
3839                                                 folder_view_dst,
3840                                                 NULL);
3841
3842
3843
3844         /* Set copied model */
3845         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3846 #ifndef MODEST_TOOLKIT_HILDON2
3847         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3848                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3849 #endif
3850 #ifdef MODEST_TOOLKIT_HILDON2
3851         if (new_tny_model)
3852                 dst_priv->activity_changed_handler = g_signal_connect (G_OBJECT (new_tny_model), "activity-changed",
3853                                                                        G_CALLBACK (on_activity_changed), folder_view_dst);
3854 #endif
3855
3856         /* Free */
3857         g_object_unref (new_filter_model);
3858 }
3859
3860 void
3861 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3862                                           gboolean show)
3863 {
3864         GtkTreeModel *model = NULL;
3865         ModestFolderViewPrivate* priv;
3866
3867         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3868
3869         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3870         priv->show_non_move = show;
3871 /*      modest_folder_view_update_model(folder_view, */
3872 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3873
3874         /* Hide special folders */
3875         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3876         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3877                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3878         }
3879 }
3880
3881 void
3882 modest_folder_view_show_message_count (ModestFolderView *folder_view,
3883                                           gboolean show)
3884 {
3885         ModestFolderViewPrivate* priv;
3886
3887         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3888
3889         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3890         priv->show_message_count = show;
3891
3892         g_object_set (G_OBJECT (priv->messages_renderer),
3893                       "visible", (priv->cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3894                       NULL);
3895 }
3896
3897 /* Returns FALSE if it did not selected anything */
3898 static gboolean
3899 _clipboard_set_selected_data (ModestFolderView *folder_view,
3900                               gboolean delete)
3901 {
3902         ModestFolderViewPrivate *priv = NULL;
3903         TnyFolderStore *folder = NULL;
3904         gboolean retval = FALSE;
3905
3906         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3907         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3908
3909         /* Set selected data on clipboard   */
3910         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3911         folder = modest_folder_view_get_selected (folder_view);
3912
3913         /* Do not allow to select an account */
3914         if (TNY_IS_FOLDER (folder)) {
3915                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3916                 retval = TRUE;
3917         }
3918
3919         /* Free */
3920         g_object_unref (folder);
3921
3922         return retval;
3923 }
3924
3925 static void
3926 _clear_hidding_filter (ModestFolderView *folder_view)
3927 {
3928         ModestFolderViewPrivate *priv;
3929         guint i;
3930
3931         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3932         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3933
3934         if (priv->hidding_ids != NULL) {
3935                 for (i=0; i < priv->n_selected; i++)
3936                         g_free (priv->hidding_ids[i]);
3937                 g_free(priv->hidding_ids);
3938         }
3939 }
3940
3941
3942 static void
3943 on_display_name_changed (ModestAccountMgr *mgr,
3944                          const gchar *account,
3945                          gpointer user_data)
3946 {
3947         ModestFolderView *self;
3948
3949         self = MODEST_FOLDER_VIEW (user_data);
3950
3951         /* Force a redraw */
3952 #if GTK_CHECK_VERSION(2, 8, 0)
3953         GtkTreeViewColumn * tree_column;
3954
3955         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3956                                                 NAME_COLUMN);
3957         gtk_tree_view_column_queue_resize (tree_column);
3958 #else
3959         gtk_widget_queue_draw (GTK_WIDGET (self));
3960 #endif
3961 }
3962
3963 void 
3964 modest_folder_view_set_cell_style (ModestFolderView *self,
3965                                    ModestFolderViewCellStyle cell_style)
3966 {
3967         ModestFolderViewPrivate *priv = NULL;
3968
3969         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3970         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3971
3972         priv->cell_style = cell_style;
3973
3974         g_object_set (G_OBJECT (priv->messages_renderer),
3975                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT && priv->show_message_count),
3976                       NULL);
3977         
3978         gtk_widget_queue_draw (GTK_WIDGET (self));
3979 }
3980
3981 static void
3982 update_style (ModestFolderView *self)
3983 {
3984         ModestFolderViewPrivate *priv;
3985         GdkColor style_color, style_active_color;
3986         PangoAttrList *attr_list;
3987         GtkStyle *style;
3988         PangoAttribute *attr;
3989
3990         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3991         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3992
3993         /* Set color */
3994
3995         attr_list = pango_attr_list_new ();
3996         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3997                 gdk_color_parse ("grey", &style_color);
3998         }
3999         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
4000         pango_attr_list_insert (attr_list, attr);
4001         
4002         /* set font */
4003         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
4004                                            (GTK_WIDGET(self)),
4005                                            "SmallSystemFont", NULL,
4006                                            G_TYPE_NONE);
4007         if (style) {
4008                 attr = pango_attr_font_desc_new (pango_font_description_copy
4009                                                  (style->font_desc));
4010                 pango_attr_list_insert (attr_list, attr);
4011
4012                 g_object_set (G_OBJECT (priv->messages_renderer),
4013                               "foreground-gdk", &style_color,
4014                               "foreground-set", TRUE,
4015                               "attributes", attr_list,
4016                               NULL);
4017                 pango_attr_list_unref (attr_list);
4018         }
4019         if (priv->active_color)
4020                 gdk_color_free (priv->active_color);
4021
4022         if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
4023                 priv->active_color = gdk_color_copy (&style_active_color);
4024         }
4025 }
4026
4027 static void 
4028 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
4029 {
4030         if (strcmp ("style", spec->name) == 0) {
4031                 update_style (MODEST_FOLDER_VIEW (obj));
4032                 gtk_widget_queue_draw (GTK_WIDGET (obj));
4033         } 
4034 }
4035
4036 void 
4037 modest_folder_view_set_filter (ModestFolderView *self,
4038                                ModestFolderViewFilter filter)
4039 {
4040         ModestFolderViewPrivate *priv;
4041         GtkTreeModel *filter_model;
4042
4043         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4044         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4045
4046         priv->filter |= filter;
4047
4048         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4049         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4050                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
4051         }
4052 }
4053
4054 void 
4055 modest_folder_view_unset_filter (ModestFolderView *self,
4056                                  ModestFolderViewFilter filter)
4057 {
4058         ModestFolderViewPrivate *priv;
4059         GtkTreeModel *filter_model;
4060
4061         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4062         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4063
4064         priv->filter &= ~filter;
4065
4066         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
4067         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
4068                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
4069         }
4070 }
4071
4072 gboolean
4073 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
4074                                              ModestTnyFolderRules rules)
4075 {
4076         GtkTreeModel *filter_model;
4077         GtkTreeIter iter;
4078         gboolean fulfil = FALSE;
4079
4080         if (!get_inner_models (self, &filter_model, NULL, NULL))
4081                 return FALSE;
4082
4083         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
4084                 return FALSE;
4085
4086         do {
4087                 TnyFolderStore *folder;
4088
4089                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
4090                 if (folder) {
4091                         if (TNY_IS_FOLDER (folder)) {
4092                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
4093                                 /* Folder rules are negative: non_writable, non_deletable... */
4094                                 if (!(folder_rules & rules))
4095                                         fulfil = TRUE;
4096                         }
4097                         g_object_unref (folder);
4098                 }
4099
4100         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
4101
4102         return fulfil;
4103 }
4104
4105 void 
4106 modest_folder_view_set_list_to_move (ModestFolderView *self,
4107                                      TnyList *list)
4108 {
4109         ModestFolderViewPrivate *priv;
4110
4111         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4112         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4113
4114         if (priv->list_to_move)
4115                 g_object_unref (priv->list_to_move);
4116
4117         if (list)
4118                 g_object_ref (list);
4119
4120         priv->list_to_move = list;
4121 }
4122
4123 void
4124 modest_folder_view_set_mailbox (ModestFolderView *self, const gchar *mailbox)
4125 {
4126         ModestFolderViewPrivate *priv;
4127
4128         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
4129         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4130
4131         if (priv->mailbox)
4132                 g_free (priv->mailbox);
4133
4134         priv->mailbox = g_strdup (mailbox);
4135
4136         /* Notify observers */
4137         g_signal_emit (G_OBJECT(self),
4138                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
4139                        priv->visible_account_id);
4140 }
4141
4142 const gchar *
4143 modest_folder_view_get_mailbox (ModestFolderView *self)
4144 {
4145         ModestFolderViewPrivate *priv;
4146
4147         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), NULL);
4148         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4149
4150         return (const gchar *) priv->mailbox;
4151 }
4152
4153 gboolean 
4154 modest_folder_view_get_activity (ModestFolderView *self)
4155 {
4156         ModestFolderViewPrivate *priv;
4157         GtkTreeModel *inner_model;
4158
4159         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (self), FALSE);
4160         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
4161         g_return_val_if_fail (get_inner_models (self, NULL, NULL, &inner_model), FALSE);
4162
4163         if (TNY_IS_GTK_FOLDER_LIST_STORE (inner_model)) {
4164                 return tny_gtk_folder_list_store_get_activity (TNY_GTK_FOLDER_LIST_STORE (inner_model));
4165         } else {
4166                 return FALSE;
4167         }
4168 }
4169
4170 #ifdef MODEST_TOOLKIT_HILDON2
4171 static void
4172 on_activity_changed (TnyGtkFolderListStore *store,
4173                      gboolean activity,
4174                      ModestFolderView *folder_view)
4175 {
4176         ModestFolderViewPrivate *priv;
4177
4178         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
4179         g_return_if_fail (TNY_IS_GTK_FOLDER_LIST_STORE (store));
4180         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
4181
4182         g_signal_emit (G_OBJECT (folder_view), signals[ACTIVITY_CHANGED_SIGNAL], 0,
4183                        activity);
4184 }
4185 #endif