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