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