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