Proper alignment of header view rows
[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         ModestHeaderViewPrivate *priv;
810         TnyList *header_list = NULL;
811         TnyHeader *header;
812         GList *list, *tmp = NULL;
813         GtkTreeModel *tree_model = NULL;
814         GtkTreeIter iter;
815
816         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
817         
818         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
819
820         /* Get selected rows */
821         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
822         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
823
824         if (list) {
825                 header_list = tny_simple_list_new();
826
827                 list = g_list_reverse (list);
828                 tmp = list;
829                 while (tmp) {                   
830                         /* get header from selection */
831                         gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
832                         gtk_tree_model_get (tree_model, &iter,
833                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
834                                             &header, -1);
835                         /* Prepend to list */
836                         tny_list_prepend (header_list, G_OBJECT (header));
837                         g_object_unref (G_OBJECT (header));
838
839                         tmp = g_list_next (tmp);
840                 }
841                 /* Clean up*/
842                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
843                 g_list_free (list);
844         }
845         return header_list;
846 }
847
848
849 /* scroll our list view so the selected item is visible */
850 static void
851 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
852 {
853 #ifdef MODEST_TOOLKIT_GTK 
854
855         GtkTreePath *selected_path;
856         GtkTreePath *start, *end;
857         
858         GtkTreeModel *model;
859         
860         model         = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
861         selected_path = gtk_tree_model_get_path (model, iter);
862
863         start = gtk_tree_path_new ();
864         end   = gtk_tree_path_new ();
865
866         gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
867
868         if (gtk_tree_path_compare (selected_path, start) < 0 ||
869             gtk_tree_path_compare (end, selected_path) < 0)
870                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
871                                               selected_path, NULL, TRUE,
872                                               up ? 0.0 : 1.0,
873                                               up ? 0.0 : 1.0);
874         gtk_tree_path_free (selected_path);
875         gtk_tree_path_free (start);
876         gtk_tree_path_free (end);
877
878 #endif /* MODEST_TOOLKIT_GTK */
879 }
880
881
882 void 
883 modest_header_view_select_next (ModestHeaderView *self)
884 {
885         GtkTreeSelection *sel;
886         GtkTreeIter iter;
887         GtkTreeModel *model;
888         GtkTreePath *path;
889
890         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
891
892         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
893         path = get_selected_row (GTK_TREE_VIEW(self), &model);
894         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
895                 /* Unselect previous path */
896                 gtk_tree_selection_unselect_path (sel, path);
897                 
898                 /* Move path down and selects new one  */
899                 if (gtk_tree_model_iter_next (model, &iter)) {
900                         gtk_tree_selection_select_iter (sel, &iter);
901                         scroll_to_selected (self, &iter, FALSE);        
902                 }
903                 gtk_tree_path_free(path);
904         }
905         
906 }
907
908 void 
909 modest_header_view_select_prev (ModestHeaderView *self)
910 {
911         GtkTreeSelection *sel;
912         GtkTreeIter iter;
913         GtkTreeModel *model;
914         GtkTreePath *path;
915
916         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
917
918         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
919         path = get_selected_row (GTK_TREE_VIEW(self), &model);
920         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
921                 /* Unselect previous path */
922                 gtk_tree_selection_unselect_path (sel, path);
923
924                 /* Move path up */
925                 if (gtk_tree_path_prev (path)) {
926                         gtk_tree_model_get_iter (model, &iter, path);
927                         
928                         /* Select the new one */
929                         gtk_tree_selection_select_iter (sel, &iter);
930                         scroll_to_selected (self, &iter, TRUE); 
931
932                 }
933                 gtk_tree_path_free (path);
934         }
935 }
936
937 GList*
938 modest_header_view_get_columns (ModestHeaderView *self)
939 {       
940         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
941         
942         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self)); 
943 }
944
945
946
947 gboolean
948 modest_header_view_set_style (ModestHeaderView *self,
949                               ModestHeaderViewStyle style)
950 {
951         ModestHeaderViewPrivate *priv;
952         gboolean show_col_headers = FALSE;
953         ModestHeaderViewStyle old_style;
954         
955         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
956         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
957                               FALSE);
958         
959         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
960         if (priv->style == style)
961                 return TRUE; /* nothing to do */
962         
963         switch (style) {
964         case MODEST_HEADER_VIEW_STYLE_DETAILS:
965                 show_col_headers = TRUE;
966                 break;
967         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
968                 break;
969         default:
970                 g_return_val_if_reached (FALSE);
971         }
972         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
973         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);    
974
975         old_style   = priv->style;
976         priv->style = style;
977
978         return TRUE;
979 }
980
981
982 ModestHeaderViewStyle
983 modest_header_view_get_style (ModestHeaderView *self)
984 {
985         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
986
987         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
988 }
989
990 /* This is used to automatically select the first header if the user
991  * has not selected any header yet.
992  */
993 static gboolean 
994 modest_header_view_on_expose_event(GtkTreeView *header_view,
995                                    GdkEventExpose *event,
996                                    gpointer user_data)
997 {
998         GtkTreeSelection *sel;
999         GtkTreeModel *model;
1000         GtkTreeIter tree_iter;
1001         ModestHeaderViewPrivate *priv;
1002
1003         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1004         model = gtk_tree_view_get_model(header_view);
1005
1006         if (!model)
1007                 return FALSE;
1008
1009 #ifdef MODEST_TOOLKIT_HILDON2
1010         return FALSE;
1011 #endif
1012         sel = gtk_tree_view_get_selection(header_view);
1013         if(!gtk_tree_selection_count_selected_rows(sel)) {
1014                 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1015                         GtkTreePath *tree_iter_path;
1016                         /* Prevent the widget from getting the focus
1017                            when selecting the first item */
1018                         tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1019                         g_object_set(header_view, "can-focus", FALSE, NULL);
1020                         gtk_tree_selection_select_iter(sel, &tree_iter);
1021                         gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1022                         g_object_set(header_view, "can-focus", TRUE, NULL);
1023                         if (priv->autoselect_reference) {
1024                                 gtk_tree_row_reference_free (priv->autoselect_reference);
1025                         }
1026                         priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1027                         gtk_tree_path_free (tree_iter_path);
1028                 }
1029         } else {
1030                 if (priv->autoselect_reference != NULL) {
1031                         gboolean moved_selection = FALSE;
1032                         GtkTreePath * last_path;
1033                         if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1034                                 moved_selection = TRUE;
1035                         } else {
1036                                 GList *rows;
1037
1038                                 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1039                                 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1040                                 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1041                                         moved_selection = TRUE;
1042                                 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1043                                 g_list_free (rows);
1044                                 gtk_tree_path_free (last_path);
1045                         }
1046                         if (moved_selection) {
1047                                 gtk_tree_row_reference_free (priv->autoselect_reference);
1048                                 priv->autoselect_reference = NULL;
1049                         } else {
1050
1051                                 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1052                                         GtkTreePath *current_path;
1053                                         current_path = gtk_tree_model_get_path (model, &tree_iter);
1054                                         last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1055                                         if (gtk_tree_path_compare (current_path, last_path) != 0) {
1056                                                 g_object_set(header_view, "can-focus", FALSE, NULL);
1057                                                 gtk_tree_selection_unselect_all (sel);
1058                                                 gtk_tree_selection_select_iter(sel, &tree_iter);
1059                                                 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1060                                                 g_object_set(header_view, "can-focus", TRUE, NULL);
1061                                                 gtk_tree_row_reference_free (priv->autoselect_reference);
1062                                                 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1063                                         }
1064                                         gtk_tree_path_free (current_path);
1065                                         gtk_tree_path_free (last_path);
1066                                 }
1067                         }
1068                 }
1069         }
1070
1071         return FALSE;
1072 }
1073
1074 TnyFolder*
1075 modest_header_view_get_folder (ModestHeaderView *self)
1076 {
1077         ModestHeaderViewPrivate *priv;
1078
1079         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1080
1081         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1082
1083         if (priv->folder)
1084                 g_object_ref (priv->folder);
1085
1086         return priv->folder;
1087 }
1088
1089 static void
1090 set_folder_intern_get_headers_async_cb (TnyFolder *folder, 
1091                                         gboolean cancelled, 
1092                                         TnyList *headers, 
1093                                         GError *err, 
1094                                         gpointer user_data)
1095 {
1096         ModestHeaderView *self;
1097         ModestHeaderViewPrivate *priv;
1098
1099         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1100
1101         self = MODEST_HEADER_VIEW (user_data);
1102         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1103
1104         if (cancelled || err)
1105                 return;
1106
1107         /* Add IDLE observer (monitor) and another folder observer for
1108            new messages (self) */
1109         g_mutex_lock (priv->observers_lock);
1110         if (priv->monitor) {
1111                 tny_folder_monitor_stop (priv->monitor);
1112                 g_object_unref (G_OBJECT (priv->monitor));
1113         }
1114         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1115         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1116         tny_folder_monitor_start (priv->monitor);
1117         g_mutex_unlock (priv->observers_lock);
1118 }
1119
1120 static void
1121 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1122 {
1123         TnyFolderType type;
1124         TnyList *headers;
1125         ModestHeaderViewPrivate *priv;
1126         GList *cols, *cursor;
1127         GtkTreeModel *filter_model, *sortable; 
1128         guint sort_colid;
1129         GtkSortType sort_type;
1130
1131         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1132
1133         headers = TNY_LIST (tny_gtk_header_list_model_new ());
1134
1135         /* Start the monitor in the callback of the
1136            tny_gtk_header_list_model_set_folder call. It's crucial to
1137            do it there and not just after the call because we want the
1138            monitor to observe only the headers returned by the
1139            tny_folder_get_headers_async call that it's inside the
1140            tny_gtk_header_list_model_set_folder call. This way the
1141            monitor infrastructure could successfully cope with
1142            duplicates. For example if a tny_folder_add_msg_async is
1143            happening while tny_gtk_header_list_model_set_folder is
1144            invoked, then the first call could add a header that will
1145            be added again by tny_gtk_header_list_model_set_folder, so
1146            we'd end up with duplicate headers. sergio */
1147         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1148                                               folder, FALSE, 
1149                                               set_folder_intern_get_headers_async_cb, 
1150                                               NULL, self);
1151
1152         sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1153         g_object_unref (G_OBJECT (headers));
1154
1155         /* Init filter_row function to examine empty status */
1156         priv->status  = HEADER_VIEW_INIT;
1157
1158         /* Create a tree model filter to hide and show rows for cut operations  */
1159         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1160         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1161                                                 filter_row,
1162                                                 self,
1163                                                 NULL);
1164         g_object_unref (G_OBJECT (sortable));
1165
1166         /* install our special sorting functions */
1167         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1168
1169         /* Restore sort column id */
1170         if (cols) {
1171                 type  = modest_tny_folder_guess_folder_type (folder);
1172                 if (type == TNY_FOLDER_TYPE_INVALID)
1173                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1174                 
1175                 sort_colid = modest_header_view_get_sort_column_id (self, type); 
1176                 sort_type = modest_header_view_get_sort_type (self, type); 
1177                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1178                                                       sort_colid,
1179                                                       sort_type);
1180                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1181                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1182                                                  (GtkTreeIterCompareFunc) cmp_rows,
1183                                                  cols->data, NULL);
1184                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1185                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1186                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
1187                                                  cols->data, NULL);
1188         }
1189
1190         /* Set new model */
1191         gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1192         modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1193                         tny_folder_get_id(folder));
1194         g_object_unref (G_OBJECT (filter_model));
1195
1196         /* Free */
1197         g_list_free (cols);
1198 }
1199
1200 void
1201 modest_header_view_sort_by_column_id (ModestHeaderView *self, 
1202                                       guint sort_colid,
1203                                       GtkSortType sort_type)
1204 {
1205         ModestHeaderViewPrivate *priv = NULL;
1206         GtkTreeModel *tree_filter, *sortable = NULL; 
1207         TnyFolderType type;
1208
1209         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1210         g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1211         
1212         /* Get model and private data */
1213         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);            
1214         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1215         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1216 /*      sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1217         
1218         /* Sort tree model */
1219         type  = modest_tny_folder_guess_folder_type (priv->folder);
1220         if (type == TNY_FOLDER_TYPE_INVALID)
1221                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1222         else {
1223                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1224                                               sort_colid,
1225                                               sort_type);
1226                 /* Store new sort parameters */
1227                 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1228         }       
1229 }
1230
1231 void
1232 modest_header_view_set_sort_params (ModestHeaderView *self, 
1233                                     guint sort_colid, 
1234                                     GtkSortType sort_type,
1235                                     TnyFolderType type)
1236 {
1237         ModestHeaderViewPrivate *priv;
1238         ModestHeaderViewStyle style;
1239         
1240         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1241         g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1242         g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1243         
1244         style = modest_header_view_get_style   (self);
1245         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1246
1247         priv->sort_colid[style][type] = sort_colid;
1248         priv->sort_type[style][type] = sort_type;
1249 }
1250
1251 gint
1252 modest_header_view_get_sort_column_id (ModestHeaderView *self, 
1253                                        TnyFolderType type)
1254 {
1255         ModestHeaderViewPrivate *priv;
1256         ModestHeaderViewStyle style;
1257
1258         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1259         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1260         
1261         style = modest_header_view_get_style   (self);
1262         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1263
1264         return priv->sort_colid[style][type];
1265 }
1266
1267 GtkSortType
1268 modest_header_view_get_sort_type (ModestHeaderView *self, 
1269                                   TnyFolderType type)
1270 {
1271         ModestHeaderViewPrivate *priv;
1272         ModestHeaderViewStyle style;
1273         
1274         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1275         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1276         
1277         style = modest_header_view_get_style   (self);
1278         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1279
1280         return priv->sort_type[style][type];
1281 }
1282
1283 typedef struct {
1284         ModestHeaderView *header_view;
1285         RefreshAsyncUserCallback cb;
1286         gpointer user_data;
1287 } SetFolderHelper;
1288
1289 static void
1290 folder_refreshed_cb (ModestMailOperation *mail_op,
1291                      TnyFolder *folder,
1292                      gpointer user_data)
1293 {
1294         ModestHeaderViewPrivate *priv;
1295         SetFolderHelper *info;
1296  
1297         info = (SetFolderHelper*) user_data;
1298
1299         priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1300
1301         /* User callback */
1302         if (info->cb)
1303                 info->cb (mail_op, folder, info->user_data);
1304
1305         /* Start the folder count changes observer. We do not need it
1306            before the refresh. Note that the monitor could still be
1307            called for this refresh but now we know that the callback
1308            was previously called */
1309         g_mutex_lock (priv->observers_lock);
1310         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1311         g_mutex_unlock (priv->observers_lock);
1312
1313         /* Notify the observers that the update is over */
1314         g_signal_emit (G_OBJECT (info->header_view), 
1315                        signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1316
1317         /* Allow filtering notifications from now on if the current
1318            folder is still the same (if not then the user has selected
1319            another one to refresh, we should wait until that refresh
1320            finishes) */
1321         if (priv->folder == folder)
1322                 priv->notify_status = TRUE;
1323
1324         /* Frees */
1325         g_object_unref (info->header_view);
1326         g_free (info);
1327 }
1328
1329 static void
1330 refresh_folder_error_handler (ModestMailOperation *mail_op, 
1331                               gpointer user_data)
1332 {
1333         const GError *error = modest_mail_operation_get_error (mail_op);
1334
1335         if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1336             error->code == TNY_IO_ERROR_WRITE ||
1337             error->code == TNY_IO_ERROR_READ) {
1338                 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1339                 /* If the mail op has been cancelled then it's not an error: don't show any message */
1340                 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1341                         gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1342                         modest_platform_information_banner (NULL, NULL, msg);
1343                         g_free (msg);
1344                 }
1345         }
1346 }
1347
1348 void
1349 modest_header_view_set_folder (ModestHeaderView *self, 
1350                                TnyFolder *folder,
1351                                gboolean refresh,
1352                                ModestWindow *progress_window,
1353                                RefreshAsyncUserCallback callback,
1354                                gpointer user_data)
1355 {
1356         ModestHeaderViewPrivate *priv;
1357         
1358         g_return_if_fail (self);
1359
1360         priv =     MODEST_HEADER_VIEW_GET_PRIVATE(self);
1361
1362         if (priv->folder) {
1363                 if (priv->status_timeout) {
1364                         g_source_remove (priv->status_timeout);
1365                         priv->status_timeout = 0;
1366                 }
1367
1368                 g_mutex_lock (priv->observers_lock);
1369                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1370                 g_object_unref (priv->folder);
1371                 priv->folder = NULL;
1372                 g_mutex_unlock (priv->observers_lock);
1373         }
1374
1375         if (folder) {
1376                 GtkTreeSelection *selection;
1377                 SetFolderHelper *info;
1378                 ModestMailOperation *mail_op = NULL;
1379
1380                 /* Set folder in the model */
1381                 modest_header_view_set_folder_intern (self, folder);
1382                 
1383                 /* Pick my reference. Nothing to do with the mail operation */
1384                 priv->folder = g_object_ref (folder);
1385
1386                 /* Do not notify about filterings until the refresh finishes */
1387                 priv->notify_status = FALSE;
1388
1389                 /* Clear the selection if exists */
1390                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1391                 gtk_tree_selection_unselect_all(selection);
1392                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1393
1394                 /* Notify the observers that the update begins */
1395                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1396                                0, TRUE, NULL);
1397
1398                 /* create the helper */
1399                 info = g_malloc0 (sizeof (SetFolderHelper));
1400                 info->header_view = g_object_ref (self);
1401                 info->cb = callback;
1402                 info->user_data = user_data;
1403
1404                 /* Create the mail operation (source will be the parent widget) */
1405                 if (progress_window)
1406                         mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1407                                                                                  refresh_folder_error_handler,
1408                                                                                  NULL, NULL);
1409                 if (refresh) {                  
1410                         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1411                                                          mail_op);
1412                         
1413                         /* Refresh the folder asynchronously */
1414                         modest_mail_operation_refresh_folder (mail_op,
1415                                                               folder,
1416                                                               folder_refreshed_cb,
1417                                                               info);
1418                 } else {
1419                         folder_refreshed_cb (mail_op, folder, info);
1420                 }
1421                 /* Free */
1422                 if (mail_op)
1423                         g_object_unref (mail_op);
1424         } else {
1425                 g_mutex_lock (priv->observers_lock);
1426
1427                 if (priv->monitor) {
1428                         tny_folder_monitor_stop (priv->monitor);
1429                         g_object_unref (G_OBJECT (priv->monitor));
1430                         priv->monitor = NULL;
1431                 }
1432
1433                 if (priv->autoselect_reference) {
1434                         gtk_tree_row_reference_free (priv->autoselect_reference);
1435                         priv->autoselect_reference = NULL;
1436                 }
1437
1438                 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL); 
1439
1440                 modest_header_view_notify_observers(self, NULL, NULL);
1441
1442                 g_mutex_unlock (priv->observers_lock);
1443
1444                 /* Notify the observers that the update is over */
1445                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1446                                0, FALSE, NULL);
1447         }
1448 }
1449
1450 static void
1451 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1452                          GtkTreeViewColumn *column, gpointer userdata)
1453 {
1454         ModestHeaderView *self = NULL;
1455         ModestHeaderViewPrivate *priv = NULL;
1456         GtkTreeIter iter;
1457         GtkTreeModel *model = NULL;
1458         TnyHeader *header = NULL;
1459         TnyHeaderFlags flags;
1460
1461         self = MODEST_HEADER_VIEW (treeview);
1462         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1463
1464         model = gtk_tree_view_get_model (treeview);     
1465         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
1466                 goto frees;
1467
1468         /* get the first selected item */
1469         gtk_tree_model_get (model, &iter,
1470                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1471                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, 
1472                             -1);
1473
1474         /* Dont open DELETED messages */
1475         if (flags & TNY_HEADER_FLAG_DELETED) {
1476                 GtkWidget *win;
1477                 gchar *msg;
1478                 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1479                 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1480                 modest_platform_information_banner (NULL, NULL, msg);
1481                 g_free (msg);
1482                 goto frees;
1483         }
1484
1485         /* Emit signal */
1486         g_signal_emit (G_OBJECT(self), 
1487                        signals[HEADER_ACTIVATED_SIGNAL], 
1488                        0, header, path);
1489
1490         /* Free */
1491  frees:
1492         if (header != NULL) 
1493                 g_object_unref (G_OBJECT (header));     
1494
1495 }
1496
1497 static void
1498 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1499 {
1500         GtkTreeModel *model;
1501         TnyHeader *header = NULL;
1502         GtkTreePath *path = NULL;       
1503         GtkTreeIter iter;
1504         ModestHeaderView *self;
1505         ModestHeaderViewPrivate *priv;
1506         GList *selected = NULL;
1507         
1508         g_return_if_fail (sel);
1509         g_return_if_fail (user_data);
1510         
1511         self = MODEST_HEADER_VIEW (user_data);
1512         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
1513
1514         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1515         if (selected != NULL) 
1516                 path = (GtkTreePath *) selected->data;
1517         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1518                 return; /* msg was _un_selected */
1519
1520         gtk_tree_model_get (model, &iter,
1521                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1522                             &header, -1);
1523
1524         /* Emit signal */
1525         g_signal_emit (G_OBJECT(self), 
1526                        signals[HEADER_SELECTED_SIGNAL], 
1527                        0, header);
1528
1529         g_object_unref (G_OBJECT (header));
1530
1531         /* free all items in 'selected' */
1532         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1533         g_list_free (selected);
1534 }
1535
1536
1537 /* PROTECTED method. It's useful when we want to force a given
1538    selection to reload a msg. For example if we have selected a header
1539    in offline mode, when Modest become online, we want to reload the
1540    message automatically without an user click over the header */
1541 void 
1542 _modest_header_view_change_selection (GtkTreeSelection *selection,
1543                                       gpointer user_data)
1544 {
1545         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1546         g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1547         
1548         on_selection_changed (selection, user_data);
1549 }
1550
1551 static gint
1552 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1553 {
1554         /* HH, LL, NN */
1555         if (p1 == p2)
1556                 return 0;
1557
1558         /* HL HN */
1559         if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1560                 return 1;
1561
1562         /* LH LN */
1563         if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1564                 return -1;
1565
1566         /* NH */
1567         if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1568                 return -1;
1569
1570         /* NL */
1571         return 1;
1572 }
1573
1574 static gint
1575 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1576           gpointer user_data)
1577 {
1578         gint col_id;
1579         gint t1, t2;
1580         gint val1, val2;
1581         gint cmp;
1582 /*      static int counter = 0; */
1583
1584         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1585 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1586         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1587
1588         
1589         switch (col_id) {
1590         case TNY_HEADER_FLAG_ATTACHMENTS:
1591
1592                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1593                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1594                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1595                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1596
1597                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1598                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1599
1600                 return cmp ? cmp : t1 - t2;
1601                 
1602         case TNY_HEADER_FLAG_PRIORITY_MASK: {
1603                 TnyHeader *header1 = NULL, *header2 = NULL;
1604
1605                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1606                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1607                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1608                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1609
1610                 /* This is for making priority values respect the intuitive sort relationship 
1611                  * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1612
1613                 if (header1 && header2) {
1614                         cmp =  compare_priorities (tny_header_get_priority (header1), 
1615                                 tny_header_get_priority (header2));
1616                         g_object_unref (header1);
1617                         g_object_unref (header2);
1618
1619                         return cmp ? cmp : t1 - t2;
1620                 }
1621
1622                 return t1 - t2;
1623         }
1624         default:
1625                 return &iter1 - &iter2; /* oughhhh  */
1626         }
1627 }
1628
1629 static gint
1630 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1631                   gpointer user_data)
1632 {
1633         gint t1, t2;
1634         gchar *val1, *val2;
1635         gint cmp;
1636 /*      static int counter = 0; */
1637
1638         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1639
1640         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1641                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1642         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1643                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1644
1645         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1646                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1647                                              TRUE);
1648         g_free (val1);
1649         g_free (val2);
1650         return cmp;
1651 }
1652
1653 /* Drag and drop stuff */
1654 static void
1655 drag_data_get_cb (GtkWidget *widget, 
1656                   GdkDragContext *context, 
1657                   GtkSelectionData *selection_data, 
1658                   guint info,  
1659                   guint time, 
1660                   gpointer data)
1661 {
1662         ModestHeaderView *self = NULL;
1663         ModestHeaderViewPrivate *priv = NULL;
1664
1665         self = MODEST_HEADER_VIEW (widget);
1666         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1667
1668         /* Set the data. Do not use the current selection because it
1669            could be different than the selection at the beginning of
1670            the d&d */
1671         modest_dnd_selection_data_set_paths (selection_data, 
1672                                              priv->drag_begin_cached_selected_rows);
1673 }
1674
1675 /**
1676  * We're caching the selected rows at the beginning because the
1677  * selection could change between drag-begin and drag-data-get, for
1678  * example if we have a set of rows already selected, and then we
1679  * click in one of them (without SHIFT key pressed) and begin a drag,
1680  * the selection at that moment contains all the selected lines, but
1681  * after dropping the selection, the release event provokes that only
1682  * the row used to begin the drag is selected, so at the end the
1683  * drag&drop affects only one rows instead of all the selected ones.
1684  *
1685  */
1686 static void
1687 drag_begin_cb (GtkWidget *widget, 
1688                GdkDragContext *context, 
1689                gpointer data)
1690 {
1691         ModestHeaderView *self = NULL;
1692         ModestHeaderViewPrivate *priv = NULL;
1693         GtkTreeSelection *selection;
1694
1695         self = MODEST_HEADER_VIEW (widget);
1696         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1697
1698         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1699         priv->drag_begin_cached_selected_rows = 
1700                 gtk_tree_selection_get_selected_rows (selection, NULL);
1701 }
1702
1703 /**
1704  * We use the drag-end signal to clear the cached selection, we use
1705  * this because this allways happens, whether or not the d&d was a
1706  * success
1707  */
1708 static void
1709 drag_end_cb (GtkWidget *widget, 
1710              GdkDragContext *dc, 
1711              gpointer data)
1712 {
1713         ModestHeaderView *self = NULL;
1714         ModestHeaderViewPrivate *priv = NULL;
1715
1716         self = MODEST_HEADER_VIEW (widget);
1717         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1718
1719         /* Free cached data */
1720         g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1721         g_list_free (priv->drag_begin_cached_selected_rows);
1722         priv->drag_begin_cached_selected_rows = NULL;
1723 }
1724
1725 /* Header view drag types */
1726 const GtkTargetEntry header_view_drag_types[] = {
1727         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1728 };
1729
1730 static void
1731 enable_drag_and_drop (GtkWidget *self)
1732 {
1733 #ifdef MODEST_TOOLKIT_HILDON2
1734         return;
1735 #endif
1736         gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1737                              header_view_drag_types,
1738                              G_N_ELEMENTS (header_view_drag_types),
1739                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1740 }
1741
1742 static void
1743 disable_drag_and_drop (GtkWidget *self)
1744 {
1745 #ifdef MODEST_TOOLKIT_HILDON2
1746         return;
1747 #endif
1748         gtk_drag_source_unset (self);
1749 }
1750
1751 static void
1752 setup_drag_and_drop (GtkWidget *self)
1753 {
1754 #ifdef MODEST_TOOLKIT_HILDON2
1755         return;
1756 #endif
1757         enable_drag_and_drop(self);
1758         g_signal_connect(G_OBJECT (self), "drag_data_get",
1759                          G_CALLBACK(drag_data_get_cb), NULL);
1760
1761         g_signal_connect(G_OBJECT (self), "drag_begin",
1762                          G_CALLBACK(drag_begin_cb), NULL);
1763
1764         g_signal_connect(G_OBJECT (self), "drag_end",
1765                          G_CALLBACK(drag_end_cb), NULL);
1766 }
1767
1768 static GtkTreePath *
1769 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1770 {
1771         GtkTreePath *path = NULL;
1772         GtkTreeSelection *sel = NULL;   
1773         GList *rows = NULL;
1774
1775         sel   = gtk_tree_view_get_selection(self);
1776         rows = gtk_tree_selection_get_selected_rows (sel, model);
1777         
1778         if ((rows == NULL) || (g_list_length(rows) != 1))
1779                 goto frees;
1780
1781         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1782         
1783
1784         /* Free */
1785  frees:
1786         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1787         g_list_free(rows);
1788
1789         return path;
1790 }
1791
1792 #ifndef MODEST_TOOLKIT_HILDON2
1793 /*
1794  * This function moves the tree view scroll to the current selected
1795  * row when the widget grabs the focus 
1796  */
1797 static gboolean 
1798 on_focus_in (GtkWidget     *self,
1799              GdkEventFocus *event,
1800              gpointer       user_data)
1801 {
1802         GtkTreeSelection *selection;
1803         GtkTreeModel *model;
1804         GList *selected = NULL;
1805         GtkTreePath *selected_path = NULL;
1806
1807         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1808         if (!model)
1809                 return FALSE;
1810
1811         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1812         /* If none selected yet, pick the first one */
1813         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1814                 GtkTreeIter iter;
1815                 GtkTreePath *path;
1816
1817                 /* Return if the model is empty */
1818                 if (!gtk_tree_model_get_iter_first (model, &iter))
1819                         return FALSE;
1820
1821                 path = gtk_tree_model_get_path (model, &iter);
1822                 gtk_tree_selection_select_path (selection, path);
1823                 gtk_tree_path_free (path);
1824         }
1825
1826         /* Need to get the all the rows because is selection multiple */
1827         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1828         if (selected == NULL) return FALSE;
1829         selected_path = (GtkTreePath *) selected->data;
1830
1831         /* Frees */     
1832         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1833         g_list_free (selected);
1834
1835         return FALSE;
1836 }
1837
1838 static gboolean 
1839 on_focus_out (GtkWidget     *self,
1840              GdkEventFocus *event,
1841              gpointer       user_data)
1842 {
1843
1844         if (!gtk_widget_is_focus (self)) {
1845                 GtkTreeSelection *selection = NULL;
1846                 GList *selected_rows = NULL;
1847                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1848                 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1849                         selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1850                         g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1851                         gtk_tree_selection_unselect_all (selection);
1852                         gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1853                         g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1854                         g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1855                         g_list_free (selected_rows);
1856                 }
1857         }
1858         return FALSE;
1859 }
1860 #endif
1861
1862 static gboolean
1863 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1864 {
1865         enable_drag_and_drop(self);
1866         return FALSE;
1867 }
1868
1869 static gboolean
1870 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1871 {
1872         GtkTreeSelection *selection = NULL;
1873         GtkTreePath *path = NULL;
1874         gboolean already_selected = FALSE, already_opened = FALSE;
1875         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1876
1877         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1878                 GtkTreeIter iter;
1879                 GtkTreeModel *model;
1880
1881                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1882                 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1883
1884                 /* Get header from model */
1885                 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1886                 if (gtk_tree_model_get_iter (model, &iter, path)) {
1887                         GValue value = {0,};
1888                         TnyHeader *header;
1889
1890                         gtk_tree_model_get_value (model, &iter, 
1891                                                   TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1892                                                   &value);
1893                         header = (TnyHeader *) g_value_get_object (&value);
1894                         if (TNY_IS_HEADER (header)) {
1895                                 status = modest_tny_all_send_queues_get_msg_status (header);
1896                                 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (), 
1897                                                                                            header, NULL);
1898                         }
1899                         g_value_unset (&value);
1900                 }
1901         }
1902
1903         /* Enable drag and drop only if the user clicks on a row that
1904            it's already selected. If not, let him select items using
1905            the pointer. If the message is in an OUTBOX and in sending
1906            status disable drag and drop as well */
1907         if (!already_selected ||
1908             status == MODEST_TNY_SEND_QUEUE_SENDING ||
1909             already_opened)
1910                 disable_drag_and_drop(self);
1911
1912         if (path != NULL)
1913                 gtk_tree_path_free(path);
1914
1915         /* If it's already opened then do not let the button-press
1916            event go on because it'll perform a message open because
1917            we're clicking on to an already selected header */
1918         return FALSE;
1919 }
1920
1921 static void
1922 folder_monitor_update (TnyFolderObserver *self, 
1923                        TnyFolderChange *change)
1924 {
1925         ModestHeaderViewPrivate *priv = NULL;
1926         TnyFolderChangeChanged changed;
1927         TnyFolder *folder = NULL;
1928
1929         changed = tny_folder_change_get_changed (change);
1930         
1931         /* Do not notify the observers if the folder of the header
1932            view has changed before this call to the observer
1933            happens */
1934         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1935         folder = tny_folder_change_get_folder (change);
1936         if (folder != priv->folder)
1937                 goto frees;
1938
1939         MODEST_DEBUG_BLOCK (
1940                             if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1941                                     g_print ("ADDED %d/%d (r/t) \n", 
1942                                              tny_folder_change_get_new_unread_count (change),
1943                                              tny_folder_change_get_new_all_count (change));
1944                             if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1945                                     g_print ("ALL COUNT %d\n", 
1946                                              tny_folder_change_get_new_all_count (change));
1947                             if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1948                                     g_print ("UNREAD COUNT %d\n", 
1949                                              tny_folder_change_get_new_unread_count (change));
1950                             if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1951                                     g_print ("EXPUNGED %d/%d (r/t) \n", 
1952                                              tny_folder_change_get_new_unread_count (change),
1953                                              tny_folder_change_get_new_all_count (change));
1954                             if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1955                                     g_print ("FOLDER RENAME\n");
1956                             if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1957                                     g_print ("MSG RECEIVED %d/%d (r/t) \n", 
1958                                              tny_folder_change_get_new_unread_count (change),
1959                                              tny_folder_change_get_new_all_count (change));
1960                             g_print ("---------------------------------------------------\n");
1961                             );
1962
1963         /* Check folder count */
1964         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1965             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1966
1967                 g_mutex_lock (priv->observers_lock);
1968
1969                 /* Emit signal to evaluate how headers changes affects
1970                    to the window view  */
1971                 g_signal_emit (G_OBJECT(self), 
1972                                signals[MSG_COUNT_CHANGED_SIGNAL], 
1973                                0, folder, change);
1974                 
1975                 /* Added or removed headers, so data stored on cliboard are invalid  */
1976                 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1977                         modest_email_clipboard_clear (priv->clipboard);
1978             
1979                 g_mutex_unlock (priv->observers_lock);
1980         }       
1981
1982         /* Free */
1983  frees:
1984         if (folder != NULL)
1985                 g_object_unref (folder);
1986 }
1987
1988 gboolean
1989 modest_header_view_is_empty (ModestHeaderView *self)
1990 {
1991         ModestHeaderViewPrivate *priv;
1992         
1993         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1994         
1995         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1996
1997         return priv->status == HEADER_VIEW_EMPTY;
1998 }
1999
2000 void
2001 modest_header_view_clear (ModestHeaderView *self)
2002 {
2003         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2004         
2005         modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2006 }
2007
2008 void 
2009 modest_header_view_copy_selection (ModestHeaderView *header_view)
2010 {
2011         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2012         
2013         /* Copy selection */
2014         _clipboard_set_selected_data (header_view, FALSE);
2015 }
2016
2017 void 
2018 modest_header_view_cut_selection (ModestHeaderView *header_view)
2019 {
2020         ModestHeaderViewPrivate *priv = NULL;
2021         const gchar **hidding = NULL;
2022         guint i, n_selected;
2023
2024         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2025         
2026         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2027
2028         /* Copy selection */
2029         _clipboard_set_selected_data (header_view, TRUE);
2030
2031         /* Get hidding ids */
2032         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
2033         
2034         /* Clear hidding array created by previous cut operation */
2035         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2036
2037         /* Copy hidding array */
2038         priv->n_selected = n_selected;
2039         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2040         for (i=0; i < n_selected; i++) 
2041                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2042
2043         /* Hide cut headers */
2044         modest_header_view_refilter (header_view);
2045 }
2046
2047
2048  
2049
2050 static void
2051 _clipboard_set_selected_data (ModestHeaderView *header_view,
2052                               gboolean delete)
2053 {
2054         ModestHeaderViewPrivate *priv = NULL;
2055         TnyList *headers = NULL;
2056
2057         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2058         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2059                 
2060         /* Set selected data on clipboard   */
2061         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2062         headers = modest_header_view_get_selected_headers (header_view);
2063         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2064
2065         /* Free */
2066         g_object_unref (headers);
2067 }
2068
2069 typedef struct {
2070         ModestHeaderView *self;
2071         TnyFolder *folder;
2072 } NotifyFilterInfo;
2073
2074 static gboolean
2075 notify_filter_change (gpointer data)
2076 {
2077         NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2078
2079         g_signal_emit (info->self, 
2080                        signals[MSG_COUNT_CHANGED_SIGNAL], 
2081                        0, info->folder, NULL);
2082
2083         return FALSE;
2084 }
2085
2086 static void
2087 notify_filter_change_destroy (gpointer data)
2088 {
2089         NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2090         ModestHeaderViewPrivate *priv;
2091
2092         priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2093         priv->status_timeout = 0;
2094
2095         g_object_unref (info->self);
2096         g_object_unref (info->folder);
2097         g_slice_free (NotifyFilterInfo, info);
2098 }
2099
2100 static gboolean
2101 filter_row (GtkTreeModel *model,
2102             GtkTreeIter *iter,
2103             gpointer user_data)
2104 {
2105         ModestHeaderViewPrivate *priv = NULL;
2106         TnyHeaderFlags flags;
2107         TnyHeader *header = NULL;
2108         guint i;
2109         gchar *id = NULL;
2110         gboolean visible = TRUE;
2111         gboolean found = FALSE;
2112         GValue value = {0,};
2113         HeaderViewStatus old_status;
2114
2115         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2116         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2117
2118         /* Get header from model */
2119         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2120         flags = (TnyHeaderFlags) g_value_get_int (&value);
2121         g_value_unset (&value);
2122         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2123         header = (TnyHeader *) g_value_get_object (&value);
2124         g_value_unset (&value);
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         /* Get message id from header (ensure is a valid id) */
2156         if (!header) {
2157                 visible = FALSE;
2158                 goto frees;
2159         }
2160         
2161         /* Check hiding */
2162         if (priv->hidding_ids != NULL) {
2163                 id = tny_header_dup_message_id (header);
2164                 for (i=0; i < priv->n_selected && !found; i++)
2165                         if (priv->hidding_ids[i] != NULL && id != NULL)
2166                                 found = (!strcmp (priv->hidding_ids[i], id));
2167         
2168                 visible = !found;
2169                 g_free(id);
2170         }
2171
2172  frees:
2173         old_status = priv->status;
2174         priv->status = ((gboolean) priv->status) && !visible;
2175         if ((priv->notify_status) && (priv->status != old_status)) {
2176                 NotifyFilterInfo *info;
2177
2178                 if (priv->status_timeout)
2179                         g_source_remove (priv->status_timeout);
2180
2181                 info = g_slice_new0 (NotifyFilterInfo);
2182                 info->self = g_object_ref (G_OBJECT (user_data));
2183                 info->folder = tny_header_get_folder (header);
2184                 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2185                                                            notify_filter_change,
2186                                                            info,
2187                                                            notify_filter_change_destroy);
2188         }
2189
2190         return visible;
2191 }
2192
2193 static void
2194 _clear_hidding_filter (ModestHeaderView *header_view) 
2195 {
2196         ModestHeaderViewPrivate *priv = NULL;
2197         guint i;
2198         
2199         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
2200         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2201
2202         if (priv->hidding_ids != NULL) {
2203                 for (i=0; i < priv->n_selected; i++) 
2204                         g_free (priv->hidding_ids[i]);
2205                 g_free(priv->hidding_ids);
2206         }       
2207 }
2208
2209 void 
2210 modest_header_view_refilter (ModestHeaderView *header_view)
2211 {
2212         GtkTreeModel *model = NULL;
2213         ModestHeaderViewPrivate *priv = NULL;
2214
2215         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2216         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2217         
2218         /* Hide cut headers */
2219         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2220         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2221                 priv->status = HEADER_VIEW_INIT;
2222                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2223         }
2224 }
2225
2226 /* 
2227  * Called when an account is removed. If I'm showing a folder of the
2228  * account that has been removed then clear the view
2229  */
2230 static void
2231 on_account_removed (TnyAccountStore *self, 
2232                     TnyAccount *account,
2233                     gpointer user_data)
2234 {
2235         ModestHeaderViewPrivate *priv = NULL;
2236
2237         /* Ignore changes in transport accounts */
2238         if (TNY_IS_TRANSPORT_ACCOUNT (account))
2239                 return;
2240
2241         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2242
2243         if (priv->folder) {
2244                 TnyAccount *my_account;
2245
2246                 my_account = tny_folder_get_account (priv->folder);
2247                 if (my_account == account)
2248                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2249                 g_object_unref (my_account);
2250         }
2251 }
2252
2253 void
2254 modest_header_view_add_observer(ModestHeaderView *header_view,
2255                                      ModestHeaderViewObserver *observer)
2256 {
2257         ModestHeaderViewPrivate *priv;
2258         
2259         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2260         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2261
2262         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2263
2264         g_mutex_lock(priv->observer_list_lock);
2265         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2266         g_mutex_unlock(priv->observer_list_lock);
2267 }
2268
2269 void 
2270 modest_header_view_remove_observer(ModestHeaderView *header_view,
2271                                    ModestHeaderViewObserver *observer)
2272 {
2273         ModestHeaderViewPrivate *priv;
2274
2275         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2276         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2277
2278         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2279
2280         g_mutex_lock(priv->observer_list_lock);
2281         priv->observer_list = g_slist_remove(priv->observer_list, observer);
2282         g_mutex_unlock(priv->observer_list_lock);
2283 }
2284
2285 static void 
2286 modest_header_view_notify_observers(ModestHeaderView *header_view,
2287                                     GtkTreeModel *model,
2288                                     const gchar *tny_folder_id)
2289 {
2290         ModestHeaderViewPrivate *priv = NULL;
2291         GSList *iter;
2292         ModestHeaderViewObserver *observer;
2293
2294
2295         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2296         
2297         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2298
2299         g_mutex_lock(priv->observer_list_lock);
2300         iter = priv->observer_list;
2301         while(iter != NULL){
2302                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2303                 modest_header_view_observer_update(observer, model,
2304                                 tny_folder_id);
2305                 iter = g_slist_next(iter);
2306         }
2307         g_mutex_unlock(priv->observer_list_lock);
2308 }
2309
2310 const gchar *
2311 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2312 {
2313         ModestHeaderViewPrivate *priv = NULL;
2314         
2315         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2316         return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2317 }
2318
2319 void 
2320 modest_header_view_set_filter (ModestHeaderView *self,
2321                                ModestHeaderViewFilter filter)
2322 {
2323         ModestHeaderViewPrivate *priv;
2324         GtkTreeModel *filter_model;
2325
2326         g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2327         priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2328
2329         priv->filter |= filter;
2330
2331         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2332         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2333                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
2334         }
2335 }
2336
2337 void 
2338 modest_header_view_unset_filter (ModestHeaderView *self,
2339                                  ModestHeaderViewFilter filter)
2340 {
2341         ModestHeaderViewPrivate *priv;
2342         GtkTreeModel *filter_model;
2343
2344         g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2345         priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2346
2347         priv->filter &= ~filter;
2348
2349         filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2350         if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2351                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));  
2352         }
2353 }
2354
2355 static void 
2356 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2357 {
2358         if (strcmp ("style", spec->name) == 0) {
2359                 update_style (MODEST_HEADER_VIEW (obj));
2360                 gtk_widget_queue_draw (GTK_WIDGET (obj));
2361         } 
2362 }
2363
2364 static void
2365 update_style (ModestHeaderView *self)
2366 {
2367         ModestHeaderViewPrivate *priv;
2368         GdkColor style_color;
2369         GdkColor style_active_color;
2370         PangoAttrList *attr_list;
2371         GtkStyle *style;
2372         PangoAttribute *attr;
2373         GdkColor *new_color;
2374
2375         g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2376         priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2377
2378         /* Set color */
2379
2380         attr_list = pango_attr_list_new ();
2381         if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2382                 gdk_color_parse ("grey", &style_color);
2383         }
2384         attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2385         pango_attr_list_insert (attr_list, attr);
2386         
2387         /* set font */
2388         style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2389                                            (GTK_WIDGET(self)),
2390                                            "SmallSystemFont", NULL,
2391                                            G_TYPE_NONE);
2392         if (style) {
2393                 attr = pango_attr_font_desc_new (pango_font_description_copy
2394                                                  (style->font_desc));
2395                 pango_attr_list_insert (attr_list, attr);
2396
2397                 g_object_set (G_OBJECT (priv->renderer_address),
2398                               "foreground-gdk", &style_color,
2399                               "foreground-set", TRUE,
2400                               "attributes", attr_list,
2401                               NULL);
2402                 g_object_set (G_OBJECT (priv->renderer_date_status),
2403                               "foreground-gdk", &style_color,
2404                               "foreground-set", TRUE,
2405                               "attributes", attr_list,
2406                               NULL);
2407                 pango_attr_list_unref (attr_list);
2408         } else {
2409                 g_object_set (G_OBJECT (priv->renderer_address),
2410                               "foreground-gdk", &style_color,
2411                               "foreground-set", TRUE,
2412                               "scale", PANGO_SCALE_SMALL,
2413                               "scale-set", TRUE,
2414                               NULL);
2415                 g_object_set (G_OBJECT (priv->renderer_date_status),
2416                               "foreground-gdk", &style_color,
2417                               "foreground-set", TRUE,
2418                               "scale", PANGO_SCALE_SMALL,
2419                               "scale-set", TRUE,
2420                               NULL);
2421         }
2422
2423         if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2424                 new_color = gdk_color_copy (&style_active_color);
2425         } else {
2426                 new_color = NULL;
2427         }
2428 #ifdef MODEST_TOOLKIT_HILDON2
2429         g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (new_color != NULL));
2430         g_object_set_data_full (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, new_color, (GDestroyNotify) gdk_color_free);
2431 #endif
2432 }
2433