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