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