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