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