Small fix in folder view filtering
[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 (retval && !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_HIDE_FOLDERS)) {
1959                 if (TNY_IS_FOLDER (instance))
1960                         return FALSE;
1961         }
1962
1963         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_CAN_HAVE_FOLDERS)) {
1964                 if (TNY_IS_FOLDER (instance)) {
1965                         /* Check folder rules */
1966                         ModestTnyFolderRules rules;
1967
1968                         rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
1969                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE);
1970                 } else if (TNY_IS_ACCOUNT (instance)) {
1971                         if (modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (instance))) {
1972                                 retval = FALSE;
1973                         } else {
1974                                 retval = TRUE;
1975                         }
1976                 }
1977         }
1978
1979         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_HIDE_MANDATORY_FOLDERS)) {
1980                 if (TNY_IS_FOLDER (instance)) {
1981                         TnyFolderType guess_type;
1982
1983                         if (TNY_FOLDER_TYPE_NORMAL) {
1984                                 guess_type = modest_tny_folder_guess_folder_type (TNY_FOLDER (instance));
1985                         } else {
1986                                 guess_type = type;
1987                         }
1988
1989                         switch (type) {
1990                         case TNY_FOLDER_TYPE_OUTBOX:
1991                         case TNY_FOLDER_TYPE_SENT:
1992                         case TNY_FOLDER_TYPE_DRAFTS:
1993                         case TNY_FOLDER_TYPE_ARCHIVE:
1994                         case TNY_FOLDER_TYPE_INBOX:
1995                                 retval = FALSE;
1996                                 break;
1997                         case TNY_FOLDER_TYPE_UNKNOWN:
1998                         case TNY_FOLDER_TYPE_NORMAL:
1999                                 break;
2000                         default:
2001                                 break;
2002                         }
2003
2004                 } else if (TNY_IS_ACCOUNT (instance)) {
2005                         retval = FALSE;
2006                 }
2007         }
2008
2009         if (retval && TNY_IS_FOLDER (instance)) {
2010                 rules = modest_tny_folder_get_rules (TNY_FOLDER (instance));
2011         }
2012
2013         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_DELETABLE)) {
2014                 if (TNY_IS_FOLDER (instance)) {
2015                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE);
2016                 } else if (TNY_IS_ACCOUNT (instance)) {
2017                         retval = FALSE;
2018                 }
2019         }
2020
2021         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_RENAMEABLE)) {
2022                 if (TNY_IS_FOLDER (instance)) {
2023                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE);
2024                 } else if (TNY_IS_ACCOUNT (instance)) {
2025                         retval = FALSE;
2026                 }
2027         }
2028
2029         if (retval && (priv->filter & MODEST_FOLDER_VIEW_FILTER_MOVEABLE)) {
2030                 if (TNY_IS_FOLDER (instance)) {
2031                         retval = !(rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE);
2032                 } else if (TNY_IS_ACCOUNT (instance)) {
2033                         retval = FALSE;
2034                 }
2035         }
2036
2037         /* Free */
2038         g_object_unref (instance);
2039
2040         return retval;
2041 }
2042
2043
2044 gboolean
2045 modest_folder_view_update_model (ModestFolderView *self,
2046                                  TnyAccountStore *account_store)
2047 {
2048         ModestFolderViewPrivate *priv;
2049         GtkTreeModel *model /* , *old_model */;
2050         GtkTreeModel *filter_model = NULL, *sortable = NULL;
2051
2052         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
2053         g_return_val_if_fail (account_store && MODEST_IS_TNY_ACCOUNT_STORE(account_store),
2054                               FALSE);
2055
2056         priv =  MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2057
2058         /* Notify that there is no folder selected */
2059         g_signal_emit (G_OBJECT(self),
2060                        signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2061                        NULL, FALSE);
2062         if (priv->cur_folder_store) {
2063                 g_object_unref (priv->cur_folder_store);
2064                 priv->cur_folder_store = NULL;
2065         }
2066
2067         /* FIXME: the local accounts are not shown when the query
2068            selects only the subscribed folders */
2069 #ifdef MODEST_TOOLKIT_HILDON2
2070         model = tny_gtk_folder_list_store_new_with_flags (NULL, 
2071                                                           TNY_GTK_FOLDER_LIST_STORE_FLAG_SHOW_PATH);
2072         tny_gtk_folder_list_store_set_path_separator (TNY_GTK_FOLDER_LIST_STORE (model),
2073                                                       MODEST_FOLDER_PATH_SEPARATOR);
2074 #else
2075         model = tny_gtk_folder_store_tree_model_new (NULL);
2076 #endif
2077
2078         /* When the model is a list store (plain representation) the
2079            outbox is not a child of any account so we have to manually
2080            delete it because removing the local folders account won't
2081            delete it (because tny_folder_get_account() is not defined
2082            for a merge folder */
2083         if (TNY_IS_GTK_FOLDER_LIST_STORE (model)) {
2084                 TnyAccount *account;
2085                 ModestTnyAccountStore *acc_store;
2086
2087                 acc_store = modest_runtime_get_account_store ();
2088                 account = modest_tny_account_store_get_local_folders_account (acc_store);
2089
2090                 if (g_signal_handler_is_connected (account,
2091                                                    priv->outbox_deleted_handler))
2092                         g_signal_handler_disconnect (account,
2093                                                      priv->outbox_deleted_handler);
2094
2095                 priv->outbox_deleted_handler =
2096                         g_signal_connect (account,
2097                                           "outbox-deleted",
2098                                           G_CALLBACK (on_outbox_deleted_cb),
2099                                           self);
2100                 g_object_unref (account);
2101         }
2102
2103         /* Get the accounts: */
2104         tny_account_store_get_accounts (TNY_ACCOUNT_STORE(account_store),
2105                                         TNY_LIST (model),
2106                                         TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
2107
2108         sortable = gtk_tree_model_sort_new_with_model (model);
2109         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
2110                                               NAME_COLUMN,
2111                                               GTK_SORT_ASCENDING);
2112         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
2113                                          NAME_COLUMN,
2114                                          cmp_rows, NULL, NULL);
2115
2116         /* Create filter model */
2117         filter_model = gtk_tree_model_filter_new (sortable, NULL);
2118         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
2119                                                 filter_row,
2120                                                 self,
2121                                                 NULL);
2122
2123         /* Set new model */
2124         gtk_tree_view_set_model (GTK_TREE_VIEW(self), filter_model);
2125 #ifndef MODEST_TOOLKIT_HILDON2
2126         g_signal_connect (G_OBJECT(filter_model), "row-inserted",
2127                           (GCallback) on_row_inserted_maybe_select_folder, self);
2128 #endif
2129
2130         g_object_unref (model);
2131         g_object_unref (filter_model);
2132         g_object_unref (sortable);
2133
2134         /* Force a reselection of the INBOX next time the widget is shown */
2135         priv->reselect = TRUE;
2136
2137         return TRUE;
2138 }
2139
2140
2141 static void
2142 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
2143 {
2144         GtkTreeModel *model = NULL;
2145         TnyFolderStore *folder = NULL;
2146         GtkTreeIter iter;
2147         ModestFolderView *tree_view = NULL;
2148         ModestFolderViewPrivate *priv = NULL;
2149         gboolean selected = FALSE;
2150
2151         g_return_if_fail (sel);
2152         g_return_if_fail (user_data);
2153
2154         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2155
2156         selected = gtk_tree_selection_get_selected (sel, &model, &iter);
2157
2158         tree_view = MODEST_FOLDER_VIEW (user_data);
2159
2160         if (selected) {
2161                 gtk_tree_model_get (model, &iter,
2162                                     INSTANCE_COLUMN, &folder,
2163                                     -1);
2164
2165                 /* If the folder is the same do not notify */
2166                 if (folder && priv->cur_folder_store == folder) {
2167                         g_object_unref (folder);
2168                         return;
2169                 }
2170         }
2171
2172         /* Current folder was unselected */
2173         if (priv->cur_folder_store) {
2174                 /* We must do this firstly because a libtinymail-camel
2175                    implementation detail. If we issue the signal
2176                    before doing the sync_async, then that signal could
2177                    cause (and it actually does it) a free of the
2178                    summary of the folder (because the main window will
2179                    clear the headers view */
2180                 if (TNY_IS_FOLDER(priv->cur_folder_store))
2181                         tny_folder_sync_async (TNY_FOLDER(priv->cur_folder_store),
2182                                                FALSE, NULL, NULL, NULL);
2183
2184                 g_signal_emit (G_OBJECT(tree_view), signals[FOLDER_SELECTION_CHANGED_SIGNAL], 0,
2185                        priv->cur_folder_store, FALSE);
2186
2187                 g_object_unref (priv->cur_folder_store);
2188                 priv->cur_folder_store = NULL;
2189         }
2190
2191         /* New current references */
2192         priv->cur_folder_store = folder;
2193
2194         /* New folder has been selected. Do not notify if there is
2195            nothing new selected */
2196         if (selected) {
2197                 g_signal_emit (G_OBJECT(tree_view),
2198                                signals[FOLDER_SELECTION_CHANGED_SIGNAL],
2199                                0, priv->cur_folder_store, TRUE);
2200         }
2201 }
2202
2203 static void
2204 on_row_activated (GtkTreeView *treeview,
2205                   GtkTreePath *treepath,
2206                   GtkTreeViewColumn *column,
2207                   gpointer user_data)
2208 {
2209         GtkTreeModel *model = NULL;
2210         TnyFolderStore *folder = NULL;
2211         GtkTreeIter iter;
2212         ModestFolderView *self = NULL;
2213         ModestFolderViewPrivate *priv = NULL;
2214
2215         g_return_if_fail (treeview);
2216         g_return_if_fail (user_data);
2217
2218         self = MODEST_FOLDER_VIEW (user_data);
2219         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(user_data);
2220
2221         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2222
2223         if (!gtk_tree_model_get_iter (model, &iter, treepath))
2224                 return;
2225
2226         gtk_tree_model_get (model, &iter,
2227                             INSTANCE_COLUMN, &folder,
2228                             -1);
2229
2230         g_signal_emit (G_OBJECT(self),
2231                        signals[FOLDER_ACTIVATED_SIGNAL],
2232                        0, folder);
2233
2234 #ifdef MODEST_TOOLKIT_HILDON2
2235         HildonUIMode ui_mode;
2236         g_object_get (G_OBJECT (self), "hildon-ui-mode", &ui_mode, NULL);
2237         if (ui_mode == HILDON_UI_MODE_NORMAL) {
2238                 if (priv->cur_folder_store)
2239                         g_object_unref (priv->cur_folder_store);
2240                 priv->cur_folder_store = g_object_ref (folder);
2241         }
2242 #endif
2243
2244         g_object_unref (folder);
2245 }
2246
2247 TnyFolderStore *
2248 modest_folder_view_get_selected (ModestFolderView *self)
2249 {
2250         ModestFolderViewPrivate *priv;
2251
2252         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
2253
2254         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
2255         if (priv->cur_folder_store)
2256                 g_object_ref (priv->cur_folder_store);
2257
2258         return priv->cur_folder_store;
2259 }
2260
2261 static gint
2262 get_cmp_rows_type_pos (GObject *folder)
2263 {
2264         /* Remote accounts -> Local account -> MMC account .*/
2265         /* 0, 1, 2 */
2266
2267         if (TNY_IS_ACCOUNT (folder) &&
2268                 modest_tny_account_is_virtual_local_folders (
2269                         TNY_ACCOUNT (folder))) {
2270                 return 1;
2271         } else if (TNY_IS_ACCOUNT (folder)) {
2272                 TnyAccount *account = TNY_ACCOUNT (folder);
2273                 const gchar *account_id = tny_account_get_id (account);
2274                 if (!strcmp (account_id, MODEST_MMC_ACCOUNT_ID))
2275                         return 2;
2276                 else
2277                         return 0;
2278         }
2279         else {
2280                 printf ("DEBUG: %s: unexpected type.\n", __FUNCTION__);
2281                 return -1; /* Should never happen */
2282         }
2283 }
2284
2285 static gboolean
2286 inbox_is_special (TnyFolderStore *folder_store)
2287 {
2288         gboolean is_special = TRUE;
2289
2290         if (TNY_IS_FOLDER (folder_store)) {
2291                 const gchar *id;
2292                 gchar *downcase;
2293                 gchar *last_bar;
2294                 gchar *last_inbox_bar;
2295
2296                 id = tny_folder_get_id (TNY_FOLDER (folder_store));
2297                 downcase = g_utf8_strdown (id, -1);
2298                 last_bar = g_strrstr (downcase, "/");
2299                 if (last_bar) {
2300                         last_inbox_bar = g_strrstr  (downcase, "inbox/");
2301                         if ((last_inbox_bar == NULL) || (last_inbox_bar + 5 != last_bar))
2302                                 is_special = FALSE;
2303                 } else {
2304                         is_special = FALSE;
2305                 }
2306                 g_free (downcase);
2307         }
2308         return is_special;
2309 }
2310
2311 static gint
2312 get_cmp_pos (TnyFolderType t, TnyFolder *folder_store)
2313 {
2314         TnyAccount *account;
2315         gboolean is_special;
2316         /* Inbox, Outbox, Drafts, Sent, User */
2317         /* 0, 1, 2, 3, 4 */
2318
2319         if (!TNY_IS_FOLDER (folder_store))
2320                 return 4;
2321         switch (t) {
2322         case TNY_FOLDER_TYPE_INBOX:
2323         {
2324                 account = tny_folder_get_account (folder_store);
2325                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 0);
2326
2327                 /* In inbox case we need to know if the inbox is really the top
2328                  * inbox of the account, or if it's a submailbox inbox. To do
2329                  * this we'll apply an heuristic rule: Find last "/" and check
2330                  * if it's preceeded by another Inbox */
2331                 is_special = is_special && !inbox_is_special (TNY_FOLDER_STORE (folder_store));
2332                 g_object_unref (account);
2333                 return is_special?0:4;
2334         }
2335         break;
2336         case TNY_FOLDER_TYPE_OUTBOX:
2337                 return (TNY_IS_MERGE_FOLDER (folder_store))?2:4;
2338                 break;
2339         case TNY_FOLDER_TYPE_DRAFTS:
2340         {
2341                 account = tny_folder_get_account (folder_store);
2342                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2343                 g_object_unref (account);
2344                 return is_special?1:4;
2345         }
2346         break;
2347         case TNY_FOLDER_TYPE_SENT:
2348         {
2349                 account = tny_folder_get_account (folder_store);
2350                 is_special = (get_cmp_rows_type_pos (G_OBJECT (account)) == 1);
2351                 g_object_unref (account);
2352                 return is_special?3:4;
2353         }
2354         break;
2355         default:
2356                 return 4;
2357         }
2358 }
2359
2360 static gint
2361 compare_account_names (TnyAccount *a1, TnyAccount *a2)
2362 {
2363         const gchar *a1_name, *a2_name;
2364
2365         a1_name = tny_account_get_name (a1);
2366         a2_name = tny_account_get_name (a2);
2367
2368         return modest_text_utils_utf8_strcmp (a1_name, a2_name, TRUE);
2369 }
2370
2371 static gint
2372 compare_accounts (TnyFolderStore *s1, TnyFolderStore *s2)
2373 {
2374         TnyAccount *a1 = NULL, *a2 = NULL;
2375         gint cmp;
2376
2377         if (TNY_IS_ACCOUNT (s1)) {
2378                 a1 = TNY_ACCOUNT (g_object_ref (s1));
2379         } else if (!TNY_IS_MERGE_FOLDER (s1)) {
2380                 a1 = tny_folder_get_account (TNY_FOLDER (s1));
2381         }
2382
2383         if (TNY_IS_ACCOUNT (s2)) {
2384                 a2 = TNY_ACCOUNT (g_object_ref (s2));
2385         } else  if (!TNY_IS_MERGE_FOLDER (s2)) {
2386                 a2 = tny_folder_get_account (TNY_FOLDER (s2));
2387         }
2388
2389         if (!a1 || !a2) {
2390                 if (!a1 && !a2)
2391                         cmp = 0;
2392                 else if (!a1)
2393                         cmp = 1;
2394                 else
2395                         cmp = -1;
2396                 goto finish;
2397         }
2398
2399         if (a1 == a2) {
2400                 cmp = 0;
2401                 goto finish;
2402         }
2403         /* First we sort with the type of account */
2404         cmp = get_cmp_rows_type_pos (G_OBJECT (a1)) - get_cmp_rows_type_pos (G_OBJECT (a2));
2405         if (cmp != 0)
2406                 goto finish;
2407
2408         cmp = compare_account_names (a1, a2);
2409
2410 finish:
2411         if (a1)
2412                 g_object_unref (a1);
2413         if (a2)
2414                 g_object_unref (a2);
2415
2416         return cmp;
2417 }
2418
2419 static gint
2420 compare_accounts_first (TnyFolderStore *s1, TnyFolderStore *s2)
2421 {
2422         gint is_account1, is_account2;
2423
2424         is_account1 = TNY_IS_ACCOUNT (s1)?1:0;
2425         is_account2 = TNY_IS_ACCOUNT (s2)?1:0;
2426
2427         return is_account2 - is_account1;
2428 }
2429
2430 /*
2431  * This function orders the mail accounts according to these rules:
2432  * 1st - remote accounts
2433  * 2nd - local account
2434  * 3rd - MMC account
2435  */
2436 static gint
2437 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
2438           gpointer user_data)
2439 {
2440         gint cmp = 0;
2441         gchar *name1 = NULL;
2442         gchar *name2 = NULL;
2443         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
2444         TnyFolderType type2 = TNY_FOLDER_TYPE_UNKNOWN;
2445         GObject *folder1 = NULL;
2446         GObject *folder2 = NULL;
2447
2448         gtk_tree_model_get (tree_model, iter1,
2449                             NAME_COLUMN, &name1,
2450                             TYPE_COLUMN, &type,
2451                             INSTANCE_COLUMN, &folder1,
2452                             -1);
2453         gtk_tree_model_get (tree_model, iter2,
2454                             NAME_COLUMN, &name2,
2455                             TYPE_COLUMN, &type2,
2456                             INSTANCE_COLUMN, &folder2,
2457                             -1);
2458
2459         /* Return if we get no folder. This could happen when folder
2460            operations are happening. The model is updated after the
2461            folder copy/move actually occurs, so there could be
2462            situations where the model to be drawn is not correct */
2463         if (!folder1 || !folder2)
2464                 goto finish;
2465
2466         /* Sort by type. First the special folders, then the archives */
2467         cmp = get_cmp_pos (type, (TnyFolder *) folder1) - get_cmp_pos (type2, (TnyFolder *) folder2);
2468         if (cmp != 0)
2469                 goto finish;
2470
2471         /* Now we sort using the account of each folder */
2472         if (TNY_IS_FOLDER_STORE (folder1) && 
2473             TNY_IS_FOLDER_STORE (folder2)) {
2474                 cmp = compare_accounts (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2475                 if (cmp != 0)
2476                         goto finish;
2477
2478                 /* Each group is preceeded by its account */
2479                 cmp = compare_accounts_first (TNY_FOLDER_STORE (folder1), TNY_FOLDER_STORE (folder2));
2480                 if (cmp != 0)
2481                         goto finish;
2482         }
2483
2484         /* Pure sort by name */
2485         cmp = modest_text_utils_utf8_strcmp (name1, name2, TRUE);
2486  finish:
2487         if (folder1)
2488                 g_object_unref(G_OBJECT(folder1));
2489         if (folder2)
2490                 g_object_unref(G_OBJECT(folder2));
2491
2492         g_free (name1);
2493         g_free (name2);
2494
2495         return cmp;
2496 }
2497
2498 /*****************************************************************************/
2499 /*                        DRAG and DROP stuff                                */
2500 /*****************************************************************************/
2501 /*
2502  * This function fills the #GtkSelectionData with the row and the
2503  * model that has been dragged. It's called when this widget is a
2504  * source for dnd after the event drop happened
2505  */
2506 static void
2507 on_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
2508                   guint info, guint time, gpointer data)
2509 {
2510         GtkTreeSelection *selection;
2511         GtkTreeModel *model;
2512         GtkTreeIter iter;
2513         GtkTreePath *source_row;
2514
2515         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2516         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2517
2518                 source_row = gtk_tree_model_get_path (model, &iter);
2519                 gtk_tree_set_row_drag_data (selection_data,
2520                                             model,
2521                                             source_row);
2522
2523                 gtk_tree_path_free (source_row);
2524         }
2525 }
2526
2527 typedef struct _DndHelper {
2528         ModestFolderView *folder_view;
2529         gboolean delete_source;
2530         GtkTreePath *source_row;
2531 } DndHelper;
2532
2533 static void
2534 dnd_helper_destroyer (DndHelper *helper)
2535 {
2536         /* Free the helper */
2537         gtk_tree_path_free (helper->source_row);
2538         g_slice_free (DndHelper, helper);
2539 }
2540
2541 static void
2542 xfer_folder_cb (ModestMailOperation *mail_op,
2543                 TnyFolder *new_folder,
2544                 gpointer user_data)
2545 {
2546         if (new_folder) {
2547                 /* Select the folder */
2548                 modest_folder_view_select_folder (MODEST_FOLDER_VIEW (user_data),
2549                                                   new_folder, FALSE);
2550         }
2551 }
2552
2553
2554 /* get the folder for the row the treepath refers to. */
2555 /* folder must be unref'd */
2556 static TnyFolderStore *
2557 tree_path_to_folder (GtkTreeModel *model, GtkTreePath *path)
2558 {
2559         GtkTreeIter iter;
2560         TnyFolderStore *folder = NULL;
2561
2562         if (gtk_tree_model_get_iter (model,&iter, path))
2563                 gtk_tree_model_get (model, &iter,
2564                                     INSTANCE_COLUMN, &folder,
2565                                     -1);
2566         return folder;
2567 }
2568
2569
2570 /*
2571  * This function is used by drag_data_received_cb to manage drag and
2572  * drop of a header, i.e, and drag from the header view to the folder
2573  * view.
2574  */
2575 static void
2576 drag_and_drop_from_header_view (GtkTreeModel *source_model,
2577                                 GtkTreeModel *dest_model,
2578                                 GtkTreePath  *dest_row,
2579                                 GtkSelectionData *selection_data)
2580 {
2581         TnyList *headers = NULL;
2582         TnyFolder *folder = NULL, *src_folder = NULL;
2583         TnyFolderType folder_type;
2584         GtkTreeIter source_iter, dest_iter;
2585         ModestWindowMgr *mgr = NULL;
2586         ModestWindow *main_win = NULL;
2587         gchar **uris, **tmp;
2588
2589         /* Build the list of headers */
2590         mgr = modest_runtime_get_window_mgr ();
2591         headers = tny_simple_list_new ();
2592         uris = modest_dnd_selection_data_get_paths (selection_data);
2593         tmp = uris;
2594
2595         while (*tmp != NULL) {
2596                 TnyHeader *header;
2597                 GtkTreePath *path;
2598                 gboolean first = TRUE;
2599
2600                 /* Get header */
2601                 path = gtk_tree_path_new_from_string (*tmp);
2602                 gtk_tree_model_get_iter (source_model, &source_iter, path);
2603                 gtk_tree_model_get (source_model, &source_iter,
2604                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2605                                     &header, -1);
2606
2607                 /* Do not enable d&d of headers already opened */
2608                 if (!modest_window_mgr_find_registered_header(mgr, header, NULL))
2609                         tny_list_append (headers, G_OBJECT (header));
2610
2611                 if (G_UNLIKELY (first)) {
2612                         src_folder = tny_header_get_folder (header);
2613                         first = FALSE;
2614                 }
2615
2616                 /* Free and go on */
2617                 gtk_tree_path_free (path);
2618                 g_object_unref (header);
2619                 tmp++;
2620         }
2621         g_strfreev (uris);
2622
2623         /* This could happen ig we perform a d&d very quickly over the
2624            same row that row could dissapear because message is
2625            transferred */
2626         if (!TNY_IS_FOLDER (src_folder))
2627                 goto cleanup;
2628
2629         /* Get the target folder */
2630         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2631         gtk_tree_model_get (dest_model, &dest_iter,
2632                             INSTANCE_COLUMN,
2633                             &folder, -1);
2634
2635         if (!folder || !TNY_IS_FOLDER(folder)) {
2636 /*              g_warning ("%s: not a valid target folder (%p)", __FUNCTION__, folder); */
2637                 goto cleanup;
2638         }
2639
2640         folder_type = modest_tny_folder_guess_folder_type (folder);
2641         if (folder_type == TNY_FOLDER_TYPE_INVALID) {
2642 /*              g_warning ("%s: invalid target folder", __FUNCTION__); */
2643                 goto cleanup;  /* cannot move messages there */
2644         }
2645
2646         if (modest_tny_folder_get_rules((TNY_FOLDER(folder))) & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2647 /*              g_warning ("folder not writable"); */
2648                 goto cleanup; /* verboten! */
2649         }
2650
2651         /* Ask for confirmation to move */
2652         main_win = modest_window_mgr_get_main_window (mgr, FALSE); /* don't create */
2653         if (!main_win) {
2654                 g_warning ("%s: BUG: no main window found", __FUNCTION__);
2655                 goto cleanup;
2656         }
2657
2658         /* Transfer messages */
2659         modest_ui_actions_transfer_messages_helper (GTK_WINDOW (main_win), src_folder,
2660                                                     headers, folder);
2661
2662         /* Frees */
2663 cleanup:
2664         if (G_IS_OBJECT (src_folder))
2665                 g_object_unref (src_folder);
2666         if (G_IS_OBJECT(folder))
2667                 g_object_unref (G_OBJECT (folder));
2668         if (G_IS_OBJECT(headers))
2669                 g_object_unref (headers);
2670 }
2671
2672 typedef struct {
2673         TnyFolderStore *src_folder;
2674         TnyFolderStore *dst_folder;
2675         ModestFolderView *folder_view;
2676         DndHelper *helper;
2677 } DndFolderInfo;
2678
2679 static void
2680 dnd_folder_info_destroyer (DndFolderInfo *info)
2681 {
2682         if (info->src_folder)
2683                 g_object_unref (info->src_folder);
2684         if (info->dst_folder)
2685                 g_object_unref (info->dst_folder);
2686         g_slice_free (DndFolderInfo, info);
2687 }
2688
2689 static void
2690 dnd_on_connection_failed_destroyer (DndFolderInfo *info,
2691                                     GtkWindow *parent_window,
2692                                     TnyAccount *account)
2693 {
2694         /* Show error */
2695         modest_ui_actions_on_account_connection_error (parent_window, account);
2696
2697         /* Free the helper & info */
2698         dnd_helper_destroyer (info->helper);
2699         dnd_folder_info_destroyer (info);
2700 }
2701
2702 static void
2703 drag_and_drop_from_folder_view_src_folder_performer (gboolean canceled,
2704                                                      GError *err,
2705                                                      GtkWindow *parent_window,
2706                                                      TnyAccount *account,
2707                                                      gpointer user_data)
2708 {
2709         DndFolderInfo *info = NULL;
2710         ModestMailOperation *mail_op;
2711
2712         info = (DndFolderInfo *) user_data;
2713
2714         if (err || canceled) {
2715                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2716                 return;
2717         }
2718
2719         /* Do the mail operation */
2720         mail_op = modest_mail_operation_new_with_error_handling ((GObject *) parent_window,
2721                                                                  modest_ui_actions_move_folder_error_handler,
2722                                                                  info->src_folder, NULL);
2723
2724         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
2725                                          mail_op);
2726
2727         /* Transfer the folder */
2728         modest_mail_operation_xfer_folder (mail_op,
2729                                            TNY_FOLDER (info->src_folder),
2730                                            info->dst_folder,
2731                                            info->helper->delete_source,
2732                                            xfer_folder_cb,
2733                                            info->helper->folder_view);
2734
2735         /* Frees */
2736         g_object_unref (G_OBJECT (mail_op));
2737         dnd_helper_destroyer (info->helper);
2738         dnd_folder_info_destroyer (info);
2739 }
2740
2741
2742 static void
2743 drag_and_drop_from_folder_view_dst_folder_performer (gboolean canceled,
2744                                                      GError *err,
2745                                                      GtkWindow *parent_window,
2746                                                      TnyAccount *account,
2747                                                      gpointer user_data)
2748 {
2749         DndFolderInfo *info = NULL;
2750
2751         info = (DndFolderInfo *) user_data;
2752
2753         if (err || canceled) {
2754                 dnd_on_connection_failed_destroyer (info, parent_window, account);
2755                 return;
2756         }
2757
2758         /* Connect to source folder and perform the copy/move */
2759         modest_platform_connect_if_remote_and_perform (NULL, TRUE,
2760                                                        info->src_folder,
2761                                                        drag_and_drop_from_folder_view_src_folder_performer,
2762                                                        info);
2763 }
2764
2765 /*
2766  * This function is used by drag_data_received_cb to manage drag and
2767  * drop of a folder, i.e, and drag from the folder view to the same
2768  * folder view.
2769  */
2770 static void
2771 drag_and_drop_from_folder_view (GtkTreeModel     *source_model,
2772                                 GtkTreeModel     *dest_model,
2773                                 GtkTreePath      *dest_row,
2774                                 GtkSelectionData *selection_data,
2775                                 DndHelper        *helper)
2776 {
2777         GtkTreeIter dest_iter, iter;
2778         TnyFolderStore *dest_folder = NULL;
2779         TnyFolderStore *folder = NULL;
2780         gboolean forbidden = FALSE;
2781         ModestWindow *win;
2782         DndFolderInfo *info = NULL;
2783
2784         win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE); /* don't create */
2785         if (!win) {
2786                 g_warning ("%s: BUG: no main window", __FUNCTION__);
2787                 dnd_helper_destroyer (helper);
2788                 return;
2789         }
2790
2791         if (!forbidden) {
2792                 /* check the folder rules for the destination */
2793                 folder = tree_path_to_folder (dest_model, dest_row);
2794                 if (TNY_IS_FOLDER(folder)) {
2795                         ModestTnyFolderRules rules =
2796                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2797                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE;
2798                 } else if (TNY_IS_FOLDER_STORE(folder)) {
2799                         /* enable local root as destination for folders */
2800                         if (!MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (folder) &&
2801                             !modest_tny_account_is_memory_card_account (TNY_ACCOUNT (folder)))
2802                                 forbidden = TRUE;
2803                 }
2804                 g_object_unref (folder);
2805         }
2806         if (!forbidden) {
2807                 /* check the folder rules for the source */
2808                 folder = tree_path_to_folder (source_model, helper->source_row);
2809                 if (TNY_IS_FOLDER(folder)) {
2810                         ModestTnyFolderRules rules =
2811                                 modest_tny_folder_get_rules (TNY_FOLDER (folder));
2812                         forbidden = rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE;
2813                 } else
2814                         forbidden = TRUE;
2815                 g_object_unref (folder);
2816         }
2817
2818
2819         /* Check if the drag is possible */
2820         if (forbidden || !gtk_tree_path_compare (helper->source_row, dest_row)) {
2821                 /* Show error */
2822                 modest_platform_run_information_dialog ((GtkWindow *) win, 
2823                                                         _("mail_in_ui_folder_move_target_error"), 
2824                                                         FALSE);
2825                 /* Restore the previous selection */
2826                 folder = tree_path_to_folder (source_model, helper->source_row);
2827                 if (folder) {
2828                         if (TNY_IS_FOLDER (folder))
2829                                 modest_folder_view_select_folder (helper->folder_view, 
2830                                                                   TNY_FOLDER (folder), FALSE);
2831                         g_object_unref (folder);
2832                 }
2833                 dnd_helper_destroyer (helper);
2834                 return;
2835         }
2836
2837         /* Get data */
2838         gtk_tree_model_get_iter (dest_model, &dest_iter, dest_row);
2839         gtk_tree_model_get (dest_model, &dest_iter,
2840                             INSTANCE_COLUMN,
2841                             &dest_folder, -1);
2842         gtk_tree_model_get_iter (source_model, &iter, helper->source_row);
2843         gtk_tree_model_get (source_model, &iter,
2844                             INSTANCE_COLUMN,
2845                             &folder, -1);
2846
2847         /* Create the info for the performer */
2848         info = g_slice_new0 (DndFolderInfo);
2849         info->src_folder = g_object_ref (folder);
2850         info->dst_folder = g_object_ref (dest_folder);
2851         info->helper = helper;
2852
2853         /* Connect to the destination folder and perform the copy/move */
2854         modest_platform_connect_if_remote_and_perform (GTK_WINDOW (win), TRUE,
2855                                                        dest_folder,
2856                                                        drag_and_drop_from_folder_view_dst_folder_performer,
2857                                                        info);
2858
2859         /* Frees */
2860         g_object_unref (dest_folder);
2861         g_object_unref (folder);
2862 }
2863
2864 /*
2865  * This function receives the data set by the "drag-data-get" signal
2866  * handler. This information comes within the #GtkSelectionData. This
2867  * function will manage both the drags of folders of the treeview and
2868  * drags of headers of the header view widget.
2869  */
2870 static void
2871 on_drag_data_received (GtkWidget *widget,
2872                        GdkDragContext *context,
2873                        gint x,
2874                        gint y,
2875                        GtkSelectionData *selection_data,
2876                        guint target_type,
2877                        guint time,
2878                        gpointer data)
2879 {
2880         GtkWidget *source_widget;
2881         GtkTreeModel *dest_model, *source_model;
2882         GtkTreePath *source_row, *dest_row;
2883         GtkTreeViewDropPosition pos;
2884         gboolean delete_source = FALSE;
2885         gboolean success = FALSE;
2886
2887         /* Do not allow further process */
2888         g_signal_stop_emission_by_name (widget, "drag-data-received");
2889         source_widget = gtk_drag_get_source_widget (context);
2890
2891         /* Get the action */
2892         if (context->action == GDK_ACTION_MOVE) {
2893                 delete_source = TRUE;
2894
2895                 /* Notify that there is no folder selected. We need to
2896                    do this in order to update the headers view (and
2897                    its monitors, because when moving, the old folder
2898                    won't longer exist. We can not wait for the end of
2899                    the operation, because the operation won't start if
2900                    the folder is in use */
2901                 if (source_widget == widget) {
2902                         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
2903                         gtk_tree_selection_unselect_all (sel);
2904                 }
2905         }
2906
2907         /* Check if the get_data failed */
2908         if (selection_data == NULL || selection_data->length < 0)
2909                 goto end;
2910
2911         /* Select the destination model */
2912         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
2913
2914         /* Get the path to the destination row. Can not call
2915            gtk_tree_view_get_drag_dest_row() because the source row
2916            is not selected anymore */
2917         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), x, y,
2918                                            &dest_row, &pos);
2919
2920         /* Only allow drops IN other rows */
2921         if (!dest_row ||
2922             pos == GTK_TREE_VIEW_DROP_BEFORE ||
2923             pos == GTK_TREE_VIEW_DROP_AFTER)
2924                 goto end;
2925
2926         success = TRUE;
2927         /* Drags from the header view */
2928         if (source_widget != widget) {
2929                 source_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source_widget));
2930
2931                 drag_and_drop_from_header_view (source_model,
2932                                                 dest_model,
2933                                                 dest_row,
2934                                                 selection_data);
2935         } else {
2936                 DndHelper *helper = NULL;
2937
2938                 /* Get the source model and row */
2939                 gtk_tree_get_row_drag_data (selection_data,
2940                                             &source_model,
2941                                             &source_row);
2942
2943                 /* Create the helper */
2944                 helper = g_slice_new0 (DndHelper);
2945                 helper->delete_source = delete_source;
2946                 helper->source_row = gtk_tree_path_copy (source_row);
2947                 helper->folder_view = MODEST_FOLDER_VIEW (widget);
2948
2949                 drag_and_drop_from_folder_view (source_model,
2950                                                 dest_model,
2951                                                 dest_row,
2952                                                 selection_data,
2953                                                 helper);
2954
2955                 gtk_tree_path_free (source_row);
2956         }
2957
2958         /* Frees */
2959         gtk_tree_path_free (dest_row);
2960
2961  end:
2962         /* Finish the drag and drop */
2963         gtk_drag_finish (context, success, FALSE, time);
2964 }
2965
2966 /*
2967  * We define a "drag-drop" signal handler because we do not want to
2968  * use the default one, because the default one always calls
2969  * gtk_drag_finish and we prefer to do it in the "drag-data-received"
2970  * signal handler, because there we have all the information available
2971  * to know if the dnd was a success or not.
2972  */
2973 static gboolean
2974 drag_drop_cb (GtkWidget      *widget,
2975               GdkDragContext *context,
2976               gint            x,
2977               gint            y,
2978               guint           time,
2979               gpointer        user_data)
2980 {
2981         gpointer target;
2982
2983         if (!context->targets)
2984                 return FALSE;
2985
2986         /* Check if we're dragging a folder row */
2987         target = gtk_drag_dest_find_target (widget, context, NULL);
2988
2989         /* Request the data from the source. */
2990         gtk_drag_get_data(widget, context, target, time);
2991
2992     return TRUE;
2993 }
2994
2995 /*
2996  * This function expands a node of a tree view if it's not expanded
2997  * yet. Not sure why it needs the threads stuff, but gtk+`example code
2998  * does that, so that's why they're here.
2999  */
3000 static gint
3001 expand_row_timeout (gpointer data)
3002 {
3003         GtkTreeView *tree_view = data;
3004         GtkTreePath *dest_path = NULL;
3005         GtkTreeViewDropPosition pos;
3006         gboolean result = FALSE;
3007
3008         gdk_threads_enter ();
3009
3010         gtk_tree_view_get_drag_dest_row (tree_view,
3011                                          &dest_path,
3012                                          &pos);
3013
3014         if (dest_path &&
3015             (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
3016              pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) {
3017                 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
3018                 gtk_tree_path_free (dest_path);
3019         }
3020         else {
3021                 if (dest_path)
3022                         gtk_tree_path_free (dest_path);
3023
3024                 result = TRUE;
3025         }
3026
3027         gdk_threads_leave ();
3028
3029         return result;
3030 }
3031
3032 /*
3033  * This function is called whenever the pointer is moved over a widget
3034  * while dragging some data. It installs a timeout that will expand a
3035  * node of the treeview if not expanded yet. This function also calls
3036  * gdk_drag_status in order to set the suggested action that will be
3037  * used by the "drag-data-received" signal handler to know if we
3038  * should do a move or just a copy of the data.
3039  */
3040 static gboolean
3041 on_drag_motion (GtkWidget      *widget,
3042                 GdkDragContext *context,
3043                 gint            x,
3044                 gint            y,
3045                 guint           time,
3046                 gpointer        user_data)
3047 {
3048         GtkTreeViewDropPosition pos;
3049         GtkTreePath *dest_row;
3050         GtkTreeModel *dest_model;
3051         ModestFolderViewPrivate *priv;
3052         GdkDragAction suggested_action;
3053         gboolean valid_location = FALSE;
3054         TnyFolderStore *folder = NULL;
3055
3056         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (widget);
3057
3058         if (priv->timer_expander != 0) {
3059                 g_source_remove (priv->timer_expander);
3060                 priv->timer_expander = 0;
3061         }
3062
3063         gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
3064                                            x, y,
3065                                            &dest_row,
3066                                            &pos);
3067
3068         /* Do not allow drops between folders */
3069         if (!dest_row ||
3070             pos == GTK_TREE_VIEW_DROP_BEFORE ||
3071             pos == GTK_TREE_VIEW_DROP_AFTER) {
3072                 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), NULL, 0);
3073                 gdk_drag_status(context, 0, time);
3074                 valid_location = FALSE;
3075                 goto out;
3076         } else {
3077                 valid_location = TRUE;
3078         }
3079
3080         /* Check that the destination folder is writable */
3081         dest_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
3082         folder = tree_path_to_folder (dest_model, dest_row);
3083         if (folder && TNY_IS_FOLDER (folder)) {
3084                 ModestTnyFolderRules rules = modest_tny_folder_get_rules(TNY_FOLDER (folder));
3085
3086                 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
3087                         valid_location = FALSE;
3088                         goto out;
3089                 }
3090         }
3091
3092         /* Expand the selected row after 1/2 second */
3093         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), dest_row)) {
3094                 priv->timer_expander = g_timeout_add (500, expand_row_timeout, widget);
3095         }
3096         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), dest_row, pos);
3097
3098         /* Select the desired action. By default we pick MOVE */
3099         suggested_action = GDK_ACTION_MOVE;
3100
3101         if (context->actions == GDK_ACTION_COPY)
3102             gdk_drag_status(context, GDK_ACTION_COPY, time);
3103         else if (context->actions == GDK_ACTION_MOVE)
3104             gdk_drag_status(context, GDK_ACTION_MOVE, time);
3105         else if (context->actions & suggested_action)
3106             gdk_drag_status(context, suggested_action, time);
3107         else
3108             gdk_drag_status(context, GDK_ACTION_DEFAULT, time);
3109
3110  out:
3111         if (folder)
3112                 g_object_unref (folder);
3113         if (dest_row) {
3114                 gtk_tree_path_free (dest_row);
3115         }
3116         g_signal_stop_emission_by_name (widget, "drag-motion");
3117
3118         return valid_location;
3119 }
3120
3121 /*
3122  * This function sets the treeview as a source and a target for dnd
3123  * events. It also connects all the requirede signals.
3124  */
3125 static void
3126 setup_drag_and_drop (GtkTreeView *self)
3127 {
3128         /* Set up the folder view as a dnd destination. Set only the
3129            highlight flag, otherwise gtk will have a different
3130            behaviour */
3131 #ifdef MODEST_TOOLKIT_HILDON2
3132         return;
3133 #endif
3134         gtk_drag_dest_set (GTK_WIDGET (self),
3135                            GTK_DEST_DEFAULT_HIGHLIGHT,
3136                            folder_view_drag_types,
3137                            G_N_ELEMENTS (folder_view_drag_types),
3138                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
3139
3140         g_signal_connect (G_OBJECT (self),
3141                           "drag_data_received",
3142                           G_CALLBACK (on_drag_data_received),
3143                           NULL);
3144
3145
3146         /* Set up the treeview as a dnd source */
3147         gtk_drag_source_set (GTK_WIDGET (self),
3148                              GDK_BUTTON1_MASK,
3149                              folder_view_drag_types,
3150                              G_N_ELEMENTS (folder_view_drag_types),
3151                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
3152
3153         g_signal_connect (G_OBJECT (self),
3154                           "drag_motion",
3155                           G_CALLBACK (on_drag_motion),
3156                           NULL);
3157
3158         g_signal_connect (G_OBJECT (self),
3159                           "drag_data_get",
3160                           G_CALLBACK (on_drag_data_get),
3161                           NULL);
3162
3163         g_signal_connect (G_OBJECT (self),
3164                           "drag_drop",
3165                           G_CALLBACK (drag_drop_cb),
3166                           NULL);
3167 }
3168
3169 /*
3170  * This function manages the navigation through the folders using the
3171  * keyboard or the hardware keys in the device
3172  */
3173 static gboolean
3174 on_key_pressed (GtkWidget *self,
3175                 GdkEventKey *event,
3176                 gpointer user_data)
3177 {
3178         GtkTreeSelection *selection;
3179         GtkTreeIter iter;
3180         GtkTreeModel *model;
3181         gboolean retval = FALSE;
3182
3183         /* Up and Down are automatically managed by the treeview */
3184         if (event->keyval == GDK_Return) {
3185                 /* Expand/Collapse the selected row */
3186                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3187                 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3188                         GtkTreePath *path;
3189
3190                         path = gtk_tree_model_get_path (model, &iter);
3191
3192                         if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path))
3193                                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (self), path);
3194                         else
3195                                 gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
3196                         gtk_tree_path_free (path);
3197                 }
3198                 /* No further processing */
3199                 retval = TRUE;
3200         }
3201
3202         return retval;
3203 }
3204
3205 /*
3206  * We listen to the changes in the local folder account name key,
3207  * because we want to show the right name in the view. The local
3208  * folder account name corresponds to the device name in the Maemo
3209  * version. We do this because we do not want to query gconf on each
3210  * tree view refresh. It's better to cache it and change whenever
3211  * necessary.
3212  */
3213 static void
3214 on_configuration_key_changed (ModestConf* conf,
3215                               const gchar *key,
3216                               ModestConfEvent event,
3217                               ModestConfNotificationId id,
3218                               ModestFolderView *self)
3219 {
3220         ModestFolderViewPrivate *priv;
3221
3222
3223         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3224         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3225
3226         if (!strcmp (key, MODEST_CONF_DEVICE_NAME)) {
3227                 g_free (priv->local_account_name);
3228
3229                 if (event == MODEST_CONF_EVENT_KEY_UNSET)
3230                         priv->local_account_name = g_strdup (MODEST_LOCAL_FOLDERS_DEFAULT_DISPLAY_NAME);
3231                 else
3232                         priv->local_account_name = modest_conf_get_string (modest_runtime_get_conf(),
3233                                                                            MODEST_CONF_DEVICE_NAME, NULL);
3234
3235                 /* Force a redraw */
3236 #if GTK_CHECK_VERSION(2, 8, 0)
3237                 GtkTreeViewColumn * tree_column;
3238
3239                 tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3240                                                         NAME_COLUMN);
3241                 gtk_tree_view_column_queue_resize (tree_column);
3242 #else
3243                 gtk_widget_queue_draw (GTK_WIDGET (self));
3244 #endif
3245         }
3246 }
3247
3248 void
3249 modest_folder_view_set_style (ModestFolderView *self,
3250                               ModestFolderViewStyle style)
3251 {
3252         ModestFolderViewPrivate *priv;
3253
3254         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3255         g_return_if_fail (style == MODEST_FOLDER_VIEW_STYLE_SHOW_ALL ||
3256                           style == MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
3257
3258         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3259
3260
3261         priv->style = style;
3262 }
3263
3264 void
3265 modest_folder_view_set_account_id_of_visible_server_account (ModestFolderView *self,
3266                                                              const gchar *account_id)
3267 {
3268         ModestFolderViewPrivate *priv;
3269         GtkTreeModel *model;
3270
3271         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3272
3273         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3274
3275         /* This will be used by the filter_row callback,
3276          * to decided which rows to show: */
3277         if (priv->visible_account_id) {
3278                 g_free (priv->visible_account_id);
3279                 priv->visible_account_id = NULL;
3280         }
3281         if (account_id)
3282                 priv->visible_account_id = g_strdup (account_id);
3283
3284         /* Refilter */
3285         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3286         if (GTK_IS_TREE_MODEL_FILTER (model))
3287                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3288
3289         /* Save settings to gconf */
3290         modest_widget_memory_save (modest_runtime_get_conf (), G_OBJECT(self),
3291                                    MODEST_CONF_FOLDER_VIEW_KEY);
3292
3293         /* Notify observers */
3294         g_signal_emit (G_OBJECT(self),
3295                        signals[VISIBLE_ACCOUNT_CHANGED_SIGNAL], 0,
3296                        account_id);
3297 }
3298
3299 const gchar *
3300 modest_folder_view_get_account_id_of_visible_server_account (ModestFolderView *self)
3301 {
3302         ModestFolderViewPrivate *priv;
3303
3304         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW(self), NULL);
3305
3306         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(self);
3307
3308         return (const gchar *) priv->visible_account_id;
3309 }
3310
3311 static gboolean
3312 find_inbox_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *inbox_iter)
3313 {
3314         do {
3315                 GtkTreeIter child;
3316                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3317
3318                 gtk_tree_model_get (model, iter,
3319                                     TYPE_COLUMN,
3320                                     &type, -1);
3321
3322                 gboolean result = FALSE;
3323                 if (type == TNY_FOLDER_TYPE_INBOX) {
3324                         result = TRUE;
3325                 }
3326                 if (result) {
3327                         *inbox_iter = *iter;
3328                         return TRUE;
3329                 }
3330
3331                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3332                         if (find_inbox_iter (model, &child, inbox_iter))
3333                                 return TRUE;
3334                 }
3335
3336         } while (gtk_tree_model_iter_next (model, iter));
3337
3338         return FALSE;
3339 }
3340
3341
3342
3343
3344 void
3345 modest_folder_view_select_first_inbox_or_local (ModestFolderView *self)
3346 {
3347         GtkTreeModel *model;
3348         GtkTreeIter iter, inbox_iter;
3349         GtkTreeSelection *sel;
3350         GtkTreePath *path = NULL;
3351
3352         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3353
3354         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3355         if (!model)
3356                 return;
3357
3358         expand_root_items (self);
3359         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3360
3361         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3362                 g_warning ("%s: model is empty", __FUNCTION__);
3363                 return;
3364         }
3365
3366         if (find_inbox_iter (model, &iter, &inbox_iter))
3367                 path = gtk_tree_model_get_path (model, &inbox_iter);
3368         else
3369                 path = gtk_tree_path_new_first ();
3370
3371         /* Select the row and free */
3372         gtk_tree_view_set_cursor (GTK_TREE_VIEW (self), path, NULL, FALSE);
3373         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self), path, NULL, FALSE, 0.0, 0.0);
3374         gtk_tree_path_free (path);
3375
3376         /* set focus */
3377         gtk_widget_grab_focus (GTK_WIDGET(self));
3378 }
3379
3380
3381 /* recursive */
3382 static gboolean
3383 find_folder_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *folder_iter,
3384                   TnyFolder* folder)
3385 {
3386         do {
3387                 GtkTreeIter child;
3388                 TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3389                 TnyFolder* a_folder;
3390                 gchar *name = NULL;
3391
3392                 gtk_tree_model_get (model, iter,
3393                                     INSTANCE_COLUMN, &a_folder,
3394                                     NAME_COLUMN, &name,
3395                                     TYPE_COLUMN, &type,
3396                                     -1);
3397                 g_free (name);
3398
3399                 if (folder == a_folder) {
3400                         g_object_unref (a_folder);
3401                         *folder_iter = *iter;
3402                         return TRUE;
3403                 }
3404                 g_object_unref (a_folder);
3405
3406                 if (gtk_tree_model_iter_children (model, &child, iter)) {
3407                         if (find_folder_iter (model, &child, folder_iter, folder))
3408                                 return TRUE;
3409                 }
3410
3411         } while (gtk_tree_model_iter_next (model, iter));
3412
3413         return FALSE;
3414 }
3415
3416 #ifndef MODEST_TOOLKIT_HILDON2
3417 static void
3418 on_row_inserted_maybe_select_folder (GtkTreeModel *tree_model,
3419                                      GtkTreePath *path,
3420                                      GtkTreeIter *iter,
3421                                      ModestFolderView *self)
3422 {
3423         ModestFolderViewPrivate *priv = NULL;
3424         GtkTreeSelection *sel;
3425         TnyFolderType type = TNY_FOLDER_TYPE_UNKNOWN;
3426         GObject *instance = NULL;
3427
3428         if (!MODEST_IS_FOLDER_VIEW(self))
3429                 return;
3430
3431         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3432
3433         priv->reexpand = TRUE;
3434
3435         gtk_tree_model_get (tree_model, iter,
3436                             TYPE_COLUMN, &type,
3437                             INSTANCE_COLUMN, &instance,
3438                             -1);
3439
3440         if (!instance)
3441                 return;
3442
3443         if (type == TNY_FOLDER_TYPE_INBOX && priv->folder_to_select == NULL) {
3444                 priv->folder_to_select = g_object_ref (instance);
3445         }
3446         g_object_unref (instance);
3447
3448         if (priv->folder_to_select) {
3449
3450                 if (!modest_folder_view_select_folder (self, priv->folder_to_select,
3451                                                        FALSE)) {
3452                         GtkTreePath *path;
3453                         path = gtk_tree_model_get_path (tree_model, iter);
3454                         gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3455
3456                         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3457
3458                         gtk_tree_selection_select_iter (sel, iter);
3459                         gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3460
3461                         gtk_tree_path_free (path);
3462                 }
3463
3464                 /* Disable next */
3465                 modest_folder_view_disable_next_folder_selection (self);
3466
3467                 /* Refilter the model */
3468                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (tree_model));
3469         }
3470 }
3471 #endif
3472
3473 void
3474 modest_folder_view_disable_next_folder_selection (ModestFolderView *self)
3475 {
3476         ModestFolderViewPrivate *priv;
3477
3478         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3479
3480         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3481
3482         if (priv->folder_to_select)
3483                 g_object_unref(priv->folder_to_select);
3484
3485         priv->folder_to_select = NULL;
3486 }
3487
3488 gboolean
3489 modest_folder_view_select_folder (ModestFolderView *self, TnyFolder *folder,
3490                                   gboolean after_change)
3491 {
3492         GtkTreeModel *model;
3493         GtkTreeIter iter, folder_iter;
3494         GtkTreeSelection *sel;
3495         ModestFolderViewPrivate *priv = NULL;
3496
3497         g_return_val_if_fail (self && MODEST_IS_FOLDER_VIEW (self), FALSE);
3498         g_return_val_if_fail (folder && TNY_IS_FOLDER (folder), FALSE);
3499
3500         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3501
3502         if (after_change) {
3503                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3504                 gtk_tree_selection_unselect_all (sel);
3505
3506                 if (priv->folder_to_select)
3507                         g_object_unref(priv->folder_to_select);
3508                 priv->folder_to_select = TNY_FOLDER(g_object_ref(folder));
3509                 return TRUE;
3510         }
3511
3512         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3513         if (!model)
3514                 return FALSE;
3515
3516
3517         /* Refilter the model, before selecting the folder */
3518         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3519
3520         if (!gtk_tree_model_get_iter_first (model, &iter)) {
3521                 g_warning ("%s: model is empty", __FUNCTION__);
3522                 return FALSE;
3523         }
3524
3525         if (find_folder_iter (model, &iter, &folder_iter, folder)) {
3526                 GtkTreePath *path;
3527
3528                 path = gtk_tree_model_get_path (model, &folder_iter);
3529                 gtk_tree_view_expand_to_path (GTK_TREE_VIEW(self), path);
3530
3531                 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
3532                 gtk_tree_selection_select_iter (sel, &folder_iter);
3533                 gtk_tree_view_set_cursor (GTK_TREE_VIEW(self), path, NULL, FALSE);
3534
3535                 gtk_tree_path_free (path);
3536                 return TRUE;
3537         }
3538         return FALSE;
3539 }
3540
3541
3542 void
3543 modest_folder_view_copy_selection (ModestFolderView *self)
3544 {
3545         g_return_if_fail (self && MODEST_IS_FOLDER_VIEW(self));
3546
3547         /* Copy selection */
3548         _clipboard_set_selected_data (self, FALSE);
3549 }
3550
3551 void
3552 modest_folder_view_cut_selection (ModestFolderView *folder_view)
3553 {
3554         ModestFolderViewPrivate *priv = NULL;
3555         GtkTreeModel *model = NULL;
3556         const gchar **hidding = NULL;
3557         guint i, n_selected;
3558
3559         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3560         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3561
3562         /* Copy selection */
3563         if (!_clipboard_set_selected_data (folder_view, TRUE))
3564                 return;
3565
3566         /* Get hidding ids */
3567         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
3568
3569         /* Clear hidding array created by previous cut operation */
3570         _clear_hidding_filter (MODEST_FOLDER_VIEW (folder_view));
3571
3572         /* Copy hidding array */
3573         priv->n_selected = n_selected;
3574         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
3575         for (i=0; i < n_selected; i++)
3576                 priv->hidding_ids[i] = g_strdup(hidding[i]);
3577
3578         /* Hide cut folders */
3579         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3580         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3581 }
3582
3583 void
3584 modest_folder_view_copy_model (ModestFolderView *folder_view_src,
3585                                ModestFolderView *folder_view_dst)
3586 {
3587         GtkTreeModel *filter_model = NULL;
3588         GtkTreeModel *model = NULL;
3589         GtkTreeModel *new_filter_model = NULL;
3590
3591         g_return_if_fail (folder_view_src && MODEST_IS_FOLDER_VIEW (folder_view_src));
3592         g_return_if_fail (folder_view_dst && MODEST_IS_FOLDER_VIEW (folder_view_dst));
3593
3594         /* Get src model*/
3595         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view_src));
3596         model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model));
3597
3598         /* Build new filter model */
3599         new_filter_model = gtk_tree_model_filter_new (model, NULL);
3600         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (new_filter_model),
3601                                                 filter_row,
3602                                                 folder_view_dst,
3603                                                 NULL);
3604         /* Set copied model */
3605         gtk_tree_view_set_model (GTK_TREE_VIEW (folder_view_dst), new_filter_model);
3606 #ifndef MODEST_TOOLKIT_HILDON2
3607         g_signal_connect (G_OBJECT(new_filter_model), "row-inserted",
3608                           (GCallback) on_row_inserted_maybe_select_folder, folder_view_dst);
3609 #endif
3610
3611         /* Free */
3612         g_object_unref (new_filter_model);
3613 }
3614
3615 void
3616 modest_folder_view_show_non_move_folders (ModestFolderView *folder_view,
3617                                           gboolean show)
3618 {
3619         GtkTreeModel *model = NULL;
3620         ModestFolderViewPrivate* priv;
3621
3622         g_return_if_fail (folder_view && MODEST_IS_FOLDER_VIEW (folder_view));
3623
3624         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3625         priv->show_non_move = show;
3626 /*      modest_folder_view_update_model(folder_view, */
3627 /*                                      TNY_ACCOUNT_STORE(modest_runtime_get_account_store())); */
3628
3629         /* Hide special folders */
3630         model = gtk_tree_view_get_model (GTK_TREE_VIEW (folder_view));
3631         if (GTK_IS_TREE_MODEL_FILTER (model)) {
3632                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
3633         }
3634 }
3635
3636 /* Returns FALSE if it did not selected anything */
3637 static gboolean
3638 _clipboard_set_selected_data (ModestFolderView *folder_view,
3639                               gboolean delete)
3640 {
3641         ModestFolderViewPrivate *priv = NULL;
3642         TnyFolderStore *folder = NULL;
3643         gboolean retval = FALSE;
3644
3645         g_return_val_if_fail (MODEST_IS_FOLDER_VIEW (folder_view), FALSE);
3646         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (folder_view);
3647
3648         /* Set selected data on clipboard   */
3649         g_return_val_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard), FALSE);
3650         folder = modest_folder_view_get_selected (folder_view);
3651
3652         /* Do not allow to select an account */
3653         if (TNY_IS_FOLDER (folder)) {
3654                 modest_email_clipboard_set_data (priv->clipboard, TNY_FOLDER(folder), NULL, delete);
3655                 retval = TRUE;
3656         }
3657
3658         /* Free */
3659         g_object_unref (folder);
3660
3661         return retval;
3662 }
3663
3664 static void
3665 _clear_hidding_filter (ModestFolderView *folder_view)
3666 {
3667         ModestFolderViewPrivate *priv;
3668         guint i;
3669
3670         g_return_if_fail (MODEST_IS_FOLDER_VIEW (folder_view));
3671         priv = MODEST_FOLDER_VIEW_GET_PRIVATE(folder_view);
3672
3673         if (priv->hidding_ids != NULL) {
3674                 for (i=0; i < priv->n_selected; i++)
3675                         g_free (priv->hidding_ids[i]);
3676                 g_free(priv->hidding_ids);
3677         }
3678 }
3679
3680
3681 static void
3682 on_display_name_changed (ModestAccountMgr *mgr,
3683                          const gchar *account,
3684                          gpointer user_data)
3685 {
3686         ModestFolderView *self;
3687
3688         self = MODEST_FOLDER_VIEW (user_data);
3689
3690         /* Force a redraw */
3691 #if GTK_CHECK_VERSION(2, 8, 0)
3692         GtkTreeViewColumn * tree_column;
3693
3694         tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (self),
3695                                                 NAME_COLUMN);
3696         gtk_tree_view_column_queue_resize (tree_column);
3697 #else
3698         gtk_widget_queue_draw (GTK_WIDGET (self));
3699 #endif
3700 }
3701
3702 void 
3703 modest_folder_view_set_cell_style (ModestFolderView *self,
3704                                    ModestFolderViewCellStyle cell_style)
3705 {
3706         ModestFolderViewPrivate *priv = NULL;
3707
3708         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3709         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3710
3711         priv->cell_style = cell_style;
3712
3713         g_object_set (G_OBJECT (priv->messages_renderer),
3714                       "visible", (cell_style == MODEST_FOLDER_VIEW_CELL_STYLE_COMPACT),
3715                       NULL);
3716         
3717         gtk_widget_queue_draw (GTK_WIDGET (self));
3718 }
3719
3720 static void
3721 update_style (ModestFolderView *self)
3722 {
3723         ModestFolderViewPrivate *priv;
3724         GdkColor style_color;
3725         PangoAttrList *attr_list;
3726         GtkStyle *style;
3727         PangoAttribute *attr;
3728
3729         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3730         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3731
3732         /* Set color */
3733
3734         attr_list = pango_attr_list_new ();
3735         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
3736                 gdk_color_parse ("grey", &style_color);
3737         }
3738         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
3739         pango_attr_list_insert (attr_list, attr);
3740         
3741         /* set font */
3742         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
3743                                            (GTK_WIDGET(self)),
3744                                            "SmallSystemFont", NULL,
3745                                            G_TYPE_NONE);
3746         if (style) {
3747                 attr = pango_attr_font_desc_new (pango_font_description_copy
3748                                                  (style->font_desc));
3749                 pango_attr_list_insert (attr_list, attr);
3750
3751                 g_object_set (G_OBJECT (priv->messages_renderer),
3752                               "foreground-gdk", &style_color,
3753                               "foreground-set", TRUE,
3754                               "attributes", attr_list,
3755                               NULL);
3756                 pango_attr_list_unref (attr_list);
3757         }
3758 }
3759
3760 static void 
3761 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
3762 {
3763         if (strcmp ("style", spec->name) == 0) {
3764                 update_style (MODEST_FOLDER_VIEW (obj));
3765                 gtk_widget_queue_draw (GTK_WIDGET (obj));
3766         } 
3767 }
3768
3769 void 
3770 modest_folder_view_set_filter (ModestFolderView *self,
3771                                ModestFolderViewFilter filter)
3772 {
3773         ModestFolderViewPrivate *priv;
3774         GtkTreeModel *filter_model;
3775
3776         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3777         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3778
3779         priv->filter |= filter;
3780
3781         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3782         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3783                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3784         }
3785 }
3786
3787 void 
3788 modest_folder_view_unset_filter (ModestFolderView *self,
3789                                  ModestFolderViewFilter filter)
3790 {
3791         ModestFolderViewPrivate *priv;
3792         GtkTreeModel *filter_model;
3793
3794         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3795         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3796
3797         priv->filter &= ~filter;
3798
3799         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
3800         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
3801                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
3802         }
3803 }
3804
3805 gboolean
3806 modest_folder_view_any_folder_fulfils_rules (ModestFolderView *self,
3807                                              ModestTnyFolderRules rules)
3808 {
3809         GtkTreeModel *filter_model;
3810         GtkTreeIter iter;
3811         gboolean fulfil = FALSE;
3812
3813         if (!get_inner_models (self, &filter_model, NULL, NULL))
3814                 return FALSE;
3815
3816         if (!gtk_tree_model_get_iter_first (filter_model, &iter))
3817                 return FALSE;
3818
3819         do {
3820                 TnyFolderStore *folder;
3821
3822                 gtk_tree_model_get (filter_model, &iter, INSTANCE_COLUMN, &folder, -1);
3823                 if (folder) {
3824                         if (TNY_IS_FOLDER (folder)) {
3825                                 ModestTnyFolderRules folder_rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
3826                                 /* Folder rules are negative: non_writable, non_deletable... */
3827                                 if (!(folder_rules & rules))
3828                                         fulfil = TRUE;
3829                         }
3830                         g_object_unref (folder);
3831                 }
3832
3833         } while (gtk_tree_model_iter_next (filter_model, &iter) && !fulfil);
3834
3835         return fulfil;
3836 }
3837
3838 void 
3839 modest_folder_view_set_list_to_move (ModestFolderView *self,
3840                                      TnyList *list)
3841 {
3842         ModestFolderViewPrivate *priv;
3843
3844         g_return_if_fail (MODEST_IS_FOLDER_VIEW (self));
3845         priv = MODEST_FOLDER_VIEW_GET_PRIVATE (self);
3846
3847         if (priv->list_to_move)
3848                 g_object_unref (priv->list_to_move);
3849
3850         if (list)
3851                 g_object_ref (list);
3852
3853         priv->list_to_move = list;
3854 }