* src/widgets/modest-header-view-render.c:
[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 <string.h>
35
36 #include <modest-header-view.h>
37 #include <modest-header-view-priv.h>
38 #include <modest-dnd.h>
39 #include <modest-tny-folder.h>
40
41 #include <modest-marshal.h>
42 #include <modest-text-utils.h>
43 #include <modest-icon-names.h>
44 #include <modest-runtime.h>
45 #include "modest-platform.h"
46 #include <modest-hbox-cell-renderer.h>
47 #include <modest-vbox-cell-renderer.h>
48
49 static void modest_header_view_class_init  (ModestHeaderViewClass *klass);
50 static void modest_header_view_init        (ModestHeaderView *obj);
51 static void modest_header_view_finalize    (GObject *obj);
52
53 static gboolean      on_header_clicked      (GtkWidget *widget, 
54                                             GdkEventButton *event, 
55                                             gpointer user_data);
56
57 static gint          cmp_rows               (GtkTreeModel *tree_model,
58                                              GtkTreeIter *iter1,
59                                              GtkTreeIter *iter2,
60                                              gpointer user_data);
61
62 static void          on_selection_changed   (GtkTreeSelection *sel, 
63                                              gpointer user_data);
64
65 static void          setup_drag_and_drop    (GtkTreeView *self);
66
67 static GtkTreePath * get_selected_row       (GtkTreeView *self, GtkTreeModel **model);
68
69 static gboolean      on_focus_in            (GtkWidget     *sef,
70                                              GdkEventFocus *event,
71                                              gpointer       user_data);
72
73 static void          folder_monitor_update  (TnyFolderObserver *self, 
74                                              TnyFolderChange *change);
75
76 static void          tny_folder_observer_init (TnyFolderObserverIface *klass);
77
78 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
79 struct _ModestHeaderViewPrivate {
80         TnyFolder            *folder;
81         ModestHeaderViewStyle style;
82
83         TnyFolderMonitor     *monitor;
84         GMutex               *observers_lock;
85
86         gint                  sort_colid[2][TNY_FOLDER_TYPE_NUM];
87         gint                  sort_type[2][TNY_FOLDER_TYPE_NUM];
88
89
90 };
91
92 #define MODEST_HEADER_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
93                                                 MODEST_TYPE_HEADER_VIEW, \
94                                                 ModestHeaderViewPrivate))
95
96
97
98 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
99
100 enum {
101         HEADER_SELECTED_SIGNAL,
102         HEADER_ACTIVATED_SIGNAL,
103         ITEM_NOT_FOUND_SIGNAL,
104         STATUS_UPDATE_SIGNAL,
105         LAST_SIGNAL
106 };
107
108 /* globals */
109 static GObjectClass *parent_class = NULL;
110
111 /* uncomment the following if you have defined any signals */
112 static guint signals[LAST_SIGNAL] = {0};
113
114 GType
115 modest_header_view_get_type (void)
116 {
117         static GType my_type = 0;
118         if (!my_type) {
119                 static const GTypeInfo my_info = {
120                         sizeof(ModestHeaderViewClass),
121                         NULL,           /* base init */
122                         NULL,           /* base finalize */
123                         (GClassInitFunc) modest_header_view_class_init,
124                         NULL,           /* class finalize */
125                         NULL,           /* class data */
126                         sizeof(ModestHeaderView),
127                         1,              /* n_preallocs */
128                         (GInstanceInitFunc) modest_header_view_init,
129                         NULL
130                 };
131
132                 static const GInterfaceInfo tny_folder_observer_info = 
133                 {
134                         (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
135                         NULL,         /* interface_finalize */
136                         NULL          /* interface_data */
137                 };
138                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
139                                                   "ModestHeaderView",
140                                                   &my_info, 0);
141
142                 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
143                                              &tny_folder_observer_info);
144
145
146         }
147         return my_type;
148 }
149
150 static void
151 modest_header_view_class_init (ModestHeaderViewClass *klass)
152 {
153         GObjectClass *gobject_class;
154         gobject_class = (GObjectClass*) klass;
155
156         parent_class            = g_type_class_peek_parent (klass);
157         gobject_class->finalize = modest_header_view_finalize;
158         
159         g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
160         
161         signals[HEADER_SELECTED_SIGNAL] = 
162                 g_signal_new ("header_selected",
163                               G_TYPE_FROM_CLASS (gobject_class),
164                               G_SIGNAL_RUN_FIRST,
165                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
166                               NULL, NULL,
167                               g_cclosure_marshal_VOID__POINTER,
168                               G_TYPE_NONE, 1, G_TYPE_POINTER);
169
170         signals[HEADER_ACTIVATED_SIGNAL] = 
171                 g_signal_new ("header_activated",
172                               G_TYPE_FROM_CLASS (gobject_class),
173                               G_SIGNAL_RUN_FIRST,
174                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
175                               NULL, NULL,
176                               g_cclosure_marshal_VOID__POINTER,
177                               G_TYPE_NONE, 1, G_TYPE_POINTER);
178         
179         
180         signals[ITEM_NOT_FOUND_SIGNAL] = 
181                 g_signal_new ("item_not_found",
182                               G_TYPE_FROM_CLASS (gobject_class),
183                               G_SIGNAL_RUN_FIRST,
184                               G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
185                               NULL, NULL,
186                               g_cclosure_marshal_VOID__INT,
187                               G_TYPE_NONE, 1, G_TYPE_INT);
188
189         signals[STATUS_UPDATE_SIGNAL] =
190                 g_signal_new ("status_update",
191                               G_TYPE_FROM_CLASS (gobject_class),
192                               G_SIGNAL_RUN_FIRST,
193                               G_STRUCT_OFFSET (ModestHeaderViewClass,status_update),
194                               NULL, NULL,
195                               modest_marshal_VOID__STRING_INT_INT,
196                               G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
197 }
198
199 static void
200 tny_folder_observer_init (TnyFolderObserverIface *klass)
201 {
202         klass->update_func = folder_monitor_update;
203 }
204
205 static GtkTreeViewColumn*
206 get_new_column (const gchar *name, GtkCellRenderer *renderer,
207                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
208                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
209 {
210         GtkTreeViewColumn *column;
211
212         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
213         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
214
215         gtk_tree_view_column_set_resizable (column, resizable);
216         if (resizable) 
217                 gtk_tree_view_column_set_expand (column, TRUE);
218         
219         if (show_as_text) 
220                 gtk_tree_view_column_add_attribute (column, renderer, "text",
221                                                     sort_col_id);
222         if (sort_col_id >= 0)
223                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
224
225         gtk_tree_view_column_set_sort_indicator (column, FALSE);
226         gtk_tree_view_column_set_reorderable (column, TRUE);
227         
228         if (cell_data_func)
229                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
230                                                         user_data, NULL);
231         return column;
232 }
233
234
235 static void
236 remove_all_columns (ModestHeaderView *obj)
237 {
238         GList *columns, *cursor;
239         
240         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
241
242         for (cursor = columns; cursor; cursor = cursor->next)
243                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
244                                              GTK_TREE_VIEW_COLUMN(cursor->data));
245         g_list_free (columns);  
246 }
247
248 gboolean
249 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
250 {
251         GtkTreeModel *sortable;
252         GtkTreeViewColumn *column=NULL;
253         GtkTreeSelection *selection = NULL;
254         GtkCellRenderer *renderer_msgtype,*renderer_header,
255                 *renderer_attach, *renderer_compact_date;
256         GtkCellRenderer *renderer_compact_header, *renderer_recpt_box, 
257                 *renderer_subject, *renderer_subject_box, *renderer_recpt,
258                 *renderer_priority;
259         ModestHeaderViewPrivate *priv;
260         GtkTreeViewColumn *compact_column = NULL;
261         const GList *cursor;
262         
263         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self); 
264
265         /* FIXME: check whether these renderers need to be freed */
266         renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
267         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
268         renderer_priority  = gtk_cell_renderer_pixbuf_new ();
269         renderer_header  = gtk_cell_renderer_text_new ();
270
271         renderer_compact_header = modest_vbox_cell_renderer_new ();
272         renderer_recpt_box = modest_hbox_cell_renderer_new ();
273         renderer_subject_box = modest_hbox_cell_renderer_new ();
274         renderer_recpt = gtk_cell_renderer_text_new ();
275         renderer_subject = gtk_cell_renderer_text_new ();
276         renderer_compact_date  = gtk_cell_renderer_text_new ();
277
278         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
279         g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
280         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
281         g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
282         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
283         g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
284         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
285         g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
286         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
287         g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
288         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
289         g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
290         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date, FALSE);
291         g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date);
292
293         g_object_set(G_OBJECT(renderer_header),
294                      "ellipsize", PANGO_ELLIPSIZE_END,
295                      NULL);
296         g_object_set (G_OBJECT (renderer_subject),
297                       "ellipsize", PANGO_ELLIPSIZE_END,
298                       NULL);
299         g_object_set (G_OBJECT (renderer_recpt),
300                       "ellipsize", PANGO_ELLIPSIZE_END,
301                       NULL);
302         g_object_set(G_OBJECT(renderer_compact_date),
303                      "xalign", 1.0,
304                      NULL);
305
306         gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 32);
307         gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 32);
308         
309         remove_all_columns (self);
310
311         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
312         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
313         sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
314
315         /* Add new columns */
316         for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
317                 ModestHeaderViewColumn col =
318                         (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
319                 
320                 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
321                         g_printerr ("modest: invalid column %d in column list\n", col);
322                         continue;
323                 }
324                 
325                 switch (col) {
326                         
327                 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
328                         column = get_new_column (_("M"), renderer_msgtype, FALSE,
329                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
330                                                  FALSE,
331                                                  (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
332                                                  NULL);
333                         gtk_tree_view_column_set_fixed_width (column, 45);
334                         break;
335
336                 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
337                         column = get_new_column (_("A"), renderer_attach, FALSE,
338                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
339                                                  FALSE,
340                                                  (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
341                                                  NULL);
342                         gtk_tree_view_column_set_fixed_width (column, 45);
343                         break;
344
345                         
346                 case MODEST_HEADER_VIEW_COLUMN_FROM:
347                         column = get_new_column (_("From"), renderer_header, TRUE,
348                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
349                                                  TRUE,
350                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
351                                                  GINT_TO_POINTER(TRUE));
352                         break;
353
354                 case MODEST_HEADER_VIEW_COLUMN_TO:
355                         column = get_new_column (_("To"), renderer_header, TRUE,
356                                                  TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
357                                                  TRUE,
358                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
359                                                  GINT_TO_POINTER(FALSE));
360                         break;
361                         
362                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
363                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
364                                                      TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
365                                                      FALSE,
366                                                      (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
367                                                      GINT_TO_POINTER(TRUE));
368                         compact_column = column;
369                         break;
370
371                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
372                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
373                                                      TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
374                                                      FALSE,
375                                                      (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
376                                                      GINT_TO_POINTER(FALSE));
377                         compact_column = column;
378                         break;
379
380                         
381                 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
382                         column = get_new_column (_("Subject"), renderer_header, TRUE,
383                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
384                                                  TRUE,
385                                                  (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
386                                                  NULL);
387                         break;
388                         
389                 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
390                         column = get_new_column (_("Received"), renderer_header, TRUE,
391                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
392                                                  TRUE,
393                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
394                                                  GINT_TO_POINTER(TRUE));
395                         break;
396                         
397                 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:  
398                         column = get_new_column (_("Sent"), renderer_header, TRUE,
399                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
400                                                  TRUE,
401                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
402                                                  GINT_TO_POINTER(FALSE));
403                         break;
404                         
405                 case MODEST_HEADER_VIEW_COLUMN_SIZE:
406                         column = get_new_column (_("Size"), renderer_header, TRUE,
407                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
408                                                  FALSE,
409                                                  (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
410                                                  NULL); 
411                         break;
412                 case MODEST_HEADER_VIEW_COLUMN_STATUS:
413                         column = get_new_column (_("Status"), renderer_compact_date, TRUE,
414                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
415                                                  FALSE,
416                                                  (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
417                                                  NULL); 
418                         break;
419
420                 default:
421                         g_return_val_if_reached(FALSE);
422                 }
423
424                 /* we keep the column id around */
425                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
426                                    GINT_TO_POINTER(col));
427                 
428                 /* we need this ptr when sorting the rows */
429                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
430                                    self);
431                 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);              
432         }               
433
434         if (sortable) {
435                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
436                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
437                                                  (GtkTreeIterCompareFunc) cmp_rows,
438                                                  compact_column, NULL);
439         }
440
441
442         return TRUE;
443 }
444
445 static void
446 modest_header_view_init (ModestHeaderView *obj)
447 {
448         ModestHeaderViewPrivate *priv;
449         guint i, j;
450
451         priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj); 
452
453         priv->folder  = NULL;
454
455         priv->monitor        = NULL;
456         priv->observers_lock = g_mutex_new ();
457
458         /* Sort parameters */
459         for (j=0; j < 2; j++) {
460                 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
461                         priv->sort_colid[j][i] = -1;
462                         priv->sort_type[j][i] = GTK_SORT_DESCENDING;
463                 }                       
464         }
465
466         setup_drag_and_drop (GTK_TREE_VIEW (obj));
467 }
468
469 static void
470 modest_header_view_finalize (GObject *obj)
471 {
472         ModestHeaderView        *self;
473         ModestHeaderViewPrivate *priv;
474         
475         self = MODEST_HEADER_VIEW(obj);
476         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
477
478         g_mutex_lock (priv->observers_lock);
479         if (priv->monitor) {
480                 tny_folder_monitor_stop (priv->monitor);
481                 g_object_unref (G_OBJECT (priv->monitor));
482         }
483         g_mutex_unlock (priv->observers_lock);
484         g_mutex_free (priv->observers_lock);
485
486         if (priv->folder) {
487                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
488                 g_object_unref (G_OBJECT (priv->folder));
489                 priv->folder   = NULL;
490         }
491
492         G_OBJECT_CLASS(parent_class)->finalize (obj);
493 }
494
495
496 GtkWidget*
497 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
498 {
499         GObject *obj;
500         GtkTreeSelection *sel;
501         ModestHeaderView *self;
502         ModestHeaderViewPrivate *priv;
503         
504         g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
505                               NULL);
506         
507         obj  = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
508         self = MODEST_HEADER_VIEW(obj);
509         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
510         
511         modest_header_view_set_style   (self, style);
512         modest_header_view_set_folder (self, NULL);
513
514         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
515         gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
516         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
517         
518         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
519                                       TRUE); /* alternating row colors */
520         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
521         
522         g_signal_connect (sel, "changed",
523                           G_CALLBACK(on_selection_changed), self);
524         
525         g_signal_connect (self, "button-press-event",
526                           G_CALLBACK(on_header_clicked), NULL);
527
528         g_signal_connect (self, "focus-in-event",
529                           G_CALLBACK(on_focus_in), NULL);
530         
531         return GTK_WIDGET(self);
532 }
533
534
535 TnyList * 
536 modest_header_view_get_selected_headers (ModestHeaderView *self)
537 {
538         GtkTreeSelection *sel;
539         ModestHeaderViewPrivate *priv;
540         TnyList *header_list = NULL;
541         TnyHeader *header;
542         GList *list, *tmp = NULL;
543         GtkTreeModel *tree_model = NULL;
544         GtkTreeIter iter;
545
546         g_return_val_if_fail (self, NULL);
547         
548         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
549
550         /* Get selected rows */
551         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
552         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
553
554         if (list) {
555                 header_list = tny_simple_list_new();
556
557                 list = g_list_reverse (list);
558                 tmp = list;
559                 while (tmp) {                   
560                         /* get header from selection */
561                         gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
562                         gtk_tree_model_get (tree_model, &iter,
563                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
564                                             &header, -1);
565                         /* Prepend to list */
566                         tny_list_prepend (header_list, G_OBJECT (header));
567                         g_object_unref (G_OBJECT (header));
568
569                         tmp = g_list_next (tmp);
570                 }
571                 /* Clean up*/
572                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
573                 g_list_free (list);
574         }
575         return header_list;
576 }
577
578
579 /* scroll our list view so the selected item is visible */
580 static void
581 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
582 {
583 #ifdef MODEST_PLATFORM_GNOME 
584
585         GtkTreePath *selected_path;
586         GtkTreePath *start, *end;
587         
588         GtkTreeModel *model;
589         
590         model         = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
591         selected_path = gtk_tree_model_get_path (model, iter);
592
593         start = gtk_tree_path_new ();
594         end   = gtk_tree_path_new ();
595
596         gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
597
598         if (gtk_tree_path_compare (selected_path, start) < 0 ||
599             gtk_tree_path_compare (end, selected_path) < 0)
600                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
601                                               selected_path, NULL, TRUE,
602                                               up ? 0.0 : 1.0,
603                                               up ? 0.0 : 1.0);
604         gtk_tree_path_free (selected_path);
605         gtk_tree_path_free (start);
606         gtk_tree_path_free (end);
607
608 #endif /* MODEST_PLATFORM_GNOME */
609 }
610
611
612 void 
613 modest_header_view_select_next (ModestHeaderView *self)
614 {
615         GtkTreeSelection *sel;
616         GtkTreeIter iter;
617         GtkTreeModel *model;
618         GtkTreePath *path;
619
620         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
621         path = get_selected_row (GTK_TREE_VIEW(self), &model);
622         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
623                 /* Unselect previous path */
624                 gtk_tree_selection_unselect_path (sel, path);
625                 
626                 /* Move path down and selects new one  */
627                 if (gtk_tree_model_iter_next (model, &iter)) {
628                         gtk_tree_selection_select_iter (sel, &iter);
629                         scroll_to_selected (self, &iter, FALSE);        
630                 }
631                 gtk_tree_path_free(path);
632         }
633         
634 }
635
636 void 
637 modest_header_view_select_prev (ModestHeaderView *self)
638 {
639         GtkTreeSelection *sel;
640         GtkTreeIter iter;
641         GtkTreeModel *model;
642         GtkTreePath *path;
643
644         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
645         path = get_selected_row (GTK_TREE_VIEW(self), &model);
646         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
647                 /* Unselect previous path */
648                 gtk_tree_selection_unselect_path (sel, path);
649
650                 /* Move path up */
651                 if (gtk_tree_path_prev (path)) {
652                         gtk_tree_model_get_iter (model, &iter, path);
653                         
654                         /* Select the new one */
655                         gtk_tree_selection_select_iter (sel, &iter);
656                         scroll_to_selected (self, &iter, TRUE); 
657
658                 }
659                 gtk_tree_path_free (path);
660         }
661 }
662
663 GList*
664 modest_header_view_get_columns (ModestHeaderView *self)
665 {
666         g_return_val_if_fail (self, FALSE);
667         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self)); 
668 }
669
670
671 gboolean
672 modest_header_view_is_empty (ModestHeaderView *self)
673 {
674         g_return_val_if_fail (self, FALSE);
675         return FALSE; /* FIXME */
676 }
677
678
679 gboolean
680 modest_header_view_set_style (ModestHeaderView *self,
681                               ModestHeaderViewStyle style)
682 {
683         ModestHeaderViewPrivate *priv;
684         gboolean show_col_headers = FALSE;
685         ModestHeaderViewStyle old_style;
686         
687         g_return_val_if_fail (self, FALSE);
688         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
689                               FALSE);
690
691         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
692         if (priv->style == style)
693                 return TRUE; /* nothing to do */
694         
695         switch (style) {
696         case MODEST_HEADER_VIEW_STYLE_DETAILS:
697                 show_col_headers = TRUE;
698                 break;
699         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
700                 break;
701         default:
702                 g_return_val_if_reached (FALSE);
703         }
704         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
705         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);    
706
707         old_style   = priv->style;
708         priv->style = style;
709
710         return TRUE;
711 }
712
713
714 ModestHeaderViewStyle
715 modest_header_view_get_style (ModestHeaderView *self)
716 {
717         g_return_val_if_fail (self, FALSE);
718         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
719 }
720
721 /* 
722  * This function sets a sortable model in the header view. It's just
723  * used for developing purposes, because it only does a
724  * gtk_tree_view_set_model
725  */
726 static void
727 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
728 {
729         GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
730
731         if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { 
732                 GtkTreeModel *old_model;
733                 ModestHeaderViewPrivate *priv;
734
735                 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
736                 old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort));
737
738                 /* Set new model */
739                 gtk_tree_view_set_model (header_view, model);
740         } else
741                 gtk_tree_view_set_model (header_view, model);
742
743         return;
744 }
745
746 TnyFolder*
747 modest_header_view_get_folder (ModestHeaderView *self)
748 {
749         ModestHeaderViewPrivate *priv;
750         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
751
752         if (priv->folder)
753                 g_object_ref (priv->folder);
754
755         return priv->folder;
756 }
757
758 static void
759 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
760 {
761         TnyFolderType type;
762         TnyList *headers;
763         ModestHeaderViewPrivate *priv;
764         GList *cols, *cursor;
765         GtkTreeModel *sortable; 
766         guint sort_colid;
767         GtkSortType sort_type;
768
769         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
770
771         headers = TNY_LIST (tny_gtk_header_list_model_new ());
772
773         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
774                                               folder, FALSE);
775
776         /* Add IDLE observer (monitor) and another folder observer for
777            new messages (self) */
778         g_mutex_lock (priv->observers_lock);
779         if (priv->monitor) {
780                 tny_folder_monitor_stop (priv->monitor);
781                 g_object_unref (G_OBJECT (priv->monitor));
782         }
783         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
784         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
785         tny_folder_monitor_start (priv->monitor);
786         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (self));
787         g_mutex_unlock (priv->observers_lock);
788
789         sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
790         g_object_unref (G_OBJECT (headers));
791
792         /* install our special sorting functions */
793         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
794
795         /* Restore sort column id */
796         if (cols) {
797                 type  = modest_tny_folder_guess_folder_type (folder);
798                 sort_colid = modest_header_view_get_sort_column_id (self, type); 
799                 sort_type = modest_header_view_get_sort_type (self, type); 
800                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
801                                                       sort_colid,
802                                                       sort_type);
803                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
804                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
805                                                  (GtkTreeIterCompareFunc) cmp_rows,
806                                                  cols->data, NULL);
807         }
808
809         /* Set new model */
810         modest_header_view_set_model (GTK_TREE_VIEW (self), sortable);
811         g_object_unref (G_OBJECT (sortable));
812
813         /* Free */
814         g_list_free (cols);
815 }
816
817 void
818 modest_header_view_sort_by_column_id (ModestHeaderView *self, 
819                                       guint sort_colid,
820                                       GtkSortType sort_type)
821 {
822         ModestHeaderViewPrivate *priv = NULL;
823         GtkTreeModel *sortable = NULL; 
824         TnyFolderType type;
825
826         /* Get model and private data */
827         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);            
828         sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
829         
830         /* Sort tree model */
831         type  = modest_tny_folder_guess_folder_type (priv->folder);
832         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
833                                               sort_colid,
834                                               sort_type);
835         /* Store new sort parameters */
836         modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
837
838         /* Save GConf parameters */
839 /*      modest_widget_memory_save (modest_runtime_get_conf(), */
840 /*                                 G_OBJECT(self), "header-view"); */
841         
842 }
843
844 void
845 modest_header_view_set_sort_params (ModestHeaderView *self, 
846                                     guint sort_colid, 
847                                     GtkSortType sort_type,
848                                     TnyFolderType type)
849 {
850         ModestHeaderViewPrivate *priv;
851         ModestHeaderViewStyle style;
852
853         style = modest_header_view_get_style   (self);
854         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
855
856         priv->sort_colid[style][type] = sort_colid;
857         priv->sort_type[style][type] = sort_type;
858 }
859
860 gint
861 modest_header_view_get_sort_column_id (ModestHeaderView *self, 
862                                        TnyFolderType type)
863 {
864         ModestHeaderViewPrivate *priv;
865         ModestHeaderViewStyle style;
866
867         style = modest_header_view_get_style   (self);
868         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
869
870         return priv->sort_colid[style][type];
871 }
872
873 GtkSortType
874 modest_header_view_get_sort_type (ModestHeaderView *self, 
875                                   TnyFolderType type)
876 {
877         ModestHeaderViewPrivate *priv;
878         ModestHeaderViewStyle style;
879
880         style = modest_header_view_get_style   (self);
881         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
882
883         return priv->sort_type[style][type];
884 }
885
886 void
887 modest_header_view_set_folder (ModestHeaderView *self, TnyFolder *folder)
888 {
889         ModestHeaderViewPrivate *priv;
890
891         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
892
893         if (priv->folder) {
894                 g_mutex_lock (priv->observers_lock);
895                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
896                 g_object_unref (priv->folder);
897                 priv->folder = NULL;
898                 g_mutex_unlock (priv->observers_lock);
899         }
900
901         if (folder) {
902                 ModestMailOperation *mail_op;
903
904                 /* Set folder in the model */
905                 modest_header_view_set_folder_intern (self, folder);
906
907                 /* Pick my reference. Nothing to do with the mail operation */
908                 priv->folder = g_object_ref (folder);
909
910                 /* no message selected */
911                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
912
913                 /* Create the mail operation */
914                 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, NULL);
915                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
916                                                  mail_op);
917
918                 /* Refresh the folder asynchronously */
919                 modest_mail_operation_refresh_folder (mail_op, folder);
920
921                 /* Free */
922                 g_object_unref (mail_op);
923
924         } else {
925                 g_mutex_lock (priv->observers_lock);
926
927                 if (priv->monitor) {
928                         tny_folder_monitor_stop (priv->monitor);
929                         g_object_unref (G_OBJECT (priv->monitor));
930                         priv->monitor = NULL;
931                 }
932                 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL); 
933
934                 g_mutex_unlock (priv->observers_lock);
935         }
936 }
937
938 static gboolean
939 on_header_clicked (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
940 {
941         ModestHeaderView *self = NULL;
942         ModestHeaderViewPrivate *priv = NULL;
943         GtkTreePath *path = NULL;
944         GtkTreeIter iter;
945         GtkTreeModel *model = NULL;
946         TnyHeader *header;
947         /* ignore everything but doubleclick */
948         if (event->type != GDK_2BUTTON_PRESS)
949                 return FALSE;
950
951         self = MODEST_HEADER_VIEW (widget);
952         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
953         
954         path = get_selected_row (GTK_TREE_VIEW(self), &model);
955         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
956                 return FALSE;
957                         
958         /* get the first selected item */
959         gtk_tree_model_get (model, &iter,
960                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
961                             &header, -1);
962         /* Emit signal */
963         g_signal_emit (G_OBJECT(self), 
964                        signals[HEADER_ACTIVATED_SIGNAL], 
965                        0, header);
966
967         /* Free */
968         g_object_unref (G_OBJECT (header));
969         gtk_tree_path_free(path);
970
971         return TRUE;
972 }
973
974
975 static void
976 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
977 {
978         GtkTreeModel *model;
979         TnyHeader *header;
980         GtkTreePath *path = NULL;       
981         GtkTreeIter iter;
982         ModestHeaderView *self;
983         ModestHeaderViewPrivate *priv;
984         
985         g_return_if_fail (sel);
986         g_return_if_fail (user_data);
987         
988         self = MODEST_HEADER_VIEW (user_data);
989         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
990
991         path = get_selected_row (GTK_TREE_VIEW(self), &model);
992         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
993                 return; /* msg was _un_selected */
994
995         gtk_tree_model_get (model, &iter,
996                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
997                             &header, -1);
998
999         /* Emit signal */
1000         g_signal_emit (G_OBJECT(self), 
1001                        signals[HEADER_SELECTED_SIGNAL], 
1002                        0, header);
1003
1004         g_object_unref (G_OBJECT (header));
1005         gtk_tree_path_free(path);
1006 }
1007
1008
1009 /* PROTECTED method. It's useful when we want to force a given
1010    selection to reload a msg. For example if we have selected a header
1011    in offline mode, when Modest become online, we want to reload the
1012    message automatically without an user click over the header */
1013 void 
1014 _modest_header_view_change_selection (GtkTreeSelection *selection,
1015                                       gpointer user_data)
1016 {
1017         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1018         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1019
1020         on_selection_changed (selection, user_data);
1021 }
1022
1023
1024 static gint
1025 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1026           gpointer user_data)
1027 {
1028         gint col_id;
1029         gint t1, t2;
1030         gint val1, val2;
1031         gint cmp;
1032         static int counter = 0;
1033
1034         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1035 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1036         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1037         
1038         if (!(++counter % 100)) {
1039                 GObject *header_view = g_object_get_data(G_OBJECT(user_data),
1040                                                          MODEST_HEADER_VIEW_PTR);
1041                 g_signal_emit (header_view,
1042                                signals[STATUS_UPDATE_SIGNAL],
1043                                0, _("Sorting..."), 0, 0);
1044         }
1045         switch (col_id) {
1046         case TNY_HEADER_FLAG_ATTACHMENTS:
1047
1048                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1049                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1050                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1051                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1052
1053                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1054                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1055
1056                 return cmp ? cmp : t1 - t2;
1057                 
1058         case TNY_HEADER_FLAG_PRIORITY:
1059                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1060                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1061                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1062                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1063
1064                 /* This is for making priority values respect the intuitive sort relationship 
1065                  * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1066                 if (val1 == 0) val1 = 2;
1067                 if (val2 == 0) val2 = 2;
1068
1069                 cmp =  (val1 & TNY_HEADER_FLAG_PRIORITY) - (val2 & TNY_HEADER_FLAG_PRIORITY);
1070
1071                 return cmp ? cmp : t1 - t2;
1072
1073         default:
1074                 return &iter1 - &iter2; /* oughhhh  */
1075         }
1076 }
1077
1078 /* Drag and drop stuff */
1079 static void
1080 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context, 
1081                   GtkSelectionData *selection_data, 
1082                   guint info,  guint time, gpointer data)
1083 {
1084         GtkTreeModel *model = NULL;
1085         GtkTreeIter iter;
1086         GtkTreePath *source_row = NULL;
1087         
1088         source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1089         if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1090
1091         switch (info) {
1092         case MODEST_HEADER_ROW:
1093                 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1094                 break;
1095         case MODEST_MSG: {
1096                 TnyHeader *hdr;
1097                 gtk_tree_model_get (model, &iter,
1098                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1099                                     -1);
1100                 if (hdr) {
1101                         g_object_unref (G_OBJECT(hdr));
1102                 }
1103                 break;
1104         }
1105         default:
1106                 g_message ("%s: default switch case.", __FUNCTION__);
1107         }
1108
1109         gtk_tree_path_free (source_row);
1110 }
1111
1112 /* Header view drag types */
1113 const GtkTargetEntry header_view_drag_types[] = {
1114         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1115         { "text/uri-list",      0,                   MODEST_MSG }, 
1116 };
1117
1118 static void
1119 setup_drag_and_drop (GtkTreeView *self)
1120 {
1121         gtk_drag_source_set (GTK_WIDGET (self),
1122                              GDK_BUTTON1_MASK,
1123                              header_view_drag_types,
1124                              G_N_ELEMENTS (header_view_drag_types),
1125                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1126
1127         g_signal_connect(G_OBJECT (self), "drag_data_get",
1128                          G_CALLBACK(drag_data_get_cb), NULL);
1129 }
1130
1131 static GtkTreePath *
1132 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1133 {
1134         GtkTreePath *path = NULL;
1135         GtkTreeSelection *sel = NULL;   
1136         GList *rows = NULL;
1137
1138         sel   = gtk_tree_view_get_selection(self);
1139         rows = gtk_tree_selection_get_selected_rows (sel, model);
1140         
1141         if ((rows == NULL) || (g_list_length(rows) != 1))
1142                 goto frees;
1143
1144         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1145         
1146
1147         /* Free */
1148  frees:
1149         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1150         g_list_free(rows);
1151
1152         return path;
1153 }
1154
1155 /*
1156  * This function moves the tree view scroll to the current selected
1157  * row when the widget grabs the focus 
1158  */
1159 static gboolean 
1160 on_focus_in (GtkWidget     *self,
1161              GdkEventFocus *event,
1162              gpointer       user_data)
1163 {
1164         GtkTreeSelection *selection;
1165         GtkTreeModel *model;
1166         GList *selected = NULL;
1167         GtkTreePath *selected_path = NULL;
1168
1169         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1170         if (!model)
1171                 return FALSE;
1172
1173         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1174         /* If none selected yet, pick the first one */
1175         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1176                 GtkTreeIter iter;
1177                 GtkTreePath *path;
1178
1179                 /* Return if the model is empty */
1180                 if (!gtk_tree_model_get_iter_first (model, &iter))
1181                         return FALSE;
1182
1183                 path = gtk_tree_model_get_path (model, &iter);
1184                 gtk_tree_selection_select_path (selection, path);
1185                 gtk_tree_path_free (path);
1186         }
1187
1188         /* Need to get the all the rows because is selection multiple */
1189         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1190         selected_path = (GtkTreePath *) selected->data;
1191
1192         /* Check if we need to scroll */
1193         #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1194         GtkTreePath *start_path = NULL;
1195         GtkTreePath *end_path = NULL;
1196         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1197                                              &start_path,
1198                                              &end_path)) {
1199
1200                 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1201                     (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1202
1203                         /* Scroll to first path */
1204                         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1205                                                       selected_path,
1206                                                       NULL,
1207                                                       TRUE,
1208                                                       0.5,
1209                                                       0.0);
1210                 }
1211         }
1212         #endif /* GTK_CHECK_VERSION */
1213
1214         /* Frees */     
1215         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1216         g_list_free (selected);
1217
1218         return FALSE;
1219 }
1220
1221 static void
1222 folder_monitor_update (TnyFolderObserver *self, 
1223                        TnyFolderChange *change)
1224 {
1225         ModestHeaderViewPrivate *priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1226         TnyFolderChangeChanged changed;
1227
1228         g_mutex_lock (priv->observers_lock);
1229
1230         changed = tny_folder_change_get_changed (change);
1231
1232         if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1233 /*      TnyIterator *iter; */
1234 /*      TnyList *list; */
1235 /*              /\* The added headers *\/ */
1236 /*              list = tny_simple_list_new (); */
1237 /*              tny_folder_change_get_added_headers (change, list); */
1238 /*              iter = tny_list_create_iterator (list); */
1239 /*              while (!tny_iterator_is_done (iter)) */
1240 /*              { */
1241 /*                      TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter)); */
1242 /*                      g_object_unref (G_OBJECT (header)); */
1243 /*                      tny_iterator_next (iter); */
1244 /*              } */
1245 /*              g_object_unref (G_OBJECT (iter)); */
1246 /*              g_object_unref (G_OBJECT (list)); */
1247                 modest_platform_on_new_msg ();
1248         }
1249
1250         g_mutex_unlock (priv->observers_lock);
1251 }