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