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