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