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