5a671fbd84642654d48bc32c285e5b2d16b976dc
[modest] / src / widgets / modest-header-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 <tny-list.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <tny-error.h>
36 #include <string.h>
37
38 #include <modest-header-view.h>
39 #include <modest-header-view-priv.h>
40 #include <modest-dnd.h>
41 #include <modest-tny-folder.h>
42 #include <modest-debug.h>
43 #include <modest-main-window.h>
44 #include <modest-ui-actions.h>
45 #include <modest-marshal.h>
46 #include <modest-text-utils.h>
47 #include <modest-icon-names.h>
48 #include <modest-runtime.h>
49 #include "modest-platform.h"
50 #include <modest-hbox-cell-renderer.h>
51 #include <modest-vbox-cell-renderer.h>
52 #include <modest-datetime-formatter.h>
53 #include <modest-ui-constants.h>
54
55 static void modest_header_view_class_init  (ModestHeaderViewClass *klass);
56 static void modest_header_view_init        (ModestHeaderView *obj);
57 static void modest_header_view_finalize    (GObject *obj);
58 static void modest_header_view_dispose     (GObject *obj);
59
60 static void          on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
61                                               GtkTreeViewColumn *column, gpointer userdata);
62
63 static gint          cmp_rows               (GtkTreeModel *tree_model,
64                                              GtkTreeIter *iter1,
65                                              GtkTreeIter *iter2,
66                                              gpointer user_data);
67
68 static gint          cmp_subject_rows       (GtkTreeModel *tree_model,
69                                              GtkTreeIter *iter1,
70                                              GtkTreeIter *iter2,
71                                              gpointer user_data);
72
73 static gboolean     filter_row             (GtkTreeModel *model,
74                                             GtkTreeIter *iter,
75                                             gpointer data);
76
77 static void         on_account_removed     (TnyAccountStore *self,
78                                             TnyAccount *account,
79                                             gpointer user_data);
80
81 static void          on_selection_changed   (GtkTreeSelection *sel,
82                                              gpointer user_data);
83
84 static gboolean      on_button_press_event  (GtkWidget * self, GdkEventButton * event,
85                                              gpointer userdata);
86
87 static gboolean      on_button_release_event(GtkWidget * self, GdkEventButton * event,
88                                              gpointer userdata);
89
90 static void          setup_drag_and_drop    (GtkWidget *self);
91
92 static void          enable_drag_and_drop   (GtkWidget *self);
93
94 static void          disable_drag_and_drop  (GtkWidget *self);
95
96 static GtkTreePath * get_selected_row       (GtkTreeView *self, GtkTreeModel **model);
97
98 #ifndef MODEST_TOOLKIT_HILDON2
99 static gboolean      on_focus_in            (GtkWidget     *sef,
100                                              GdkEventFocus *event,
101                                              gpointer       user_data);
102
103 static gboolean      on_focus_out            (GtkWidget     *self,
104                                               GdkEventFocus *event,
105                                               gpointer       user_data);
106 #endif
107
108 static void          folder_monitor_update  (TnyFolderObserver *self,
109                                              TnyFolderChange *change);
110
111 static void          tny_folder_observer_init (TnyFolderObserverIface *klass);
112
113 static void          _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
114
115 static void          _clear_hidding_filter (ModestHeaderView *header_view);
116
117 static void          modest_header_view_notify_observers(ModestHeaderView *header_view,
118                                                          GtkTreeModel *model,
119                                                          const gchar *tny_folder_id);
120
121 static gboolean      modest_header_view_on_expose_event (GtkTreeView *header_view,
122                                                          GdkEventExpose *event,
123                                                          gpointer user_data);
124
125 static void         on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
126 static void         update_style (ModestHeaderView *self);
127
128 typedef enum {
129         HEADER_VIEW_NON_EMPTY,
130         HEADER_VIEW_EMPTY,
131         HEADER_VIEW_INIT
132 } HeaderViewStatus;
133
134 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
135 struct _ModestHeaderViewPrivate {
136         TnyFolder            *folder;
137         ModestHeaderViewStyle style;
138         gboolean is_outbox;
139
140         TnyFolderMonitor     *monitor;
141         GMutex               *observers_lock;
142
143         /*header-view-observer observer*/
144         GMutex *observer_list_lock;
145         GSList *observer_list;
146
147         /* not unref this object, its a singlenton */
148         ModestEmailClipboard *clipboard;
149
150         /* Filter tree model */
151         gchar **hidding_ids;
152         guint   n_selected;
153         GtkTreeRowReference *autoselect_reference;
154         ModestHeaderViewFilter filter;
155
156         gint    sort_colid[2][TNY_FOLDER_TYPE_NUM];
157         gint    sort_type[2][TNY_FOLDER_TYPE_NUM];
158
159         gulong  selection_changed_handler;
160         gulong  acc_removed_handler;
161
162         GList *drag_begin_cached_selected_rows;
163
164         HeaderViewStatus status;
165         guint status_timeout;
166         gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
167
168         ModestDatetimeFormatter *datetime_formatter;
169
170         GtkCellRenderer *renderer_subject;
171         GtkCellRenderer *renderer_address;
172         GtkCellRenderer *renderer_date_status;
173 };
174
175 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
176 struct _HeadersCountChangedHelper {
177         ModestHeaderView *self;
178         TnyFolderChange  *change;
179 };
180
181
182 #define MODEST_HEADER_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
183                                                 MODEST_TYPE_HEADER_VIEW, \
184                                                 ModestHeaderViewPrivate))
185
186
187
188 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
189
190 enum {
191         HEADER_SELECTED_SIGNAL,
192         HEADER_ACTIVATED_SIGNAL,
193         ITEM_NOT_FOUND_SIGNAL,
194         MSG_COUNT_CHANGED_SIGNAL,
195         UPDATING_MSG_LIST_SIGNAL,
196         LAST_SIGNAL
197 };
198
199 /* globals */
200 static GObjectClass *parent_class = NULL;
201
202 /* uncomment the following if you have defined any signals */
203 static guint signals[LAST_SIGNAL] = {0};
204
205 GType
206 modest_header_view_get_type (void)
207 {
208         static GType my_type = 0;
209         if (!my_type) {
210                 static const GTypeInfo my_info = {
211                         sizeof(ModestHeaderViewClass),
212                         NULL,           /* base init */
213                         NULL,           /* base finalize */
214                         (GClassInitFunc) modest_header_view_class_init,
215                         NULL,           /* class finalize */
216                         NULL,           /* class data */
217                         sizeof(ModestHeaderView),
218                         1,              /* n_preallocs */
219                         (GInstanceInitFunc) modest_header_view_init,
220                         NULL
221                 };
222
223                 static const GInterfaceInfo tny_folder_observer_info =
224                 {
225                         (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
226                         NULL,         /* interface_finalize */
227                         NULL          /* interface_data */
228                 };
229                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
230                                                   "ModestHeaderView",
231                                                   &my_info, 0);
232
233                 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
234                                              &tny_folder_observer_info);
235
236
237         }
238         return my_type;
239 }
240
241 static void
242 modest_header_view_class_init (ModestHeaderViewClass *klass)
243 {
244         GObjectClass *gobject_class;
245         gobject_class = (GObjectClass*) klass;
246
247         parent_class            = g_type_class_peek_parent (klass);
248         gobject_class->finalize = modest_header_view_finalize;
249         gobject_class->dispose = modest_header_view_dispose;
250
251         g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
252
253         signals[HEADER_SELECTED_SIGNAL] =
254                 g_signal_new ("header_selected",
255                               G_TYPE_FROM_CLASS (gobject_class),
256                               G_SIGNAL_RUN_FIRST,
257                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
258                               NULL, NULL,
259                               g_cclosure_marshal_VOID__POINTER,
260                               G_TYPE_NONE, 1, G_TYPE_POINTER);
261
262         signals[HEADER_ACTIVATED_SIGNAL] =
263                 g_signal_new ("header_activated",
264                               G_TYPE_FROM_CLASS (gobject_class),
265                               G_SIGNAL_RUN_FIRST,
266                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
267                               NULL, NULL,
268                               gtk_marshal_VOID__POINTER_POINTER,
269                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
270
271
272         signals[ITEM_NOT_FOUND_SIGNAL] =
273                 g_signal_new ("item_not_found",
274                               G_TYPE_FROM_CLASS (gobject_class),
275                               G_SIGNAL_RUN_FIRST,
276                               G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
277                               NULL, NULL,
278                               g_cclosure_marshal_VOID__INT,
279                               G_TYPE_NONE, 1, G_TYPE_INT);
280
281         signals[MSG_COUNT_CHANGED_SIGNAL] =
282                 g_signal_new ("msg_count_changed",
283                               G_TYPE_FROM_CLASS (gobject_class),
284                               G_SIGNAL_RUN_FIRST,
285                               G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
286                               NULL, NULL,
287                               modest_marshal_VOID__POINTER_POINTER,
288                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
289
290         signals[UPDATING_MSG_LIST_SIGNAL] =
291                 g_signal_new ("updating-msg-list",
292                               G_TYPE_FROM_CLASS (gobject_class),
293                               G_SIGNAL_RUN_FIRST,
294                               G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
295                               NULL, NULL,
296                               g_cclosure_marshal_VOID__BOOLEAN,
297                               G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
298
299 #ifdef MODEST_TOOLKIT_HILDON2
300         gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
301
302 #endif
303 }
304
305 static void
306 tny_folder_observer_init (TnyFolderObserverIface *klass)
307 {
308         klass->update = folder_monitor_update;
309 }
310
311 static GtkTreeViewColumn*
312 get_new_column (const gchar *name, GtkCellRenderer *renderer,
313                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
314                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
315 {
316         GtkTreeViewColumn *column;
317
318         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
319         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
320
321         gtk_tree_view_column_set_resizable (column, resizable);
322         if (resizable)
323                 gtk_tree_view_column_set_expand (column, TRUE);
324
325         if (show_as_text)
326                 gtk_tree_view_column_add_attribute (column, renderer, "text",
327                                                     sort_col_id);
328         if (sort_col_id >= 0)
329                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
330
331         gtk_tree_view_column_set_sort_indicator (column, FALSE);
332         gtk_tree_view_column_set_reorderable (column, TRUE);
333
334         if (cell_data_func)
335                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
336                                                         user_data, NULL);
337         return column;
338 }
339
340
341 static void
342 remove_all_columns (ModestHeaderView *obj)
343 {
344         GList *columns, *cursor;
345
346         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
347
348         for (cursor = columns; cursor; cursor = cursor->next)
349                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
350                                              GTK_TREE_VIEW_COLUMN(cursor->data));
351         g_list_free (columns);
352 }
353
354 gboolean
355 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
356 {
357         GtkTreeModel *sortable;
358         GtkTreeViewColumn *column=NULL;
359         GtkTreeSelection *selection = NULL;
360         GtkCellRenderer *renderer_header,
361                 *renderer_attach, *renderer_compact_date_or_status;
362         GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
363                 *renderer_subject_box, *renderer_recpt,
364                 *renderer_priority;
365         ModestHeaderViewPrivate *priv;
366         GtkTreeViewColumn *compact_column = NULL;
367         const GList *cursor;
368
369         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
370         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
371
372         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
373
374         priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
375
376         /* TODO: check whether these renderers need to be freed */
377         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
378         renderer_priority  = gtk_cell_renderer_pixbuf_new ();
379         renderer_header  = gtk_cell_renderer_text_new ();
380
381         renderer_compact_header = modest_vbox_cell_renderer_new ();
382         renderer_recpt_box = modest_hbox_cell_renderer_new ();
383         renderer_subject_box = modest_hbox_cell_renderer_new ();
384         renderer_recpt = gtk_cell_renderer_text_new ();
385         priv->renderer_address = renderer_recpt;
386         priv->renderer_subject = gtk_cell_renderer_text_new ();
387         renderer_compact_date_or_status  = gtk_cell_renderer_text_new ();
388         priv->renderer_date_status = renderer_compact_date_or_status;
389
390         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
391         g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
392         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
393         g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
394         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
395         g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
396         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
397         g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
398         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
399         g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
400         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
401         g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
402         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
403         g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
404
405 #ifdef MODEST_TOOLKIT_HILDON2
406         g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
407 #endif
408         g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
409 #ifndef MODEST_TOOLKIT_GTK
410         gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
411         gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
412 #endif
413         g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
414         g_object_set(G_OBJECT(renderer_header),
415                      "ellipsize", PANGO_ELLIPSIZE_END,
416                      NULL);
417         g_object_set (G_OBJECT (priv->renderer_subject),
418                       "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
419                       NULL);
420         gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
421         g_object_set (G_OBJECT (renderer_recpt),
422                       "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
423                       NULL);
424         gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
425         g_object_set(G_OBJECT(renderer_compact_date_or_status),
426                      "xalign", 1.0, "yalign", 0.1,
427                      NULL);
428         gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
429 #ifdef MODEST_TOOLKIT_HILDON2
430         g_object_set (G_OBJECT (renderer_priority),
431                       "yalign", 0.5,
432                       "xalign", 0.0, NULL);
433         g_object_set (G_OBJECT (renderer_attach),
434                       "yalign", 0.5,
435                       "xalign", 0.0, NULL);
436 #else
437         g_object_set (G_OBJECT (renderer_priority),
438                       "yalign", 0.5, NULL);
439         g_object_set (G_OBJECT (renderer_attach),
440                       "yalign", 0.0, NULL);
441 #endif
442
443 #ifdef MODEST_TOOLKIT_HILDON1
444         gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
445         gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
446         gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
447 #elif MODEST_TOOLKIT_HILDON2
448         gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
449         gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
450         gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
451 #else
452         gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
453         gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
454         /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
455 #endif
456
457         remove_all_columns (self);
458
459         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
460         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
461         sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
462
463         /* Add new columns */
464         for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
465                 ModestHeaderViewColumn col =
466                         (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
467
468                 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
469                         g_printerr ("modest: invalid column %d in column list\n", col);
470                         continue;
471                 }
472
473                 switch (col) {
474
475                 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
476                         column = get_new_column (_("A"), renderer_attach, FALSE,
477                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
478                                                  FALSE,
479                                                  (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
480                                                  NULL);
481                         gtk_tree_view_column_set_fixed_width (column, 45);
482                         break;
483
484
485                 case MODEST_HEADER_VIEW_COLUMN_FROM:
486                         column = get_new_column (_("From"), renderer_header, TRUE,
487                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
488                                                  TRUE,
489                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
490                                                  GINT_TO_POINTER(TRUE));
491                         break;
492
493                 case MODEST_HEADER_VIEW_COLUMN_TO:
494                         column = get_new_column (_("To"), renderer_header, TRUE,
495                                                  TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
496                                                  TRUE,
497                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
498                                                  GINT_TO_POINTER(FALSE));
499                         break;
500
501                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
502                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
503                                                      TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
504                                                      FALSE,
505                                                      (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
506                                                      GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
507                         compact_column = column;
508                         break;
509
510                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
511                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
512                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
513                                                  FALSE,
514                                                  (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
515                                                  GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
516                                                                  MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
517                                                                  MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
518                         compact_column = column;
519                         break;
520
521
522                 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
523                         column = get_new_column (_("Subject"), renderer_header, TRUE,
524                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
525                                                  TRUE,
526                                                  (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
527                                                  NULL);
528                         break;
529
530                 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
531                         column = get_new_column (_("Received"), renderer_header, TRUE,
532                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
533                                                  TRUE,
534                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
535                                                  GINT_TO_POINTER(TRUE));
536                         break;
537
538                 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
539                         column = get_new_column (_("Sent"), renderer_header, TRUE,
540                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
541                                                  TRUE,
542                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
543                                                  GINT_TO_POINTER(FALSE));
544                         break;
545
546                 case MODEST_HEADER_VIEW_COLUMN_SIZE:
547                         column = get_new_column (_("Size"), renderer_header, TRUE,
548                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
549                                                  FALSE,
550                                                  (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
551                                                  NULL);
552                         break;
553                 case MODEST_HEADER_VIEW_COLUMN_STATUS:
554                         column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
555                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
556                                                  FALSE,
557                                                  (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
558                                                  NULL);
559                         break;
560
561                 default:
562                         g_return_val_if_reached(FALSE);
563                 }
564
565                 /* we keep the column id around */
566                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
567                                    GINT_TO_POINTER(col));
568
569                 /* we need this ptr when sorting the rows */
570                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
571                                    self);
572                 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
573         }
574
575         if (sortable) {
576                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
577                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
578                                                  (GtkTreeIterCompareFunc) cmp_rows,
579                                                  compact_column, NULL);
580                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
581                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
582                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
583                                                  compact_column, NULL);
584         }
585
586         update_style (self);
587         g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
588
589         return TRUE;
590 }
591
592 static void
593 datetime_format_changed (ModestDatetimeFormatter *formatter,
594                          ModestHeaderView *self)
595 {
596         gtk_widget_queue_draw (GTK_WIDGET (self));
597 }
598
599 static void
600 modest_header_view_init (ModestHeaderView *obj)
601 {
602         ModestHeaderViewPrivate *priv;
603         guint i, j;
604
605         priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
606
607         priv->folder  = NULL;
608         priv->is_outbox = FALSE;
609
610         priv->monitor        = NULL;
611         priv->observers_lock = g_mutex_new ();
612         priv->autoselect_reference = NULL;
613
614         priv->status  = HEADER_VIEW_INIT;
615         priv->status_timeout = 0;
616         priv->notify_status = TRUE;
617
618         priv->observer_list_lock = g_mutex_new();
619         priv->observer_list = NULL;
620
621         priv->clipboard = modest_runtime_get_email_clipboard ();
622         priv->hidding_ids = NULL;
623         priv->n_selected = 0;
624         priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
625         priv->selection_changed_handler = 0;
626         priv->acc_removed_handler = 0;
627
628         /* Sort parameters */
629         for (j=0; j < 2; j++) {
630                 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
631                         priv->sort_colid[j][i] = -1;
632                         priv->sort_type[j][i] = GTK_SORT_DESCENDING;
633                 }
634         }
635
636         priv->datetime_formatter = modest_datetime_formatter_new ();
637         g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
638                           G_CALLBACK (datetime_format_changed), (gpointer) obj);
639
640         setup_drag_and_drop (GTK_WIDGET(obj));
641 }
642
643 static void
644 modest_header_view_dispose (GObject *obj)
645 {
646         ModestHeaderView        *self;
647         ModestHeaderViewPrivate *priv;
648         GtkTreeSelection *sel;
649
650         self = MODEST_HEADER_VIEW(obj);
651         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
652
653         if (priv->datetime_formatter) {
654                 g_object_unref (priv->datetime_formatter);
655                 priv->datetime_formatter = NULL;
656         }
657
658         /* Free in the dispose to avoid unref cycles */
659         if (priv->folder) {
660                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
661                 g_object_unref (G_OBJECT (priv->folder));
662                 priv->folder = NULL;
663         }
664
665         /* We need to do this here in the dispose because the
666            selection won't exist when finalizing */
667         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
668         if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
669                 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
670                 priv->selection_changed_handler = 0;
671         }
672
673         G_OBJECT_CLASS(parent_class)->dispose (obj);
674 }
675
676 static void
677 modest_header_view_finalize (GObject *obj)
678 {
679         ModestHeaderView        *self;
680         ModestHeaderViewPrivate *priv;
681
682         self = MODEST_HEADER_VIEW(obj);
683         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
684
685         if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
686                                            priv->acc_removed_handler)) {
687                 g_signal_handler_disconnect (modest_runtime_get_account_store (),
688                                              priv->acc_removed_handler);
689         }
690
691         /* There is no need to lock because there should not be any
692          * reference to self now. */
693         g_mutex_free(priv->observer_list_lock);
694         g_slist_free(priv->observer_list);
695
696         g_mutex_lock (priv->observers_lock);
697         if (priv->monitor) {
698                 tny_folder_monitor_stop (priv->monitor);
699                 g_object_unref (G_OBJECT (priv->monitor));
700         }
701         g_mutex_unlock (priv->observers_lock);
702         g_mutex_free (priv->observers_lock);
703
704         /* Clear hidding array created by cut operation */
705         _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
706
707         if (priv->autoselect_reference != NULL) {
708                 gtk_tree_row_reference_free (priv->autoselect_reference);
709                 priv->autoselect_reference = NULL;
710         }
711
712         G_OBJECT_CLASS(parent_class)->finalize (obj);
713 }
714
715
716 GtkWidget*
717 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
718 {
719         GObject *obj;
720         GtkTreeSelection *sel;
721         ModestHeaderView *self;
722         ModestHeaderViewPrivate *priv;
723
724         g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
725                               NULL);
726
727         obj  = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
728         self = MODEST_HEADER_VIEW(obj);
729         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
730
731         modest_header_view_set_style   (self, style);
732
733         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
734         gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
735         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
736
737         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
738                                       TRUE); /* alternating row colors */
739
740         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
741         priv->selection_changed_handler =
742                 g_signal_connect_after (sel, "changed",
743                                         G_CALLBACK(on_selection_changed), self);
744
745         g_signal_connect (self, "row-activated",
746                           G_CALLBACK (on_header_row_activated), NULL);
747
748 #ifndef MODEST_TOOLKIT_HILDON2
749         g_signal_connect (self, "focus-in-event",
750                           G_CALLBACK(on_focus_in), NULL);
751         g_signal_connect (self, "focus-out-event",
752                           G_CALLBACK(on_focus_out), NULL);
753 #endif
754
755         g_signal_connect (self, "button-press-event",
756                           G_CALLBACK(on_button_press_event), NULL);
757         g_signal_connect (self, "button-release-event",
758                           G_CALLBACK(on_button_release_event), NULL);
759
760         priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
761                                                       "account_removed",
762                                                       G_CALLBACK (on_account_removed),
763                                                       self);
764
765         g_signal_connect (self, "expose-event",
766                         G_CALLBACK(modest_header_view_on_expose_event),
767                         NULL);
768
769         return GTK_WIDGET(self);
770 }
771
772
773 guint
774 modest_header_view_count_selected_headers (ModestHeaderView *self)
775 {
776         GtkTreeSelection *sel;
777         guint selected_rows;
778
779         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
780
781         /* Get selection object and check selected rows count */
782         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
783         selected_rows = gtk_tree_selection_count_selected_rows (sel);
784
785         return selected_rows;
786 }
787
788 gboolean
789 modest_header_view_has_selected_headers (ModestHeaderView *self)
790 {
791         GtkTreeSelection *sel;
792         gboolean empty;
793
794         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
795
796         /* Get selection object and check selected rows count */
797         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
798         empty = gtk_tree_selection_count_selected_rows (sel) == 0;
799
800         return !empty;
801 }
802
803
804 TnyList *
805 modest_header_view_get_selected_headers (ModestHeaderView *self)
806 {
807         GtkTreeSelection *sel;
808         TnyList *header_list = NULL;
809         TnyHeader *header;
810         GList *list, *tmp = NULL;
811         GtkTreeModel *tree_model = NULL;
812         GtkTreeIter iter;
813
814         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
815
816
817         /* Get selected rows */
818         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
819         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
820
821         if (list) {
822                 header_list = tny_simple_list_new();
823
824                 list = g_list_reverse (list);
825                 tmp = list;
826                 while (tmp) {
827                         /* get header from selection */
828                         gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
829                         gtk_tree_model_get (tree_model, &iter,
830                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
831                                             &header, -1);
832                         /* Prepend to list */
833                         tny_list_prepend (header_list, G_OBJECT (header));
834                         g_object_unref (G_OBJECT (header));
835
836                         tmp = g_list_next (tmp);
837                 }
838                 /* Clean up*/
839                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
840                 g_list_free (list);
841         }
842         return header_list;
843 }
844
845
846 /* scroll our list view so the selected item is visible */
847 static void
848 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
849 {
850 #ifdef MODEST_TOOLKIT_GTK
851
852         GtkTreePath *selected_path;
853         GtkTreePath *start, *end;
854
855         GtkTreeModel *model;
856
857         model         = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
858         selected_path = gtk_tree_model_get_path (model, iter);
859
860         start = gtk_tree_path_new ();
861         end   = gtk_tree_path_new ();
862
863         gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
864
865         if (gtk_tree_path_compare (selected_path, start) < 0 ||
866             gtk_tree_path_compare (end, selected_path) < 0)
867                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
868                                               selected_path, NULL, TRUE,
869                                               up ? 0.0 : 1.0,
870                                               up ? 0.0 : 1.0);
871         gtk_tree_path_free (selected_path);
872         gtk_tree_path_free (start);
873         gtk_tree_path_free (end);
874
875 #endif /* MODEST_TOOLKIT_GTK */
876 }
877
878
879 void
880 modest_header_view_select_next (ModestHeaderView *self)
881 {
882         GtkTreeSelection *sel;
883         GtkTreeIter iter;
884         GtkTreeModel *model;
885         GtkTreePath *path;
886
887         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
888
889         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
890         path = get_selected_row (GTK_TREE_VIEW(self), &model);
891         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
892                 /* Unselect previous path */
893                 gtk_tree_selection_unselect_path (sel, path);
894
895                 /* Move path down and selects new one  */
896                 if (gtk_tree_model_iter_next (model, &iter)) {
897                         gtk_tree_selection_select_iter (sel, &iter);
898                         scroll_to_selected (self, &iter, FALSE);
899                 }
900                 gtk_tree_path_free(path);
901         }
902
903 }
904
905 void
906 modest_header_view_select_prev (ModestHeaderView *self)
907 {
908         GtkTreeSelection *sel;
909         GtkTreeIter iter;
910         GtkTreeModel *model;
911         GtkTreePath *path;
912
913         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
914
915         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
916         path = get_selected_row (GTK_TREE_VIEW(self), &model);
917         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
918                 /* Unselect previous path */
919                 gtk_tree_selection_unselect_path (sel, path);
920
921                 /* Move path up */
922                 if (gtk_tree_path_prev (path)) {
923                         gtk_tree_model_get_iter (model, &iter, path);
924
925                         /* Select the new one */
926                         gtk_tree_selection_select_iter (sel, &iter);
927                         scroll_to_selected (self, &iter, TRUE);
928
929                 }
930                 gtk_tree_path_free (path);
931         }
932 }
933
934 GList*
935 modest_header_view_get_columns (ModestHeaderView *self)
936 {
937         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
938
939         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
940 }
941
942
943
944 gboolean
945 modest_header_view_set_style (ModestHeaderView *self,
946                               ModestHeaderViewStyle style)
947 {
948         ModestHeaderViewPrivate *priv;
949         gboolean show_col_headers = FALSE;
950         ModestHeaderViewStyle old_style;
951
952         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
953         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
954                               FALSE);
955
956         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
957         if (priv->style == style)
958                 return TRUE; /* nothing to do */
959
960         switch (style) {
961         case MODEST_HEADER_VIEW_STYLE_DETAILS:
962                 show_col_headers = TRUE;
963                 break;
964         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
965                 break;
966         default:
967                 g_return_val_if_reached (FALSE);
968         }
969         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
970         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
971
972         old_style   = priv->style;
973         priv->style = style;
974
975         return TRUE;
976 }
977
978
979 ModestHeaderViewStyle
980 modest_header_view_get_style (ModestHeaderView *self)
981 {
982         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
983
984         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
985 }
986
987 /* This is used to automatically select the first header if the user
988  * has not selected any header yet.
989  */
990 static gboolean
991 modest_header_view_on_expose_event(GtkTreeView *header_view,
992                                    GdkEventExpose *event,
993                                    gpointer user_data)
994 {
995         GtkTreeSelection *sel;
996         GtkTreeModel *model;
997         GtkTreeIter tree_iter;
998         ModestHeaderViewPrivate *priv;
999
1000         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1001         model = gtk_tree_view_get_model(header_view);
1002
1003         if (!model)
1004                 return FALSE;
1005
1006 #ifdef MODEST_TOOLKIT_HILDON2
1007         return FALSE;
1008 #endif
1009         sel = gtk_tree_view_get_selection(header_view);
1010         if(!gtk_tree_selection_count_selected_rows(sel)) {
1011                 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1012                         GtkTreePath *tree_iter_path;
1013                         /* Prevent the widget from getting the focus
1014                            when selecting the first item */
1015                         tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1016                         g_object_set(header_view, "can-focus", FALSE, NULL);
1017                         gtk_tree_selection_select_iter(sel, &tree_iter);
1018                         gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1019                         g_object_set(header_view, "can-focus", TRUE, NULL);
1020                         if (priv->autoselect_reference) {
1021                                 gtk_tree_row_reference_free (priv->autoselect_reference);
1022                         }
1023                         priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1024                         gtk_tree_path_free (tree_iter_path);
1025                 }
1026         } else {
1027                 if (priv->autoselect_reference != NULL) {
1028                         gboolean moved_selection = FALSE;
1029                         GtkTreePath * last_path;
1030                         if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1031                                 moved_selection = TRUE;
1032                         } else {
1033                                 GList *rows;
1034
1035                                 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1036                                 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1037                                 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1038                                         moved_selection = TRUE;
1039                                 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1040                                 g_list_free (rows);
1041                                 gtk_tree_path_free (last_path);
1042                         }
1043                         if (moved_selection) {
1044                                 gtk_tree_row_reference_free (priv->autoselect_reference);
1045                                 priv->autoselect_reference = NULL;
1046                         } else {
1047
1048                                 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1049                                         GtkTreePath *current_path;
1050                                         current_path = gtk_tree_model_get_path (model, &tree_iter);
1051                                         last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1052                                         if (gtk_tree_path_compare (current_path, last_path) != 0) {
1053                                                 g_object_set(header_view, "can-focus", FALSE, NULL);
1054                                                 gtk_tree_selection_unselect_all (sel);
1055                                                 gtk_tree_selection_select_iter(sel, &tree_iter);
1056                                                 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1057                                                 g_object_set(header_view, "can-focus", TRUE, NULL);
1058                                                 gtk_tree_row_reference_free (priv->autoselect_reference);
1059                                                 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1060                                         }
1061                                         gtk_tree_path_free (current_path);
1062                                         gtk_tree_path_free (last_path);
1063                                 }
1064                         }
1065                 }
1066         }
1067
1068         return FALSE;
1069 }
1070
1071 TnyFolder*
1072 modest_header_view_get_folder (ModestHeaderView *self)
1073 {
1074         ModestHeaderViewPrivate *priv;
1075
1076         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1077
1078         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1079
1080         if (priv->folder)
1081                 g_object_ref (priv->folder);
1082
1083         return priv->folder;
1084 }
1085
1086 static void
1087 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1088                                         gboolean cancelled,
1089                                         TnyList *headers,
1090                                         GError *err,
1091                                         gpointer user_data)
1092 {
1093         ModestHeaderView *self;
1094         ModestHeaderViewPrivate *priv;
1095
1096         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1097
1098         self = MODEST_HEADER_VIEW (user_data);
1099         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1100
1101         if (cancelled || err)
1102                 return;
1103
1104         /* Add IDLE observer (monitor) and another folder observer for
1105            new messages (self) */
1106         g_mutex_lock (priv->observers_lock);
1107         if (priv->monitor) {
1108                 tny_folder_monitor_stop (priv->monitor);
1109                 g_object_unref (G_OBJECT (priv->monitor));
1110         }
1111         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1112         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1113         tny_folder_monitor_start (priv->monitor);
1114         g_mutex_unlock (priv->observers_lock);
1115 }
1116
1117 static void
1118 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1119 {
1120         TnyFolderType type;
1121         TnyList *headers;
1122         ModestHeaderViewPrivate *priv;
1123         GList *cols, *cursor;
1124         GtkTreeModel *filter_model, *sortable;
1125         guint sort_colid;
1126         GtkSortType sort_type;
1127
1128         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1129
1130         headers = TNY_LIST (tny_gtk_header_list_model_new ());
1131
1132         /* Start the monitor in the callback of the
1133            tny_gtk_header_list_model_set_folder call. It's crucial to
1134            do it there and not just after the call because we want the
1135            monitor to observe only the headers returned by the
1136            tny_folder_get_headers_async call that it's inside the
1137            tny_gtk_header_list_model_set_folder call. This way the
1138            monitor infrastructure could successfully cope with
1139            duplicates. For example if a tny_folder_add_msg_async is
1140            happening while tny_gtk_header_list_model_set_folder is
1141            invoked, then the first call could add a header that will
1142            be added again by tny_gtk_header_list_model_set_folder, so
1143            we'd end up with duplicate headers. sergio */
1144         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1145                                               folder, FALSE,
1146                                               set_folder_intern_get_headers_async_cb,
1147                                               NULL, self);
1148
1149         /* Create a tree model filter to hide and show rows for cut operations  */
1150         filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1151         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1152                                                 filter_row, self, NULL);
1153         g_object_unref (headers);
1154
1155         /* Init filter_row function to examine empty status */
1156         priv->status  = HEADER_VIEW_INIT;
1157
1158         /* Create sortable model */
1159         sortable = gtk_tree_model_sort_new_with_model (filter_model);
1160
1161         /* install our special sorting functions */
1162         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1163
1164         /* Restore sort column id */
1165         if (cols) {
1166                 type  = modest_tny_folder_guess_folder_type (folder);
1167                 if (type == TNY_FOLDER_TYPE_INVALID)
1168                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1169
1170                 sort_colid = modest_header_view_get_sort_column_id (self, type);
1171                 sort_type = modest_header_view_get_sort_type (self, type);
1172                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1173                                                       sort_colid,
1174                                                       sort_type);
1175                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1176                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1177                                                  (GtkTreeIterCompareFunc) cmp_rows,
1178                                                  cols->data, NULL);
1179                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1180                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1181                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
1182                                                  cols->data, NULL);
1183         }
1184
1185         /* Set new model */
1186         gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1187         modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1188         g_object_unref (sortable);
1189
1190         /* Free */
1191         g_list_free (cols);
1192 }
1193
1194 void
1195 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1196                                       guint sort_colid,
1197                                       GtkSortType sort_type)
1198 {
1199         ModestHeaderViewPrivate *priv = NULL;
1200         GtkTreeModel *sortable = NULL;
1201         TnyFolderType type;
1202
1203         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1204         g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1205
1206         /* Get model and private data */
1207         priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1208         sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1209
1210         /* Sort tree model */
1211         type  = modest_tny_folder_guess_folder_type (priv->folder);
1212         if (type == TNY_FOLDER_TYPE_INVALID)
1213                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1214         else {
1215                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1216                                                       sort_colid,
1217                                                       sort_type);
1218                 /* Store new sort parameters */
1219                 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1220         }
1221 }
1222
1223 void
1224 modest_header_view_set_sort_params (ModestHeaderView *self,
1225                                     guint sort_colid,
1226                                     GtkSortType sort_type,
1227                                     TnyFolderType type)
1228 {
1229         ModestHeaderViewPrivate *priv;
1230         ModestHeaderViewStyle style;
1231
1232         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1233         g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1234         g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1235
1236         style = modest_header_view_get_style   (self);
1237         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1238
1239         priv->sort_colid[style][type] = sort_colid;
1240         priv->sort_type[style][type] = sort_type;
1241 }
1242
1243 gint
1244 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1245                                        TnyFolderType type)
1246 {
1247         ModestHeaderViewPrivate *priv;
1248         ModestHeaderViewStyle style;
1249
1250         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1251         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1252
1253         style = modest_header_view_get_style   (self);
1254         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1255
1256         return priv->sort_colid[style][type];
1257 }
1258
1259 GtkSortType
1260 modest_header_view_get_sort_type (ModestHeaderView *self,
1261                                   TnyFolderType type)
1262 {
1263         ModestHeaderViewPrivate *priv;
1264         ModestHeaderViewStyle style;
1265
1266         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1267         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1268
1269         style = modest_header_view_get_style   (self);
1270         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1271
1272         return priv->sort_type[style][type];
1273 }
1274
1275 typedef struct {
1276         ModestHeaderView *header_view;
1277         RefreshAsyncUserCallback cb;
1278         gpointer user_data;
1279 } SetFolderHelper;
1280
1281 static void
1282 folder_refreshed_cb (ModestMailOperation *mail_op,
1283                      TnyFolder *folder,
1284                      gpointer user_data)
1285 {
1286         ModestHeaderViewPrivate *priv;
1287         SetFolderHelper *info;
1288
1289         info = (SetFolderHelper*) user_data;
1290
1291         priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1292
1293         /* User callback */
1294         if (info->cb)
1295                 info->cb (mail_op, folder, info->user_data);
1296
1297         /* Start the folder count changes observer. We do not need it
1298            before the refresh. Note that the monitor could still be
1299            called for this refresh but now we know that the callback
1300            was previously called */
1301         g_mutex_lock (priv->observers_lock);
1302         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1303         g_mutex_unlock (priv->observers_lock);
1304
1305         /* Notify the observers that the update is over */
1306         g_signal_emit (G_OBJECT (info->header_view),
1307                        signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1308
1309         /* Allow filtering notifications from now on if the current
1310            folder is still the same (if not then the user has selected
1311            another one to refresh, we should wait until that refresh
1312            finishes) */
1313         if (priv->folder == folder)
1314                 priv->notify_status = TRUE;
1315
1316         /* Frees */
1317         g_object_unref (info->header_view);
1318         g_free (info);
1319 }
1320
1321 static void
1322 refresh_folder_error_handler (ModestMailOperation *mail_op,
1323                               gpointer user_data)
1324 {
1325         const GError *error = modest_mail_operation_get_error (mail_op);
1326
1327         if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1328             error->code == TNY_IO_ERROR_WRITE ||
1329             error->code == TNY_IO_ERROR_READ) {
1330                 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1331                 /* If the mail op has been cancelled then it's not an error: don't show any message */
1332                 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1333                         gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1334                         modest_platform_information_banner (NULL, NULL, msg);
1335                         g_free (msg);
1336                 }
1337         }
1338 }
1339
1340 void
1341 modest_header_view_set_folder (ModestHeaderView *self,
1342                                TnyFolder *folder,
1343                                gboolean refresh,
1344                                ModestWindow *progress_window,
1345                                RefreshAsyncUserCallback callback,
1346                                gpointer user_data)
1347 {
1348         ModestHeaderViewPrivate *priv;
1349
1350         g_return_if_fail (self);
1351
1352         priv =     MODEST_HEADER_VIEW_GET_PRIVATE(self);
1353
1354         if (priv->folder) {
1355                 if (priv->status_timeout) {
1356                         g_source_remove (priv->status_timeout);
1357                         priv->status_timeout = 0;
1358                 }
1359
1360                 g_mutex_lock (priv->observers_lock);
1361                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1362                 g_object_unref (priv->folder);
1363                 priv->folder = NULL;
1364                 g_mutex_unlock (priv->observers_lock);
1365         }
1366
1367         if (folder) {
1368                 GtkTreeSelection *selection;
1369                 SetFolderHelper *info;
1370                 ModestMailOperation *mail_op = NULL;
1371
1372                 /* Set folder in the model */
1373                 modest_header_view_set_folder_intern (self, folder);
1374
1375                 /* Pick my reference. Nothing to do with the mail operation */
1376                 priv->folder = g_object_ref (folder);
1377
1378                 /* Do not notify about filterings until the refresh finishes */
1379                 priv->notify_status = FALSE;
1380
1381                 /* Clear the selection if exists */
1382                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1383                 gtk_tree_selection_unselect_all(selection);
1384                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1385
1386                 /* Notify the observers that the update begins */
1387                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1388                                0, TRUE, NULL);
1389
1390                 /* create the helper */
1391                 info = g_malloc0 (sizeof (SetFolderHelper));
1392                 info->header_view = g_object_ref (self);
1393                 info->cb = callback;
1394                 info->user_data = user_data;
1395
1396                 /* Create the mail operation (source will be the parent widget) */
1397                 if (progress_window)
1398                         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1399                                                                                  refresh_folder_error_handler,
1400                                                                                  NULL, NULL);
1401                 if (refresh) {
1402                         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1403                                                          mail_op);
1404
1405                         /* Refresh the folder asynchronously */
1406                         modest_mail_operation_refresh_folder (mail_op,
1407                                                               folder,
1408                                                               folder_refreshed_cb,
1409                                                               info);
1410                 } else {
1411                         folder_refreshed_cb (mail_op, folder, info);
1412                 }
1413                 /* Free */
1414                 if (mail_op)
1415                         g_object_unref (mail_op);
1416         } else {
1417                 g_mutex_lock (priv->observers_lock);
1418
1419                 if (priv->monitor) {
1420                         tny_folder_monitor_stop (priv->monitor);
1421                         g_object_unref (G_OBJECT (priv->monitor));
1422                         priv->monitor = NULL;
1423                 }
1424
1425                 if (priv->autoselect_reference) {
1426                         gtk_tree_row_reference_free (priv->autoselect_reference);
1427                         priv->autoselect_reference = NULL;
1428                 }
1429
1430                 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1431
1432                 modest_header_view_notify_observers(self, NULL, NULL);
1433
1434                 g_mutex_unlock (priv->observers_lock);
1435
1436                 /* Notify the observers that the update is over */
1437                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1438                                0, FALSE, NULL);
1439         }
1440 }
1441
1442 static void
1443 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1444                          GtkTreeViewColumn *column, gpointer userdata)
1445 {
1446         ModestHeaderView *self = NULL;
1447         GtkTreeIter iter;
1448         GtkTreeModel *model = NULL;
1449         TnyHeader *header = NULL;
1450         TnyHeaderFlags flags;
1451
1452         self = MODEST_HEADER_VIEW (treeview);
1453
1454         model = gtk_tree_view_get_model (treeview);
1455         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1456                 goto frees;
1457
1458         /* get the first selected item */
1459         gtk_tree_model_get (model, &iter,
1460                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1461                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1462                             -1);
1463
1464         /* Dont open DELETED messages */
1465         if (flags & TNY_HEADER_FLAG_DELETED) {
1466                 GtkWidget *win;
1467                 gchar *msg;
1468                 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1469                 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1470                 modest_platform_information_banner (NULL, NULL, msg);
1471                 g_free (msg);
1472                 goto frees;
1473         }
1474
1475         /* Emit signal */
1476         g_signal_emit (G_OBJECT(self),
1477                        signals[HEADER_ACTIVATED_SIGNAL],
1478                        0, header, path);
1479
1480         /* Free */
1481  frees:
1482         if (header != NULL)
1483                 g_object_unref (G_OBJECT (header));
1484
1485 }
1486
1487 static void
1488 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1489 {
1490         GtkTreeModel *model;
1491         TnyHeader *header = NULL;
1492         GtkTreePath *path = NULL;
1493         GtkTreeIter iter;
1494         ModestHeaderView *self;
1495         GList *selected = NULL;
1496
1497         g_return_if_fail (sel);
1498         g_return_if_fail (user_data);
1499
1500         self = MODEST_HEADER_VIEW (user_data);
1501
1502         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1503         if (selected != NULL)
1504                 path = (GtkTreePath *) selected->data;
1505         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1506                 return; /* msg was _un_selected */
1507
1508         gtk_tree_model_get (model, &iter,
1509                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1510                             &header, -1);
1511
1512         /* Emit signal */
1513         g_signal_emit (G_OBJECT(self),
1514                        signals[HEADER_SELECTED_SIGNAL],
1515                        0, header);
1516
1517         g_object_unref (G_OBJECT (header));
1518
1519         /* free all items in 'selected' */
1520         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1521         g_list_free (selected);
1522 }
1523
1524
1525 /* PROTECTED method. It's useful when we want to force a given
1526    selection to reload a msg. For example if we have selected a header
1527    in offline mode, when Modest become online, we want to reload the
1528    message automatically without an user click over the header */
1529 void
1530 _modest_header_view_change_selection (GtkTreeSelection *selection,
1531                                       gpointer user_data)
1532 {
1533         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1534         g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1535
1536         on_selection_changed (selection, user_data);
1537 }
1538
1539 static gint
1540 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1541 {
1542         /* HH, LL, NN */
1543         if (p1 == p2)
1544                 return 0;
1545
1546         /* HL HN */
1547         if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1548                 return 1;
1549
1550         /* LH LN */
1551         if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1552                 return -1;
1553
1554         /* NH */
1555         if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1556                 return -1;
1557
1558         /* NL */
1559         return 1;
1560 }
1561
1562 static gint
1563 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1564           gpointer user_data)
1565 {
1566         gint col_id;
1567         gint t1, t2;
1568         gint val1, val2;
1569         gint cmp;
1570
1571         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1572         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1573
1574
1575         switch (col_id) {
1576         case TNY_HEADER_FLAG_ATTACHMENTS:
1577
1578                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1579                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1580                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1581                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1582
1583                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1584                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1585
1586                 return cmp ? cmp : t1 - t2;
1587
1588         case TNY_HEADER_FLAG_PRIORITY_MASK: {
1589                 TnyHeader *header1 = NULL, *header2 = NULL;
1590
1591                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1592                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1593                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1594                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1595
1596                 /* This is for making priority values respect the intuitive sort relationship
1597                  * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1598
1599                 if (header1 && header2) {
1600                         cmp =  compare_priorities (tny_header_get_priority (header1),
1601                                 tny_header_get_priority (header2));
1602                         g_object_unref (header1);
1603                         g_object_unref (header2);
1604
1605                         return cmp ? cmp : t1 - t2;
1606                 }
1607
1608                 return t1 - t2;
1609         }
1610         default:
1611                 return &iter1 - &iter2; /* oughhhh  */
1612         }
1613 }
1614
1615 static gint
1616 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1617                   gpointer user_data)
1618 {
1619         gint t1, t2;
1620         gchar *val1, *val2;
1621         gint cmp;
1622 /*      static int counter = 0; */
1623
1624         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1625
1626         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1627                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1628         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1629                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1630
1631         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1632                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1633                                              TRUE);
1634         g_free (val1);
1635         g_free (val2);
1636         return cmp;
1637 }
1638
1639 /* Drag and drop stuff */
1640 static void
1641 drag_data_get_cb (GtkWidget *widget,
1642                   GdkDragContext *context,
1643                   GtkSelectionData *selection_data,
1644                   guint info,
1645                   guint time,
1646                   gpointer data)
1647 {
1648         ModestHeaderView *self = NULL;
1649         ModestHeaderViewPrivate *priv = NULL;
1650
1651         self = MODEST_HEADER_VIEW (widget);
1652         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1653
1654         /* Set the data. Do not use the current selection because it
1655            could be different than the selection at the beginning of
1656            the d&d */
1657         modest_dnd_selection_data_set_paths (selection_data,
1658                                              priv->drag_begin_cached_selected_rows);
1659 }
1660
1661 /**
1662  * We're caching the selected rows at the beginning because the
1663  * selection could change between drag-begin and drag-data-get, for
1664  * example if we have a set of rows already selected, and then we
1665  * click in one of them (without SHIFT key pressed) and begin a drag,
1666  * the selection at that moment contains all the selected lines, but
1667  * after dropping the selection, the release event provokes that only
1668  * the row used to begin the drag is selected, so at the end the
1669  * drag&drop affects only one rows instead of all the selected ones.
1670  *
1671  */
1672 static void
1673 drag_begin_cb (GtkWidget *widget,
1674                GdkDragContext *context,
1675                gpointer data)
1676 {
1677         ModestHeaderView *self = NULL;
1678         ModestHeaderViewPrivate *priv = NULL;
1679         GtkTreeSelection *selection;
1680
1681         self = MODEST_HEADER_VIEW (widget);
1682         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1683
1684         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1685         priv->drag_begin_cached_selected_rows =
1686                 gtk_tree_selection_get_selected_rows (selection, NULL);
1687 }
1688
1689 /**
1690  * We use the drag-end signal to clear the cached selection, we use
1691  * this because this allways happens, whether or not the d&d was a
1692  * success
1693  */
1694 static void
1695 drag_end_cb (GtkWidget *widget,
1696              GdkDragContext *dc,
1697              gpointer data)
1698 {
1699         ModestHeaderView *self = NULL;
1700         ModestHeaderViewPrivate *priv = NULL;
1701
1702         self = MODEST_HEADER_VIEW (widget);
1703         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1704
1705         /* Free cached data */
1706         g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1707         g_list_free (priv->drag_begin_cached_selected_rows);
1708         priv->drag_begin_cached_selected_rows = NULL;
1709 }
1710
1711 /* Header view drag types */
1712 const GtkTargetEntry header_view_drag_types[] = {
1713         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1714 };
1715
1716 static void
1717 enable_drag_and_drop (GtkWidget *self)
1718 {
1719 #ifdef MODEST_TOOLKIT_HILDON2
1720         return;
1721 #endif
1722         gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1723                              header_view_drag_types,
1724                              G_N_ELEMENTS (header_view_drag_types),
1725                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1726 }
1727
1728 static void
1729 disable_drag_and_drop (GtkWidget *self)
1730 {
1731 #ifdef MODEST_TOOLKIT_HILDON2
1732         return;
1733 #endif
1734         gtk_drag_source_unset (self);
1735 }
1736
1737 static void
1738 setup_drag_and_drop (GtkWidget *self)
1739 {
1740 #ifdef MODEST_TOOLKIT_HILDON2
1741         return;
1742 #endif
1743         enable_drag_and_drop(self);
1744         g_signal_connect(G_OBJECT (self), "drag_data_get",
1745                          G_CALLBACK(drag_data_get_cb), NULL);
1746
1747         g_signal_connect(G_OBJECT (self), "drag_begin",
1748                          G_CALLBACK(drag_begin_cb), NULL);
1749
1750         g_signal_connect(G_OBJECT (self), "drag_end",
1751                          G_CALLBACK(drag_end_cb), NULL);
1752 }
1753
1754 static GtkTreePath *
1755 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1756 {
1757         GtkTreePath *path = NULL;
1758         GtkTreeSelection *sel = NULL;
1759         GList *rows = NULL;
1760
1761         sel   = gtk_tree_view_get_selection(self);
1762         rows = gtk_tree_selection_get_selected_rows (sel, model);
1763
1764         if ((rows == NULL) || (g_list_length(rows) != 1))
1765                 goto frees;
1766
1767         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1768
1769
1770         /* Free */
1771  frees:
1772         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1773         g_list_free(rows);
1774
1775         return path;
1776 }
1777
1778 #ifndef MODEST_TOOLKIT_HILDON2
1779 /*
1780  * This function moves the tree view scroll to the current selected
1781  * row when the widget grabs the focus
1782  */
1783 static gboolean
1784 on_focus_in (GtkWidget     *self,
1785              GdkEventFocus *event,
1786              gpointer       user_data)
1787 {
1788         GtkTreeSelection *selection;
1789         GtkTreeModel *model;
1790         GList *selected = NULL;
1791         GtkTreePath *selected_path = NULL;
1792
1793         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1794         if (!model)
1795                 return FALSE;
1796
1797         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1798         /* If none selected yet, pick the first one */
1799         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1800                 GtkTreeIter iter;
1801                 GtkTreePath *path;
1802
1803                 /* Return if the model is empty */
1804                 if (!gtk_tree_model_get_iter_first (model, &iter))
1805                         return FALSE;
1806
1807                 path = gtk_tree_model_get_path (model, &iter);
1808                 gtk_tree_selection_select_path (selection, path);
1809                 gtk_tree_path_free (path);
1810         }
1811
1812         /* Need to get the all the rows because is selection multiple */
1813         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1814         if (selected == NULL) return FALSE;
1815         selected_path = (GtkTreePath *) selected->data;
1816
1817         /* Frees */
1818         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1819         g_list_free (selected);
1820
1821         return FALSE;
1822 }
1823
1824 static gboolean
1825 on_focus_out (GtkWidget     *self,
1826              GdkEventFocus *event,
1827              gpointer       user_data)
1828 {
1829
1830         if (!gtk_widget_is_focus (self)) {
1831                 GtkTreeSelection *selection = NULL;
1832                 GList *selected_rows = NULL;
1833                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1834                 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1835                         selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1836                         g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1837                         gtk_tree_selection_unselect_all (selection);
1838                         gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1839                         g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1840                         g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1841                         g_list_free (selected_rows);
1842                 }
1843         }
1844         return FALSE;
1845 }
1846 #endif
1847
1848 static gboolean
1849 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1850 {
1851         enable_drag_and_drop(self);
1852         return FALSE;
1853 }
1854
1855 static gboolean
1856 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1857 {
1858         GtkTreeSelection *selection = NULL;
1859         GtkTreePath *path = NULL;
1860         gboolean already_selected = FALSE, already_opened = FALSE;
1861         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1862
1863         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1864                 GtkTreeIter iter;
1865                 GtkTreeModel *model;
1866
1867                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1868                 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1869
1870                 /* Get header from model */
1871                 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1872                 if (gtk_tree_model_get_iter (model, &iter, path)) {
1873                         GValue value = {0,};
1874                         TnyHeader *header;
1875
1876                         gtk_tree_model_get_value (model, &iter,
1877                                                   TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1878                                                   &value);
1879                         header = (TnyHeader *) g_value_get_object (&value);
1880                         if (TNY_IS_HEADER (header)) {
1881                                 status = modest_tny_all_send_queues_get_msg_status (header);
1882                                 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1883                                                                                            header, NULL);
1884                         }
1885                         g_value_unset (&value);
1886                 }
1887         }
1888
1889         /* Enable drag and drop only if the user clicks on a row that
1890            it's already selected. If not, let him select items using
1891            the pointer. If the message is in an OUTBOX and in sending
1892            status disable drag and drop as well */
1893         if (!already_selected ||
1894             status == MODEST_TNY_SEND_QUEUE_SENDING ||
1895             already_opened)
1896                 disable_drag_and_drop(self);
1897
1898         if (path != NULL)
1899                 gtk_tree_path_free(path);
1900
1901         /* If it's already opened then do not let the button-press
1902            event go on because it'll perform a message open because
1903            we're clicking on to an already selected header */
1904         return FALSE;
1905 }
1906
1907 static void
1908 folder_monitor_update (TnyFolderObserver *self,
1909                        TnyFolderChange *change)
1910 {
1911         ModestHeaderViewPrivate *priv = NULL;
1912         TnyFolderChangeChanged changed;
1913         TnyFolder *folder = NULL;
1914
1915         changed = tny_folder_change_get_changed (change);
1916
1917         /* Do not notify the observers if the folder of the header
1918            view has changed before this call to the observer
1919            happens */
1920         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1921         folder = tny_folder_change_get_folder (change);
1922         if (folder != priv->folder)
1923                 goto frees;
1924
1925         MODEST_DEBUG_BLOCK (
1926                             if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1927                                     g_print ("ADDED %d/%d (r/t) \n",
1928                                              tny_folder_change_get_new_unread_count (change),
1929                                              tny_folder_change_get_new_all_count (change));
1930                             if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1931                                     g_print ("ALL COUNT %d\n",
1932                                              tny_folder_change_get_new_all_count (change));
1933                             if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1934                                     g_print ("UNREAD COUNT %d\n",
1935                                              tny_folder_change_get_new_unread_count (change));
1936                             if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1937                                     g_print ("EXPUNGED %d/%d (r/t) \n",
1938                                              tny_folder_change_get_new_unread_count (change),
1939                                              tny_folder_change_get_new_all_count (change));
1940                             if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1941                                     g_print ("FOLDER RENAME\n");
1942                             if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1943                                     g_print ("MSG RECEIVED %d/%d (r/t) \n",
1944                                              tny_folder_change_get_new_unread_count (change),
1945                                              tny_folder_change_get_new_all_count (change));
1946                             g_print ("---------------------------------------------------\n");
1947                             );
1948
1949         /* Check folder count */
1950         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1951             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1952
1953                 g_mutex_lock (priv->observers_lock);
1954
1955                 /* Emit signal to evaluate how headers changes affects
1956                    to the window view  */
1957                 g_signal_emit (G_OBJECT(self),
1958                                signals[MSG_COUNT_CHANGED_SIGNAL],
1959                                0, folder, change);
1960
1961                 /* Added or removed headers, so data stored on cliboard are invalid  */
1962                 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1963                         modest_email_clipboard_clear (priv->clipboard);
1964
1965                 g_mutex_unlock (priv->observers_lock);
1966         }
1967
1968         /* Free */
1969  frees:
1970         if (folder != NULL)
1971                 g_object_unref (folder);
1972 }
1973
1974 gboolean
1975 modest_header_view_is_empty (ModestHeaderView *self)
1976 {
1977         ModestHeaderViewPrivate *priv;
1978
1979         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1980
1981         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1982
1983         return priv->status == HEADER_VIEW_EMPTY;
1984 }
1985
1986 void
1987 modest_header_view_clear (ModestHeaderView *self)
1988 {
1989         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1990
1991         modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
1992 }
1993
1994 void
1995 modest_header_view_copy_selection (ModestHeaderView *header_view)
1996 {
1997         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1998
1999         /* Copy selection */
2000         _clipboard_set_selected_data (header_view, FALSE);
2001 }
2002
2003 void
2004 modest_header_view_cut_selection (ModestHeaderView *header_view)
2005 {
2006         ModestHeaderViewPrivate *priv = NULL;
2007         const gchar **hidding = NULL;
2008         guint i, n_selected;
2009
2010         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2011
2012         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2013
2014         /* Copy selection */
2015         _clipboard_set_selected_data (header_view, TRUE);
2016
2017         /* Get hidding ids */
2018         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2019
2020         /* Clear hidding array created by previous cut operation */
2021         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2022
2023         /* Copy hidding array */
2024         priv->n_selected = n_selected;
2025         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2026         for (i=0; i < n_selected; i++)
2027                 priv->hidding_ids[i] = g_strdup(hidding[i]);
2028
2029         /* Hide cut headers */
2030         modest_header_view_refilter (header_view);
2031 }
2032
2033
2034
2035
2036 static void
2037 _clipboard_set_selected_data (ModestHeaderView *header_view,
2038                               gboolean delete)
2039 {
2040         ModestHeaderViewPrivate *priv = NULL;
2041         TnyList *headers = NULL;
2042
2043         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2044         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2045
2046         /* Set selected data on clipboard   */
2047         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2048         headers = modest_header_view_get_selected_headers (header_view);
2049         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2050
2051         /* Free */
2052         g_object_unref (headers);
2053 }
2054
2055 typedef struct {
2056         ModestHeaderView *self;
2057         TnyFolder *folder;
2058 } NotifyFilterInfo;
2059
2060 static gboolean
2061 notify_filter_change (gpointer data)
2062 {
2063         NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2064
2065         g_signal_emit (info->self,
2066                        signals[MSG_COUNT_CHANGED_SIGNAL],
2067                        0, info->folder, NULL);
2068
2069         return FALSE;
2070 }
2071
2072 static void
2073 notify_filter_change_destroy (gpointer data)
2074 {
2075         NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2076         ModestHeaderViewPrivate *priv;
2077
2078         priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2079         priv->status_timeout = 0;
2080
2081         g_object_unref (info->self);
2082         g_object_unref (info->folder);
2083         g_slice_free (NotifyFilterInfo, info);
2084 }
2085
2086 static gboolean
2087 filter_row (GtkTreeModel *model,
2088             GtkTreeIter *iter,
2089             gpointer user_data)
2090 {
2091         ModestHeaderViewPrivate *priv = NULL;
2092         TnyHeaderFlags flags;
2093         TnyHeader *header = NULL;
2094         guint i;
2095         gchar *id = NULL;
2096         gboolean visible = TRUE;
2097         gboolean found = FALSE;
2098         GValue value = {0,};
2099         HeaderViewStatus old_status;
2100
2101         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2102         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2103
2104         /* Get header from model */
2105         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2106         flags = (TnyHeaderFlags) g_value_get_int (&value);
2107         g_value_unset (&value);
2108         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2109         header = (TnyHeader *) g_value_get_object (&value);
2110         g_value_unset (&value);
2111
2112         /* Get message id from header (ensure is a valid id) */
2113         if (!header) {
2114                 visible = FALSE;
2115                 goto frees;
2116         }
2117
2118         /* Hide deleted and mark as deleted heders */
2119         if (flags & TNY_HEADER_FLAG_DELETED ||
2120             flags & TNY_HEADER_FLAG_EXPUNGED) {
2121                 visible = FALSE;
2122                 goto frees;
2123         }
2124
2125         if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2126                 if (priv->is_outbox &&
2127                     modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2128                         visible = FALSE;
2129                         goto frees;
2130                 }
2131         }
2132
2133         if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2134                 if (priv->is_outbox &&
2135                     modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2136                         visible = FALSE;
2137                         goto frees;
2138                 }
2139         }
2140
2141         /* If no data on clipboard, return always TRUE */
2142         if (modest_email_clipboard_cleared(priv->clipboard)) {
2143                 visible = TRUE;
2144                 goto frees;
2145         }
2146
2147         /* Check hiding */
2148         if (priv->hidding_ids != NULL) {
2149                 id = tny_header_dup_message_id (header);
2150                 for (i=0; i < priv->n_selected && !found; i++)
2151                         if (priv->hidding_ids[i] != NULL && id != NULL)
2152                                 found = (!strcmp (priv->hidding_ids[i], id));
2153
2154                 visible = !found;
2155                 g_free(id);
2156         }
2157
2158  frees:
2159         old_status = priv->status;
2160         priv->status = ((gboolean) priv->status) && !visible;
2161         if ((priv->notify_status) && (priv->status != old_status)) {
2162                 if (priv->status_timeout)
2163                         g_source_remove (priv->status_timeout);
2164
2165                 if (header) {
2166                         NotifyFilterInfo *info;
2167
2168                         info = g_slice_new0 (NotifyFilterInfo);
2169                         info->self = g_object_ref (G_OBJECT (user_data));
2170                         if (header)
2171                                 info->folder = tny_header_get_folder (header);
2172                         priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2173                                                                    notify_filter_change,
2174                                                                    info,
2175                                                                    notify_filter_change_destroy);
2176                 }
2177         }
2178
2179         return visible;
2180 }
2181
2182 static void
2183 _clear_hidding_filter (ModestHeaderView *header_view)
2184 {
2185         ModestHeaderViewPrivate *priv = NULL;
2186         guint i;
2187
2188         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2189         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2190
2191         if (priv->hidding_ids != NULL) {
2192                 for (i=0; i < priv->n_selected; i++)
2193                         g_free (priv->hidding_ids[i]);
2194                 g_free(priv->hidding_ids);
2195         }
2196 }
2197
2198 void
2199 modest_header_view_refilter (ModestHeaderView *header_view)
2200 {
2201         GtkTreeModel *model = NULL;
2202         ModestHeaderViewPrivate *priv = NULL;
2203
2204         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2205         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2206
2207         /* Hide cut headers */
2208         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2209         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2210                 priv->status = HEADER_VIEW_INIT;
2211                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2212         }
2213 }
2214
2215 /*
2216  * Called when an account is removed. If I'm showing a folder of the
2217  * account that has been removed then clear the view
2218  */
2219 static void
2220 on_account_removed (TnyAccountStore *self,
2221                     TnyAccount *account,
2222                     gpointer user_data)
2223 {
2224         ModestHeaderViewPrivate *priv = NULL;
2225
2226         /* Ignore changes in transport accounts */
2227         if (TNY_IS_TRANSPORT_ACCOUNT (account))
2228                 return;
2229
2230         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2231
2232         if (priv->folder) {
2233                 TnyAccount *my_account;
2234
2235                 my_account = tny_folder_get_account (priv->folder);
2236                 if (my_account == account)
2237                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2238                 g_object_unref (my_account);
2239         }
2240 }
2241
2242 void
2243 modest_header_view_add_observer(ModestHeaderView *header_view,
2244                                      ModestHeaderViewObserver *observer)
2245 {
2246         ModestHeaderViewPrivate *priv;
2247
2248         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2249         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2250
2251         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2252
2253         g_mutex_lock(priv->observer_list_lock);
2254         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2255         g_mutex_unlock(priv->observer_list_lock);
2256 }
2257
2258 void
2259 modest_header_view_remove_observer(ModestHeaderView *header_view,
2260                                    ModestHeaderViewObserver *observer)
2261 {
2262         ModestHeaderViewPrivate *priv;
2263
2264         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2265         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2266
2267         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2268
2269         g_mutex_lock(priv->observer_list_lock);
2270         priv->observer_list = g_slist_remove(priv->observer_list, observer);
2271         g_mutex_unlock(priv->observer_list_lock);
2272 }
2273
2274 static void
2275 modest_header_view_notify_observers(ModestHeaderView *header_view,
2276                                     GtkTreeModel *model,
2277                                     const gchar *tny_folder_id)
2278 {
2279         ModestHeaderViewPrivate *priv = NULL;
2280         GSList *iter;
2281         ModestHeaderViewObserver *observer;
2282
2283
2284         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2285
2286         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2287
2288         g_mutex_lock(priv->observer_list_lock);
2289         iter = priv->observer_list;
2290         while(iter != NULL){
2291                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2292                 modest_header_view_observer_update(observer, model,
2293                                 tny_folder_id);
2294                 iter = g_slist_next(iter);
2295         }
2296         g_mutex_unlock(priv->observer_list_lock);
2297 }
2298
2299 const gchar *
2300 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2301 {
2302         ModestHeaderViewPrivate *priv = NULL;
2303
2304         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2305         return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2306 }
2307
2308 void
2309 modest_header_view_set_filter (ModestHeaderView *self,
2310                                ModestHeaderViewFilter filter)
2311 {
2312         ModestHeaderViewPrivate *priv;
2313         GtkTreeModel *filter_model;
2314
2315         g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2316         priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2317
2318         priv->filter |= filter;
2319
2320         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2321         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2322                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2323         }
2324 }
2325
2326 void
2327 modest_header_view_unset_filter (ModestHeaderView *self,
2328                                  ModestHeaderViewFilter filter)
2329 {
2330         ModestHeaderViewPrivate *priv;
2331         GtkTreeModel *filter_model;
2332
2333         g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2334         priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2335
2336         priv->filter &= ~filter;
2337
2338         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2339         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2340                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2341         }
2342 }
2343
2344 static void
2345 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2346 {
2347         if (strcmp ("style", spec->name) == 0) {
2348                 update_style (MODEST_HEADER_VIEW (obj));
2349                 gtk_widget_queue_draw (GTK_WIDGET (obj));
2350         }
2351 }
2352
2353 static void
2354 update_style (ModestHeaderView *self)
2355 {
2356         ModestHeaderViewPrivate *priv;
2357         GdkColor style_color;
2358         GdkColor style_active_color;
2359         PangoAttrList *attr_list;
2360         GtkStyle *style;
2361         PangoAttribute *attr;
2362         GdkColor *new_color;
2363
2364         g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2365         priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2366
2367         /* Set color */
2368
2369         attr_list = pango_attr_list_new ();
2370         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2371                 gdk_color_parse ("grey", &style_color);
2372         }
2373         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2374         pango_attr_list_insert (attr_list, attr);
2375
2376         /* set font */
2377         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2378                                            (GTK_WIDGET(self)),
2379                                            "SmallSystemFont", NULL,
2380                                            G_TYPE_NONE);
2381         if (style) {
2382                 attr = pango_attr_font_desc_new (pango_font_description_copy
2383                                                  (style->font_desc));
2384                 pango_attr_list_insert (attr_list, attr);
2385
2386                 g_object_set (G_OBJECT (priv->renderer_address),
2387                               "foreground-gdk", &style_color,
2388                               "foreground-set", TRUE,
2389                               "attributes", attr_list,
2390                               NULL);
2391                 g_object_set (G_OBJECT (priv->renderer_date_status),
2392                               "foreground-gdk", &style_color,
2393                               "foreground-set", TRUE,
2394                               "attributes", attr_list,
2395                               NULL);
2396                 pango_attr_list_unref (attr_list);
2397         } else {
2398                 g_object_set (G_OBJECT (priv->renderer_address),
2399                               "foreground-gdk", &style_color,
2400                               "foreground-set", TRUE,
2401                               "scale", PANGO_SCALE_SMALL,
2402                               "scale-set", TRUE,
2403                               NULL);
2404                 g_object_set (G_OBJECT (priv->renderer_date_status),
2405                               "foreground-gdk", &style_color,
2406                               "foreground-set", TRUE,
2407                               "scale", PANGO_SCALE_SMALL,
2408                               "scale-set", TRUE,
2409                               NULL);
2410         }
2411
2412         if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2413                 new_color = gdk_color_copy (&style_active_color);
2414         } else {
2415                 new_color = NULL;
2416         }
2417 #ifdef MODEST_TOOLKIT_HILDON2
2418         g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (new_color != NULL));
2419         g_object_set_data_full (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, new_color, (GDestroyNotify) gdk_color_free);
2420 #endif
2421 }