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