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