fixes NB#66301
[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         ModestWindowMgr *mgr = NULL;
1343         TnyHeader *header = NULL;
1344         
1345         source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1346         
1347         if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1348         gtk_tree_model_get (model, &iter,
1349                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1350                             &header, -1);
1351         mgr = modest_runtime_get_window_mgr ();
1352         if (modest_window_mgr_find_registered_header(mgr, header, NULL))
1353                 return;
1354
1355         switch (info) {
1356         case MODEST_HEADER_ROW:
1357                 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1358                 break;
1359         case MODEST_MSG: {
1360                 TnyHeader *hdr = NULL;
1361                 gtk_tree_model_get (model, &iter,
1362                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1363                                     -1);
1364                 if (hdr) {
1365                         g_object_unref (G_OBJECT(hdr));
1366                 }
1367                 break;
1368         }
1369         default:
1370                 g_message ("%s: default switch case.", __FUNCTION__);
1371         }
1372
1373         /* Set focus on next header */
1374         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1375         gtk_tree_path_next (source_row);
1376         gtk_tree_selection_select_path (sel, source_row);
1377
1378         gtk_tree_path_free (source_row);
1379 }
1380
1381 /* Header view drag types */
1382 const GtkTargetEntry header_view_drag_types[] = {
1383         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1384         { "text/uri-list",      0,                   MODEST_MSG }, 
1385 };
1386
1387 static void
1388 setup_drag_and_drop (GtkTreeView *self)
1389 {
1390         gtk_drag_source_set (GTK_WIDGET (self),
1391                              GDK_BUTTON1_MASK,
1392                              header_view_drag_types,
1393                              G_N_ELEMENTS (header_view_drag_types),
1394                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1395
1396         g_signal_connect(G_OBJECT (self), "drag_data_get",
1397                          G_CALLBACK(drag_data_get_cb), NULL);
1398 }
1399
1400 static GtkTreePath *
1401 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1402 {
1403         GtkTreePath *path = NULL;
1404         GtkTreeSelection *sel = NULL;   
1405         GList *rows = NULL;
1406
1407         sel   = gtk_tree_view_get_selection(self);
1408         rows = gtk_tree_selection_get_selected_rows (sel, model);
1409         
1410         if ((rows == NULL) || (g_list_length(rows) != 1))
1411                 goto frees;
1412
1413         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1414         
1415
1416         /* Free */
1417  frees:
1418         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1419         g_list_free(rows);
1420
1421         return path;
1422 }
1423
1424 /*
1425  * This function moves the tree view scroll to the current selected
1426  * row when the widget grabs the focus 
1427  */
1428 static gboolean 
1429 on_focus_in (GtkWidget     *self,
1430              GdkEventFocus *event,
1431              gpointer       user_data)
1432 {
1433         GtkTreeSelection *selection;
1434         GtkTreeModel *model;
1435         GList *selected = NULL;
1436         GtkTreePath *selected_path = NULL;
1437
1438         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1439         if (!model)
1440                 return FALSE;
1441
1442         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1443         /* If none selected yet, pick the first one */
1444         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1445                 GtkTreeIter iter;
1446                 GtkTreePath *path;
1447
1448                 /* Return if the model is empty */
1449                 if (!gtk_tree_model_get_iter_first (model, &iter))
1450                         return FALSE;
1451
1452                 path = gtk_tree_model_get_path (model, &iter);
1453                 gtk_tree_selection_select_path (selection, path);
1454                 gtk_tree_path_free (path);
1455         }
1456
1457         /* Need to get the all the rows because is selection multiple */
1458         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1459         if (selected == NULL) return FALSE;
1460         selected_path = (GtkTreePath *) selected->data;
1461
1462         /* Check if we need to scroll */
1463         #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1464         GtkTreePath *start_path = NULL;
1465         GtkTreePath *end_path = NULL;
1466         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1467                                              &start_path,
1468                                              &end_path)) {
1469
1470                 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1471                     (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1472
1473                         /* Scroll to first path */
1474                         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1475                                                       selected_path,
1476                                                       NULL,
1477                                                       TRUE,
1478                                                       0.5,
1479                                                       0.0);
1480                 }
1481         }
1482         if (start_path)
1483                 gtk_tree_path_free (start_path);
1484         if (end_path)
1485                 gtk_tree_path_free (end_path);
1486
1487         #endif /* GTK_CHECK_VERSION */
1488
1489         /* Frees */     
1490         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1491         g_list_free (selected);
1492
1493         return FALSE;
1494 }
1495
1496 static void
1497 idle_notify_headers_count_changed_destroy (gpointer data)
1498 {
1499         HeadersCountChangedHelper *helper = NULL;
1500
1501         g_return_if_fail (data != NULL);
1502         helper = (HeadersCountChangedHelper *) data; 
1503
1504         g_object_unref (helper->change);
1505         g_slice_free (HeadersCountChangedHelper, helper);
1506 }
1507
1508 static gboolean
1509 idle_notify_headers_count_changed (gpointer data)
1510 {
1511         TnyFolder *folder = NULL;
1512         ModestHeaderViewPrivate *priv = NULL;
1513         HeadersCountChangedHelper *helper = NULL;
1514
1515         g_return_val_if_fail (data != NULL, FALSE);
1516         helper = (HeadersCountChangedHelper *) data; 
1517         g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1518         g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1519
1520         folder = tny_folder_change_get_folder (helper->change);
1521
1522         priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1523
1524         g_mutex_lock (priv->observers_lock);
1525
1526         /* Emit signal to evaluate how headers changes affects to the window view  */
1527         g_signal_emit (G_OBJECT(helper->self), 
1528                        signals[MSG_COUNT_CHANGED_SIGNAL], 
1529                        0, folder, helper->change);
1530                 
1531         /* Added or removed headers, so data stored on cliboard are invalid  */
1532         if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1533             modest_email_clipboard_clear (priv->clipboard);
1534             
1535         g_mutex_unlock (priv->observers_lock);
1536
1537         return FALSE;
1538 }
1539
1540 static void
1541 folder_monitor_update (TnyFolderObserver *self, 
1542                        TnyFolderChange *change)
1543 {
1544         ModestHeaderViewPrivate *priv = NULL;
1545         TnyFolderChangeChanged changed;
1546         HeadersCountChangedHelper *helper = NULL;
1547
1548         changed = tny_folder_change_get_changed (change);
1549         
1550         /* Do not notify the observers if the folder of the header
1551            view has changed before this call to the observer
1552            happens */
1553         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1554         if (tny_folder_change_get_folder (change) != priv->folder)
1555                 return;
1556
1557         /* Check folder count */
1558         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1559             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1560                 helper = g_slice_new0 (HeadersCountChangedHelper);
1561                 helper->self = MODEST_HEADER_VIEW(self);
1562                 helper->change = g_object_ref(change);
1563                 
1564                 g_idle_add_full (G_PRIORITY_DEFAULT, 
1565                                  idle_notify_headers_count_changed, 
1566                                  helper,
1567                                  idle_notify_headers_count_changed_destroy);
1568         }       
1569 }
1570
1571 gboolean
1572 modest_header_view_is_empty (ModestHeaderView *self)
1573 {
1574         ModestHeaderViewPrivate *priv = NULL;
1575                 
1576         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1577
1578         return priv->empty;
1579 }
1580
1581 void
1582 modest_header_view_clear (ModestHeaderView *self)
1583 {
1584         modest_header_view_set_folder (self, NULL, NULL, NULL);
1585 }
1586
1587 void 
1588 modest_header_view_copy_selection (ModestHeaderView *header_view)
1589 {
1590         /* Copy selection */
1591         _clipboard_set_selected_data (header_view, FALSE);
1592 }
1593
1594 void 
1595 modest_header_view_cut_selection (ModestHeaderView *header_view)
1596 {
1597         ModestHeaderViewPrivate *priv = NULL;
1598         const gchar **hidding = NULL;
1599         guint i, n_selected;
1600
1601         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1602         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1603
1604         /* Copy selection */
1605         _clipboard_set_selected_data (header_view, TRUE);
1606
1607         /* Get hidding ids */
1608         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1609         
1610         /* Clear hidding array created by previous cut operation */
1611         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1612
1613         /* Copy hidding array */
1614         priv->n_selected = n_selected;
1615         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1616         for (i=0; i < n_selected; i++) 
1617                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1618
1619         /* Hide cut headers */
1620         modest_header_view_refilter (header_view);
1621 }
1622
1623
1624  
1625
1626 static void
1627 _clipboard_set_selected_data (ModestHeaderView *header_view,
1628                               gboolean delete)
1629 {
1630         ModestHeaderViewPrivate *priv = NULL;
1631         TnyList *headers = NULL;
1632
1633         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1634         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1635                 
1636         /* Set selected data on clipboard   */
1637         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1638         headers = modest_header_view_get_selected_headers (header_view);
1639         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1640
1641         /* Free */
1642         g_object_unref (headers);
1643 }
1644
1645
1646
1647 static gboolean
1648 filter_row (GtkTreeModel *model,
1649             GtkTreeIter *iter,
1650             gpointer user_data)
1651 {
1652         ModestHeaderViewPrivate *priv = NULL;
1653         TnyHeaderFlags flags;
1654         TnyHeader *header = NULL;
1655         guint i;
1656         gchar *id = NULL;
1657         gboolean visible = TRUE;
1658         gboolean found = FALSE;
1659         
1660         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1661         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1662
1663         /* Get header from model */
1664         gtk_tree_model_get (model, iter,
1665                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1666                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1667                             -1);
1668         
1669         /* Hide mark as deleted heders */
1670         if (flags & TNY_HEADER_FLAG_DELETED) {
1671                 visible = FALSE;
1672                 goto frees;
1673         }
1674
1675         /* If no data on clipboard, return always TRUE */
1676         if (modest_email_clipboard_cleared(priv->clipboard)) {
1677                 visible = TRUE;
1678                 goto frees;
1679         }               
1680
1681         /* Get message id from header (ensure is a valid id) */
1682         if (!header) return FALSE;
1683         id = g_strdup(tny_header_get_message_id (header));
1684         
1685         /* Check hiding */
1686         if (priv->hidding_ids != NULL) {
1687                 for (i=0; i < priv->n_selected && !found; i++)
1688                         if (priv->hidding_ids[i] != NULL && id != NULL)
1689                                 found = (!strcmp (priv->hidding_ids[i], id));
1690         
1691                 visible = !found;
1692         }
1693
1694  frees:
1695         priv->empty = priv->empty && !visible;
1696         
1697         /* Free */
1698         if (header)
1699                 g_object_unref (header);
1700         g_free(id);
1701
1702         return visible;
1703 }
1704
1705 static void
1706 _clear_hidding_filter (ModestHeaderView *header_view) 
1707 {
1708         ModestHeaderViewPrivate *priv = NULL;
1709         guint i;
1710         
1711         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
1712         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1713
1714         if (priv->hidding_ids != NULL) {
1715                 for (i=0; i < priv->n_selected; i++) 
1716                         g_free (priv->hidding_ids[i]);
1717                 g_free(priv->hidding_ids);
1718         }       
1719 }
1720
1721 void 
1722 modest_header_view_refilter (ModestHeaderView *header_view)
1723 {
1724         GtkTreeModel *model = NULL;
1725         ModestHeaderViewPrivate *priv = NULL;
1726
1727         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1728         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1729
1730         priv->empty = TRUE;
1731
1732         /* Hide cut headers */
1733         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1734         if (GTK_IS_TREE_MODEL_FILTER (model))
1735                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1736 }
1737
1738 /* 
1739  * Called when an account is removed. If I'm showing a folder of the
1740  * account that has been removed then clear the view
1741  */
1742 static void
1743 on_account_removed (TnyAccountStore *self, 
1744                     TnyAccount *account,
1745                     gpointer user_data)
1746 {
1747         ModestHeaderViewPrivate *priv = NULL;
1748
1749         /* Ignore changes in transport accounts */
1750         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1751                 return;
1752
1753         g_print ("--------------------- HEADER ---------------\n");
1754
1755         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1756
1757         if (priv->folder) {
1758                 TnyAccount *my_account;
1759
1760                 my_account = tny_folder_get_account (priv->folder);
1761                 if (my_account == account)
1762                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1763                 g_object_unref (account);
1764         }
1765 }
1766
1767 void modest_header_view_add_observer(
1768                 ModestHeaderView *header_view,
1769                 ModestHeaderViewObserver *observer)
1770 {
1771         ModestHeaderViewPrivate *priv = NULL;
1772
1773         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1774         g_assert(observer != NULL);
1775         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1776
1777         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1778
1779         g_mutex_lock(priv->observer_list_lock);
1780         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1781         g_mutex_unlock(priv->observer_list_lock);
1782 }
1783
1784 void modest_header_view_remove_observer(
1785                 ModestHeaderView *header_view,
1786                 ModestHeaderViewObserver *observer)
1787 {
1788         ModestHeaderViewPrivate *priv = NULL;
1789
1790         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1791         g_assert(observer != NULL);
1792         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1793
1794         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1795
1796         g_mutex_lock(priv->observer_list_lock);
1797         priv->observer_list = g_slist_remove(priv->observer_list, observer);
1798         g_mutex_unlock(priv->observer_list_lock);
1799 }
1800
1801 static void modest_header_view_notify_observers(
1802                 ModestHeaderView *header_view,
1803                 GtkTreeModel *model,
1804                 const gchar *tny_folder_id)
1805 {
1806         ModestHeaderViewPrivate *priv = NULL;
1807         GSList *iter;
1808         ModestHeaderViewObserver *observer;
1809
1810         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1811
1812         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1813
1814         g_mutex_lock(priv->observer_list_lock);
1815         iter = priv->observer_list;
1816         while(iter != NULL){
1817                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1818                 modest_header_view_observer_update(observer, model,
1819                                 tny_folder_id);
1820                 iter = g_slist_next(iter);
1821         }
1822         g_mutex_unlock(priv->observer_list_lock);
1823 }
1824