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