fixes NB#58917
[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 <string.h>
36
37 #include <modest-header-view.h>
38 #include <modest-header-view-priv.h>
39 #include <modest-dnd.h>
40 #include <modest-tny-folder.h>
41
42 #include <modest-main-window.h>
43
44 #include <modest-marshal.h>
45 #include <modest-text-utils.h>
46 #include <modest-icon-names.h>
47 #include <modest-runtime.h>
48 #include "modest-platform.h"
49 #include <modest-hbox-cell-renderer.h>
50 #include <modest-vbox-cell-renderer.h>
51
52 static void modest_header_view_class_init  (ModestHeaderViewClass *klass);
53 static void modest_header_view_init        (ModestHeaderView *obj);
54 static void modest_header_view_finalize    (GObject *obj);
55 static void modest_header_view_dispose     (GObject *obj);
56
57 static void          on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
58                                               GtkTreeViewColumn *column, gpointer userdata);
59
60 static gint          cmp_rows               (GtkTreeModel *tree_model,
61                                              GtkTreeIter *iter1,
62                                              GtkTreeIter *iter2,
63                                              gpointer user_data);
64
65 static gint          cmp_subject_rows       (GtkTreeModel *tree_model,
66                                              GtkTreeIter *iter1,
67                                              GtkTreeIter *iter2,
68                                              gpointer user_data);
69
70 static gboolean     filter_row             (GtkTreeModel *model,
71                                             GtkTreeIter *iter,
72                                             gpointer data);
73
74 static void         on_account_removed     (TnyAccountStore *self, 
75                                             TnyAccount *account,
76                                             gpointer user_data);
77
78 static void          on_selection_changed   (GtkTreeSelection *sel,
79                                              gpointer user_data);
80
81 static void          setup_drag_and_drop    (GtkTreeView *self);
82
83 static GtkTreePath * get_selected_row       (GtkTreeView *self, GtkTreeModel **model);
84
85 static gboolean      on_focus_in            (GtkWidget     *sef,
86                                              GdkEventFocus *event,
87                                              gpointer       user_data);
88
89 static void          folder_monitor_update  (TnyFolderObserver *self, 
90                                              TnyFolderChange *change);
91
92 static void          tny_folder_observer_init (TnyFolderObserverIface *klass);
93
94 static void          _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
95
96 static void          _clear_hidding_filter (ModestHeaderView *header_view);
97
98 static void modest_header_view_notify_observers(
99                 ModestHeaderView *header_view,
100                 GtkTreeModel *model,
101                 const gchar *tny_folder_id);
102
103 static gboolean modest_header_view_on_expose_event(
104                 GtkTreeView *header_view,
105                 GdkEventExpose *event,
106                 gpointer user_data);
107
108 typedef enum {
109         HEADER_VIEW_NON_EMPTY,
110         HEADER_VIEW_EMPTY,
111         HEADER_VIEW_INIT
112 } HeaderViewStatus;
113
114 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
115 struct _ModestHeaderViewPrivate {
116         TnyFolder            *folder;
117         ModestHeaderViewStyle style;
118
119         TnyFolderMonitor     *monitor;
120         GMutex               *observers_lock;
121
122         /*header-view-observer observer*/
123         GMutex *observer_list_lock;
124         GSList *observer_list;
125
126         /* not unref this object, its a singlenton */
127         ModestEmailClipboard *clipboard;
128
129         /* Filter tree model */
130         gchar **hidding_ids;
131         guint   n_selected;
132
133         gint    sort_colid[2][TNY_FOLDER_TYPE_NUM];
134         gint    sort_type[2][TNY_FOLDER_TYPE_NUM];
135
136         gulong  selection_changed_handler;
137         gulong  acc_removed_handler;
138
139         HeaderViewStatus status;
140 };
141
142 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
143 struct _HeadersCountChangedHelper {
144         ModestHeaderView *self;
145         TnyFolderChange  *change;       
146 };
147
148
149 #define MODEST_HEADER_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
150                                                 MODEST_TYPE_HEADER_VIEW, \
151                                                 ModestHeaderViewPrivate))
152
153
154
155 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
156
157 enum {
158         HEADER_SELECTED_SIGNAL,
159         HEADER_ACTIVATED_SIGNAL,
160         ITEM_NOT_FOUND_SIGNAL,
161         MSG_COUNT_CHANGED_SIGNAL,
162         LAST_SIGNAL
163 };
164
165 /* globals */
166 static GObjectClass *parent_class = NULL;
167
168 /* uncomment the following if you have defined any signals */
169 static guint signals[LAST_SIGNAL] = {0};
170
171 GType
172 modest_header_view_get_type (void)
173 {
174         static GType my_type = 0;
175         if (!my_type) {
176                 static const GTypeInfo my_info = {
177                         sizeof(ModestHeaderViewClass),
178                         NULL,           /* base init */
179                         NULL,           /* base finalize */
180                         (GClassInitFunc) modest_header_view_class_init,
181                         NULL,           /* class finalize */
182                         NULL,           /* class data */
183                         sizeof(ModestHeaderView),
184                         1,              /* n_preallocs */
185                         (GInstanceInitFunc) modest_header_view_init,
186                         NULL
187                 };
188
189                 static const GInterfaceInfo tny_folder_observer_info = 
190                 {
191                         (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
192                         NULL,         /* interface_finalize */
193                         NULL          /* interface_data */
194                 };
195                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
196                                                   "ModestHeaderView",
197                                                   &my_info, 0);
198
199                 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
200                                              &tny_folder_observer_info);
201
202
203         }
204         return my_type;
205 }
206
207 static void
208 modest_header_view_class_init (ModestHeaderViewClass *klass)
209 {
210         GObjectClass *gobject_class;
211         gobject_class = (GObjectClass*) klass;
212
213         parent_class            = g_type_class_peek_parent (klass);
214         gobject_class->finalize = modest_header_view_finalize;
215         gobject_class->dispose = modest_header_view_dispose;
216         
217         g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
218         
219         signals[HEADER_SELECTED_SIGNAL] = 
220                 g_signal_new ("header_selected",
221                               G_TYPE_FROM_CLASS (gobject_class),
222                               G_SIGNAL_RUN_FIRST,
223                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
224                               NULL, NULL,
225                               g_cclosure_marshal_VOID__POINTER,
226                               G_TYPE_NONE, 1, G_TYPE_POINTER);
227
228         signals[HEADER_ACTIVATED_SIGNAL] = 
229                 g_signal_new ("header_activated",
230                               G_TYPE_FROM_CLASS (gobject_class),
231                               G_SIGNAL_RUN_FIRST,
232                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
233                               NULL, NULL,
234                               g_cclosure_marshal_VOID__POINTER,
235                               G_TYPE_NONE, 1, G_TYPE_POINTER);
236         
237         
238         signals[ITEM_NOT_FOUND_SIGNAL] = 
239                 g_signal_new ("item_not_found",
240                               G_TYPE_FROM_CLASS (gobject_class),
241                               G_SIGNAL_RUN_FIRST,
242                               G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
243                               NULL, NULL,
244                               g_cclosure_marshal_VOID__INT,
245                               G_TYPE_NONE, 1, G_TYPE_INT);
246
247         signals[MSG_COUNT_CHANGED_SIGNAL] =
248                 g_signal_new ("msg_count_changed",
249                               G_TYPE_FROM_CLASS (gobject_class),
250                               G_SIGNAL_RUN_FIRST,
251                               G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
252                               NULL, NULL,
253                               modest_marshal_VOID__POINTER_POINTER,
254                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
255 }
256
257 static void
258 tny_folder_observer_init (TnyFolderObserverIface *klass)
259 {
260         klass->update_func = folder_monitor_update;
261 }
262
263 static GtkTreeViewColumn*
264 get_new_column (const gchar *name, GtkCellRenderer *renderer,
265                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
266                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
267 {
268         GtkTreeViewColumn *column;
269
270         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
271         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
272
273         gtk_tree_view_column_set_resizable (column, resizable);
274         if (resizable) 
275                 gtk_tree_view_column_set_expand (column, TRUE);
276         
277         if (show_as_text)
278                 gtk_tree_view_column_add_attribute (column, renderer, "text",
279                                                     sort_col_id);
280         if (sort_col_id >= 0)
281                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
282
283         gtk_tree_view_column_set_sort_indicator (column, FALSE);
284         gtk_tree_view_column_set_reorderable (column, TRUE);
285         
286         if (cell_data_func)
287                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
288                                                         user_data, NULL);
289         return column;
290 }
291
292
293 static void
294 remove_all_columns (ModestHeaderView *obj)
295 {
296         GList *columns, *cursor;
297         
298         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
299
300         for (cursor = columns; cursor; cursor = cursor->next)
301                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
302                                              GTK_TREE_VIEW_COLUMN(cursor->data));
303         g_list_free (columns);  
304 }
305
306 gboolean
307 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
308 {
309         GtkTreeModel *tree_filter, *sortable;
310         GtkTreeViewColumn *column=NULL;
311         GtkTreeSelection *selection = NULL;
312         GtkCellRenderer *renderer_msgtype,*renderer_header,
313                 *renderer_attach, *renderer_compact_date_or_status;
314         GtkCellRenderer *renderer_compact_header, *renderer_recpt_box, 
315                 *renderer_subject, *renderer_subject_box, *renderer_recpt,
316                 *renderer_priority;
317         ModestHeaderViewPrivate *priv;
318         GtkTreeViewColumn *compact_column = NULL;
319         const GList *cursor;
320         
321         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self); 
322
323         /* FIXME: check whether these renderers need to be freed */
324         renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
325         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
326         renderer_priority  = gtk_cell_renderer_pixbuf_new ();
327         renderer_header  = gtk_cell_renderer_text_new ();
328
329         renderer_compact_header = modest_vbox_cell_renderer_new ();
330         renderer_recpt_box = modest_hbox_cell_renderer_new ();
331         renderer_subject_box = modest_hbox_cell_renderer_new ();
332         renderer_recpt = gtk_cell_renderer_text_new ();
333         renderer_subject = gtk_cell_renderer_text_new ();
334         renderer_compact_date_or_status  = gtk_cell_renderer_text_new ();
335
336         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
337         g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
338         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
339         g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
340         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
341         g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
342         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
343         g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
344         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
345         g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
346         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
347         g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
348         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
349         g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
350
351         g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
352         gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
353         gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
354         g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
355         g_object_set(G_OBJECT(renderer_header),
356                      "ellipsize", PANGO_ELLIPSIZE_END,
357                      NULL);
358         g_object_set (G_OBJECT (renderer_subject),
359                       "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
360                       NULL);
361         g_object_set (G_OBJECT (renderer_recpt),
362                       "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
363                       NULL);
364         g_object_set(G_OBJECT(renderer_compact_date_or_status),
365                      "xalign", 1.0, "yalign", 0.0,
366                      NULL);
367         g_object_set (G_OBJECT (renderer_priority),
368                       "yalign", 1.0, NULL);
369         g_object_set (G_OBJECT (renderer_attach),
370                       "yalign", 0.0, NULL);
371
372         gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
373         gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
374         gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
375         
376         remove_all_columns (self);
377
378         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
379         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
380         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
381         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
382
383         /* Add new columns */
384         for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
385                 ModestHeaderViewColumn col =
386                         (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
387                 
388                 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
389                         g_printerr ("modest: invalid column %d in column list\n", col);
390                         continue;
391                 }
392                 
393                 switch (col) {
394                         
395                 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
396                         column = get_new_column (_("M"), renderer_msgtype, FALSE,
397                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
398                                                  FALSE,
399                                                  (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
400                                                  NULL);
401                         gtk_tree_view_column_set_fixed_width (column, 45);
402                         break;
403
404                 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
405                         column = get_new_column (_("A"), renderer_attach, FALSE,
406                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
407                                                  FALSE,
408                                                  (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
409                                                  NULL);
410                         gtk_tree_view_column_set_fixed_width (column, 45);
411                         break;
412
413                         
414                 case MODEST_HEADER_VIEW_COLUMN_FROM:
415                         column = get_new_column (_("From"), renderer_header, TRUE,
416                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
417                                                  TRUE,
418                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
419                                                  GINT_TO_POINTER(TRUE));
420                         break;
421
422                 case MODEST_HEADER_VIEW_COLUMN_TO:
423                         column = get_new_column (_("To"), renderer_header, TRUE,
424                                                  TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
425                                                  TRUE,
426                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
427                                                  GINT_TO_POINTER(FALSE));
428                         break;
429                         
430                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
431                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
432                                                      TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
433                                                      FALSE,
434                                                      (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
435                                                      GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
436                         compact_column = column;
437                         break;
438
439                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
440                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
441                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
442                                                  FALSE,
443                                                  (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
444                                                  GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
445                                                                  MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
446                                                                  MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
447                         compact_column = column;
448                         break;
449
450                         
451                 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
452                         column = get_new_column (_("Subject"), renderer_header, TRUE,
453                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
454                                                  TRUE,
455                                                  (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
456                                                  NULL);
457                         break;
458                         
459                 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
460                         column = get_new_column (_("Received"), renderer_header, TRUE,
461                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
462                                                  TRUE,
463                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
464                                                  GINT_TO_POINTER(TRUE));
465                         break;
466                         
467                 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:  
468                         column = get_new_column (_("Sent"), renderer_header, TRUE,
469                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
470                                                  TRUE,
471                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
472                                                  GINT_TO_POINTER(FALSE));
473                         break;
474                         
475                 case MODEST_HEADER_VIEW_COLUMN_SIZE:
476                         column = get_new_column (_("Size"), renderer_header, TRUE,
477                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
478                                                  FALSE,
479                                                  (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
480                                                  NULL); 
481                         break;
482                 case MODEST_HEADER_VIEW_COLUMN_STATUS:
483                         column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
484                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
485                                                  FALSE,
486                                                  (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
487                                                  NULL); 
488                         break;
489
490                 default:
491                         g_return_val_if_reached(FALSE);
492                 }
493
494                 /* we keep the column id around */
495                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
496                                    GINT_TO_POINTER(col));
497                 
498                 /* we need this ptr when sorting the rows */
499                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
500                                    self);
501                 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);              
502         }               
503
504         if (sortable) {
505                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
506                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
507                                                  (GtkTreeIterCompareFunc) cmp_rows,
508                                                  compact_column, NULL);
509                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
510                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
511                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
512                                                  compact_column, NULL);
513         }
514
515
516         return TRUE;
517 }
518
519 static void
520 modest_header_view_init (ModestHeaderView *obj)
521 {
522         ModestHeaderViewPrivate *priv;
523         guint i, j;
524
525         priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj); 
526
527         priv->folder  = NULL;
528
529         priv->monitor        = NULL;
530         priv->observers_lock = g_mutex_new ();
531
532         priv->status  = HEADER_VIEW_INIT;
533
534         priv->observer_list_lock = g_mutex_new();
535         priv->observer_list = NULL;
536
537         priv->clipboard = modest_runtime_get_email_clipboard ();
538         priv->hidding_ids = NULL;
539         priv->n_selected = 0;
540         priv->selection_changed_handler = 0;
541         priv->acc_removed_handler = 0;
542
543         /* Sort parameters */
544         for (j=0; j < 2; j++) {
545                 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
546                         priv->sort_colid[j][i] = -1;
547                         priv->sort_type[j][i] = GTK_SORT_DESCENDING;
548                 }                       
549         }
550
551         setup_drag_and_drop (GTK_TREE_VIEW (obj));
552 }
553
554 static void
555 modest_header_view_dispose (GObject *obj)
556 {
557         ModestHeaderView        *self;
558         ModestHeaderViewPrivate *priv;
559         GtkTreeSelection *sel;
560         
561         self = MODEST_HEADER_VIEW(obj);
562         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
563
564         /* Free in the dispose to avoid unref cycles */
565         if (priv->folder) {
566                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
567                 g_object_unref (G_OBJECT (priv->folder));
568                 priv->folder = NULL;
569         }
570
571         /* We need to do this here in the dispose because the
572            selection won't exist when finalizing */
573         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
574         if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
575                 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
576                 priv->selection_changed_handler = 0;
577         }
578
579         G_OBJECT_CLASS(parent_class)->dispose (obj);
580 }
581
582 static void
583 modest_header_view_finalize (GObject *obj)
584 {
585         ModestHeaderView        *self;
586         ModestHeaderViewPrivate *priv;
587         
588         self = MODEST_HEADER_VIEW(obj);
589         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
590
591         if (g_signal_handler_is_connected (modest_runtime_get_account_store (), 
592                                            priv->acc_removed_handler)) {
593                 g_signal_handler_disconnect (modest_runtime_get_account_store (), 
594                                              priv->acc_removed_handler);
595         }
596
597         /* There is no need to lock because there should not be any
598          * reference to self now. */
599         g_mutex_free(priv->observer_list_lock);
600         g_slist_free(priv->observer_list);
601
602         g_mutex_lock (priv->observers_lock);
603         if (priv->monitor) {
604                 tny_folder_monitor_stop (priv->monitor);
605                 g_object_unref (G_OBJECT (priv->monitor));
606         }
607         g_mutex_unlock (priv->observers_lock);
608         g_mutex_free (priv->observers_lock);
609
610         /* Clear hidding array created by cut operation */
611         _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
612
613         G_OBJECT_CLASS(parent_class)->finalize (obj);
614 }
615
616
617 GtkWidget*
618 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
619 {
620         GObject *obj;
621         GtkTreeSelection *sel;
622         ModestHeaderView *self;
623         ModestHeaderViewPrivate *priv;
624         
625         g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
626                               NULL);
627         
628         obj  = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
629         self = MODEST_HEADER_VIEW(obj);
630         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
631         
632         modest_header_view_set_style   (self, style);
633 /*      modest_header_view_set_folder (self, NULL, NULL, NULL); */
634
635         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
636         gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
637         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
638         
639         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
640                                       TRUE); /* alternating row colors */
641
642         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self)); 
643         priv->selection_changed_handler =
644                 g_signal_connect_after (sel, "changed",
645                                         G_CALLBACK(on_selection_changed), self);
646         
647         g_signal_connect (self, "row-activated",
648                           G_CALLBACK (on_header_row_activated), NULL);
649
650         g_signal_connect (self, "focus-in-event",
651                           G_CALLBACK(on_focus_in), NULL);
652         
653         priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
654                                                       "account_removed",
655                                                       G_CALLBACK (on_account_removed),
656                                                       self);
657
658         g_signal_connect (self, "expose-event",
659                         G_CALLBACK(modest_header_view_on_expose_event),
660                         NULL);
661
662         return GTK_WIDGET(self);
663 }
664
665
666 guint
667 modest_header_view_count_selected_headers (ModestHeaderView *self)
668 {
669         GtkTreeSelection *sel;
670         guint selected_rows;
671
672         g_return_val_if_fail (self, 0);
673         
674         /* Get selection object and check selected rows count */
675         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
676         selected_rows = gtk_tree_selection_count_selected_rows (sel);
677         
678         return selected_rows;
679 }
680
681 gboolean
682 modest_header_view_has_selected_headers (ModestHeaderView *self)
683 {
684         GtkTreeSelection *sel;
685         gboolean empty;
686
687         g_return_val_if_fail (self, FALSE);
688         
689         /* Get selection object and check selected rows count */
690         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
691         empty = gtk_tree_selection_count_selected_rows (sel) == 0;
692         
693         return !empty;
694 }
695
696
697 TnyList * 
698 modest_header_view_get_selected_headers (ModestHeaderView *self)
699 {
700         GtkTreeSelection *sel;
701         ModestHeaderViewPrivate *priv;
702         TnyList *header_list = NULL;
703         TnyHeader *header;
704         GList *list, *tmp = NULL;
705         GtkTreeModel *tree_model = NULL;
706         GtkTreeIter iter;
707
708         g_return_val_if_fail (self, NULL);
709         
710         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
711
712         /* Get selected rows */
713         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
714         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
715
716         if (list) {
717                 header_list = tny_simple_list_new();
718
719                 list = g_list_reverse (list);
720                 tmp = list;
721                 while (tmp) {                   
722                         /* get header from selection */
723                         gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
724                         gtk_tree_model_get (tree_model, &iter,
725                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
726                                             &header, -1);
727                         /* Prepend to list */
728                         tny_list_prepend (header_list, G_OBJECT (header));
729                         g_object_unref (G_OBJECT (header));
730
731                         tmp = g_list_next (tmp);
732                 }
733                 /* Clean up*/
734                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
735                 g_list_free (list);
736         }
737         return header_list;
738 }
739
740
741 /* scroll our list view so the selected item is visible */
742 static void
743 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
744 {
745 #ifdef MODEST_PLATFORM_GNOME 
746
747         GtkTreePath *selected_path;
748         GtkTreePath *start, *end;
749         
750         GtkTreeModel *model;
751         
752         model         = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
753         selected_path = gtk_tree_model_get_path (model, iter);
754
755         start = gtk_tree_path_new ();
756         end   = gtk_tree_path_new ();
757
758         gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
759
760         if (gtk_tree_path_compare (selected_path, start) < 0 ||
761             gtk_tree_path_compare (end, selected_path) < 0)
762                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
763                                               selected_path, NULL, TRUE,
764                                               up ? 0.0 : 1.0,
765                                               up ? 0.0 : 1.0);
766         gtk_tree_path_free (selected_path);
767         gtk_tree_path_free (start);
768         gtk_tree_path_free (end);
769
770 #endif /* MODEST_PLATFORM_GNOME */
771 }
772
773
774 void 
775 modest_header_view_select_next (ModestHeaderView *self)
776 {
777         GtkTreeSelection *sel;
778         GtkTreeIter iter;
779         GtkTreeModel *model;
780         GtkTreePath *path;
781
782         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
783         path = get_selected_row (GTK_TREE_VIEW(self), &model);
784         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
785                 /* Unselect previous path */
786                 gtk_tree_selection_unselect_path (sel, path);
787                 
788                 /* Move path down and selects new one  */
789                 if (gtk_tree_model_iter_next (model, &iter)) {
790                         gtk_tree_selection_select_iter (sel, &iter);
791                         scroll_to_selected (self, &iter, FALSE);        
792                 }
793                 gtk_tree_path_free(path);
794         }
795         
796 }
797
798 void 
799 modest_header_view_select_prev (ModestHeaderView *self)
800 {
801         GtkTreeSelection *sel;
802         GtkTreeIter iter;
803         GtkTreeModel *model;
804         GtkTreePath *path;
805
806         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
807         path = get_selected_row (GTK_TREE_VIEW(self), &model);
808         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
809                 /* Unselect previous path */
810                 gtk_tree_selection_unselect_path (sel, path);
811
812                 /* Move path up */
813                 if (gtk_tree_path_prev (path)) {
814                         gtk_tree_model_get_iter (model, &iter, path);
815                         
816                         /* Select the new one */
817                         gtk_tree_selection_select_iter (sel, &iter);
818                         scroll_to_selected (self, &iter, TRUE); 
819
820                 }
821                 gtk_tree_path_free (path);
822         }
823 }
824
825 GList*
826 modest_header_view_get_columns (ModestHeaderView *self)
827 {
828         g_return_val_if_fail (self, FALSE);
829         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self)); 
830 }
831
832
833
834 gboolean
835 modest_header_view_set_style (ModestHeaderView *self,
836                               ModestHeaderViewStyle style)
837 {
838         ModestHeaderViewPrivate *priv;
839         gboolean show_col_headers = FALSE;
840         ModestHeaderViewStyle old_style;
841         
842         g_return_val_if_fail (self, FALSE);
843         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
844                               FALSE);
845
846         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
847         if (priv->style == style)
848                 return TRUE; /* nothing to do */
849         
850         switch (style) {
851         case MODEST_HEADER_VIEW_STYLE_DETAILS:
852                 show_col_headers = TRUE;
853                 break;
854         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
855                 break;
856         default:
857                 g_return_val_if_reached (FALSE);
858         }
859         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
860         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);    
861
862         old_style   = priv->style;
863         priv->style = style;
864
865         return TRUE;
866 }
867
868
869 ModestHeaderViewStyle
870 modest_header_view_get_style (ModestHeaderView *self)
871 {
872         g_return_val_if_fail (self, FALSE);
873         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
874 }
875
876 /* This is used to automatically select the first header if the user
877  * has not selected any header yet. Fixes NB#58917.
878  */
879 static gboolean modest_header_view_on_expose_event(
880                 GtkTreeView *header_view,
881                 GdkEventExpose *event,
882                 gpointer user_data)
883 {
884         GtkTreeSelection *sel;
885         GtkTreeModel *model;
886         GtkTreeIter tree_iter;
887
888         model = gtk_tree_view_get_model(header_view);
889
890         sel = gtk_tree_view_get_selection(header_view);
891         if(!gtk_tree_selection_count_selected_rows(sel))
892                 if (gtk_tree_model_get_iter_first(model, &tree_iter))
893                         gtk_tree_selection_select_iter(sel, &tree_iter);
894
895         return FALSE;
896 }
897
898 /* 
899  * This function sets a sortable model in the header view. It's just
900  * used for developing purposes, because it only does a
901  * gtk_tree_view_set_model
902  */
903 static void
904 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
905 {
906 /*      GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
907 /*      if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
908 /*              GtkTreeModel *old_model; */
909 /*              ModestHeaderViewPrivate *priv; */
910 /*              priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
911 /*              old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
912
913 /*              /\* Set new model *\/ */
914 /*              gtk_tree_view_set_model (header_view, model); */
915 /*      } else */
916         gtk_tree_view_set_model (header_view, model);
917 }
918
919 TnyFolder*
920 modest_header_view_get_folder (ModestHeaderView *self)
921 {
922         ModestHeaderViewPrivate *priv;
923         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
924
925         if (priv->folder)
926                 g_object_ref (priv->folder);
927
928         return priv->folder;
929 }
930
931 static void
932 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
933 {
934         TnyFolderType type;
935         TnyList *headers;
936         ModestHeaderViewPrivate *priv;
937         GList *cols, *cursor;
938         GtkTreeModel *filter_model, *sortable; 
939         guint sort_colid;
940         GtkSortType sort_type;
941
942         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
943
944         headers = TNY_LIST (tny_gtk_header_list_model_new ());
945
946         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
947                                               folder, FALSE);
948
949         /* Add IDLE observer (monitor) and another folder observer for
950            new messages (self) */
951         g_mutex_lock (priv->observers_lock);
952         if (priv->monitor) {
953                 tny_folder_monitor_stop (priv->monitor);
954                 g_object_unref (G_OBJECT (priv->monitor));
955         }
956         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
957         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
958         tny_folder_monitor_start (priv->monitor);
959         g_mutex_unlock (priv->observers_lock);
960
961         sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
962         g_object_unref (G_OBJECT (headers));
963
964         /* Init filter_row function to examine empty status */
965         priv->status  = HEADER_VIEW_INIT;
966
967         /* Create a tree model filter to hide and show rows for cut operations  */
968         filter_model = gtk_tree_model_filter_new (sortable, NULL);
969         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
970                                                 filter_row,
971                                                 self,
972                                                 NULL);
973         g_object_unref (G_OBJECT (sortable));
974
975         /* install our special sorting functions */
976         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
977
978         /* Restore sort column id */
979         if (cols) {
980                 type  = modest_tny_folder_guess_folder_type (folder);
981                 sort_colid = modest_header_view_get_sort_column_id (self, type); 
982                 sort_type = modest_header_view_get_sort_type (self, type); 
983                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
984                                                       sort_colid,
985                                                       sort_type);
986                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
987                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
988                                                  (GtkTreeIterCompareFunc) cmp_rows,
989                                                  cols->data, NULL);
990                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
991                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
992                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
993                                                  cols->data, NULL);
994         }
995
996         /* Set new model */
997         modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
998         modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
999                         tny_folder_get_id(folder));
1000         g_object_unref (G_OBJECT (filter_model));
1001 /*      modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1002 /*      g_object_unref (G_OBJECT (sortable)); */
1003
1004         /* Free */
1005         g_list_free (cols);
1006 }
1007
1008 void
1009 modest_header_view_sort_by_column_id (ModestHeaderView *self, 
1010                                       guint sort_colid,
1011                                       GtkSortType sort_type)
1012 {
1013         ModestHeaderViewPrivate *priv = NULL;
1014         GtkTreeModel *tree_filter, *sortable = NULL; 
1015         TnyFolderType type;
1016
1017         /* Get model and private data */
1018         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);            
1019         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1020         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1021 /*      sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1022         
1023         /* Sort tree model */
1024         type  = modest_tny_folder_guess_folder_type (priv->folder);
1025         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1026                                               sort_colid,
1027                                               sort_type);
1028         /* Store new sort parameters */
1029         modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1030
1031         /* Save GConf parameters */
1032 /*      modest_widget_memory_save (modest_runtime_get_conf(), */
1033 /*                                 G_OBJECT(self), "header-view"); */
1034         
1035 }
1036
1037 void
1038 modest_header_view_set_sort_params (ModestHeaderView *self, 
1039                                     guint sort_colid, 
1040                                     GtkSortType sort_type,
1041                                     TnyFolderType type)
1042 {
1043         ModestHeaderViewPrivate *priv;
1044         ModestHeaderViewStyle style;
1045
1046         style = modest_header_view_get_style   (self);
1047         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1048
1049         priv->sort_colid[style][type] = sort_colid;
1050         priv->sort_type[style][type] = sort_type;
1051 }
1052
1053 gint
1054 modest_header_view_get_sort_column_id (ModestHeaderView *self, 
1055                                        TnyFolderType type)
1056 {
1057         ModestHeaderViewPrivate *priv;
1058         ModestHeaderViewStyle style;
1059
1060         style = modest_header_view_get_style   (self);
1061         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1062
1063         return priv->sort_colid[style][type];
1064 }
1065
1066 GtkSortType
1067 modest_header_view_get_sort_type (ModestHeaderView *self, 
1068                                   TnyFolderType type)
1069 {
1070         ModestHeaderViewPrivate *priv;
1071         ModestHeaderViewStyle style;
1072
1073         style = modest_header_view_get_style   (self);
1074         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1075
1076         return priv->sort_type[style][type];
1077 }
1078
1079 typedef struct {
1080         ModestHeaderView *header_view;
1081         RefreshAsyncUserCallback cb;
1082         gpointer user_data;
1083 } SetFolderHelper;
1084
1085 static void
1086 folder_refreshed_cb (ModestMailOperation *mail_op,
1087                      TnyFolder *folder,
1088                      gpointer user_data)
1089 {
1090         ModestHeaderViewPrivate *priv;
1091         SetFolderHelper *info;
1092  
1093         info = (SetFolderHelper*) user_data;
1094
1095         priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1096
1097         /* User callback */
1098         if (info->cb)
1099                 info->cb (mail_op, folder, info->user_data);
1100
1101         /* Start the folder count changes observer. We do not need it
1102            before the refresh. Note that the monitor could still be
1103            called for this refresh but now we know that the callback
1104            was previously called */
1105         g_mutex_lock (priv->observers_lock);
1106         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1107         g_mutex_unlock (priv->observers_lock);
1108
1109         /* Frees */
1110         g_free (info);
1111 }
1112
1113 void
1114 modest_header_view_set_folder (ModestHeaderView *self, 
1115                                TnyFolder *folder,
1116                                RefreshAsyncUserCallback callback,
1117                                gpointer user_data)
1118 {
1119         ModestHeaderViewPrivate *priv;
1120         ModestWindowMgr *mgr = NULL;
1121         GObject *source = NULL;
1122         SetFolderHelper *info;
1123  
1124         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1125
1126         if (priv->folder) {
1127                 g_mutex_lock (priv->observers_lock);
1128                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1129                 g_object_unref (priv->folder);
1130                 priv->folder = NULL;
1131                 g_mutex_unlock (priv->observers_lock);
1132         }
1133
1134         if (folder) {
1135                 ModestMailOperation *mail_op = NULL;
1136                 GtkTreeSelection *selection;
1137
1138                 /* Get main window to use it as source of mail operation */
1139                 mgr = modest_runtime_get_window_mgr ();
1140                 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1141
1142                 /* Set folder in the model */
1143                 modest_header_view_set_folder_intern (self, folder);
1144
1145                 /* Pick my reference. Nothing to do with the mail operation */
1146                 priv->folder = g_object_ref (folder);
1147
1148                 /* no message selected */
1149                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1150
1151                 info = g_malloc0 (sizeof(SetFolderHelper));
1152                 info->header_view = self;
1153                 info->cb = callback;
1154                 info->user_data = user_data;
1155
1156                 /* bug 57631: Clear the selection if exists */
1157                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1158                 gtk_tree_selection_unselect_all(selection);
1159
1160                 /* Create the mail operation (source will be the parent widget) */
1161                 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1162                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1163                                                  mail_op);
1164
1165                 /* Refresh the folder asynchronously */
1166                 modest_mail_operation_refresh_folder (mail_op,
1167                                                       folder,
1168                                                       folder_refreshed_cb,
1169                                                       info);
1170
1171                 /* Free */
1172                 g_object_unref (mail_op);
1173         } else {
1174                 g_mutex_lock (priv->observers_lock);
1175
1176                 if (priv->monitor) {
1177                         tny_folder_monitor_stop (priv->monitor);
1178                         g_object_unref (G_OBJECT (priv->monitor));
1179                         priv->monitor = NULL;
1180                 }
1181                 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL); 
1182
1183                 modest_header_view_notify_observers(self, NULL, NULL);
1184
1185                 g_mutex_unlock (priv->observers_lock);
1186         }
1187 }
1188
1189 static void
1190 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1191                          GtkTreeViewColumn *column, gpointer userdata)
1192 {
1193         ModestHeaderView *self = NULL;
1194         ModestHeaderViewPrivate *priv = NULL;
1195         GtkTreeIter iter;
1196         GtkTreeModel *model = NULL;
1197         TnyHeader *header = NULL;
1198         TnyHeaderFlags flags;
1199
1200         self = MODEST_HEADER_VIEW (treeview);
1201         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1202
1203         model = gtk_tree_view_get_model (treeview);     
1204         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
1205                 goto frees;
1206
1207         /* get the first selected item */
1208         gtk_tree_model_get (model, &iter,
1209                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1210                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, 
1211                             -1);
1212
1213         /* Dont open DELETED messages */
1214         if (flags & TNY_HEADER_FLAG_DELETED) {
1215                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1216                 goto frees;
1217         }
1218
1219         /* Emit signal */
1220         g_signal_emit (G_OBJECT(self), 
1221                        signals[HEADER_ACTIVATED_SIGNAL], 
1222                        0, header);
1223
1224         /* Free */
1225  frees:
1226         if (header != NULL) 
1227                 g_object_unref (G_OBJECT (header));     
1228
1229 }
1230
1231 static void
1232 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1233 {
1234         GtkTreeModel *model;
1235         TnyHeader *header = NULL;
1236         GtkTreePath *path = NULL;       
1237         GtkTreeIter iter;
1238         ModestHeaderView *self;
1239         ModestHeaderViewPrivate *priv;
1240         GList *selected = NULL;
1241         
1242         g_return_if_fail (sel);
1243         g_return_if_fail (user_data);
1244         
1245         self = MODEST_HEADER_VIEW (user_data);
1246         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
1247
1248         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1249         if (selected != NULL) 
1250                 path = (GtkTreePath *) selected->data;
1251         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1252                 return; /* msg was _un_selected */
1253
1254         gtk_tree_model_get (model, &iter,
1255                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1256                             &header, -1);
1257
1258         /* Emit signal */
1259         g_signal_emit (G_OBJECT(self), 
1260                        signals[HEADER_SELECTED_SIGNAL], 
1261                        0, header);
1262
1263         g_object_unref (G_OBJECT (header));
1264
1265         /* free all items in 'selected' */
1266         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1267         g_list_free (selected);
1268 }
1269
1270
1271 /* PROTECTED method. It's useful when we want to force a given
1272    selection to reload a msg. For example if we have selected a header
1273    in offline mode, when Modest become online, we want to reload the
1274    message automatically without an user click over the header */
1275 void 
1276 _modest_header_view_change_selection (GtkTreeSelection *selection,
1277                                       gpointer user_data)
1278 {
1279         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1280         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1281
1282         on_selection_changed (selection, user_data);
1283 }
1284
1285 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1286 {
1287         p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1288         p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1289         if (p1 == 0) 
1290                 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1291         if (p2 == 0) 
1292                 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1293         return p1 - p2;
1294 }
1295
1296 static gint
1297 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1298           gpointer user_data)
1299 {
1300         gint col_id;
1301         gint t1, t2;
1302         gint val1, val2;
1303         gint cmp;
1304 /*      static int counter = 0; */
1305
1306         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1307 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1308         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1309
1310         
1311         switch (col_id) {
1312         case TNY_HEADER_FLAG_ATTACHMENTS:
1313
1314                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1315                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1316                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1317                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1318
1319                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1320                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1321
1322                 return cmp ? cmp : t1 - t2;
1323                 
1324         case TNY_HEADER_FLAG_PRIORITY:
1325                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1326                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1327                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1328                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1329
1330                 /* This is for making priority values respect the intuitive sort relationship 
1331                  * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1332                 cmp =  compare_priorities (val1, val2);
1333
1334                 return cmp ? cmp : t1 - t2;
1335
1336         default:
1337                 return &iter1 - &iter2; /* oughhhh  */
1338         }
1339 }
1340
1341 static gint
1342 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1343                   gpointer user_data)
1344 {
1345         gint t1, t2;
1346         gchar *val1, *val2;
1347         gint cmp;
1348 /*      static int counter = 0; */
1349
1350         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1351
1352         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1353                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1354         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1355                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1356
1357         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1358                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1359                                              TRUE);
1360         g_free (val1);
1361         g_free (val2);
1362         return cmp;
1363 }
1364
1365 /* Drag and drop stuff */
1366 static void
1367 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context, 
1368                   GtkSelectionData *selection_data, 
1369                   guint info,  guint time, gpointer data)
1370 {
1371         GtkTreeModel *model = NULL;
1372         GtkTreeIter iter;
1373         GtkTreePath *source_row = NULL;
1374 /*      GtkTreeSelection *sel = NULL;*/
1375         
1376         source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1377         
1378         if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1379
1380         switch (info) {
1381         case MODEST_HEADER_ROW:
1382                 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1383                 break;
1384         case MODEST_MSG: {
1385                 TnyHeader *hdr = NULL;
1386                 gtk_tree_model_get (model, &iter,
1387                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1388                                     -1);
1389                 if (hdr) {
1390                         g_object_unref (G_OBJECT(hdr));
1391                 }
1392                 break;
1393         }
1394         default:
1395                 g_message ("%s: default switch case.", __FUNCTION__);
1396         }
1397
1398         /* commenting out the next, fixes NB#62963 */
1399 #if 0
1400         /* Set focus on next header */
1401         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1402         gtk_tree_path_next (source_row);
1403         gtk_tree_selection_select_path (sel, source_row);
1404
1405         gtk_tree_path_free (source_row);
1406 #endif
1407 }
1408
1409 /* Header view drag types */
1410 const GtkTargetEntry header_view_drag_types[] = {
1411         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1412         { "text/uri-list",      0,                   MODEST_MSG }, 
1413 };
1414
1415 static void
1416 setup_drag_and_drop (GtkTreeView *self)
1417 {
1418         gtk_drag_source_set (GTK_WIDGET (self),
1419                              GDK_BUTTON1_MASK,
1420                              header_view_drag_types,
1421                              G_N_ELEMENTS (header_view_drag_types),
1422                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1423
1424         g_signal_connect(G_OBJECT (self), "drag_data_get",
1425                          G_CALLBACK(drag_data_get_cb), NULL);
1426 }
1427
1428 static GtkTreePath *
1429 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1430 {
1431         GtkTreePath *path = NULL;
1432         GtkTreeSelection *sel = NULL;   
1433         GList *rows = NULL;
1434
1435         sel   = gtk_tree_view_get_selection(self);
1436         rows = gtk_tree_selection_get_selected_rows (sel, model);
1437         
1438         if ((rows == NULL) || (g_list_length(rows) != 1))
1439                 goto frees;
1440
1441         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1442         
1443
1444         /* Free */
1445  frees:
1446         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1447         g_list_free(rows);
1448
1449         return path;
1450 }
1451
1452 /*
1453  * This function moves the tree view scroll to the current selected
1454  * row when the widget grabs the focus 
1455  */
1456 static gboolean 
1457 on_focus_in (GtkWidget     *self,
1458              GdkEventFocus *event,
1459              gpointer       user_data)
1460 {
1461         GtkTreeSelection *selection;
1462         GtkTreeModel *model;
1463         GList *selected = NULL;
1464         GtkTreePath *selected_path = NULL;
1465
1466         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1467         if (!model)
1468                 return FALSE;
1469
1470         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1471         /* If none selected yet, pick the first one */
1472         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1473                 GtkTreeIter iter;
1474                 GtkTreePath *path;
1475
1476                 /* Return if the model is empty */
1477                 if (!gtk_tree_model_get_iter_first (model, &iter))
1478                         return FALSE;
1479
1480                 path = gtk_tree_model_get_path (model, &iter);
1481                 gtk_tree_selection_select_path (selection, path);
1482                 gtk_tree_path_free (path);
1483         }
1484
1485         /* Need to get the all the rows because is selection multiple */
1486         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1487         if (selected == NULL) return FALSE;
1488         selected_path = (GtkTreePath *) selected->data;
1489
1490         /* Check if we need to scroll */
1491         #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1492         GtkTreePath *start_path = NULL;
1493         GtkTreePath *end_path = NULL;
1494         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1495                                              &start_path,
1496                                              &end_path)) {
1497
1498                 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1499                     (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1500
1501                         /* Scroll to first path */
1502                         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1503                                                       selected_path,
1504                                                       NULL,
1505                                                       TRUE,
1506                                                       0.5,
1507                                                       0.0);
1508                 }
1509         }
1510         if (start_path)
1511                 gtk_tree_path_free (start_path);
1512         if (end_path)
1513                 gtk_tree_path_free (end_path);
1514
1515         #endif /* GTK_CHECK_VERSION */
1516
1517         /* Frees */     
1518         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1519         g_list_free (selected);
1520
1521         return FALSE;
1522 }
1523
1524 static void
1525 idle_notify_headers_count_changed_destroy (gpointer data)
1526 {
1527         HeadersCountChangedHelper *helper = NULL;
1528
1529         g_return_if_fail (data != NULL);
1530         helper = (HeadersCountChangedHelper *) data; 
1531
1532         g_object_unref (helper->change);
1533         g_slice_free (HeadersCountChangedHelper, helper);
1534 }
1535
1536 static gboolean
1537 idle_notify_headers_count_changed (gpointer data)
1538 {
1539         TnyFolder *folder = NULL;
1540         ModestHeaderViewPrivate *priv = NULL;
1541         HeadersCountChangedHelper *helper = NULL;
1542
1543         g_return_val_if_fail (data != NULL, FALSE);
1544         helper = (HeadersCountChangedHelper *) data; 
1545         g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1546         g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1547
1548         folder = tny_folder_change_get_folder (helper->change);
1549
1550         priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1551
1552         g_mutex_lock (priv->observers_lock);
1553
1554         /* Emit signal to evaluate how headers changes affects to the window view  */
1555         g_signal_emit (G_OBJECT(helper->self), 
1556                        signals[MSG_COUNT_CHANGED_SIGNAL], 
1557                        0, folder, helper->change);
1558                 
1559         /* Added or removed headers, so data stored on cliboard are invalid  */
1560         if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1561             modest_email_clipboard_clear (priv->clipboard);
1562             
1563         g_mutex_unlock (priv->observers_lock);
1564
1565         return FALSE;
1566 }
1567
1568 static void
1569 folder_monitor_update (TnyFolderObserver *self, 
1570                        TnyFolderChange *change)
1571 {
1572         ModestHeaderViewPrivate *priv = NULL;
1573         TnyFolderChangeChanged changed;
1574         HeadersCountChangedHelper *helper = NULL;
1575
1576         changed = tny_folder_change_get_changed (change);
1577         
1578         /* Do not notify the observers if the folder of the header
1579            view has changed before this call to the observer
1580            happens */
1581         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1582         if (tny_folder_change_get_folder (change) != priv->folder)
1583                 return;
1584
1585         /* Check folder count */
1586         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1587             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1588                 helper = g_slice_new0 (HeadersCountChangedHelper);
1589                 helper->self = MODEST_HEADER_VIEW(self);
1590                 helper->change = g_object_ref(change);
1591                 
1592                 g_idle_add_full (G_PRIORITY_DEFAULT, 
1593                                  idle_notify_headers_count_changed, 
1594                                  helper,
1595                                  idle_notify_headers_count_changed_destroy);
1596         }       
1597 }
1598
1599 gboolean
1600 modest_header_view_is_empty (ModestHeaderView *self)
1601 {
1602         ModestHeaderViewPrivate *priv = NULL;
1603                 
1604         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1605
1606         return priv->status == HEADER_VIEW_EMPTY;
1607 }
1608
1609 void
1610 modest_header_view_clear (ModestHeaderView *self)
1611 {
1612         modest_header_view_set_folder (self, NULL, NULL, NULL);
1613 }
1614
1615 void 
1616 modest_header_view_copy_selection (ModestHeaderView *header_view)
1617 {
1618         /* Copy selection */
1619         _clipboard_set_selected_data (header_view, FALSE);
1620 }
1621
1622 void 
1623 modest_header_view_cut_selection (ModestHeaderView *header_view)
1624 {
1625         ModestHeaderViewPrivate *priv = NULL;
1626         const gchar **hidding = NULL;
1627         guint i, n_selected;
1628
1629         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1630         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1631
1632         /* Copy selection */
1633         _clipboard_set_selected_data (header_view, TRUE);
1634
1635         /* Get hidding ids */
1636         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1637         
1638         /* Clear hidding array created by previous cut operation */
1639         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1640
1641         /* Copy hidding array */
1642         priv->n_selected = n_selected;
1643         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1644         for (i=0; i < n_selected; i++) 
1645                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1646
1647         /* Hide cut headers */
1648         modest_header_view_refilter (header_view);
1649 }
1650
1651
1652  
1653
1654 static void
1655 _clipboard_set_selected_data (ModestHeaderView *header_view,
1656                               gboolean delete)
1657 {
1658         ModestHeaderViewPrivate *priv = NULL;
1659         TnyList *headers = NULL;
1660
1661         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1662         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1663                 
1664         /* Set selected data on clipboard   */
1665         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1666         headers = modest_header_view_get_selected_headers (header_view);
1667         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1668
1669         /* Free */
1670         g_object_unref (headers);
1671 }
1672
1673
1674
1675 static gboolean
1676 filter_row (GtkTreeModel *model,
1677             GtkTreeIter *iter,
1678             gpointer user_data)
1679 {
1680         ModestHeaderViewPrivate *priv = NULL;
1681         TnyHeaderFlags flags;
1682         TnyHeader *header = NULL;
1683         guint i;
1684         gchar *id = NULL;
1685         gboolean visible = TRUE;
1686         gboolean found = FALSE;
1687         
1688         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1689         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1690
1691         /* Get header from model */
1692         gtk_tree_model_get (model, iter,
1693                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1694                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1695                             -1);
1696         
1697         /* Hide mark as deleted heders */
1698         if (flags & TNY_HEADER_FLAG_DELETED) {
1699                 visible = FALSE;
1700                 goto frees;
1701         }
1702
1703         /* If no data on clipboard, return always TRUE */
1704         if (modest_email_clipboard_cleared(priv->clipboard)) {
1705                 visible = TRUE;
1706                 goto frees;
1707         }               
1708
1709         /* Get message id from header (ensure is a valid id) */
1710         if (!header) return FALSE;
1711         id = g_strdup(tny_header_get_message_id (header));
1712         
1713         /* Check hiding */
1714         if (priv->hidding_ids != NULL) {
1715                 for (i=0; i < priv->n_selected && !found; i++)
1716                         if (priv->hidding_ids[i] != NULL && id != NULL)
1717                                 found = (!strcmp (priv->hidding_ids[i], id));
1718         
1719                 visible = !found;
1720         }
1721
1722  frees:
1723         priv->status = ((gboolean) priv->status) && !visible;
1724         
1725         /* Free */
1726         if (header)
1727                 g_object_unref (header);
1728         g_free(id);
1729
1730         return visible;
1731 }
1732
1733 static void
1734 _clear_hidding_filter (ModestHeaderView *header_view) 
1735 {
1736         ModestHeaderViewPrivate *priv = NULL;
1737         guint i;
1738         
1739         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
1740         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1741
1742         if (priv->hidding_ids != NULL) {
1743                 for (i=0; i < priv->n_selected; i++) 
1744                         g_free (priv->hidding_ids[i]);
1745                 g_free(priv->hidding_ids);
1746         }       
1747 }
1748
1749 void 
1750 modest_header_view_refilter (ModestHeaderView *header_view)
1751 {
1752         GtkTreeModel *model = NULL;
1753         ModestHeaderViewPrivate *priv = NULL;
1754
1755         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1756         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1757
1758         priv->status = HEADER_VIEW_EMPTY;
1759
1760         /* Hide cut headers */
1761         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1762         if (GTK_IS_TREE_MODEL_FILTER (model))
1763                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1764 }
1765
1766 /* 
1767  * Called when an account is removed. If I'm showing a folder of the
1768  * account that has been removed then clear the view
1769  */
1770 static void
1771 on_account_removed (TnyAccountStore *self, 
1772                     TnyAccount *account,
1773                     gpointer user_data)
1774 {
1775         ModestHeaderViewPrivate *priv = NULL;
1776
1777         /* Ignore changes in transport accounts */
1778         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1779                 return;
1780
1781         g_print ("--------------------- HEADER ---------------\n");
1782
1783         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1784
1785         if (priv->folder) {
1786                 TnyAccount *my_account;
1787
1788                 my_account = tny_folder_get_account (priv->folder);
1789                 if (my_account == account)
1790                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1791                 g_object_unref (account);
1792         }
1793 }
1794
1795 void modest_header_view_add_observer(
1796                 ModestHeaderView *header_view,
1797                 ModestHeaderViewObserver *observer)
1798 {
1799         ModestHeaderViewPrivate *priv = NULL;
1800
1801         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1802         g_assert(observer != NULL);
1803         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1804
1805         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1806
1807         g_mutex_lock(priv->observer_list_lock);
1808         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1809         g_mutex_unlock(priv->observer_list_lock);
1810 }
1811
1812 void modest_header_view_remove_observer(
1813                 ModestHeaderView *header_view,
1814                 ModestHeaderViewObserver *observer)
1815 {
1816         ModestHeaderViewPrivate *priv = NULL;
1817
1818         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1819         g_assert(observer != NULL);
1820         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1821
1822         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1823
1824         g_mutex_lock(priv->observer_list_lock);
1825         priv->observer_list = g_slist_remove(priv->observer_list, observer);
1826         g_mutex_unlock(priv->observer_list_lock);
1827 }
1828
1829 static void modest_header_view_notify_observers(
1830                 ModestHeaderView *header_view,
1831                 GtkTreeModel *model,
1832                 const gchar *tny_folder_id)
1833 {
1834         ModestHeaderViewPrivate *priv = NULL;
1835         GSList *iter;
1836         ModestHeaderViewObserver *observer;
1837
1838         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1839
1840         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1841
1842         g_mutex_lock(priv->observer_list_lock);
1843         iter = priv->observer_list;
1844         while(iter != NULL){
1845                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1846                 modest_header_view_observer_update(observer, model,
1847                                 tny_folder_id);
1848                 iter = g_slist_next(iter);
1849         }
1850         g_mutex_unlock(priv->observer_list_lock);
1851 }
1852