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