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