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