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