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