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