2f98c511f396425165de5ad60085361851be2488
[modest] / src / modest-tny-header-tree-view.c
1 /* modest-tny-header-tree-view.c 
2  */
3 #include <glib/gi18n.h>
4 #include "modest-tny-header-tree-view.h"
5 #include <tny-list-iface.h>
6 #include <string.h>
7
8 /* 'private'/'protected' functions */
9 static void modest_tny_header_tree_view_class_init  (ModestTnyHeaderTreeViewClass *klass);
10 static void modest_tny_header_tree_view_init        (ModestTnyHeaderTreeView *obj);
11 static void modest_tny_header_tree_view_finalize    (GObject *obj);
12
13 static void selection_changed (GtkTreeSelection *sel, gpointer user_data);
14 static void column_clicked (GtkTreeViewColumn *treeviewcolumn, gpointer user_data);
15
16 #define PIXMAP_PREFIX PREFIX "/share/modest/pixmaps/"
17
18 enum {
19         MESSAGE_SELECTED_SIGNAL,
20         LAST_SIGNAL
21 };
22
23 enum {
24         HEADER_ICON_READ  = 1,
25         HEADER_ICON_UNREAD,
26         HEADER_ICON_ATTACH,
27         HEADER_ICON_NUM
28 };
29
30
31 typedef struct _ModestTnyHeaderTreeViewPrivate ModestTnyHeaderTreeViewPrivate;
32 struct _ModestTnyHeaderTreeViewPrivate {
33
34         TnyMsgFolderIface *tny_msg_folder;
35         TnyListIface      *headers;
36
37         GdkPixbuf   *icons[HEADER_ICON_NUM];
38         guint sort_columns[MODEST_TNY_HEADER_TREE_VIEW_COLUMN_NUM];
39
40         GSList *columns;
41         ModestTnyHeaderTreeViewStyle style;
42 };
43 #define MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
44                                                          MODEST_TYPE_TNY_HEADER_TREE_VIEW, \
45                                                          ModestTnyHeaderTreeViewPrivate))
46 /* globals */
47 static GObjectClass *parent_class = NULL;
48
49 /* uncomment the following if you have defined any signals */
50 static guint signals[LAST_SIGNAL] = {0};
51
52 GType
53 modest_tny_header_tree_view_get_type (void)
54 {
55         static GType my_type = 0;
56         if (!my_type) {
57                 static const GTypeInfo my_info = {
58                         sizeof(ModestTnyHeaderTreeViewClass),
59                         NULL,           /* base init */
60                         NULL,           /* base finalize */
61                         (GClassInitFunc) modest_tny_header_tree_view_class_init,
62                         NULL,           /* class finalize */
63                         NULL,           /* class data */
64                         sizeof(ModestTnyHeaderTreeView),
65                         1,              /* n_preallocs */
66                         (GInstanceInitFunc) modest_tny_header_tree_view_init,
67                 };
68                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
69                                                   "ModestTnyHeaderTreeView",
70                                                   &my_info, 0);
71         }
72         return my_type;
73 }
74
75 static void
76 modest_tny_header_tree_view_class_init (ModestTnyHeaderTreeViewClass *klass)
77 {
78         GObjectClass *gobject_class;
79         gobject_class = (GObjectClass*) klass;
80
81         parent_class            = g_type_class_peek_parent (klass);
82         gobject_class->finalize = modest_tny_header_tree_view_finalize;
83
84         g_type_class_add_private (gobject_class, sizeof(ModestTnyHeaderTreeViewPrivate));
85
86         signals[MESSAGE_SELECTED_SIGNAL] = 
87                 g_signal_new ("message_selected",
88                               G_TYPE_FROM_CLASS (gobject_class),
89                               G_SIGNAL_RUN_FIRST,
90                               G_STRUCT_OFFSET (ModestTnyHeaderTreeViewClass,message_selected),
91                               NULL, NULL,
92                               g_cclosure_marshal_VOID__POINTER,
93                               G_TYPE_NONE, 1, G_TYPE_POINTER);  
94 }
95
96
97
98
99
100 static void
101 msgtype_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
102                    GtkTreeModel *tree_model, GtkTreeIter *iter,
103                    GdkPixbuf *icons[HEADER_ICON_NUM])
104 {
105         TnyMsgHeaderFlags flags;
106         GdkPixbuf *pixbuf;
107
108         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
109                             &flags, -1);
110
111         if (flags & TNY_MSG_HEADER_FLAG_SEEN)
112                 pixbuf = icons[HEADER_ICON_READ];
113         else
114                 pixbuf = icons[HEADER_ICON_UNREAD];
115         
116         g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf,
117                       NULL);
118 }
119
120 static void
121 attach_cell_data (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
122                   GtkTreeModel *tree_model,  GtkTreeIter *iter, GdkPixbuf *icons[HEADER_ICON_NUM])
123 {
124         TnyMsgHeaderFlags flags;
125         GdkPixbuf *pixbuf;
126
127         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
128                             &flags, -1);
129         if (flags & TNY_MSG_HEADER_FLAG_ATTACHMENTS)
130                 pixbuf = icons[HEADER_ICON_ATTACH];
131         else
132                 pixbuf = NULL;
133         
134         g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
135 }
136
137
138
139 static void
140 header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
141                   GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
142 {
143         GObject *rendobj;
144         TnyMsgHeaderFlags flags;
145         
146         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
147                             &flags, -1);
148         rendobj = G_OBJECT(renderer);           
149         
150         if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
151                 g_object_set (rendobj, "weight", 800, NULL);
152         else
153                 g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
154 }
155
156
157
158 static void
159 sender_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
160                    GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
161 {
162         GObject *rendobj;
163         TnyMsgHeaderFlags flags;
164         gchar *from;
165         gchar *address;
166         
167         gtk_tree_model_get (tree_model, iter,
168                             TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,  &from,
169                             TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
170                             -1);
171         rendobj = G_OBJECT(renderer);           
172
173         /* simplistic --> remove <email@address> from display */
174         address = g_strstr_len (from, strlen(from), "<");
175         if (address) {
176                 address[0]='\0';
177                 g_object_set (rendobj, "text", from, NULL);
178                 g_free (from);
179         }
180                              
181         if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
182                 g_object_set (rendobj, "weight", 800, NULL);
183         else
184                 g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
185 }
186
187
188 static void
189 init_icons (GdkPixbuf *icons[HEADER_ICON_NUM])
190 {
191         icons[HEADER_ICON_READ] =
192                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "read.xpm",NULL);
193         icons[HEADER_ICON_UNREAD] =
194                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "unread.xpm",NULL);
195         icons[HEADER_ICON_ATTACH] =
196                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "clip.xpm",NULL);
197 }
198
199
200
201 static GtkTreeViewColumn*
202 get_new_column (const gchar *name, GtkCellRenderer *renderer,
203                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
204                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
205 {
206         GtkTreeViewColumn *column;
207
208         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
209         gtk_tree_view_column_set_resizable (column, resizable);
210
211         if (show_as_text) 
212                 gtk_tree_view_column_add_attribute (column, renderer, "text",
213                                                     sort_col_id);
214         if (sort_col_id >= 0)
215                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
216
217         gtk_tree_view_column_set_sort_indicator (column, FALSE);
218         gtk_tree_view_column_set_reorderable (column, TRUE);
219
220         if (cell_data_func)
221                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
222                                                         user_data, NULL);
223
224 /*      g_signal_connect (G_OBJECT (column), "clicked", */
225 /*                        G_CALLBACK (column_clicked), obj);  */
226
227         return column;
228 }
229
230
231
232
233 static void
234 remove_all_columns (ModestTnyHeaderTreeView *obj)
235 {
236         GList *columns, *cursor;
237
238         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
239
240         for (cursor = columns; cursor; cursor = cursor->next)
241                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
242                                              GTK_TREE_VIEW_COLUMN(cursor->data));
243         g_list_free (columns);  
244 }
245
246
247
248
249 static void
250 init_columns (ModestTnyHeaderTreeView *obj)
251 {
252         GtkTreeViewColumn *column;
253         GtkCellRenderer *renderer_msgtype,
254                 *renderer_header,
255                 *renderer_attach;
256
257         ModestTnyHeaderTreeViewPrivate *priv;
258         GSList *cursor;
259         
260         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(obj); 
261                         
262         renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
263         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
264         renderer_header = gtk_cell_renderer_text_new (); 
265         
266         remove_all_columns (obj);
267         
268         for (cursor = priv->columns; cursor; cursor = cursor->next) {
269                 ModestTnyHeaderTreeViewColumn col =
270                         (ModestTnyHeaderTreeViewColumn) GPOINTER_TO_INT(cursor->data);
271                 
272                 switch (col) {
273                         
274                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_MSGTYPE:
275
276                         column = get_new_column (_("M"), renderer_msgtype, FALSE,
277                                                  TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
278                                                  FALSE, (GtkTreeCellDataFunc)msgtype_cell_data, priv->icons);
279                         break;
280
281                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_ATTACH:
282
283                         column = get_new_column (_("A"), renderer_attach, FALSE,
284                                                  TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
285                                                  FALSE, (GtkTreeCellDataFunc)attach_cell_data, priv->icons);
286                         break;
287                         
288                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_RECEIVED_DATE:
289                         column = get_new_column (_("Received"), renderer_header, TRUE,
290                                                  TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
291                                                  TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
292                         break;
293                         
294                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_FROM:
295                         column = get_new_column (_("From"), renderer_header, TRUE,
296                                                  TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,
297                                                  TRUE, (GtkTreeCellDataFunc)sender_cell_data, NULL);
298                         break;
299
300                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_SUBJECT:
301                         column = get_new_column (_("Subject"), renderer_header, TRUE,
302                                                  TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
303                                                  TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
304                         break;
305                         
306                         
307                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_SENT_DATE:
308                         column = get_new_column (_("Sent"), renderer_header, TRUE,
309                                                  TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
310                                                  TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
311                         break;
312                         
313                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_TO:
314                         column = get_new_column (_("To"), renderer_header, TRUE,
315                                                  TNY_MSG_HEADER_LIST_MODEL_TO_COLUMN,
316                                                  TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
317                         break;
318                 }
319                 gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);               
320         }       
321 }
322
323
324
325
326
327 static void
328 modest_tny_header_tree_view_init (ModestTnyHeaderTreeView *obj)
329 {
330         ModestTnyHeaderTreeViewPrivate *priv;
331         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(obj); 
332
333         init_icons (priv->icons);       
334 }
335
336 static void
337 modest_tny_header_tree_view_finalize (GObject *obj)
338 {
339         ModestTnyHeaderTreeView        *self;
340         ModestTnyHeaderTreeViewPrivate *priv;
341         int i;
342         
343         self = MODEST_TNY_HEADER_TREE_VIEW(obj);
344         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
345
346         if (priv->headers)      
347                 g_object_unref (G_OBJECT(priv->headers));
348         
349         priv->headers = NULL;
350         priv->tny_msg_folder    = NULL;
351
352         /* cleanup our icons */
353         for (i = 0; i != HEADER_ICON_NUM; ++i) {
354                 if (priv->icons[i]) {
355                         g_object_unref (G_OBJECT(priv->icons[i]));
356                         priv->icons[i] = NULL;
357                 }
358         }
359 }
360
361 GtkWidget*
362 modest_tny_header_tree_view_new (TnyMsgFolderIface *folder,
363                                  GSList *columns,
364                                  ModestTnyHeaderTreeViewStyle style)
365 {
366         GObject *obj;
367         GtkTreeSelection *sel;
368         ModestTnyHeaderTreeView *self;
369         
370         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_HEADER_TREE_VIEW, NULL));
371         self = MODEST_TNY_HEADER_TREE_VIEW(obj);
372         
373         if (!modest_tny_header_tree_view_set_folder (self, NULL)) {
374                 g_warning ("could not set the folder");
375                 g_object_unref (obj);
376                 return NULL;
377         }
378         
379         modest_tny_header_tree_view_set_style   (self, style);
380         modest_tny_header_tree_view_set_columns (self, columns);
381         
382         /* all cols */
383         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(obj), TRUE);
384         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), TRUE);
385         
386         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
387                                       TRUE); /* alternating row colors */
388
389         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
390         g_signal_connect (sel, "changed",
391                           G_CALLBACK(selection_changed), self);
392
393         return GTK_WIDGET(self);
394 }
395
396 gboolean
397 modest_tny_header_tree_view_set_columns (ModestTnyHeaderTreeView *self, GSList *columns)
398 {
399         ModestTnyHeaderTreeViewPrivate *priv;
400         GSList *cursor;
401
402         g_return_val_if_fail (self, FALSE);
403         
404         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
405
406         g_slist_free (priv->columns);
407         
408         for (cursor = columns; cursor; cursor = cursor->next) {
409                 ModestTnyHeaderTreeViewColumn col = 
410                         (ModestTnyHeaderTreeViewColumn) GPOINTER_TO_INT(cursor->data);
411                 if (0 > col || col >= MODEST_TNY_HEADER_TREE_VIEW_COLUMN_NUM)
412                         g_warning ("invalid column in column list");
413                 else
414                         priv->columns = g_slist_append (priv->columns, cursor->data);
415         }
416
417         init_columns (self); /* redraw them */
418         return TRUE;
419 }
420
421
422
423 const GSList*
424 modest_tny_header_tree_view_get_columns (ModestTnyHeaderTreeView *self)
425 {
426         ModestTnyHeaderTreeViewPrivate *priv;
427         GSList *cursor;
428
429         g_return_val_if_fail (self, FALSE);
430         
431         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
432         return priv->columns;
433 }
434
435
436
437
438 gboolean
439 modest_tny_header_tree_view_set_style (ModestTnyHeaderTreeView *self,
440                                        ModestTnyHeaderTreeViewStyle style)
441 {
442         g_return_val_if_fail (self, FALSE);
443         g_return_val_if_fail (style >= 0 && style < MODEST_TNY_HEADER_TREE_VIEW_STYLE_NUM,
444                               FALSE);
445         
446         MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self)->style = style;
447         
448         return TRUE;
449 }
450
451 ModestTnyHeaderTreeViewStyle
452 modest_tny_header_tree_view_get_style (ModestTnyHeaderTreeView *self)
453 {
454         g_return_val_if_fail (self, FALSE);
455
456         return MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self)->style;
457 }
458
459
460 # if 0
461 static gint
462 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
463           gpointer user_data)
464 {
465         gint col_id = GPOINTER_TO_INT (user_data);
466         gint val1, val2;
467
468         g_return_val_if_fail (GTK_IS_TREE_MODEL(tree_model), -1);
469         
470         switch (col_id) {
471
472         case SORT_COLUMN_RECEIVED:
473                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
474                                     &val1,-1);
475                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
476                                     &val2,-1);
477
478                 g_message ("%d %d %d %d", col_id, val1, val2, val1 - val2);
479                 
480                 return val1 - val2;
481                 
482         case SORT_COLUMN_SENT:
483                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
484                                     &val1,-1);
485                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
486                                     &val2,-1);
487                 return val1 - val2;
488         
489         case SORT_COLUMN_ATTACH:
490                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
491                                     &val1,-1);
492                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
493                                     &val2,-1);
494                 
495                 return (val1 & TNY_MSG_HEADER_FLAG_ATTACHMENTS) - (val2 & TNY_MSG_HEADER_FLAG_ATTACHMENTS);
496         
497
498         case SORT_COLUMN_MSGTYPE:
499                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
500                                     &val1,-1);
501                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
502                                     &val2,-1);
503                 
504                 return (val1 & TNY_MSG_HEADER_FLAG_SEEN) - (val2 & TNY_MSG_HEADER_FLAG_SEEN);
505
506         default:
507                 g_message ("%p %p", iter1, iter2);
508                 return &iter1 - &iter2;
509         }
510 }
511 #endif
512
513
514 gboolean
515 modest_tny_header_tree_view_set_folder (ModestTnyHeaderTreeView *self,
516                                         TnyMsgFolderIface *folder)
517 {
518         int i;
519         GtkTreeModel *oldsortable, *sortable, *oldmodel;
520         ModestTnyHeaderTreeViewPrivate *priv;
521         
522         g_return_val_if_fail (self, FALSE);
523
524         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
525         
526         if (folder) {
527                 priv->headers = TNY_LIST_IFACE(tny_msg_header_list_model_new ());
528                 tny_msg_folder_iface_get_headers (folder, priv->headers,
529                                                   FALSE);
530                 tny_msg_header_list_model_set_folder (TNY_MSG_HEADER_LIST_MODEL(priv->headers),
531                                                       folder, TRUE);
532                 
533                 oldsortable = gtk_tree_view_get_model(GTK_TREE_VIEW (self));
534                 if (oldsortable && GTK_IS_TREE_MODEL_SORT(oldsortable)) {
535                         GtkTreeModel *oldmodel = gtk_tree_model_sort_get_model
536                                 (GTK_TREE_MODEL_SORT(oldsortable));
537                         if (oldmodel)
538                                 g_object_unref (G_OBJECT(oldmodel));
539                         g_object_unref (oldsortable);
540                 }
541         
542                 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
543                 
544                 /* set special sorting functions */
545 #if 0 /* FIXME */
546                 gtk_tree_model_sort_reset_default_sort_func (sortable);
547
548                 for (i = 0; i != SORT_COLUMN_NUM; ++i) {
549                         int col_id = priv->sort_columns[i];
550                         if (col_id >= 0) {
551                                 g_message ("%d: %p: %p: %d", i, GTK_TREE_SORTABLE(sortable), sortable, col_id);
552                                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable), col_id,
553                                                                  (GtkTreeIterCompareFunc)cmp_rows,
554                                                                  GINT_TO_POINTER(col_id), NULL);
555                         }
556                 }
557 #endif
558                 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
559                 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), TRUE);
560
561                 /* no need to unref sortable */
562                 
563         } else /* when there is no folder */
564                 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), FALSE);
565                                          
566         return TRUE;
567 }
568
569
570 static void
571 selection_changed (GtkTreeSelection *sel, gpointer user_data)
572 {
573         GtkTreeModel            *model;
574         TnyMsgHeaderIface       *header;
575         GtkTreeIter             iter;
576         ModestTnyHeaderTreeView *tree_view;
577
578         g_return_if_fail (sel);
579         g_return_if_fail (user_data);
580         
581         if (!gtk_tree_selection_get_selected (sel, &model, &iter))
582                 return; /* msg was _un_selected */
583         
584         tree_view = MODEST_TNY_HEADER_TREE_VIEW (user_data);
585         
586         gtk_tree_model_get (model, &iter,
587                             TNY_MSG_HEADER_LIST_MODEL_INSTANCE_COLUMN,
588                             &header, -1);
589         
590         if (header) {
591                 TnyMsgHeaderFlags flags;
592                 const TnyMsgIface *msg;
593                 const TnyMsgFolderIface *folder;
594                 
595                 folder = tny_msg_header_iface_get_folder (TNY_MSG_HEADER_IFACE(header));
596                 if (!folder)
597                         g_message ("cannot find folder");
598                 else {
599                         msg = tny_msg_folder_iface_get_message (TNY_MSG_FOLDER_IFACE(folder),
600                                                                 header);
601                         if (!msg) {
602                                 g_message ("cannot find msg");
603                                 /* FIXME: update display */
604                         }
605                 }
606                                         
607                 g_signal_emit (G_OBJECT(tree_view), signals[MESSAGE_SELECTED_SIGNAL], 0,
608                                msg);
609
610                 /* mark message as seen; _set_flags crashes, bug in tinymail? */
611                 flags = tny_msg_header_iface_get_flags (TNY_MSG_HEADER_IFACE(header));
612                 //tny_msg_header_iface_set_flags (header, flags | TNY_MSG_HEADER_FLAG_SEEN);
613         }
614 }
615
616 static void
617 column_clicked (GtkTreeViewColumn *col, gpointer user_data)
618 {
619         GtkTreeView *treeview;
620         gint id;
621
622         treeview = GTK_TREE_VIEW (user_data);
623         id = gtk_tree_view_column_get_sort_column_id (col);
624         
625         gtk_tree_view_set_search_column (treeview, id);
626 }