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