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