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