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