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