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