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