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