* added extra checking to on_account_inserted, on_account_changed.
[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         
1200         g_object_unref (G_OBJECT (device));
1201 }
1202
1203 static void
1204 on_account_inserted (TnyAccountStore *account_store, 
1205                      TnyAccount *account,
1206                      gpointer user_data)
1207 {
1208         ModestFolderViewPrivate *priv;
1209         GtkTreeModel *sort_model, *filter_model;
1210
1211         /* Ignore transport account insertions, we're not showing them
1212            in the folder view */
1213         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1214                 return;
1215
1216         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1217
1218
1219         /* If we're adding a new account, and there is no previous
1220            one, we need to select the visible server account */
1221         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1222             !priv->visible_account_id)
1223                 modest_widget_memory_restore (modest_runtime_get_conf(), 
1224                                               G_OBJECT (user_data),
1225                                               MODEST_CONF_FOLDER_VIEW_KEY);
1226
1227         if (!GTK_IS_TREE_VIEW(user_data)) {
1228                 g_warning ("BUG: %s: not a valid tree view", __FUNCTION__);
1229                 return;
1230         }
1231         
1232         /* Get the inner model */
1233         /* check, is some rare cases, we did not get the right thing here,
1234          * NB#84097 */
1235         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1236         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1237                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1238                 return;
1239         }
1240
1241         /* check, is some rare cases, we did not get the right thing here,
1242          * NB#84097 */
1243         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1244         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1245                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1246                 return;
1247         }
1248
1249         /* Insert the account in the model */
1250         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1251                          G_OBJECT (account));
1252         
1253         /* Refilter the model */
1254         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1255 }
1256
1257
1258 static void
1259 on_account_changed (TnyAccountStore *account_store, 
1260                     TnyAccount *tny_account,
1261                     gpointer user_data)
1262 {
1263         ModestFolderViewPrivate *priv;
1264         GtkTreeModel *sort_model, *filter_model;
1265
1266         /* Ignore transport account insertions, we're not showing them
1267            in the folder view */
1268         if (TNY_IS_TRANSPORT_ACCOUNT (tny_account))
1269                 return;
1270
1271         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1272                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1273                 return;
1274         }
1275
1276
1277         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (user_data);
1278
1279         /* Get the inner model */
1280         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
1281         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1282                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1283                 return;
1284         }
1285
1286
1287         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1288         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1289                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1290                 return;
1291         }
1292
1293         /* Remove the account from the model */
1294         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1295                          G_OBJECT (tny_account));
1296         
1297         /* Insert the account in the model */
1298         tny_list_append (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1299                          G_OBJECT (tny_account));
1300
1301         /* Refilter the model */
1302         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1303 }
1304
1305 /**
1306  *
1307  * Selects the first inbox or the local account in an idle
1308  */
1309 static gboolean
1310 on_idle_select_first_inbox_or_local (gpointer user_data)
1311 {
1312         ModestFolderView *self = MODEST_FOLDER_VIEW (user_data);
1313
1314         gdk_threads_enter ();
1315         modest_folder_view_select_first_inbox_or_local (self);
1316         gdk_threads_leave ();
1317
1318         return FALSE;
1319 }
1320
1321
1322 static void
1323 on_account_removed (TnyAccountStore *account_store, 
1324                     TnyAccount *account,
1325                     gpointer user_data)
1326 {
1327         ModestFolderView *self = NULL;
1328         ModestFolderViewPrivate *priv;
1329         GtkTreeModel *sort_model, *filter_model;
1330         GtkTreeSelection *sel = NULL;
1331         gboolean same_account_selected = FALSE;
1332
1333         /* Ignore transport account removals, we're not showing them
1334            in the folder view */
1335         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1336                 return;
1337
1338         if (!MODEST_IS_FOLDER_VIEW(user_data)) {
1339                 g_warning ("BUG: %s: not a valid folder view", __FUNCTION__);
1340                 return;
1341         }
1342
1343         self = MODEST_FOLDER_VIEW (user_data);
1344         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1345
1346         /* Invalidate the cur_folder_store only if the selected folder
1347            belongs to the account that is being removed */
1348         if (priv->cur_folder_store) {
1349                 TnyAccount *selected_folder_account = NULL;
1350
1351                 if (TNY_IS_FOLDER (priv->cur_folder_store)) {
1352                         selected_folder_account = 
1353                                 tny_folder_get_account (TNY_FOLDER (priv->cur_folder_store));
1354                 } else {
1355                         selected_folder_account = 
1356                                 TNY_ACCOUNT (g_object_ref (priv->cur_folder_store));
1357                 }
1358
1359                 if (selected_folder_account == account) {
1360                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1361                         gtk_tree_selection_unselect_all (sel);
1362                         same_account_selected = TRUE;
1363                 }
1364                 g_object_unref (selected_folder_account);
1365         }
1366
1367         /* Invalidate row to select only if the folder to select
1368            belongs to the account that is being removed*/
1369         if (priv->folder_to_select) {
1370                 TnyAccount *folder_to_select_account = NULL;
1371
1372                 folder_to_select_account = tny_folder_get_account (priv->folder_to_select);
1373                 if (folder_to_select_account == account) {
1374                         modest_folder_view_disable_next_folder_selection (self);
1375                         g_object_unref (priv->folder_to_select);
1376                         priv->folder_to_select = NULL;
1377                 }
1378                 g_object_unref (folder_to_select_account);
1379         }
1380
1381         /* Remove the account from the model */
1382         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1383         if (!GTK_IS_TREE_MODEL_FILTER(filter_model)) {
1384                 g_warning ("BUG: %s: not a valid filter model", __FUNCTION__);
1385                 return;
1386         }
1387
1388         sort_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1389         if (!GTK_IS_TREE_MODEL_SORT(sort_model)) {
1390                 g_warning ("BUG: %s: not a valid sort model", __FUNCTION__);
1391                 return;
1392         }
1393         
1394         tny_list_remove (TNY_LIST (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sort_model))),
1395                          G_OBJECT (account));
1396
1397         /* If the removed account is the currently viewed one then
1398            clear the configuration value. The new visible account will be the default account */
1399         if (priv->visible_account_id &&
1400             !strcmp (priv->visible_account_id, tny_account_get_id (account))) {
1401
1402                 /* Clear the current visible account_id */
1403                 modest_folder_view_set_account_id_of_visible_server_account (self, NULL);
1404
1405                 /* Call the restore method, this will set the new visible account */
1406                 modest_widget_memory_restore (modest_runtime_get_conf(), G_OBJECT(self),
1407                                               MODEST_CONF_FOLDER_VIEW_KEY);
1408         }
1409
1410         /* Refilter the model */
1411         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
1412
1413         /* Select the first INBOX if the currently selected folder
1414            belongs to the account that is being deleted */
1415         if (same_account_selected)
1416                 g_idle_add (on_idle_select_first_inbox_or_local, self);
1417 }
1418
1419 void
1420 modest_folder_view_set_title (ModestFolderView *self, const gchar *title)
1421 {
1422         GtkTreeViewColumn *col;
1423         
1424         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
1425
1426         col = gtk_tree_view_get_column (GTK_TREE_VIEW(self), 0);
1427         if (!col) {
1428                 g_printerr ("modest: failed get column for title\n");
1429                 return;
1430         }
1431
1432         gtk_tree_view_column_set_title (col, title);
1433         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self),
1434                                            title != NULL);
1435 }
1436
1437 static gboolean
1438 modest_folder_view_on_map (ModestFolderView *self, 
1439                            GdkEventExpose *event,
1440                            gpointer data)
1441 {
1442         ModestFolderViewPrivate *priv;
1443
1444         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1445
1446         /* This won't happen often */
1447         if (G_UNLIKELY (priv->reselect)) {
1448                 /* Select the first inbox or the local account if not found */
1449
1450                 /* TODO: this could cause a lock at startup, so we
1451                    comment it for the moment. We know that this will
1452                    be a bug, because the INBOX is not selected, but we
1453                    need to rewrite some parts of Modest to avoid the
1454                    deathlock situation */
1455                 /* TODO: check if this is still the case */
1456                 priv->reselect = FALSE;
1457                 modest_folder_view_select_first_inbox_or_local (self);
1458                 /* Notify the display name observers */
1459                 g_signal_emit (G_OBJECT(self),
1460                                signals[FOLDER_DISPLAY_NAME_CHANGED_SIGNAL], 0,
1461                                NULL);
1462         }
1463
1464         if (priv->reexpand) {
1465                 expand_root_items (self); 
1466                 priv->reexpand = FALSE;
1467         }
1468
1469         return FALSE;
1470 }
1471
1472 GtkWidget*
1473 modest_folder_view_new (TnyFolderStoreQuery *query)
1474 {
1475         GObject *self;
1476         ModestFolderViewPrivate *priv;
1477         GtkTreeSelection *sel;
1478         
1479         self = G_OBJECT (g_object_new (MODEST_TYPE_FOLDER_VIEW, NULL));
1480         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
1481
1482         if (query)
1483                 priv->query = g_object_ref (query);
1484         
1485         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1486         priv->changed_signal = g_signal_connect (sel, "changed",
1487                                                  G_CALLBACK (on_selection_changed), self);
1488
1489         g_signal_connect (self, "expose-event", G_CALLBACK (modest_folder_view_on_map), NULL);
1490
1491         return GTK_WIDGET(self);
1492 }
1493
1494 /* this feels dirty; any other way to expand all the root items? */
1495 static void
1496 expand_root_items (ModestFolderView *self)
1497 {
1498         GtkTreePath *path;
1499         GtkTreeModel *model;
1500         GtkTreeIter iter;
1501
1502         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1503         path = gtk_tree_path_new_first ();
1504
1505         /* all folders should have child items, so.. */
1506         do {
1507                 gtk_tree_view_expand_row (GTK_TREE_VIEW(self), path, FALSE);
1508                 gtk_tree_path_next (path);
1509         } while (gtk_tree_model_get_iter (model, &iter, path));
1510         
1511         gtk_tree_path_free (path);
1512 }
1513
1514 /*
1515  * We use this function to implement the
1516  * MODEST_FOLDER_VIEW_STYLE_SHOW_ONE style. We only show the default
1517  * account in this case, and the local folders.
1518  */
1519 static gboolean 
1520 filter_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1521 {
1522         ModestFolderViewPrivate *priv;
1523         gboolean retval = TRUE;
1524         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1525         GObject *instance = NULL;
1526         const gchar *id = NULL;
1527         guint i;
1528         gboolean found = FALSE;
1529         gboolean cleared = FALSE;
1530
1531         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (data), FALSE);
1532         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (data);
1533
1534         gtk_tree_model_get (model, iter,
1535                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1536                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
1537                             -1);
1538
1539         /* Do not show if there is no instance, this could indeed
1540            happen when the model is being modified while it's being
1541            drawn. This could occur for example when moving folders
1542            using drag&drop */
1543         if (!instance)
1544                 return FALSE;
1545
1546         if (type == TNY_FOLDER_TYPE_ROOT) {
1547                 /* TNY_FOLDER_TYPE_ROOT means that the instance is an
1548                    account instead of a folder. */
1549                 if (TNY_IS_ACCOUNT (instance)) {
1550                         TnyAccount *acc = TNY_ACCOUNT (instance);
1551                         const gchar *account_id = tny_account_get_id (acc);
1552         
1553                         /* If it isn't a special folder, 
1554                          * don't show it unless it is the visible account: */
1555                         if (priv->style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE &&
1556                             !modest_tny_account_is_virtual_local_folders (acc) &&
1557                             strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1558                                 
1559                                 /* Show only the visible account id */
1560                                 if (priv->visible_account_id) {
1561                                         if (strcmp (account_id, priv->visible_account_id))
1562                                                 retval = FALSE;
1563                                 } else {
1564                                         retval = FALSE;
1565                                 }                               
1566                         }
1567                         
1568                         /* Never show these to the user. They are merged into one folder 
1569                          * in the local-folders account instead: */
1570                         if (retval && MODEST_IS_TNY_OUTBOX_ACCOUNT (acc))
1571                                 retval = FALSE;
1572                 }
1573         }
1574
1575         /* Check hiding (if necessary) */
1576         cleared = modest_email_clipboard_cleared (priv->clipboard);            
1577         if ((retval) && (!cleared) && (TNY_IS_FOLDER (instance))) {
1578                 id = tny_folder_get_id (TNY_FOLDER(instance));
1579                 if (priv->hidding_ids != NULL)
1580                         for (i=0; i < priv->n_selected && !found; i++)
1581                                 if (priv->hidding_ids[i] != NULL && id != NULL)
1582                                         found = (!strcmp (priv->hidding_ids[i], id));
1583                 
1584                 retval = !found;
1585         }
1586         
1587         
1588         /* If this is a move to dialog, hide Sent, Outbox and Drafts
1589         folder as no message can be move there according to UI specs */
1590         if (!priv->show_non_move) {
1591                 switch (type) {
1592                         case TNY_FOLDER_TYPE_OUTBOX:
1593                         case TNY_FOLDER_TYPE_SENT:
1594                         case TNY_FOLDER_TYPE_DRAFTS:
1595                                 retval = FALSE;
1596                                 break;
1597                         case TNY_FOLDER_TYPE_UNKNOWN:
1598                         case TNY_FOLDER_TYPE_NORMAL:
1599                                 type = modest_tny_folder_guess_folder_type(TNY_FOLDER(instance));
1600                                 if (type == TNY_FOLDER_TYPE_INVALID)
1601                                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1602                                 
1603                                 if (type == TNY_FOLDER_TYPE_OUTBOX || 
1604                                     type == TNY_FOLDER_TYPE_SENT
1605                                     || type == TNY_FOLDER_TYPE_DRAFTS)
1606                                         retval = FALSE;
1607                                 break;
1608                         default:
1609                                 break;
1610                 }
1611         }
1612         
1613         /* Free */
1614         g_object_unref (instance);
1615
1616         return retval;
1617 }
1618
1619
1620 gboolean
1621 modest_folder_view_update_model (ModestFolderView *self,
1622                                  TnyAccountStore *account_store)
1623 {
1624         ModestFolderViewPrivate *priv;
1625         GtkTreeModel *model /* , *old_model */;                                                    
1626         GtkTreeModel *filter_model = NULL, *sortable = NULL;
1627
1628         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
1629         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
1630                               FALSE);
1631         
1632         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1633         
1634         /* Notify that there is no folder selected */
1635         g_signal_emit (G_OBJECT(self),
1636                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1637                        NULL, FALSE);
1638         if (priv->cur_folder_store) {
1639                 g_object_unref (priv->cur_folder_store);
1640                 priv->cur_folder_store = NULL;
1641         }
1642
1643         /* FIXME: the local accounts are not shown when the query
1644            selects only the subscribed folders */
1645         model = tny_gtk_folder_store_tree_model_new (NULL);
1646
1647         /* Get the accounts: */
1648         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
1649                                         TNY_LIST (model),
1650                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
1651
1652         sortable = gtk_tree_model_sort_new_with_model (model);
1653         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1654                                               TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, 
1655                                               GTK_SORT_ASCENDING);
1656         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1657                                          TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN,
1658                                          cmp_rows, NULL, NULL);
1659
1660         /* Create filter model */
1661         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1662         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1663                                                 filter_row,
1664                                                 self,
1665                                                 NULL);
1666
1667         /* Set new model */
1668         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
1669         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
1670                           (GCallback) on_row_inserted_maybe_select_folder, self);
1671
1672         g_object_unref (model);
1673         g_object_unref (filter_model);          
1674         g_object_unref (sortable);
1675         
1676         /* Force a reselection of the INBOX next time the widget is shown */
1677         priv->reselect = TRUE;
1678                         
1679         return TRUE;
1680 }
1681
1682
1683 static void
1684 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1685 {
1686         GtkTreeModel *model = NULL;
1687         TnyFolderStore *folder = NULL;
1688         GtkTreeIter iter;
1689         ModestFolderView *tree_view = NULL;
1690         ModestFolderViewPrivate *priv = NULL;
1691         gboolean selected = FALSE;
1692
1693         g_return_if_fail (sel);
1694         g_return_if_fail (user_data);
1695         
1696         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
1697
1698         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
1699
1700         tree_view = MODEST_FOLDER_VIEW (user_data);
1701
1702         if (selected) {
1703                 gtk_tree_model_get (model, &iter,
1704                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
1705                                     -1);
1706
1707                 /* If the folder is the same do not notify */
1708                 if (folder && priv->cur_folder_store == folder) {
1709                         g_object_unref (folder);
1710                         return;
1711                 }
1712         }
1713         
1714         /* Current folder was unselected */
1715         if (priv->cur_folder_store) {
1716                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
1717                        priv->cur_folder_store, FALSE);
1718
1719                 if (TNY_IS_FOLDER(priv->cur_folder_store))
1720                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
1721                                                FALSE, NULL, NULL, NULL);
1722
1723                 /* FALSE --> don't expunge the messages */
1724
1725                 g_object_unref (priv->cur_folder_store);
1726                 priv->cur_folder_store = NULL;
1727         }
1728
1729         /* New current references */
1730         priv->cur_folder_store = folder;
1731
1732         /* New folder has been selected. Do not notify if there is
1733            nothing new selected */
1734         if (selected) {
1735                 g_signal_emit (G_OBJECT(tree_view),
1736                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
1737                                0, priv->cur_folder_store, TRUE);
1738         }
1739 }
1740
1741 TnyFolderStore *
1742 modest_folder_view_get_selected (ModestFolderView *self)
1743 {
1744         ModestFolderViewPrivate *priv;
1745         
1746         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
1747         
1748         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
1749         if (priv->cur_folder_store)
1750                 g_object_ref (priv->cur_folder_store);
1751
1752         return priv->cur_folder_store;
1753 }
1754
1755 static gint
1756 get_cmp_rows_type_pos (GObject *folder)
1757 {
1758         /* Remote accounts -> Local account -> MMC account .*/
1759         /* 0, 1, 2 */
1760         
1761         if (TNY_IS_ACCOUNT (folder) && 
1762                 modest_tny_account_is_virtual_local_folders (
1763                         TNY_ACCOUNT (folder))) {
1764                 return 1;
1765         } else if (TNY_IS_ACCOUNT (folder)) {
1766                 TnyAccount *account = TNY_ACCOUNT (folder);
1767                 const gchar *account_id = tny_account_get_id (account);
1768                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
1769                         return 2;
1770                 else
1771                         return 0;
1772         }
1773         else {
1774                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
1775                 return -1; /* Should never happen */
1776         }
1777 }
1778
1779 static gint
1780 get_cmp_subfolder_type_pos (TnyFolderType t)
1781 {
1782         /* Inbox, Outbox, Drafts, Sent, User */
1783         /* 0, 1, 2, 3, 4 */
1784
1785         switch (t) {
1786         case TNY_FOLDER_TYPE_INBOX:
1787                 return 0;
1788                 break;
1789         case TNY_FOLDER_TYPE_OUTBOX:
1790                 return 1;
1791                 break;
1792         case TNY_FOLDER_TYPE_DRAFTS:
1793                 return 2;
1794                 break;
1795         case TNY_FOLDER_TYPE_SENT:
1796                 return 3;
1797                 break;
1798         default:
1799                 return 4;
1800         }
1801 }
1802
1803 /*
1804  * This function orders the mail accounts according to these rules:
1805  * 1st - remote accounts
1806  * 2nd - local account
1807  * 3rd - MMC account
1808  */
1809 static gint
1810 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1811           gpointer user_data)
1812 {
1813         gint cmp = 0;
1814         gchar *name1 = NULL;
1815         gchar *name2 = NULL;
1816         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
1817         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
1818         GObject *folder1 = NULL;
1819         GObject *folder2 = NULL;
1820
1821         gtk_tree_model_get (tree_model, iter1,
1822                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name1,
1823                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
1824                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder1,
1825                             -1);
1826         gtk_tree_model_get (tree_model, iter2,
1827                             TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name2,
1828                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type2,
1829                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder2,
1830                             -1);
1831
1832         /* Return if we get no folder. This could happen when folder
1833            operations are happening. The model is updated after the
1834            folder copy/move actually occurs, so there could be
1835            situations where the model to be drawn is not correct */
1836         if (!folder1 || !folder2)
1837                 goto finish;
1838
1839         if (type == TNY_FOLDER_TYPE_ROOT) {
1840                 /* Compare the types, so that 
1841                  * Remote accounts -> Local account -> MMC account .*/
1842                 const gint pos1 = get_cmp_rows_type_pos (folder1);
1843                 const gint pos2 = get_cmp_rows_type_pos (folder2);
1844                 /* printf ("DEBUG: %s:\n  type1=%s, pos1=%d\n  type2=%s, pos2=%d\n", 
1845                         __FUNCTION__, G_OBJECT_TYPE_NAME(folder1), pos1, G_OBJECT_TYPE_NAME(folder2), pos2); */
1846                 if (pos1 <  pos2)
1847                         cmp = -1;
1848                 else if (pos1 > pos2)
1849                         cmp = 1;
1850                 else {
1851                         /* Compare items of the same type: */
1852                         
1853                         TnyAccount *account1 = NULL;
1854                         if (TNY_IS_ACCOUNT (folder1))
1855                                 account1 = TNY_ACCOUNT (folder1);
1856                                 
1857                         TnyAccount *account2 = NULL;
1858                         if (TNY_IS_ACCOUNT (folder2))
1859                                 account2 = TNY_ACCOUNT (folder2);
1860                                 
1861                         const gchar *account_id = account1 ? tny_account_get_id (account1) : NULL;
1862                         const gchar *account_id2 = account2 ? tny_account_get_id (account2) : NULL;
1863         
1864                         if (!account_id && !account_id2) {
1865                                 cmp = 0;
1866                         } else if (!account_id) {
1867                                 cmp = -1;
1868                         } else if (!account_id2) {
1869                                 cmp = +1;
1870                         } else if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID)) {
1871                                 cmp = +1;
1872                         } else {
1873                                 cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1874                         }
1875                 }
1876         } else {
1877                 gint cmp1 = 0, cmp2 = 0;
1878                 /* get the parent to know if it's a local folder */
1879
1880                 GtkTreeIter parent;
1881                 gboolean has_parent;
1882                 has_parent = gtk_tree_model_iter_parent (tree_model, &parent, iter1);
1883                 if (has_parent) {
1884                         GObject *parent_folder;
1885                         TnyFolderType parent_type = TNY_FOLDER_TYPE_UNKNOWN;
1886                         gtk_tree_model_get (tree_model, &parent, 
1887                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &parent_type,
1888                                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &parent_folder,
1889                                             -1);
1890                         if ((parent_type == TNY_FOLDER_TYPE_ROOT) &&
1891                             TNY_IS_ACCOUNT (parent_folder)) {
1892                                 if (modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent_folder))) {
1893                                         cmp1 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1894                                                                            (TNY_FOLDER (folder1)));
1895                                         cmp2 = get_cmp_subfolder_type_pos (modest_tny_folder_get_local_or_mmc_folder_type
1896                                                                            (TNY_FOLDER (folder2)));
1897                                 } else if (modest_tny_account_is_memory_card_account (TNY_ACCOUNT (parent_folder))) {
1898                                         if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder1))) == TNY_FOLDER_TYPE_ARCHIVE) {
1899                                                 cmp1 = 0;
1900                                                 cmp2 = 1;
1901                                                 } else if (modest_local_folder_info_get_type (tny_folder_get_name (TNY_FOLDER (folder2))) == TNY_FOLDER_TYPE_ARCHIVE) {
1902                                                 cmp1 = 1;
1903                                                 cmp2 = 0;
1904                                         }
1905                                 }
1906                         }
1907                         g_object_unref (parent_folder);
1908                 }
1909                 
1910                 /* if they are not local folders */
1911                 if (cmp1 == cmp2) {
1912                         cmp1 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder1)));
1913                         cmp2 = get_cmp_subfolder_type_pos (tny_folder_get_folder_type (TNY_FOLDER (folder2)));
1914                 }
1915
1916                 if (cmp1 == cmp2)
1917                         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
1918                 else 
1919                         cmp = (cmp1 - cmp2);
1920         }
1921
1922 finish: 
1923         if (folder1)
1924                 g_object_unref(G_OBJECT(folder1));
1925         if (folder2)
1926                 g_object_unref(G_OBJECT(folder2));
1927
1928         g_free (name1);
1929         g_free (name2);
1930
1931         return cmp;     
1932 }
1933
1934 /*****************************************************************************/
1935 /*                        DRAG and DROP stuff                                */
1936 /*****************************************************************************/
1937 /*
1938  * This function fills the #GtkSelectionData with the row and the
1939  * model that has been dragged. It's called when this widget is a
1940  * source for dnd after the event drop happened
1941  */
1942 static void
1943 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, 
1944                   guint info, guint time, gpointer data)
1945 {
1946         GtkTreeSelection *selection;
1947         GtkTreeModel *model;
1948         GtkTreeIter iter;
1949         GtkTreePath *source_row;
1950         
1951         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1952         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1953
1954                 source_row = gtk_tree_model_get_path (model, &iter);
1955                 gtk_tree_set_row_drag_data (selection_data,
1956                                             model,
1957                                             source_row);
1958                 
1959                 gtk_tree_path_free (source_row);
1960         }
1961 }
1962
1963 typedef struct _DndHelper {
1964         ModestFolderView *folder_view;
1965         gboolean delete_source;
1966         GtkTreePath *source_row;
1967         GdkDragContext *context;
1968         guint time;
1969 } DndHelper;
1970
1971 static void
1972 dnd_helper_destroyer (DndHelper *helper)
1973 {
1974         /* Free the helper */
1975         g_object_unref (helper->folder_view);
1976         gtk_tree_path_free (helper->source_row);
1977         g_slice_free (DndHelper, helper);
1978 }
1979
1980 static void
1981 xfer_cb (ModestMailOperation *mail_op, 
1982          gpointer user_data)
1983 {
1984         gboolean success;
1985         DndHelper *helper;
1986
1987         helper = (DndHelper *) user_data;
1988
1989         if (modest_mail_operation_get_status (mail_op) == 
1990             MODEST_MAIL_OPERATION_STATUS_SUCCESS) {
1991                 success = TRUE;
1992         } else {
1993                 success = FALSE;
1994         }
1995
1996         /* Notify the drag source. Never call delete, the monitor will
1997            do the job if needed */
1998         gtk_drag_finish (helper->context, success, FALSE, helper->time);
1999
2000         /* Free the helper */
2001         dnd_helper_destroyer (helper);
2002 }
2003
2004 static void
2005 xfer_msgs_cb (ModestMailOperation *mail_op, 
2006               gpointer user_data)
2007 {
2008         /* Common part */
2009         xfer_cb (mail_op, user_data);
2010 }
2011
2012 static void
2013 xfer_folder_cb (ModestMailOperation *mail_op, 
2014                 TnyFolder *new_folder,
2015                 gpointer user_data)
2016 {
2017         DndHelper *helper;
2018         GtkWidget *folder_view;
2019
2020         helper = (DndHelper *) user_data;
2021         folder_view = g_object_ref (helper->folder_view);
2022
2023         /* Common part */
2024         xfer_cb (mail_op, user_data);
2025
2026         /* Select the folder */
2027         if (new_folder)
2028                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (folder_view),
2029                                                   new_folder, FALSE);
2030         g_object_unref (folder_view);
2031 }
2032
2033
2034 /* get the folder for the row the treepath refers to. */
2035 /* folder must be unref'd */
2036 static TnyFolderStore *
2037 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2038 {
2039         GtkTreeIter iter;
2040         TnyFolderStore *folder = NULL;
2041         
2042         if (gtk_tree_model_get_iter (model,&iter, path))
2043                 gtk_tree_model_get (model, &iter,
2044                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &folder,
2045                                     -1);
2046         return folder;
2047 }
2048
2049 /*
2050  * This function is used by drag_data_received_cb to manage drag and
2051  * drop of a header, i.e, and drag from the header view to the folder
2052  * view.
2053  */
2054 static void
2055 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2056                                 GtkTreeModel *dest_model,
2057                                 GtkTreePath  *dest_row,
2058                                 GtkSelectionData *selection_data,
2059                                 DndHelper    *helper)
2060 {
2061         TnyList *headers = NULL;
2062         TnyFolder *folder = NULL;
2063         TnyFolderType folder_type;
2064         ModestMailOperation *mail_op = NULL;
2065         GtkTreeIter source_iter, dest_iter;
2066         ModestWindowMgr *mgr = NULL;
2067         ModestWindow *main_win = NULL;
2068         gchar **uris, **tmp;
2069         gint response;
2070
2071         /* Build the list of headers */
2072         mgr = modest_runtime_get_window_mgr ();
2073         headers = tny_simple_list_new ();
2074         uris = modest_dnd_selection_data_get_paths (selection_data);
2075         tmp = uris;
2076
2077         while (*tmp != NULL) {
2078                 TnyHeader *header;
2079                 GtkTreePath *path;
2080
2081                 /* Get header */
2082                 path = gtk_tree_path_new_from_string (*tmp);
2083                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2084                 gtk_tree_model_get (source_model, &source_iter, 
2085                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
2086                                     &header, -1);
2087
2088                 /* Do not enable d&d of headers already opened */
2089                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2090                         tny_list_append (headers, G_OBJECT (header));
2091
2092                 /* Free and go on */
2093                 gtk_tree_path_free (path);
2094                 g_object_unref (header);
2095                 tmp++;
2096         }
2097         g_strfreev (uris);
2098
2099         /* Get the target folder */
2100         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2101         gtk_tree_model_get (dest_model, &dest_iter, 
2102                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2103                             &folder, -1);
2104         
2105         if (!folder || !TNY_IS_FOLDER(folder)) {
2106 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2107                 goto cleanup;
2108         }
2109         
2110         folder_type = modest_tny_folder_guess_folder_type (folder);
2111         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2112 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2113                 goto cleanup;  /* cannot move messages there */
2114         }
2115         
2116         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2117 /*              g_warning ("folder not writable"); */
2118                 goto cleanup; /* verboten! */
2119         }
2120         
2121         /* Ask for confirmation to move */
2122         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2123         if (!main_win) {
2124                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2125                 goto cleanup;
2126         }
2127
2128         response = modest_ui_actions_msgs_move_to_confirmation (main_win, folder, 
2129                                                                 TRUE, headers);
2130         if (response == GTK_RESPONSE_CANCEL)
2131                 goto cleanup;
2132
2133         /* Transfer messages */
2134         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) main_win,
2135                                                                  modest_ui_actions_move_folder_error_handler,
2136                                                                  NULL, NULL);
2137
2138         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2139                                          mail_op);
2140
2141         modest_mail_operation_xfer_msgs (mail_op,
2142                                          headers, 
2143                                          folder, 
2144                                          helper->delete_source, 
2145                                          xfer_msgs_cb, helper);
2146         
2147         /* Frees */
2148 cleanup:
2149         if (G_IS_OBJECT(mail_op))
2150                 g_object_unref (G_OBJECT (mail_op));
2151         if (G_IS_OBJECT(folder))
2152                 g_object_unref (G_OBJECT (folder));
2153         if (G_IS_OBJECT(headers))
2154                 g_object_unref (headers);
2155 }
2156
2157 typedef struct {
2158         TnyFolderStore *src_folder;
2159         TnyFolderStore *dst_folder;
2160         ModestFolderView *folder_view;
2161         DndHelper *helper; 
2162 } DndFolderInfo;
2163
2164 static void
2165 dnd_folder_info_destroyer (DndFolderInfo *info)
2166 {
2167         if (info->src_folder)
2168                 g_object_unref (info->src_folder);
2169         if (info->dst_folder)
2170                 g_object_unref (info->dst_folder);
2171         if (info->folder_view)
2172                 g_object_unref (info->folder_view);
2173         g_slice_free (DndFolderInfo, info);
2174 }
2175
2176 static void
2177 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2178                                     GtkWindow *parent_window,
2179                                     TnyAccount *account)
2180 {
2181         time_t dnd_time = info->helper->time;
2182         GdkDragContext *context = info->helper->context;
2183         
2184         /* Show error */
2185         modest_ui_actions_on_account_connection_error (parent_window, account);
2186
2187         /* Free the helper & info */
2188         dnd_helper_destroyer (info->helper);
2189         dnd_folder_info_destroyer (info);
2190         
2191         /* Notify the drag source. Never call delete, the monitor will
2192            do the job if needed */
2193         gtk_drag_finish (context, FALSE, FALSE, dnd_time);
2194         return;
2195 }
2196
2197 static void
2198 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled, 
2199                                                      GError *err,
2200                                                      GtkWindow *parent_window, 
2201                                                      TnyAccount *account, 
2202                                                      gpointer user_data)
2203 {
2204         DndFolderInfo *info = NULL;
2205         ModestMailOperation *mail_op;
2206
2207         info = (DndFolderInfo *) user_data;
2208
2209         if (err || canceled) {
2210                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2211                 return;
2212         }
2213
2214         /* Do the mail operation */
2215         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2216                                                                  modest_ui_actions_move_folder_error_handler,
2217                                                                  info->src_folder, NULL);
2218
2219         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2220                                          mail_op);
2221
2222         /* Transfer the folder */
2223         modest_mail_operation_xfer_folder (mail_op,
2224                                            TNY_FOLDER (info->src_folder),
2225                                            info->dst_folder,
2226                                            info->helper->delete_source,
2227                                            xfer_folder_cb,
2228                                            info->helper);
2229         
2230 /*      modest_folder_view_select_folder (MODEST_FOLDER_VIEW(info->folder_view), */
2231 /*                                        TNY_FOLDER (info->dst_folder), TRUE); */
2232
2233         g_object_unref (G_OBJECT (mail_op));
2234 }
2235
2236
2237 static void
2238 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled, 
2239                                                      GError *err,
2240                                                      GtkWindow *parent_window, 
2241                                                      TnyAccount *account, 
2242                                                      gpointer user_data)
2243 {
2244         DndFolderInfo *info = NULL;
2245
2246         info = (DndFolderInfo *) user_data;
2247
2248         if (err || canceled) {
2249                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2250                 return;
2251         }
2252
2253         /* Connect to source folder and perform the copy/move */
2254         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2255                                                        info->src_folder,
2256                                                        drag_and_drop_from_folder_view_src_folder_performer,
2257                                                        info);
2258 }
2259
2260 /*
2261  * This function is used by drag_data_received_cb to manage drag and
2262  * drop of a folder, i.e, and drag from the folder view to the same
2263  * folder view.
2264  */
2265 static void
2266 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2267                                 GtkTreeModel     *dest_model,
2268                                 GtkTreePath      *dest_row,
2269                                 GtkSelectionData *selection_data,
2270                                 DndHelper        *helper)
2271 {
2272         GtkTreeIter dest_iter, iter;
2273         TnyFolderStore *dest_folder = NULL;
2274         TnyFolderStore *folder = NULL;
2275         gboolean forbidden = FALSE;
2276         ModestWindow *win;
2277         DndFolderInfo *info = NULL;
2278
2279         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2280         if (!win) {
2281                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2282                 return;
2283         }
2284         
2285         if (!forbidden) {
2286                 /* check the folder rules for the destination */
2287                 folder = tree_path_to_folder (dest_model, dest_row);
2288                 if (TNY_IS_FOLDER(folder)) {
2289                         ModestTnyFolderRules rules =
2290                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2291                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2292                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2293                         /* enable local root as destination for folders */
2294                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) && 
2295                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2296                                 forbidden = TRUE;
2297                 }
2298                 g_object_unref (folder);
2299         }
2300         if (!forbidden) {
2301                 /* check the folder rules for the source */
2302                 folder = tree_path_to_folder (source_model, helper->source_row);
2303                 if (TNY_IS_FOLDER(folder)) {
2304                         ModestTnyFolderRules rules =
2305                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2306                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2307                 } else
2308                         forbidden = TRUE;
2309                 g_object_unref (folder);
2310         }
2311
2312         
2313         /* Check if the drag is possible */
2314         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2315                 gtk_drag_finish (helper->context, FALSE, FALSE, helper->time);
2316                 gtk_tree_path_free (helper->source_row);        
2317                 g_slice_free (DndHelper, helper);
2318                 return;
2319         }
2320
2321         /* Get data */
2322         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2323         gtk_tree_model_get (dest_model, &dest_iter, 
2324                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, 
2325                             &dest_folder, -1);
2326         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2327         gtk_tree_model_get (source_model, &iter,
2328                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN,
2329                             &folder, -1);
2330
2331         /* Create the info for the performer */
2332         info = g_slice_new (DndFolderInfo);
2333         info->src_folder = g_object_ref (folder);
2334         info->dst_folder = g_object_ref (dest_folder);
2335         info->folder_view = g_object_ref (helper->folder_view);
2336         info->helper = helper;
2337
2338         /* Connect to the destination folder and perform the copy/move */
2339         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2340                                                        dest_folder,
2341                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2342                                                        info);
2343         
2344         /* Frees */
2345         g_object_unref (dest_folder);
2346         g_object_unref (folder);
2347 }
2348
2349 /*
2350  * This function receives the data set by the "drag-data-get" signal
2351  * handler. This information comes within the #GtkSelectionData. This
2352  * function will manage both the drags of folders of the treeview and
2353  * drags of headers of the header view widget.
2354  */
2355 static void 
2356 on_drag_data_received (GtkWidget *widget, 
2357                        GdkDragContext *context, 
2358                        gint x, 
2359                        gint y, 
2360                        GtkSelectionData *selection_data, 
2361                        guint target_type, 
2362                        guint time, 
2363                        gpointer data)
2364 {
2365         GtkWidget *source_widget;
2366         GtkTreeModel *dest_model, *source_model;
2367         GtkTreePath *source_row, *dest_row;
2368         GtkTreeViewDropPosition pos;
2369         gboolean success = FALSE, delete_source = FALSE;
2370         DndHelper *helper = NULL; 
2371
2372         /* Do not allow further process */
2373         g_signal_stop_emission_by_name (widget, "drag-data-received");
2374         source_widget = gtk_drag_get_source_widget (context);
2375
2376         /* Get the action */
2377         if (context->action == GDK_ACTION_MOVE) {
2378                 delete_source = TRUE;
2379
2380                 /* Notify that there is no folder selected. We need to
2381                    do this in order to update the headers view (and
2382                    its monitors, because when moving, the old folder
2383                    won't longer exist. We can not wait for the end of
2384                    the operation, because the operation won't start if
2385                    the folder is in use */
2386                 if (source_widget == widget) {
2387                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2388                         gtk_tree_selection_unselect_all (sel);
2389                 }
2390         }
2391
2392         /* Check if the get_data failed */
2393         if (selection_data == NULL || selection_data->length < 0)
2394                 gtk_drag_finish (context, success, FALSE, time);
2395
2396         /* Select the destination model */
2397         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));  
2398
2399         /* Get the path to the destination row. Can not call
2400            gtk_tree_view_get_drag_dest_row() because the source row
2401            is not selected anymore */
2402         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2403                                            &dest_row, &pos);
2404
2405         /* Only allow drops IN other rows */
2406         if (!dest_row || 
2407             pos == GTK_TREE_VIEW_DROP_BEFORE || 
2408             pos == GTK_TREE_VIEW_DROP_AFTER)
2409                 gtk_drag_finish (context, success, FALSE, time);
2410
2411         /* Create the helper */
2412         helper = g_slice_new0 (DndHelper);
2413         helper->delete_source = delete_source;
2414         helper->context = context;
2415         helper->time = time;
2416         helper->folder_view = g_object_ref (widget);
2417
2418         /* Drags from the header view */
2419         if (source_widget != widget) {
2420                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2421
2422                 drag_and_drop_from_header_view (source_model,
2423                                                 dest_model,
2424                                                 dest_row,
2425                                                 selection_data,
2426                                                 helper);
2427         } else {
2428                 /* Get the source model and row */
2429                 gtk_tree_get_row_drag_data (selection_data,
2430                                             &source_model,
2431                                             &source_row);
2432                 helper->source_row = gtk_tree_path_copy (source_row);
2433
2434                 drag_and_drop_from_folder_view (source_model,
2435                                                 dest_model,
2436                                                 dest_row,
2437                                                 selection_data, 
2438                                                 helper);
2439
2440                 gtk_tree_path_free (source_row);
2441         }
2442
2443         /* Frees */
2444         gtk_tree_path_free (dest_row);
2445 }
2446
2447 /*
2448  * We define a "drag-drop" signal handler because we do not want to
2449  * use the default one, because the default one always calls
2450  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2451  * signal handler, because there we have all the information available
2452  * to know if the dnd was a success or not.
2453  */
2454 static gboolean
2455 drag_drop_cb (GtkWidget      *widget,
2456               GdkDragContext *context,
2457               gint            x,
2458               gint            y,
2459               guint           time,
2460               gpointer        user_data) 
2461 {
2462         gpointer target;
2463
2464         if (!context->targets)
2465                 return FALSE;
2466
2467         /* Check if we're dragging a folder row */
2468         target = gtk_drag_dest_find_target (widget, context, NULL);
2469
2470         /* Request the data from the source. */
2471         gtk_drag_get_data(widget, context, target, time);
2472
2473     return TRUE;
2474 }
2475
2476 /*
2477  * This function expands a node of a tree view if it's not expanded
2478  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2479  * does that, so that's why they're here.
2480  */
2481 static gint
2482 expand_row_timeout (gpointer data)
2483 {
2484         GtkTreeView *tree_view = data;
2485         GtkTreePath *dest_path = NULL;
2486         GtkTreeViewDropPosition pos;
2487         gboolean result = FALSE;
2488         
2489         gdk_threads_enter ();
2490         
2491         gtk_tree_view_get_drag_dest_row (tree_view,
2492                                          &dest_path,
2493                                          &pos);
2494         
2495         if (dest_path &&
2496             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
2497              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
2498                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
2499                 gtk_tree_path_free (dest_path);
2500         }
2501         else {
2502                 if (dest_path)
2503                         gtk_tree_path_free (dest_path);
2504                 
2505                 result = TRUE;
2506         }
2507         
2508         gdk_threads_leave ();
2509
2510         return result;
2511 }
2512
2513 /*
2514  * This function is called whenever the pointer is moved over a widget
2515  * while dragging some data. It installs a timeout that will expand a
2516  * node of the treeview if not expanded yet. This function also calls
2517  * gdk_drag_status in order to set the suggested action that will be
2518  * used by the "drag-data-received" signal handler to know if we
2519  * should do a move or just a copy of the data.
2520  */
2521 static gboolean
2522 on_drag_motion (GtkWidget      *widget,
2523                 GdkDragContext *context,
2524                 gint            x,
2525                 gint            y,
2526                 guint           time,
2527                 gpointer        user_data)  
2528 {
2529         GtkTreeViewDropPosition pos;
2530         GtkTreePath *dest_row;
2531         GtkTreeModel *dest_model;
2532         ModestFolderViewPrivate *priv;
2533         GdkDragAction suggested_action;
2534         gboolean valid_location = FALSE;
2535         TnyFolderStore *folder = NULL;
2536
2537         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
2538
2539         if (priv->timer_expander != 0) {
2540                 g_source_remove (priv->timer_expander);
2541                 priv->timer_expander = 0;
2542         }
2543
2544         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
2545                                            x, y,
2546                                            &dest_row,
2547                                            &pos);
2548
2549         /* Do not allow drops between folders */
2550         if (!dest_row ||
2551             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2552             pos == GTK_TREE_VIEW_DROP_AFTER) {
2553                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
2554                 gdk_drag_status(context, 0, time);
2555                 valid_location = FALSE;
2556                 goto out;
2557         } else {
2558                 valid_location = TRUE;
2559         }
2560
2561         /* Check that the destination folder is writable */
2562         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2563         folder = tree_path_to_folder (dest_model, dest_row);
2564         if (folder && TNY_IS_FOLDER (folder)) {
2565                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
2566
2567                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2568                         valid_location = FALSE;
2569                         goto out;
2570                 }
2571         }
2572
2573         /* Expand the selected row after 1/2 second */
2574         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
2575                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
2576         }
2577         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
2578
2579         /* Select the desired action. By default we pick MOVE */
2580         suggested_action = GDK_ACTION_MOVE;
2581
2582         if (context->actions == GDK_ACTION_COPY)
2583             gdk_drag_status(context, GDK_ACTION_COPY, time);
2584         else if (context->actions == GDK_ACTION_MOVE)
2585             gdk_drag_status(context, GDK_ACTION_MOVE, time);
2586         else if (context->actions & suggested_action)
2587             gdk_drag_status(context, suggested_action, time);
2588         else
2589             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
2590
2591  out:
2592         if (folder)
2593                 g_object_unref (folder);
2594         if (dest_row) {
2595                 gtk_tree_path_free (dest_row);
2596         }
2597         g_signal_stop_emission_by_name (widget, "drag-motion");
2598
2599         return valid_location;
2600 }
2601
2602 /*
2603  * This function sets the treeview as a source and a target for dnd
2604  * events. It also connects all the requirede signals.
2605  */
2606 static void
2607 setup_drag_and_drop (GtkTreeView *self)
2608 {
2609         /* Set up the folder view as a dnd destination. Set only the
2610            highlight flag, otherwise gtk will have a different
2611            behaviour */
2612         gtk_drag_dest_set (GTK_WIDGET (self),
2613                            GTK_DEST_DEFAULT_HIGHLIGHT,
2614                            folder_view_drag_types,
2615                            G_N_ELEMENTS (folder_view_drag_types),
2616                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
2617
2618         g_signal_connect (G_OBJECT (self),
2619                           "drag_data_received",
2620                           G_CALLBACK (on_drag_data_received),
2621                           NULL);
2622
2623
2624         /* Set up the treeview as a dnd source */
2625         gtk_drag_source_set (GTK_WIDGET (self),
2626                              GDK_BUTTON1_MASK,
2627                              folder_view_drag_types,
2628                              G_N_ELEMENTS (folder_view_drag_types),
2629                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
2630
2631         g_signal_connect (G_OBJECT (self),
2632                           "drag_motion",
2633                           G_CALLBACK (on_drag_motion),
2634                           NULL);
2635         
2636         g_signal_connect (G_OBJECT (self),
2637                           "drag_data_get",
2638                           G_CALLBACK (on_drag_data_get),
2639                           NULL);
2640
2641         g_signal_connect (G_OBJECT (self),
2642                           "drag_drop",
2643                           G_CALLBACK (drag_drop_cb),
2644                           NULL);
2645 }
2646
2647 /*
2648  * This function manages the navigation through the folders using the
2649  * keyboard or the hardware keys in the device
2650  */
2651 static gboolean
2652 on_key_pressed (GtkWidget *self,
2653                 GdkEventKey *event,
2654                 gpointer user_data)
2655 {
2656         GtkTreeSelection *selection;
2657         GtkTreeIter iter;
2658         GtkTreeModel *model;
2659         gboolean retval = FALSE;
2660
2661         /* Up and Down are automatically managed by the treeview */
2662         if (event->keyval == GDK_Return) {
2663                 /* Expand/Collapse the selected row */
2664                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2665                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2666                         GtkTreePath *path;
2667
2668                         path = gtk_tree_model_get_path (model, &iter);
2669
2670                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
2671                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
2672                         else
2673                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
2674                         gtk_tree_path_free (path);
2675                 }
2676                 /* No further processing */
2677                 retval = TRUE;
2678         }
2679
2680         return retval;
2681 }
2682
2683 /*
2684  * We listen to the changes in the local folder account name key,
2685  * because we want to show the right name in the view. The local
2686  * folder account name corresponds to the device name in the Maemo
2687  * version. We do this because we do not want to query gconf on each
2688  * tree view refresh. It's better to cache it and change whenever
2689  * necessary.
2690  */
2691 static void 
2692 on_configuration_key_changed (ModestConf* conf, 
2693                               const gchar *key, 
2694                               ModestConfEvent event,
2695                               ModestConfNotificationId id, 
2696                               ModestFolderView *self)
2697 {
2698         ModestFolderViewPrivate *priv;
2699
2700
2701         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
2702         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2703
2704         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
2705                 g_free (priv->local_account_name);
2706
2707                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
2708                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
2709                 else
2710                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
2711                                                                            MODEST_CONF_DEVICE_NAME, NULL);
2712
2713                 /* Force a redraw */
2714 #if GTK_CHECK_VERSION(2, 8, 0)
2715                 GtkTreeViewColumn * tree_column;
2716
2717                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
2718                                                         TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
2719                 gtk_tree_view_column_queue_resize (tree_column);
2720 #else
2721                 gtk_widget_queue_draw (GTK_WIDGET (self));
2722 #endif
2723         }
2724 }
2725
2726 void
2727 modest_folder_view_set_style (ModestFolderView *self,
2728                               ModestFolderViewStyle style)
2729 {
2730         ModestFolderViewPrivate *priv;
2731
2732         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2733         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
2734                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
2735         
2736         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2737
2738         
2739         priv->style = style;
2740 }
2741
2742 void
2743 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
2744                                                              const gchar *account_id)
2745 {
2746         ModestFolderViewPrivate *priv;
2747         GtkTreeModel *model;
2748
2749         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2750         
2751         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2752
2753         /* This will be used by the filter_row callback,
2754          * to decided which rows to show: */
2755         if (priv->visible_account_id) {
2756                 g_free (priv->visible_account_id);
2757                 priv->visible_account_id = NULL;
2758         }
2759         if (account_id)
2760                 priv->visible_account_id = g_strdup (account_id);
2761
2762         /* Refilter */
2763         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2764         if (GTK_IS_TREE_MODEL_FILTER (model))
2765                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2766
2767         /* Save settings to gconf */
2768         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
2769                                    MODEST_CONF_FOLDER_VIEW_KEY);
2770 }
2771
2772 const gchar *
2773 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
2774 {
2775         ModestFolderViewPrivate *priv;
2776
2777         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2778         
2779         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2780
2781         return (const gchar *) priv->visible_account_id;
2782 }
2783
2784 static gboolean
2785 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
2786 {
2787         do {
2788                 GtkTreeIter child;
2789                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2790
2791                 gtk_tree_model_get (model, iter, 
2792                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, 
2793                                     &type, -1);
2794                         
2795                 gboolean result = FALSE;
2796                 if (type == TNY_FOLDER_TYPE_INBOX) {
2797                         result = TRUE;
2798                 }               
2799                 if (result) {
2800                         *inbox_iter = *iter;
2801                         return TRUE;
2802                 }
2803
2804                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2805                         if (find_inbox_iter (model, &child, inbox_iter))
2806                                 return TRUE;
2807                 }
2808
2809         } while (gtk_tree_model_iter_next (model, iter));
2810
2811         return FALSE;
2812 }
2813
2814
2815
2816
2817 void 
2818 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
2819 {
2820         GtkTreeModel *model;
2821         GtkTreeIter iter, inbox_iter;
2822         GtkTreeSelection *sel;
2823         GtkTreePath *path = NULL;
2824
2825         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2826         
2827         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2828         if (!model)
2829                 return;
2830
2831         expand_root_items (self);
2832         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2833
2834         if (!gtk_tree_model_get_iter_first (model, &iter)) {
2835                 g_warning ("%s: model is empty", __FUNCTION__);
2836                 return;
2837         }
2838
2839         if (find_inbox_iter (model, &iter, &inbox_iter))
2840                 path = gtk_tree_model_get_path (model, &inbox_iter);
2841         else
2842                 path = gtk_tree_path_new_first ();
2843
2844         /* Select the row and free */
2845         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
2846         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
2847         gtk_tree_path_free (path);
2848
2849         /* set focus */
2850         gtk_widget_grab_focus (GTK_WIDGET(self));
2851 }
2852
2853
2854 /* recursive */
2855 static gboolean
2856 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter, 
2857                   TnyFolder* folder)
2858 {
2859         do {
2860                 GtkTreeIter child;
2861                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2862                 TnyFolder* a_folder;
2863                 gchar *name = NULL;
2864                 
2865                 gtk_tree_model_get (model, iter, 
2866                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &a_folder,
2867                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN, &name,
2868                                     TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type, 
2869                                     -1);                
2870                 g_free (name);
2871
2872                 if (folder == a_folder) {
2873                         g_object_unref (a_folder);
2874                         *folder_iter = *iter;
2875                         return TRUE;
2876                 }
2877                 g_object_unref (a_folder);
2878                 
2879                 if (gtk_tree_model_iter_children (model, &child, iter)) {
2880                         if (find_folder_iter (model, &child, folder_iter, folder)) 
2881                                 return TRUE;
2882                 }
2883
2884         } while (gtk_tree_model_iter_next (model, iter));
2885
2886         return FALSE;
2887 }
2888
2889
2890 static void
2891 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model, 
2892                                      GtkTreePath *path, 
2893                                      GtkTreeIter *iter,
2894                                      ModestFolderView *self)
2895 {
2896         ModestFolderViewPrivate *priv = NULL;
2897         GtkTreeSelection *sel;
2898         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2899         GObject *instance = NULL;
2900
2901         if (!MODEST_IS_FOLDER_VIEW(self))
2902                 return;
2903         
2904         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2905
2906         priv->reexpand = TRUE;
2907
2908         gtk_tree_model_get (tree_model, iter, 
2909                             TNY_GTK_FOLDER_STORE_TREE_MODEL_TYPE_COLUMN, &type,
2910                             TNY_GTK_FOLDER_STORE_TREE_MODEL_INSTANCE_COLUMN, &instance,
2911                             -1);
2912         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
2913                 priv->folder_to_select = g_object_ref (instance);
2914         }
2915         g_object_unref (instance);
2916
2917         
2918         if (priv->folder_to_select) {
2919                 
2920                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
2921                                                        FALSE)) {
2922                         GtkTreePath *path;
2923                         path = gtk_tree_model_get_path (tree_model, iter);
2924                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
2925                         
2926                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2927
2928                         gtk_tree_selection_select_iter (sel, iter);
2929                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
2930
2931                         gtk_tree_path_free (path);
2932                 
2933                 }
2934
2935                 /* Disable next */
2936                 modest_folder_view_disable_next_folder_selection (self);
2937                 
2938                 /* Refilter the model */
2939                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
2940         }
2941 }
2942
2943
2944 void
2945 modest_folder_view_disable_next_folder_selection (ModestFolderView *self) 
2946 {
2947         ModestFolderViewPrivate *priv;
2948
2949         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
2950
2951         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2952
2953         if (priv->folder_to_select)
2954                 g_object_unref(priv->folder_to_select);
2955         
2956         priv->folder_to_select = NULL;
2957 }
2958
2959 gboolean
2960 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder, 
2961                                   gboolean after_change)
2962 {
2963         GtkTreeModel *model;
2964         GtkTreeIter iter, folder_iter;
2965         GtkTreeSelection *sel;
2966         ModestFolderViewPrivate *priv = NULL;
2967         
2968         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);     
2969         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE); 
2970                 
2971         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
2972
2973         if (after_change) {
2974                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
2975                 gtk_tree_selection_unselect_all (sel);
2976
2977                 if (priv->folder_to_select)
2978                         g_object_unref(priv->folder_to_select);
2979                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
2980                 return TRUE;
2981         }
2982                 
2983         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2984         if (!model)
2985                 return FALSE;
2986
2987
2988         /* Refilter the model, before selecting the folder */
2989         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2990
2991         if (!gtk_tree_model_get_iter_first (model, &iter)) {
2992                 g_warning ("%s: model is empty", __FUNCTION__);
2993                 return FALSE;
2994         }
2995         
2996         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
2997                 GtkTreePath *path;
2998
2999                 path = gtk_tree_model_get_path (model, &folder_iter);
3000                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3001
3002                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3003                 gtk_tree_selection_select_iter (sel, &folder_iter);
3004                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3005
3006                 gtk_tree_path_free (path);
3007                 return TRUE;
3008         }
3009         return FALSE;
3010 }
3011
3012
3013 void 
3014 modest_folder_view_copy_selection (ModestFolderView *self)
3015 {
3016         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3017         
3018         /* Copy selection */
3019         _clipboard_set_selected_data (self, FALSE);
3020 }
3021
3022 void 
3023 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3024 {
3025         ModestFolderViewPrivate *priv = NULL;
3026         GtkTreeModel *model = NULL;
3027         const gchar **hidding = NULL;
3028         guint i, n_selected;
3029
3030         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3031         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3032
3033         /* Copy selection */
3034         if (!_clipboard_set_selected_data (folder_view, TRUE))
3035                 return;
3036
3037         /* Get hidding ids */
3038         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
3039         
3040         /* Clear hidding array created by previous cut operation */
3041         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3042
3043         /* Copy hidding array */
3044         priv->n_selected = n_selected;
3045         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3046         for (i=0; i < n_selected; i++) 
3047                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
3048
3049         /* Hide cut folders */
3050         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3051         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3052 }
3053
3054 void
3055 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3056                                ModestFolderView *folder_view_dst)
3057 {
3058         GtkTreeModel *filter_model = NULL;
3059         GtkTreeModel *model = NULL;
3060         GtkTreeModel *new_filter_model = NULL;
3061         
3062         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3063         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3064         
3065         /* Get src model*/
3066         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3067         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3068
3069         /* Build new filter model */
3070         new_filter_model = gtk_tree_model_filter_new (model, NULL);     
3071         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3072                                                 filter_row,
3073                                                 folder_view_dst,
3074                                                 NULL);
3075         /* Set copied model */
3076         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3077         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3078                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3079
3080         /* Free */
3081         g_object_unref (new_filter_model);
3082 }
3083
3084 void
3085 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3086                                           gboolean show)
3087 {
3088         GtkTreeModel *model = NULL;
3089         ModestFolderViewPrivate* priv;
3090
3091         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3092
3093         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);     
3094         priv->show_non_move = show;
3095 /*      modest_folder_view_update_model(folder_view, */
3096 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3097
3098         /* Hide special folders */
3099         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3100         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3101                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3102         }
3103 }
3104
3105 /* Returns FALSE if it did not selected anything */
3106 static gboolean
3107 _clipboard_set_selected_data (ModestFolderView *folder_view,
3108                               gboolean delete)
3109 {
3110         ModestFolderViewPrivate *priv = NULL;
3111         TnyFolderStore *folder = NULL;
3112         gboolean retval = FALSE;
3113
3114         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3115         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3116                 
3117         /* Set selected data on clipboard   */
3118         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3119         folder = modest_folder_view_get_selected (folder_view);
3120
3121         /* Do not allow to select an account */
3122         if (TNY_IS_FOLDER (folder)) {
3123                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3124                 retval = TRUE;
3125         }
3126
3127         /* Free */
3128         g_object_unref (folder);
3129
3130         return retval;
3131 }
3132
3133 static void
3134 _clear_hidding_filter (ModestFolderView *folder_view) 
3135 {
3136         ModestFolderViewPrivate *priv;
3137         guint i;
3138         
3139         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view)); 
3140         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3141
3142         if (priv->hidding_ids != NULL) {
3143                 for (i=0; i < priv->n_selected; i++) 
3144                         g_free (priv->hidding_ids[i]);
3145                 g_free(priv->hidding_ids);
3146         }       
3147 }
3148
3149
3150 static void 
3151 on_display_name_changed (ModestAccountMgr *mgr, 
3152                          const gchar *account,
3153                          gpointer user_data)
3154 {
3155         ModestFolderView *self;
3156
3157         self = MODEST_FOLDER_VIEW (user_data);
3158
3159         /* Force a redraw */
3160 #if GTK_CHECK_VERSION(2, 8, 0)
3161         GtkTreeViewColumn * tree_column;
3162         
3163         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self), 
3164                                                 TNY_GTK_FOLDER_STORE_TREE_MODEL_NAME_COLUMN);
3165         gtk_tree_view_column_queue_resize (tree_column);
3166 #else
3167         gtk_widget_queue_draw (GTK_WIDGET (self));
3168 #endif
3169 }