* Partially fixes NB#85461, do not unload the folder before synchronizing it
[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         /* Add IDLE observer (monitor) and another folder observer for
1007            new messages (self) */
1008         g_mutex_lock (priv->observers_lock);
1009         if (priv->monitor) {
1010                 tny_folder_monitor_stop (priv->monitor);
1011                 g_object_unref (G_OBJECT (priv->monitor));
1012         }
1013         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1014         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1015         tny_folder_monitor_start (priv->monitor);
1016         g_mutex_unlock (priv->observers_lock);
1017 }
1018
1019 static void
1020 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1021 {
1022         TnyFolderType type;
1023         TnyList *headers;
1024         ModestHeaderViewPrivate *priv;
1025         GList *cols, *cursor;
1026         GtkTreeModel *filter_model, *sortable; 
1027         guint sort_colid;
1028         GtkSortType sort_type;
1029
1030         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1031
1032         headers = TNY_LIST (tny_gtk_header_list_model_new ());
1033
1034         /* Start the monitor in the callback of the
1035            tny_gtk_header_list_model_set_folder call. It's crucial to
1036            do it there and not just after the call because we want the
1037            monitor to observe only the headers returned by the
1038            tny_folder_get_headers_async call that it's inside the
1039            tny_gtk_header_list_model_set_folder call. This way the
1040            monitor infrastructure could successfully cope with
1041            duplicates. For example if a tny_folder_add_msg_async is
1042            happening while tny_gtk_header_list_model_set_folder is
1043            invoked, then the first call could add a header that will
1044            be added again by tny_gtk_header_list_model_set_folder, so
1045            we'd end up with duplicate headers. sergio */
1046         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1047                                               folder, FALSE, 
1048                                               set_folder_intern_get_headers_async_cb, 
1049                                               NULL, self);
1050
1051         sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1052         g_object_unref (G_OBJECT (headers));
1053
1054         /* Init filter_row function to examine empty status */
1055         priv->status  = HEADER_VIEW_INIT;
1056
1057         /* Create a tree model filter to hide and show rows for cut operations  */
1058         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1059         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1060                                                 filter_row,
1061                                                 self,
1062                                                 NULL);
1063         g_object_unref (G_OBJECT (sortable));
1064
1065         /* install our special sorting functions */
1066         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1067
1068         /* Restore sort column id */
1069         if (cols) {
1070                 type  = modest_tny_folder_guess_folder_type (folder);
1071                 if (type == TNY_FOLDER_TYPE_INVALID)
1072                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1073                 
1074                 sort_colid = modest_header_view_get_sort_column_id (self, type); 
1075                 sort_type = modest_header_view_get_sort_type (self, type); 
1076                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1077                                                       sort_colid,
1078                                                       sort_type);
1079                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1080                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1081                                                  (GtkTreeIterCompareFunc) cmp_rows,
1082                                                  cols->data, NULL);
1083                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1084                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1085                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
1086                                                  cols->data, NULL);
1087         }
1088
1089         /* Set new model */
1090         modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1091         modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1092                         tny_folder_get_id(folder));
1093         g_object_unref (G_OBJECT (filter_model));
1094 /*      modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1095 /*      g_object_unref (G_OBJECT (sortable)); */
1096
1097         /* Free */
1098         g_list_free (cols);
1099 }
1100
1101 void
1102 modest_header_view_sort_by_column_id (ModestHeaderView *self, 
1103                                       guint sort_colid,
1104                                       GtkSortType sort_type)
1105 {
1106         ModestHeaderViewPrivate *priv = NULL;
1107         GtkTreeModel *tree_filter, *sortable = NULL; 
1108         TnyFolderType type;
1109
1110         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1111         g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1112         
1113         /* Get model and private data */
1114         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);            
1115         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1116         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1117 /*      sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1118         
1119         /* Sort tree model */
1120         type  = modest_tny_folder_guess_folder_type (priv->folder);
1121         if (type == TNY_FOLDER_TYPE_INVALID)
1122                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1123         else {
1124                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1125                                               sort_colid,
1126                                               sort_type);
1127                 /* Store new sort parameters */
1128                 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1129         }       
1130 }
1131
1132 void
1133 modest_header_view_set_sort_params (ModestHeaderView *self, 
1134                                     guint sort_colid, 
1135                                     GtkSortType sort_type,
1136                                     TnyFolderType type)
1137 {
1138         ModestHeaderViewPrivate *priv;
1139         ModestHeaderViewStyle style;
1140         
1141         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1142         g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1143         g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1144         
1145         style = modest_header_view_get_style   (self);
1146         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1147
1148         priv->sort_colid[style][type] = sort_colid;
1149         priv->sort_type[style][type] = sort_type;
1150 }
1151
1152 gint
1153 modest_header_view_get_sort_column_id (ModestHeaderView *self, 
1154                                        TnyFolderType type)
1155 {
1156         ModestHeaderViewPrivate *priv;
1157         ModestHeaderViewStyle style;
1158
1159         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1160         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1161         
1162         style = modest_header_view_get_style   (self);
1163         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1164
1165         return priv->sort_colid[style][type];
1166 }
1167
1168 GtkSortType
1169 modest_header_view_get_sort_type (ModestHeaderView *self, 
1170                                   TnyFolderType type)
1171 {
1172         ModestHeaderViewPrivate *priv;
1173         ModestHeaderViewStyle style;
1174         
1175         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1176         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1177         
1178         style = modest_header_view_get_style   (self);
1179         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1180
1181         return priv->sort_type[style][type];
1182 }
1183
1184 typedef struct {
1185         ModestHeaderView *header_view;
1186         RefreshAsyncUserCallback cb;
1187         gpointer user_data;
1188 } SetFolderHelper;
1189
1190 static void
1191 folder_refreshed_cb (ModestMailOperation *mail_op,
1192                      TnyFolder *folder,
1193                      gpointer user_data)
1194 {
1195         ModestHeaderViewPrivate *priv;
1196         SetFolderHelper *info;
1197  
1198         info = (SetFolderHelper*) user_data;
1199
1200         priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1201
1202         /* User callback */
1203         if (info->cb)
1204                 info->cb (mail_op, folder, info->user_data);
1205
1206         /* Start the folder count changes observer. We do not need it
1207            before the refresh. Note that the monitor could still be
1208            called for this refresh but now we know that the callback
1209            was previously called */
1210         g_mutex_lock (priv->observers_lock);
1211         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1212         g_mutex_unlock (priv->observers_lock);
1213
1214         /* Notify the observers that the update is over */
1215         g_signal_emit (G_OBJECT (info->header_view), 
1216                        signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1217
1218         /* Allow filtering notifications from now on if the current
1219            folder is still the same (if not then the user has selected
1220            another one to refresh, we should wait until that refresh
1221            finishes) */
1222         if (priv->folder == folder)
1223                 priv->notify_status = TRUE;
1224
1225         /* Frees */
1226         g_object_unref (info->header_view);
1227         g_free (info);
1228 }
1229
1230 static void
1231 refresh_folder_error_handler (ModestMailOperation *mail_op, 
1232                               gpointer user_data)
1233 {
1234         const GError *error = modest_mail_operation_get_error (mail_op);
1235
1236         if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1237             error->code == TNY_IO_ERROR_WRITE ||
1238             error->code == TNY_IO_ERROR_READ) {
1239                 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1240                 /* If the mail op has been cancelled then it's not an error: don't show any message */
1241                 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1242                         modest_platform_information_banner (NULL, NULL,
1243                                                             dgettext("ke-recv",
1244                                                                      "cerm_device_memory_full"));
1245                 }
1246         }
1247 }
1248
1249 void
1250 modest_header_view_set_folder (ModestHeaderView *self, 
1251                                TnyFolder *folder,
1252                                RefreshAsyncUserCallback callback,
1253                                gpointer user_data)
1254 {
1255         ModestHeaderViewPrivate *priv;
1256         SetFolderHelper *info;
1257         ModestWindow *main_win;
1258         
1259         g_return_if_fail (self);
1260
1261         priv =     MODEST_HEADER_VIEW_GET_PRIVATE(self);
1262
1263         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1264                                                       FALSE); /* don't create */
1265         if (!main_win) {
1266                 g_warning ("%s: BUG: no main window", __FUNCTION__);
1267                 return;
1268         }
1269                                                       
1270         if (priv->folder) {
1271                 if (priv->status_timeout) {
1272                         g_source_remove (priv->status_timeout);
1273                         priv->status_timeout = 0;
1274                 }
1275
1276                 g_mutex_lock (priv->observers_lock);
1277                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1278                 g_object_unref (priv->folder);
1279                 priv->folder = NULL;
1280                 g_mutex_unlock (priv->observers_lock);
1281         }
1282
1283         if (folder) {
1284                 ModestMailOperation *mail_op = NULL;
1285                 GtkTreeSelection *selection;
1286
1287                 /* Set folder in the model */
1288                 modest_header_view_set_folder_intern (self, folder);
1289                 
1290                 /* Pick my reference. Nothing to do with the mail operation */
1291                 priv->folder = g_object_ref (folder);
1292
1293                 /* Do not notify about filterings until the refresh finishes */
1294                 priv->notify_status = FALSE;
1295
1296                 /* Clear the selection if exists */
1297                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1298                 gtk_tree_selection_unselect_all(selection);
1299                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1300
1301                 /* Notify the observers that the update begins */
1302                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1303                                0, TRUE, NULL);
1304
1305                 /* create the helper */
1306                 info = g_malloc0 (sizeof(SetFolderHelper));
1307                 info->header_view = g_object_ref (self);
1308                 info->cb = callback;
1309                 info->user_data = user_data;
1310
1311                 /* Create the mail operation (source will be the parent widget) */
1312                 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1313                                                                          refresh_folder_error_handler,
1314                                                                          NULL, NULL);
1315                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1316                                                  mail_op);
1317
1318                 /* Refresh the folder asynchronously */
1319                 modest_mail_operation_refresh_folder (mail_op,
1320                                                       folder,
1321                                                       folder_refreshed_cb,
1322                                                       info);
1323
1324                 /* Free */
1325                 g_object_unref (mail_op);
1326         } else {
1327                 g_mutex_lock (priv->observers_lock);
1328
1329                 if (priv->monitor) {
1330                         tny_folder_monitor_stop (priv->monitor);
1331                         g_object_unref (G_OBJECT (priv->monitor));
1332                         priv->monitor = NULL;
1333                 }
1334                 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL); 
1335
1336                 modest_header_view_notify_observers(self, NULL, NULL);
1337
1338                 g_mutex_unlock (priv->observers_lock);
1339
1340                 /* Notify the observers that the update is over */
1341                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1342                                0, FALSE, NULL);
1343         }
1344 }
1345
1346 static void
1347 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1348                          GtkTreeViewColumn *column, gpointer userdata)
1349 {
1350         ModestHeaderView *self = NULL;
1351         ModestHeaderViewPrivate *priv = NULL;
1352         GtkTreeIter iter;
1353         GtkTreeModel *model = NULL;
1354         TnyHeader *header = NULL;
1355         TnyHeaderFlags flags;
1356
1357         self = MODEST_HEADER_VIEW (treeview);
1358         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1359
1360         model = gtk_tree_view_get_model (treeview);     
1361         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
1362                 goto frees;
1363
1364         /* get the first selected item */
1365         gtk_tree_model_get (model, &iter,
1366                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1367                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, 
1368                             -1);
1369
1370         /* Dont open DELETED messages */
1371         if (flags & TNY_HEADER_FLAG_DELETED) {
1372                 GtkWidget *win;
1373                 gchar *msg;
1374                 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1375                 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1376                 modest_platform_information_banner (NULL, NULL, msg);
1377                 g_free (msg);
1378                 goto frees;
1379         }
1380
1381         /* Emit signal */
1382         g_signal_emit (G_OBJECT(self), 
1383                        signals[HEADER_ACTIVATED_SIGNAL], 
1384                        0, header);
1385
1386         /* Free */
1387  frees:
1388         if (header != NULL) 
1389                 g_object_unref (G_OBJECT (header));     
1390
1391 }
1392
1393 static void
1394 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1395 {
1396         GtkTreeModel *model;
1397         TnyHeader *header = NULL;
1398         GtkTreePath *path = NULL;       
1399         GtkTreeIter iter;
1400         ModestHeaderView *self;
1401         ModestHeaderViewPrivate *priv;
1402         GList *selected = NULL;
1403         
1404         g_return_if_fail (sel);
1405         g_return_if_fail (user_data);
1406         
1407         self = MODEST_HEADER_VIEW (user_data);
1408         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
1409
1410         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1411         if (selected != NULL) 
1412                 path = (GtkTreePath *) selected->data;
1413         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1414                 return; /* msg was _un_selected */
1415
1416         gtk_tree_model_get (model, &iter,
1417                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1418                             &header, -1);
1419
1420         /* Emit signal */
1421         g_signal_emit (G_OBJECT(self), 
1422                        signals[HEADER_SELECTED_SIGNAL], 
1423                        0, header);
1424
1425         g_object_unref (G_OBJECT (header));
1426
1427         /* free all items in 'selected' */
1428         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1429         g_list_free (selected);
1430 }
1431
1432
1433 /* PROTECTED method. It's useful when we want to force a given
1434    selection to reload a msg. For example if we have selected a header
1435    in offline mode, when Modest become online, we want to reload the
1436    message automatically without an user click over the header */
1437 void 
1438 _modest_header_view_change_selection (GtkTreeSelection *selection,
1439                                       gpointer user_data)
1440 {
1441         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1442         g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1443         
1444         on_selection_changed (selection, user_data);
1445 }
1446
1447 static gint
1448 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1449 {
1450         /* HH, LL, NN */
1451         if (p1 == p2)
1452                 return 0;
1453
1454         /* HL HN */
1455         if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1456                 return 1;
1457
1458         /* LH LN */
1459         if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1460                 return -1;
1461
1462         /* NH */
1463         if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1464                 return -1;
1465
1466         /* NL */
1467         return 1;
1468 }
1469
1470 static gint
1471 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1472           gpointer user_data)
1473 {
1474         gint col_id;
1475         gint t1, t2;
1476         gint val1, val2;
1477         gint cmp;
1478 /*      static int counter = 0; */
1479
1480         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1481 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1482         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1483
1484         
1485         switch (col_id) {
1486         case TNY_HEADER_FLAG_ATTACHMENTS:
1487
1488                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1489                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1490                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1491                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1492
1493                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1494                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1495
1496                 return cmp ? cmp : t1 - t2;
1497                 
1498         case TNY_HEADER_FLAG_PRIORITY_MASK: {
1499                 TnyHeader *header1 = NULL, *header2 = NULL;
1500
1501                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1502                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1503                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1504                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1505
1506                 /* This is for making priority values respect the intuitive sort relationship 
1507                  * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1508
1509                 if (header1 && header2) {
1510                         cmp =  compare_priorities (tny_header_get_priority (header1), 
1511                                 tny_header_get_priority (header2));
1512                         g_object_unref (header1);
1513                         g_object_unref (header2);
1514
1515                         return cmp ? cmp : t1 - t2;
1516                 }
1517
1518                 return t1 - t2;
1519         }
1520         default:
1521                 return &iter1 - &iter2; /* oughhhh  */
1522         }
1523 }
1524
1525 static gint
1526 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1527                   gpointer user_data)
1528 {
1529         gint t1, t2;
1530         gchar *val1, *val2;
1531         gint cmp;
1532 /*      static int counter = 0; */
1533
1534         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1535
1536         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1537                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1538         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1539                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1540
1541         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1542                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1543                                              TRUE);
1544         g_free (val1);
1545         g_free (val2);
1546         return cmp;
1547 }
1548
1549 /* Drag and drop stuff */
1550 static void
1551 drag_data_get_cb (GtkWidget *widget, 
1552                   GdkDragContext *context, 
1553                   GtkSelectionData *selection_data, 
1554                   guint info,  
1555                   guint time, 
1556                   gpointer data)
1557 {
1558         ModestHeaderView *self = NULL;
1559         ModestHeaderViewPrivate *priv = NULL;
1560
1561         self = MODEST_HEADER_VIEW (widget);
1562         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1563
1564         /* Set the data. Do not use the current selection because it
1565            could be different than the selection at the beginning of
1566            the d&d */
1567         modest_dnd_selection_data_set_paths (selection_data, 
1568                                              priv->drag_begin_cached_selected_rows);
1569 }
1570
1571 /**
1572  * We're caching the selected rows at the beginning because the
1573  * selection could change between drag-begin and drag-data-get, for
1574  * example if we have a set of rows already selected, and then we
1575  * click in one of them (without SHIFT key pressed) and begin a drag,
1576  * the selection at that moment contains all the selected lines, but
1577  * after dropping the selection, the release event provokes that only
1578  * the row used to begin the drag is selected, so at the end the
1579  * drag&drop affects only one rows instead of all the selected ones.
1580  *
1581  */
1582 static void
1583 drag_begin_cb (GtkWidget *widget, 
1584                GdkDragContext *context, 
1585                gpointer data)
1586 {
1587         ModestHeaderView *self = NULL;
1588         ModestHeaderViewPrivate *priv = NULL;
1589         GtkTreeSelection *selection;
1590
1591         self = MODEST_HEADER_VIEW (widget);
1592         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1593
1594         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1595         priv->drag_begin_cached_selected_rows = 
1596                 gtk_tree_selection_get_selected_rows (selection, NULL);
1597 }
1598
1599 /**
1600  * We use the drag-end signal to clear the cached selection, we use
1601  * this because this allways happens, whether or not the d&d was a
1602  * success
1603  */
1604 static void
1605 drag_end_cb (GtkWidget *widget, 
1606              GdkDragContext *dc, 
1607              gpointer data)
1608 {
1609         ModestHeaderView *self = NULL;
1610         ModestHeaderViewPrivate *priv = NULL;
1611
1612         self = MODEST_HEADER_VIEW (widget);
1613         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1614
1615         /* Free cached data */
1616         g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1617         g_list_free (priv->drag_begin_cached_selected_rows);
1618         priv->drag_begin_cached_selected_rows = NULL;
1619 }
1620
1621 /* Header view drag types */
1622 const GtkTargetEntry header_view_drag_types[] = {
1623         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1624 };
1625
1626 static void
1627 enable_drag_and_drop (GtkWidget *self)
1628 {
1629         gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1630                              header_view_drag_types,
1631                              G_N_ELEMENTS (header_view_drag_types),
1632                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1633 }
1634
1635 static void
1636 disable_drag_and_drop (GtkWidget *self)
1637 {
1638         gtk_drag_source_unset (self);
1639 }
1640
1641 static void
1642 setup_drag_and_drop (GtkWidget *self)
1643 {
1644         enable_drag_and_drop(self);
1645         g_signal_connect(G_OBJECT (self), "drag_data_get",
1646                          G_CALLBACK(drag_data_get_cb), NULL);
1647
1648         g_signal_connect(G_OBJECT (self), "drag_begin",
1649                          G_CALLBACK(drag_begin_cb), NULL);
1650
1651         g_signal_connect(G_OBJECT (self), "drag_end",
1652                          G_CALLBACK(drag_end_cb), NULL);
1653 }
1654
1655 static GtkTreePath *
1656 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1657 {
1658         GtkTreePath *path = NULL;
1659         GtkTreeSelection *sel = NULL;   
1660         GList *rows = NULL;
1661
1662         sel   = gtk_tree_view_get_selection(self);
1663         rows = gtk_tree_selection_get_selected_rows (sel, model);
1664         
1665         if ((rows == NULL) || (g_list_length(rows) != 1))
1666                 goto frees;
1667
1668         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1669         
1670
1671         /* Free */
1672  frees:
1673         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1674         g_list_free(rows);
1675
1676         return path;
1677 }
1678
1679 /*
1680  * This function moves the tree view scroll to the current selected
1681  * row when the widget grabs the focus 
1682  */
1683 static gboolean 
1684 on_focus_in (GtkWidget     *self,
1685              GdkEventFocus *event,
1686              gpointer       user_data)
1687 {
1688         GtkTreeSelection *selection;
1689         GtkTreeModel *model;
1690         GList *selected = NULL;
1691         GtkTreePath *selected_path = NULL;
1692
1693         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1694         if (!model)
1695                 return FALSE;
1696
1697         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1698         /* If none selected yet, pick the first one */
1699         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1700                 GtkTreeIter iter;
1701                 GtkTreePath *path;
1702
1703                 /* Return if the model is empty */
1704                 if (!gtk_tree_model_get_iter_first (model, &iter))
1705                         return FALSE;
1706
1707                 path = gtk_tree_model_get_path (model, &iter);
1708                 gtk_tree_selection_select_path (selection, path);
1709                 gtk_tree_path_free (path);
1710         }
1711
1712         /* Need to get the all the rows because is selection multiple */
1713         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1714         if (selected == NULL) return FALSE;
1715         selected_path = (GtkTreePath *) selected->data;
1716
1717         /* Check if we need to scroll */
1718         #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1719         GtkTreePath *start_path = NULL;
1720         GtkTreePath *end_path = NULL;
1721         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1722                                              &start_path,
1723                                              &end_path)) {
1724
1725                 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1726                     (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1727
1728                         /* Scroll to first path */
1729                         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1730                                                       selected_path,
1731                                                       NULL,
1732                                                       TRUE,
1733                                                       0.5,
1734                                                       0.0);
1735                 }
1736         }
1737         if (start_path)
1738                 gtk_tree_path_free (start_path);
1739         if (end_path)
1740                 gtk_tree_path_free (end_path);
1741
1742         #endif /* GTK_CHECK_VERSION */
1743
1744         /* Frees */     
1745         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1746         g_list_free (selected);
1747
1748         return FALSE;
1749 }
1750
1751 static gboolean 
1752 on_focus_out (GtkWidget     *self,
1753              GdkEventFocus *event,
1754              gpointer       user_data)
1755 {
1756
1757         if (!gtk_widget_is_focus (self)) {
1758                 GtkTreeSelection *selection = NULL;
1759                 GList *selected_rows = NULL;
1760                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1761                 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1762                         selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1763                         g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1764                         gtk_tree_selection_unselect_all (selection);
1765                         gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1766                         g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1767                         g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1768                         g_list_free (selected_rows);
1769                 }
1770         }
1771         return FALSE;
1772 }
1773
1774 static gboolean
1775 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1776 {
1777         enable_drag_and_drop(self);
1778         return FALSE;
1779 }
1780
1781 static gboolean
1782 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1783 {
1784         GtkTreeSelection *selection = NULL;
1785         GtkTreePath *path = NULL;
1786         gboolean already_selected = FALSE;
1787         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1788
1789         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1790                 GtkTreeIter iter;
1791                 GtkTreeModel *model;
1792
1793                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1794                 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1795
1796                 /* Get header from model */
1797                 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1798                 if (gtk_tree_model_get_iter (model, &iter, path)) {
1799                         GValue value = {0,};
1800                         TnyHeader *header;
1801
1802                         gtk_tree_model_get_value (model, &iter, 
1803                                                   TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1804                                                   &value);
1805                         header = (TnyHeader *) g_value_get_object (&value);
1806                         if (TNY_IS_HEADER (header))
1807                                 status = modest_tny_all_send_queues_get_msg_status (header);
1808                         g_value_unset (&value);
1809                 }
1810         }
1811
1812         /* Enable drag and drop onlly if the user clicks on a row that
1813            it's already selected. If not, let him select items using
1814            the pointer. If the message is in an outbos and in sending
1815            status disable drag and drop as well */
1816         if (!already_selected || status == MODEST_TNY_SEND_QUEUE_SENDING)
1817                 disable_drag_and_drop(self);
1818
1819         if (path != NULL)
1820                 gtk_tree_path_free(path);
1821
1822         return FALSE;
1823 }
1824
1825 static void
1826 folder_monitor_update (TnyFolderObserver *self, 
1827                        TnyFolderChange *change)
1828 {
1829         ModestHeaderViewPrivate *priv = NULL;
1830         TnyFolderChangeChanged changed;
1831         TnyFolder *folder = NULL;
1832
1833         changed = tny_folder_change_get_changed (change);
1834         
1835         /* Do not notify the observers if the folder of the header
1836            view has changed before this call to the observer
1837            happens */
1838         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1839         folder = tny_folder_change_get_folder (change);
1840         if (folder != priv->folder)
1841                 goto frees;
1842
1843         MODEST_DEBUG_BLOCK (
1844                             if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1845                                     g_print ("ADDED %d/%d (r/t) \n", 
1846                                              tny_folder_change_get_new_unread_count (change),
1847                                              tny_folder_change_get_new_all_count (change));
1848                             if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1849                                     g_print ("ALL COUNT %d\n", 
1850                                              tny_folder_change_get_new_all_count (change));
1851                             if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1852                                     g_print ("UNREAD COUNT %d\n", 
1853                                              tny_folder_change_get_new_unread_count (change));
1854                             if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1855                                     g_print ("EXPUNGED %d/%d (r/t) \n", 
1856                                              tny_folder_change_get_new_unread_count (change),
1857                                              tny_folder_change_get_new_all_count (change));
1858                             if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1859                                     g_print ("FOLDER RENAME\n");
1860                             if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1861                                     g_print ("MSG RECEIVED %d/%d (r/t) \n", 
1862                                              tny_folder_change_get_new_unread_count (change),
1863                                              tny_folder_change_get_new_all_count (change));
1864                             g_print ("---------------------------------------------------\n");
1865                             );
1866
1867         /* Check folder count */
1868         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1869             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1870
1871                 g_mutex_lock (priv->observers_lock);
1872
1873                 /* Emit signal to evaluate how headers changes affects
1874                    to the window view  */
1875                 g_signal_emit (G_OBJECT(self), 
1876                                signals[MSG_COUNT_CHANGED_SIGNAL], 
1877                                0, folder, change);
1878                 
1879                 /* Added or removed headers, so data stored on cliboard are invalid  */
1880                 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1881                         modest_email_clipboard_clear (priv->clipboard);
1882             
1883                 g_mutex_unlock (priv->observers_lock);
1884         }       
1885
1886         /* Free */
1887  frees:
1888         if (folder != NULL)
1889                 g_object_unref (folder);
1890 }
1891
1892 gboolean
1893 modest_header_view_is_empty (ModestHeaderView *self)
1894 {
1895         ModestHeaderViewPrivate *priv;
1896         
1897         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1898         
1899         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1900
1901         return priv->status == HEADER_VIEW_EMPTY;
1902 }
1903
1904 void
1905 modest_header_view_clear (ModestHeaderView *self)
1906 {
1907         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1908         
1909         modest_header_view_set_folder (self, NULL, NULL, NULL);
1910 }
1911
1912 void 
1913 modest_header_view_copy_selection (ModestHeaderView *header_view)
1914 {
1915         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1916         
1917         /* Copy selection */
1918         _clipboard_set_selected_data (header_view, FALSE);
1919 }
1920
1921 void 
1922 modest_header_view_cut_selection (ModestHeaderView *header_view)
1923 {
1924         ModestHeaderViewPrivate *priv = NULL;
1925         const gchar **hidding = NULL;
1926         guint i, n_selected;
1927
1928         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1929         
1930         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1931
1932         /* Copy selection */
1933         _clipboard_set_selected_data (header_view, TRUE);
1934
1935         /* Get hidding ids */
1936         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1937         
1938         /* Clear hidding array created by previous cut operation */
1939         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1940
1941         /* Copy hidding array */
1942         priv->n_selected = n_selected;
1943         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1944         for (i=0; i < n_selected; i++) 
1945                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1946
1947         /* Hide cut headers */
1948         modest_header_view_refilter (header_view);
1949 }
1950
1951
1952  
1953
1954 static void
1955 _clipboard_set_selected_data (ModestHeaderView *header_view,
1956                               gboolean delete)
1957 {
1958         ModestHeaderViewPrivate *priv = NULL;
1959         TnyList *headers = NULL;
1960
1961         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1962         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1963                 
1964         /* Set selected data on clipboard   */
1965         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1966         headers = modest_header_view_get_selected_headers (header_view);
1967         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1968
1969         /* Free */
1970         g_object_unref (headers);
1971 }
1972
1973 typedef struct {
1974         ModestHeaderView *self;
1975         TnyFolder *folder;
1976 } NotifyFilterInfo;
1977
1978 static gboolean
1979 notify_filter_change (gpointer data)
1980 {
1981         NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1982
1983         g_signal_emit (info->self, 
1984                        signals[MSG_COUNT_CHANGED_SIGNAL], 
1985                        0, info->folder, NULL);
1986
1987         return FALSE;
1988 }
1989
1990 static void
1991 notify_filter_change_destroy (gpointer data)
1992 {
1993         NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1994         ModestHeaderViewPrivate *priv;
1995
1996         priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
1997         priv->status_timeout = 0;
1998
1999         g_object_unref (info->self);
2000         g_object_unref (info->folder);
2001         g_slice_free (NotifyFilterInfo, info);
2002 }
2003
2004 static gboolean
2005 filter_row (GtkTreeModel *model,
2006             GtkTreeIter *iter,
2007             gpointer user_data)
2008 {
2009         ModestHeaderViewPrivate *priv = NULL;
2010         TnyHeaderFlags flags;
2011         TnyHeader *header = NULL;
2012         guint i;
2013         gchar *id = NULL;
2014         gboolean visible = TRUE;
2015         gboolean found = FALSE;
2016         GValue value = {0,};
2017         HeaderViewStatus old_status;
2018
2019         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2020         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2021
2022         /* Get header from model */
2023         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2024         flags = (TnyHeaderFlags) g_value_get_int (&value);
2025         g_value_unset (&value);
2026         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2027         header = (TnyHeader *) g_value_get_object (&value);
2028         g_value_unset (&value);
2029         
2030         /* Hide deleted and mark as deleted heders */
2031         if (flags & TNY_HEADER_FLAG_DELETED ||
2032             flags & TNY_HEADER_FLAG_EXPUNGED) {
2033                 visible = FALSE;
2034                 goto frees;
2035         }
2036
2037         /* If no data on clipboard, return always TRUE */
2038         if (modest_email_clipboard_cleared(priv->clipboard)) {
2039                 visible = TRUE;
2040                 goto frees;
2041         }               
2042
2043         /* Get message id from header (ensure is a valid id) */
2044         if (!header) {
2045                 visible = FALSE;
2046                 goto frees;
2047         }
2048         
2049         /* Check hiding */
2050         if (priv->hidding_ids != NULL) {
2051                 id = tny_header_dup_message_id (header);
2052                 for (i=0; i < priv->n_selected && !found; i++)
2053                         if (priv->hidding_ids[i] != NULL && id != NULL)
2054                                 found = (!strcmp (priv->hidding_ids[i], id));
2055         
2056                 visible = !found;
2057                 g_free(id);
2058         }
2059
2060  frees:
2061         old_status = priv->status;
2062         priv->status = ((gboolean) priv->status) && !visible;
2063         if ((priv->notify_status) && (priv->status != old_status)) {
2064                 NotifyFilterInfo *info;
2065
2066                 if (priv->status_timeout)
2067                         g_source_remove (priv->status_timeout);
2068
2069                 info = g_slice_new0 (NotifyFilterInfo);
2070                 info->self = g_object_ref (G_OBJECT (user_data));
2071                 info->folder = tny_header_get_folder (header);
2072                 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2073                                                            notify_filter_change,
2074                                                            info,
2075                                                            notify_filter_change_destroy);
2076         }
2077
2078         return visible;
2079 }
2080
2081 static void
2082 _clear_hidding_filter (ModestHeaderView *header_view) 
2083 {
2084         ModestHeaderViewPrivate *priv = NULL;
2085         guint i;
2086         
2087         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
2088         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2089
2090         if (priv->hidding_ids != NULL) {
2091                 for (i=0; i < priv->n_selected; i++) 
2092                         g_free (priv->hidding_ids[i]);
2093                 g_free(priv->hidding_ids);
2094         }       
2095 }
2096
2097 void 
2098 modest_header_view_refilter (ModestHeaderView *header_view)
2099 {
2100         GtkTreeModel *model = NULL;
2101         ModestHeaderViewPrivate *priv = NULL;
2102
2103         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2104         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2105         
2106         /* Hide cut headers */
2107         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2108         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2109                 priv->status = HEADER_VIEW_INIT;
2110                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2111         }
2112 }
2113
2114 /* 
2115  * Called when an account is removed. If I'm showing a folder of the
2116  * account that has been removed then clear the view
2117  */
2118 static void
2119 on_account_removed (TnyAccountStore *self, 
2120                     TnyAccount *account,
2121                     gpointer user_data)
2122 {
2123         ModestHeaderViewPrivate *priv = NULL;
2124
2125         /* Ignore changes in transport accounts */
2126         if (TNY_IS_TRANSPORT_ACCOUNT (account))
2127                 return;
2128
2129         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2130
2131         if (priv->folder) {
2132                 TnyAccount *my_account;
2133
2134                 my_account = tny_folder_get_account (priv->folder);
2135                 if (my_account == account)
2136                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2137                 g_object_unref (my_account);
2138         }
2139 }
2140
2141 void
2142 modest_header_view_add_observer(ModestHeaderView *header_view,
2143                                      ModestHeaderViewObserver *observer)
2144 {
2145         ModestHeaderViewPrivate *priv;
2146         
2147         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2148         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2149
2150         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2151
2152         g_mutex_lock(priv->observer_list_lock);
2153         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2154         g_mutex_unlock(priv->observer_list_lock);
2155 }
2156
2157 void 
2158 modest_header_view_remove_observer(ModestHeaderView *header_view,
2159                                    ModestHeaderViewObserver *observer)
2160 {
2161         ModestHeaderViewPrivate *priv;
2162
2163         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2164         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2165
2166         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2167
2168         g_mutex_lock(priv->observer_list_lock);
2169         priv->observer_list = g_slist_remove(priv->observer_list, observer);
2170         g_mutex_unlock(priv->observer_list_lock);
2171 }
2172
2173 static void 
2174 modest_header_view_notify_observers(ModestHeaderView *header_view,
2175                                     GtkTreeModel *model,
2176                                     const gchar *tny_folder_id)
2177 {
2178         ModestHeaderViewPrivate *priv = NULL;
2179         GSList *iter;
2180         ModestHeaderViewObserver *observer;
2181
2182
2183         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2184         
2185         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2186
2187         g_mutex_lock(priv->observer_list_lock);
2188         iter = priv->observer_list;
2189         while(iter != NULL){
2190                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2191                 modest_header_view_observer_update(observer, model,
2192                                 tny_folder_id);
2193                 iter = g_slist_next(iter);
2194         }
2195         g_mutex_unlock(priv->observer_list_lock);
2196 }
2197