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