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