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