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