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