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