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