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