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