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