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