* Renamed one method in modest UI actions
[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.
878  */
879 static gboolean 
880 modest_header_view_on_expose_event(GtkTreeView *header_view,
881                                    GdkEventExpose *event,
882                                    gpointer user_data)
883 {
884         GtkTreeSelection *sel;
885         GtkTreeModel *model;
886         GtkTreeIter tree_iter;
887
888         /* I'm invalidating this method because it causes an annoying
889            efect, the focus changes to the header view when selecting
890            a folder in the folder view because of this code and it
891            shouldn't. We need to find another way to set the passive
892            focus on it. Sergio. */
893         return FALSE;
894
895         model = gtk_tree_view_get_model(header_view);
896
897         sel = gtk_tree_view_get_selection(header_view);
898         if(!gtk_tree_selection_count_selected_rows(sel))
899                 if (gtk_tree_model_get_iter_first(model, &tree_iter))
900                         gtk_tree_selection_select_iter(sel, &tree_iter);
901
902         return FALSE;
903 }
904
905 /* 
906  * This function sets a sortable model in the header view. It's just
907  * used for developing purposes, because it only does a
908  * gtk_tree_view_set_model
909  */
910 static void
911 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
912 {
913 /*      GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
914 /*      if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
915 /*              GtkTreeModel *old_model; */
916 /*              ModestHeaderViewPrivate *priv; */
917 /*              priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
918 /*              old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
919
920 /*              /\* Set new model *\/ */
921 /*              gtk_tree_view_set_model (header_view, model); */
922 /*      } else */
923         gtk_tree_view_set_model (header_view, model);
924 }
925
926 TnyFolder*
927 modest_header_view_get_folder (ModestHeaderView *self)
928 {
929         ModestHeaderViewPrivate *priv;
930         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
931
932         if (priv->folder)
933                 g_object_ref (priv->folder);
934
935         return priv->folder;
936 }
937
938 static void
939 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
940 {
941         TnyFolderType type;
942         TnyList *headers;
943         ModestHeaderViewPrivate *priv;
944         GList *cols, *cursor;
945         GtkTreeModel *filter_model, *sortable; 
946         guint sort_colid;
947         GtkSortType sort_type;
948
949         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
950
951         headers = TNY_LIST (tny_gtk_header_list_model_new ());
952
953         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
954                                               folder, FALSE, NULL, NULL, NULL);
955
956         /* Add IDLE observer (monitor) and another folder observer for
957            new messages (self) */
958         g_mutex_lock (priv->observers_lock);
959         if (priv->monitor) {
960                 tny_folder_monitor_stop (priv->monitor);
961                 g_object_unref (G_OBJECT (priv->monitor));
962         }
963         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
964         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
965         tny_folder_monitor_start (priv->monitor);
966         g_mutex_unlock (priv->observers_lock);
967
968         sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
969         g_object_unref (G_OBJECT (headers));
970
971         /* Init filter_row function to examine empty status */
972         priv->status  = HEADER_VIEW_INIT;
973
974         /* Create a tree model filter to hide and show rows for cut operations  */
975         filter_model = gtk_tree_model_filter_new (sortable, NULL);
976         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
977                                                 filter_row,
978                                                 self,
979                                                 NULL);
980         g_object_unref (G_OBJECT (sortable));
981
982         /* install our special sorting functions */
983         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
984
985         /* Restore sort column id */
986         if (cols) {
987                 type  = modest_tny_folder_guess_folder_type (folder);
988                 sort_colid = modest_header_view_get_sort_column_id (self, type); 
989                 sort_type = modest_header_view_get_sort_type (self, type); 
990                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
991                                                       sort_colid,
992                                                       sort_type);
993                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
994                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
995                                                  (GtkTreeIterCompareFunc) cmp_rows,
996                                                  cols->data, NULL);
997                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
998                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
999                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
1000                                                  cols->data, NULL);
1001         }
1002
1003         /* Set new model */
1004         modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1005         modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1006                         tny_folder_get_id(folder));
1007         g_object_unref (G_OBJECT (filter_model));
1008 /*      modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1009 /*      g_object_unref (G_OBJECT (sortable)); */
1010
1011         /* Free */
1012         g_list_free (cols);
1013 }
1014
1015 void
1016 modest_header_view_sort_by_column_id (ModestHeaderView *self, 
1017                                       guint sort_colid,
1018                                       GtkSortType sort_type)
1019 {
1020         ModestHeaderViewPrivate *priv = NULL;
1021         GtkTreeModel *tree_filter, *sortable = NULL; 
1022         TnyFolderType type;
1023
1024         /* Get model and private data */
1025         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);            
1026         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1027         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1028 /*      sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1029         
1030         /* Sort tree model */
1031         type  = modest_tny_folder_guess_folder_type (priv->folder);
1032         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1033                                               sort_colid,
1034                                               sort_type);
1035         /* Store new sort parameters */
1036         modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1037
1038         /* Save GConf parameters */
1039 /*      modest_widget_memory_save (modest_runtime_get_conf(), */
1040 /*                                 G_OBJECT(self), "header-view"); */
1041         
1042 }
1043
1044 void
1045 modest_header_view_set_sort_params (ModestHeaderView *self, 
1046                                     guint sort_colid, 
1047                                     GtkSortType sort_type,
1048                                     TnyFolderType type)
1049 {
1050         ModestHeaderViewPrivate *priv;
1051         ModestHeaderViewStyle style;
1052
1053         style = modest_header_view_get_style   (self);
1054         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1055
1056         priv->sort_colid[style][type] = sort_colid;
1057         priv->sort_type[style][type] = sort_type;
1058 }
1059
1060 gint
1061 modest_header_view_get_sort_column_id (ModestHeaderView *self, 
1062                                        TnyFolderType type)
1063 {
1064         ModestHeaderViewPrivate *priv;
1065         ModestHeaderViewStyle style;
1066
1067         style = modest_header_view_get_style   (self);
1068         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1069
1070         return priv->sort_colid[style][type];
1071 }
1072
1073 GtkSortType
1074 modest_header_view_get_sort_type (ModestHeaderView *self, 
1075                                   TnyFolderType type)
1076 {
1077         ModestHeaderViewPrivate *priv;
1078         ModestHeaderViewStyle style;
1079
1080         style = modest_header_view_get_style   (self);
1081         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1082
1083         return priv->sort_type[style][type];
1084 }
1085
1086 typedef struct {
1087         ModestHeaderView *header_view;
1088         RefreshAsyncUserCallback cb;
1089         gpointer user_data;
1090 } SetFolderHelper;
1091
1092 static void
1093 folder_refreshed_cb (ModestMailOperation *mail_op,
1094                      TnyFolder *folder,
1095                      gpointer user_data)
1096 {
1097         ModestHeaderViewPrivate *priv;
1098         SetFolderHelper *info;
1099  
1100         info = (SetFolderHelper*) user_data;
1101
1102         priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1103
1104         /* User callback */
1105         if (info->cb)
1106                 info->cb (mail_op, folder, info->user_data);
1107
1108         /* Start the folder count changes observer. We do not need it
1109            before the refresh. Note that the monitor could still be
1110            called for this refresh but now we know that the callback
1111            was previously called */
1112         g_mutex_lock (priv->observers_lock);
1113         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1114         g_mutex_unlock (priv->observers_lock);
1115
1116         /* Frees */
1117         g_free (info);
1118 }
1119
1120 void
1121 modest_header_view_set_folder (ModestHeaderView *self, 
1122                                TnyFolder *folder,
1123                                RefreshAsyncUserCallback callback,
1124                                gpointer user_data)
1125 {
1126         ModestHeaderViewPrivate *priv;
1127         ModestWindowMgr *mgr = NULL;
1128         GObject *source = NULL;
1129         SetFolderHelper *info;
1130  
1131         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1132
1133         if (priv->folder) {
1134                 g_mutex_lock (priv->observers_lock);
1135                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1136                 g_object_unref (priv->folder);
1137                 priv->folder = NULL;
1138                 g_mutex_unlock (priv->observers_lock);
1139         }
1140
1141         if (folder) {
1142                 ModestMailOperation *mail_op = NULL;
1143                 GtkTreeSelection *selection;
1144
1145                 /* Get main window to use it as source of mail operation */
1146                 mgr = modest_runtime_get_window_mgr ();
1147                 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1148
1149                 /* Set folder in the model */
1150                 modest_header_view_set_folder_intern (self, folder);
1151
1152                 /* Pick my reference. Nothing to do with the mail operation */
1153                 priv->folder = g_object_ref (folder);
1154
1155                 /* Clear the selection if exists */
1156                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1157                 gtk_tree_selection_unselect_all(selection);
1158                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1159
1160                 /* create the helper */
1161                 info = g_malloc0 (sizeof(SetFolderHelper));
1162                 info->header_view = self;
1163                 info->cb = callback;
1164                 info->user_data = user_data;
1165
1166                 /* Create the mail operation (source will be the parent widget) */
1167                 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1168                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1169                                                  mail_op);
1170
1171                 /* Refresh the folder asynchronously */
1172                 modest_mail_operation_refresh_folder (mail_op,
1173                                                       folder,
1174                                                       folder_refreshed_cb,
1175                                                       info);
1176
1177                 /* Free */
1178                 g_object_unref (mail_op);
1179         } else {
1180                 g_mutex_lock (priv->observers_lock);
1181
1182                 if (priv->monitor) {
1183                         tny_folder_monitor_stop (priv->monitor);
1184                         g_object_unref (G_OBJECT (priv->monitor));
1185                         priv->monitor = NULL;
1186                 }
1187                 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL); 
1188
1189                 modest_header_view_notify_observers(self, NULL, NULL);
1190
1191                 g_mutex_unlock (priv->observers_lock);
1192         }
1193 }
1194
1195 static void
1196 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1197                          GtkTreeViewColumn *column, gpointer userdata)
1198 {
1199         ModestHeaderView *self = NULL;
1200         ModestHeaderViewPrivate *priv = NULL;
1201         GtkTreeIter iter;
1202         GtkTreeModel *model = NULL;
1203         TnyHeader *header = NULL;
1204         TnyHeaderFlags flags;
1205
1206         self = MODEST_HEADER_VIEW (treeview);
1207         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1208
1209         model = gtk_tree_view_get_model (treeview);     
1210         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
1211                 goto frees;
1212
1213         /* get the first selected item */
1214         gtk_tree_model_get (model, &iter,
1215                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1216                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, 
1217                             -1);
1218
1219         /* Dont open DELETED messages */
1220         if (flags & TNY_HEADER_FLAG_DELETED) {
1221                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1222                 goto frees;
1223         }
1224
1225         /* Emit signal */
1226         g_signal_emit (G_OBJECT(self), 
1227                        signals[HEADER_ACTIVATED_SIGNAL], 
1228                        0, header);
1229
1230         /* Free */
1231  frees:
1232         if (header != NULL) 
1233                 g_object_unref (G_OBJECT (header));     
1234
1235 }
1236
1237 static void
1238 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1239 {
1240         GtkTreeModel *model;
1241         TnyHeader *header = NULL;
1242         GtkTreePath *path = NULL;       
1243         GtkTreeIter iter;
1244         ModestHeaderView *self;
1245         ModestHeaderViewPrivate *priv;
1246         GList *selected = NULL;
1247         
1248         g_return_if_fail (sel);
1249         g_return_if_fail (user_data);
1250         
1251         self = MODEST_HEADER_VIEW (user_data);
1252         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
1253
1254         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1255         if (selected != NULL) 
1256                 path = (GtkTreePath *) selected->data;
1257         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1258                 return; /* msg was _un_selected */
1259
1260         gtk_tree_model_get (model, &iter,
1261                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1262                             &header, -1);
1263
1264         /* Emit signal */
1265         g_signal_emit (G_OBJECT(self), 
1266                        signals[HEADER_SELECTED_SIGNAL], 
1267                        0, header);
1268
1269         g_object_unref (G_OBJECT (header));
1270
1271         /* free all items in 'selected' */
1272         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1273         g_list_free (selected);
1274 }
1275
1276
1277 /* PROTECTED method. It's useful when we want to force a given
1278    selection to reload a msg. For example if we have selected a header
1279    in offline mode, when Modest become online, we want to reload the
1280    message automatically without an user click over the header */
1281 void 
1282 _modest_header_view_change_selection (GtkTreeSelection *selection,
1283                                       gpointer user_data)
1284 {
1285         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1286         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1287
1288         on_selection_changed (selection, user_data);
1289 }
1290
1291 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1292 {
1293         p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1294         p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1295         if (p1 == 0) 
1296                 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1297         if (p2 == 0) 
1298                 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1299         return p1 - p2;
1300 }
1301
1302 static gint
1303 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1304           gpointer user_data)
1305 {
1306         gint col_id;
1307         gint t1, t2;
1308         gint val1, val2;
1309         gint cmp;
1310 /*      static int counter = 0; */
1311
1312         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1313 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1314         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1315
1316         
1317         switch (col_id) {
1318         case TNY_HEADER_FLAG_ATTACHMENTS:
1319
1320                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1321                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1322                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1323                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1324
1325                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1326                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1327
1328                 return cmp ? cmp : t1 - t2;
1329                 
1330         case TNY_HEADER_FLAG_PRIORITY:
1331                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1332                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1333                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1334                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1335
1336                 /* This is for making priority values respect the intuitive sort relationship 
1337                  * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1338                 cmp =  compare_priorities (val1, val2);
1339
1340                 return cmp ? cmp : t1 - t2;
1341
1342         default:
1343                 return &iter1 - &iter2; /* oughhhh  */
1344         }
1345 }
1346
1347 static gint
1348 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1349                   gpointer user_data)
1350 {
1351         gint t1, t2;
1352         gchar *val1, *val2;
1353         gint cmp;
1354 /*      static int counter = 0; */
1355
1356         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1357
1358         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1359                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1360         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1361                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1362
1363         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1364                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1365                                              TRUE);
1366         g_free (val1);
1367         g_free (val2);
1368         return cmp;
1369 }
1370
1371 /* Drag and drop stuff */
1372 static void
1373 drag_data_get_cb (GtkWidget *widget, 
1374                   GdkDragContext *context, 
1375                   GtkSelectionData *selection_data, 
1376                   guint info,  
1377                   guint time, 
1378                   gpointer data)
1379 {
1380         GtkTreeSelection *selection;
1381         GtkTreeModel *model;
1382         GList *selected_rows;
1383
1384         /* Get selected rows */
1385         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1386         selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
1387
1388         /* Set the data */
1389         modest_dnd_selection_data_set_paths (selection_data, selected_rows);
1390
1391         /* Frees */
1392         g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1393         g_list_free (selected_rows);
1394 }
1395
1396 /* Header view drag types */
1397 const GtkTargetEntry header_view_drag_types[] = {
1398         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1399 };
1400
1401 static void
1402 setup_drag_and_drop (GtkTreeView *self)
1403 {
1404         gtk_drag_source_set (GTK_WIDGET (self),
1405                              GDK_BUTTON1_MASK,
1406                              header_view_drag_types,
1407                              G_N_ELEMENTS (header_view_drag_types),
1408                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1409
1410         g_signal_connect(G_OBJECT (self), "drag_data_get",
1411                          G_CALLBACK(drag_data_get_cb), NULL);
1412 }
1413
1414 static GtkTreePath *
1415 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1416 {
1417         GtkTreePath *path = NULL;
1418         GtkTreeSelection *sel = NULL;   
1419         GList *rows = NULL;
1420
1421         sel   = gtk_tree_view_get_selection(self);
1422         rows = gtk_tree_selection_get_selected_rows (sel, model);
1423         
1424         if ((rows == NULL) || (g_list_length(rows) != 1))
1425                 goto frees;
1426
1427         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1428         
1429
1430         /* Free */
1431  frees:
1432         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1433         g_list_free(rows);
1434
1435         return path;
1436 }
1437
1438 /*
1439  * This function moves the tree view scroll to the current selected
1440  * row when the widget grabs the focus 
1441  */
1442 static gboolean 
1443 on_focus_in (GtkWidget     *self,
1444              GdkEventFocus *event,
1445              gpointer       user_data)
1446 {
1447         GtkTreeSelection *selection;
1448         GtkTreeModel *model;
1449         GList *selected = NULL;
1450         GtkTreePath *selected_path = NULL;
1451
1452         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1453         if (!model)
1454                 return FALSE;
1455
1456         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1457         /* If none selected yet, pick the first one */
1458         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1459                 GtkTreeIter iter;
1460                 GtkTreePath *path;
1461
1462                 /* Return if the model is empty */
1463                 if (!gtk_tree_model_get_iter_first (model, &iter))
1464                         return FALSE;
1465
1466                 path = gtk_tree_model_get_path (model, &iter);
1467                 gtk_tree_selection_select_path (selection, path);
1468                 gtk_tree_path_free (path);
1469         }
1470
1471         /* Need to get the all the rows because is selection multiple */
1472         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1473         if (selected == NULL) return FALSE;
1474         selected_path = (GtkTreePath *) selected->data;
1475
1476         /* Check if we need to scroll */
1477         #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1478         GtkTreePath *start_path = NULL;
1479         GtkTreePath *end_path = NULL;
1480         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1481                                              &start_path,
1482                                              &end_path)) {
1483
1484                 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1485                     (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1486
1487                         /* Scroll to first path */
1488                         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1489                                                       selected_path,
1490                                                       NULL,
1491                                                       TRUE,
1492                                                       0.5,
1493                                                       0.0);
1494                 }
1495         }
1496         if (start_path)
1497                 gtk_tree_path_free (start_path);
1498         if (end_path)
1499                 gtk_tree_path_free (end_path);
1500
1501         #endif /* GTK_CHECK_VERSION */
1502
1503         /* Frees */     
1504         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1505         g_list_free (selected);
1506
1507         return FALSE;
1508 }
1509
1510 static void
1511 folder_monitor_update (TnyFolderObserver *self, 
1512                        TnyFolderChange *change)
1513 {
1514         ModestHeaderViewPrivate *priv = NULL;
1515         TnyFolderChangeChanged changed;
1516         TnyFolder *folder = NULL;
1517
1518         changed = tny_folder_change_get_changed (change);
1519         
1520         /* Do not notify the observers if the folder of the header
1521            view has changed before this call to the observer
1522            happens */
1523         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1524         folder = tny_folder_change_get_folder (change);
1525         if (folder != priv->folder)
1526                 goto frees;
1527
1528         /* Check folder count */
1529         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1530             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1531
1532                 g_mutex_lock (priv->observers_lock);
1533
1534                 /* Emit signal to evaluate how headers changes affects
1535                    to the window view  */
1536                 g_signal_emit (G_OBJECT(self), 
1537                                signals[MSG_COUNT_CHANGED_SIGNAL], 
1538                                0, folder, change);
1539                 
1540                 /* Added or removed headers, so data stored on cliboard are invalid  */
1541                 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1542                         modest_email_clipboard_clear (priv->clipboard);
1543             
1544                 g_mutex_unlock (priv->observers_lock);
1545         }       
1546
1547         /* Free */
1548  frees:
1549         if (folder != NULL)
1550                 g_object_unref (folder);
1551 }
1552
1553 gboolean
1554 modest_header_view_is_empty (ModestHeaderView *self)
1555 {
1556         ModestHeaderViewPrivate *priv = NULL;
1557                 
1558         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1559
1560         return priv->status == HEADER_VIEW_EMPTY;
1561 }
1562
1563 void
1564 modest_header_view_clear (ModestHeaderView *self)
1565 {
1566         modest_header_view_set_folder (self, NULL, NULL, NULL);
1567 }
1568
1569 void 
1570 modest_header_view_copy_selection (ModestHeaderView *header_view)
1571 {
1572         /* Copy selection */
1573         _clipboard_set_selected_data (header_view, FALSE);
1574 }
1575
1576 void 
1577 modest_header_view_cut_selection (ModestHeaderView *header_view)
1578 {
1579         ModestHeaderViewPrivate *priv = NULL;
1580         const gchar **hidding = NULL;
1581         guint i, n_selected;
1582
1583         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1584         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1585
1586         /* Copy selection */
1587         _clipboard_set_selected_data (header_view, TRUE);
1588
1589         /* Get hidding ids */
1590         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1591         
1592         /* Clear hidding array created by previous cut operation */
1593         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1594
1595         /* Copy hidding array */
1596         priv->n_selected = n_selected;
1597         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1598         for (i=0; i < n_selected; i++) 
1599                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1600
1601         /* Hide cut headers */
1602         modest_header_view_refilter (header_view);
1603 }
1604
1605
1606  
1607
1608 static void
1609 _clipboard_set_selected_data (ModestHeaderView *header_view,
1610                               gboolean delete)
1611 {
1612         ModestHeaderViewPrivate *priv = NULL;
1613         TnyList *headers = NULL;
1614
1615         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1616         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1617                 
1618         /* Set selected data on clipboard   */
1619         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1620         headers = modest_header_view_get_selected_headers (header_view);
1621         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1622
1623         /* Free */
1624         g_object_unref (headers);
1625 }
1626
1627
1628
1629 static gboolean
1630 filter_row (GtkTreeModel *model,
1631             GtkTreeIter *iter,
1632             gpointer user_data)
1633 {
1634         ModestHeaderViewPrivate *priv = NULL;
1635         TnyHeaderFlags flags;
1636         TnyHeader *header = NULL;
1637         guint i;
1638         gchar *id = NULL;
1639         gboolean visible = TRUE;
1640         gboolean found = FALSE;
1641         
1642         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1643         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1644
1645         /* Get header from model */
1646         gtk_tree_model_get (model, iter,
1647                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1648                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1649                             -1);
1650         
1651         /* Hide mark as deleted heders */
1652         if (flags & TNY_HEADER_FLAG_DELETED) {
1653                 visible = FALSE;
1654                 goto frees;
1655         }
1656
1657         /* If no data on clipboard, return always TRUE */
1658         if (modest_email_clipboard_cleared(priv->clipboard)) {
1659                 visible = TRUE;
1660                 goto frees;
1661         }               
1662
1663         /* Get message id from header (ensure is a valid id) */
1664         if (!header) return FALSE;
1665         id = g_strdup(tny_header_get_message_id (header));
1666         
1667         /* Check hiding */
1668         if (priv->hidding_ids != NULL) {
1669                 for (i=0; i < priv->n_selected && !found; i++)
1670                         if (priv->hidding_ids[i] != NULL && id != NULL)
1671                                 found = (!strcmp (priv->hidding_ids[i], id));
1672         
1673                 visible = !found;
1674         }
1675
1676  frees:
1677         priv->status = ((gboolean) priv->status) && !visible;
1678         
1679         /* Free */
1680         if (header)
1681                 g_object_unref (header);
1682         g_free(id);
1683
1684         return visible;
1685 }
1686
1687 static void
1688 _clear_hidding_filter (ModestHeaderView *header_view) 
1689 {
1690         ModestHeaderViewPrivate *priv = NULL;
1691         guint i;
1692         
1693         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
1694         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1695
1696         if (priv->hidding_ids != NULL) {
1697                 for (i=0; i < priv->n_selected; i++) 
1698                         g_free (priv->hidding_ids[i]);
1699                 g_free(priv->hidding_ids);
1700         }       
1701 }
1702
1703 void 
1704 modest_header_view_refilter (ModestHeaderView *header_view)
1705 {
1706         GtkTreeModel *model = NULL;
1707         ModestHeaderViewPrivate *priv = NULL;
1708
1709         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1710         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1711
1712         /* Hide cut headers */
1713         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1714         if (GTK_IS_TREE_MODEL_FILTER (model)) {
1715                 priv->status = 0;
1716                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1717         }
1718 }
1719
1720 /* 
1721  * Called when an account is removed. If I'm showing a folder of the
1722  * account that has been removed then clear the view
1723  */
1724 static void
1725 on_account_removed (TnyAccountStore *self, 
1726                     TnyAccount *account,
1727                     gpointer user_data)
1728 {
1729         ModestHeaderViewPrivate *priv = NULL;
1730
1731         /* Ignore changes in transport accounts */
1732         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1733                 return;
1734
1735         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1736
1737         if (priv->folder) {
1738                 TnyAccount *my_account;
1739
1740                 my_account = tny_folder_get_account (priv->folder);
1741                 if (my_account == account)
1742                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1743                 g_object_unref (account);
1744         }
1745 }
1746
1747 void modest_header_view_add_observer(
1748                 ModestHeaderView *header_view,
1749                 ModestHeaderViewObserver *observer)
1750 {
1751         ModestHeaderViewPrivate *priv = NULL;
1752
1753         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1754         g_assert(observer != NULL);
1755         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1756
1757         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1758
1759         g_mutex_lock(priv->observer_list_lock);
1760         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1761         g_mutex_unlock(priv->observer_list_lock);
1762 }
1763
1764 void 
1765 modest_header_view_remove_observer(ModestHeaderView *header_view,
1766                                    ModestHeaderViewObserver *observer)
1767 {
1768         ModestHeaderViewPrivate *priv = NULL;
1769
1770         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1771         g_assert(observer != NULL);
1772         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1773
1774         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1775
1776         g_mutex_lock(priv->observer_list_lock);
1777         priv->observer_list = g_slist_remove(priv->observer_list, observer);
1778         g_mutex_unlock(priv->observer_list_lock);
1779 }
1780
1781 static void 
1782 modest_header_view_notify_observers(ModestHeaderView *header_view,
1783                                     GtkTreeModel *model,
1784                                     const gchar *tny_folder_id)
1785 {
1786         ModestHeaderViewPrivate *priv = NULL;
1787         GSList *iter;
1788         ModestHeaderViewObserver *observer;
1789
1790         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1791
1792         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1793
1794         g_mutex_lock(priv->observer_list_lock);
1795         iter = priv->observer_list;
1796         while(iter != NULL){
1797                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1798                 modest_header_view_observer_update(observer, model,
1799                                 tny_folder_id);
1800                 iter = g_slist_next(iter);
1801         }
1802         g_mutex_unlock(priv->observer_list_lock);
1803 }
1804