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