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