* add a compact display of a header (ie. from/date/subject in one column)
[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,
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                             TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN, &flags
171                             -1);
172         rendobj = G_OBJECT(renderer);           
173
174         /* simplistic --> remove <email@address> from display */
175         address = g_strstr_len (from, strlen(from), "<");
176         if (address) {
177                 address[0]='\0';
178                 g_object_set (rendobj, "text", from, NULL);
179                 g_free (from);
180         }
181                              
182         if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
183                 g_object_set (rendobj, "weight", 800, NULL);
184         else
185                 g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
186 }
187
188
189
190 compact_header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
191                            GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
192 {
193         GObject *rendobj;
194         TnyMsgHeaderFlags flags;
195         gchar *from, *subject, *date;
196         gchar *address;
197         gchar *header;
198         
199         gtk_tree_model_get (tree_model, iter,
200                             TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
201                             TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,  &from,
202                             TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
203                             TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN, &date,
204                             -1);
205         rendobj = G_OBJECT(renderer);           
206         
207         /* simplistic --> remove <email@address> from display */
208         address = g_strstr_len (from, strlen(from), "<");
209         if (address)
210                 address[0]='\0'; /* set a new endpoint */
211         
212         header = g_strdup_printf ("%s %s\n%s", from, date, subject);
213         g_object_set (rendobj, "text", header, NULL);
214
215         g_free (header);
216         g_free (from);
217         g_free (subject);
218         g_free (date);
219         
220         if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
221                 g_object_set (rendobj, "weight", 800, NULL);
222         else
223                 g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
224 }
225
226
227
228
229
230 static void
231 init_icons (GdkPixbuf *icons[HEADER_ICON_NUM])
232 {
233         icons[HEADER_ICON_UNREAD] =
234                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "qgn_list_messagin_mail_unread.png",NULL);
235         icons[HEADER_ICON_READ] =
236                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "qgn_list_messagin_mail.png",NULL);
237         icons[HEADER_ICON_ATTACH] =
238                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "clip.xpm",NULL);
239 }
240
241
242
243 static GtkTreeViewColumn*
244 get_new_column (const gchar *name, GtkCellRenderer *renderer,
245                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
246                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
247 {
248         GtkTreeViewColumn *column;
249
250         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
251         gtk_tree_view_column_set_resizable (column, resizable);
252
253         if (show_as_text) 
254                 gtk_tree_view_column_add_attribute (column, renderer, "text",
255                                                     sort_col_id);
256         if (sort_col_id >= 0)
257                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
258
259         gtk_tree_view_column_set_sort_indicator (column, FALSE);
260         gtk_tree_view_column_set_reorderable (column, TRUE);
261
262         if (cell_data_func)
263                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
264                                                         user_data, NULL);
265
266 /*      g_signal_connect (G_OBJECT (column), "clicked", */
267 /*                        G_CALLBACK (column_clicked), obj);  */
268
269         return column;
270 }
271
272
273
274
275 static void
276 remove_all_columns (ModestTnyHeaderTreeView *obj)
277 {
278         GList *columns, *cursor;
279
280         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
281
282         for (cursor = columns; cursor; cursor = cursor->next)
283                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
284                                              GTK_TREE_VIEW_COLUMN(cursor->data));
285         g_list_free (columns);  
286 }
287
288
289
290
291 static void
292 init_columns (ModestTnyHeaderTreeView *obj)
293 {
294         GtkTreeViewColumn *column;
295         GtkCellRenderer *renderer_msgtype,
296                 *renderer_header,
297                 *renderer_attach;
298
299         ModestTnyHeaderTreeViewPrivate *priv;
300         GSList *cursor;
301         
302         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(obj); 
303                         
304         renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
305         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
306         renderer_header = gtk_cell_renderer_text_new (); 
307         
308         remove_all_columns (obj);
309         
310         for (cursor = priv->columns; cursor; cursor = cursor->next) {
311                 ModestTnyHeaderTreeViewColumn col =
312                         (ModestTnyHeaderTreeViewColumn) GPOINTER_TO_INT(cursor->data);
313                 
314                 switch (col) {
315                         
316                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_MSGTYPE:
317
318                         column = get_new_column (_("M"), renderer_msgtype, FALSE,
319                                                  TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
320                                                  FALSE, (GtkTreeCellDataFunc)msgtype_cell_data, priv->icons);
321                         break;
322
323                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_ATTACH:
324
325                         column = get_new_column (_("A"), renderer_attach, FALSE,
326                                                  TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
327                                                  FALSE, (GtkTreeCellDataFunc)attach_cell_data, priv->icons);
328                         break;
329                         
330                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_RECEIVED_DATE:
331                         column = get_new_column (_("Received"), renderer_header, TRUE,
332                                                  TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
333                                                  TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
334                         break;
335                         
336                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_FROM:
337                         column = get_new_column (_("From"), renderer_header, TRUE,
338                                                  TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,
339                                                  TRUE, (GtkTreeCellDataFunc)sender_cell_data, NULL);
340                         break;
341
342                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_COMPACT_HEADER:
343                         column = get_new_column (_("Header"), renderer_header, TRUE,
344                                                  TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,
345                                                  TRUE, (GtkTreeCellDataFunc)compact_header_cell_data, NULL);
346                         break;
347                         
348                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_SUBJECT:
349                         column = get_new_column (_("Subject"), renderer_header, TRUE,
350                                                  TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
351                                                  TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
352                         break;
353                         
354                         
355                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_SENT_DATE:
356                         column = get_new_column (_("Sent"), renderer_header, TRUE,
357                                                  TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
358                                                  TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
359                         break;
360                         
361                 case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_TO:
362                         column = get_new_column (_("To"), renderer_header, TRUE,
363                                                  TNY_MSG_HEADER_LIST_MODEL_TO_COLUMN,
364                                                  TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
365                         break;
366                 }
367                 gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);               
368         }       
369 }
370
371
372
373
374
375 static void
376 modest_tny_header_tree_view_init (ModestTnyHeaderTreeView *obj)
377 {
378         ModestTnyHeaderTreeViewPrivate *priv;
379         int i;
380
381         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(obj); 
382         
383         for (i = 0; i != MODEST_TNY_HEADER_TREE_VIEW_COLUMN_NUM; ++i)
384                 priv->sort_columns[i] = -1;
385
386         
387         init_icons (priv->icons);       
388 }
389
390 static void
391 modest_tny_header_tree_view_finalize (GObject *obj)
392 {
393         ModestTnyHeaderTreeView        *self;
394         ModestTnyHeaderTreeViewPrivate *priv;
395         int i;
396         
397         self = MODEST_TNY_HEADER_TREE_VIEW(obj);
398         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
399
400         if (priv->headers)      
401                 g_object_unref (G_OBJECT(priv->headers));
402         
403         priv->headers = NULL;
404         priv->tny_msg_folder    = NULL;
405
406         /* cleanup our icons */
407         for (i = 0; i != HEADER_ICON_NUM; ++i) {
408                 if (priv->icons[i]) {
409                         g_object_unref (G_OBJECT(priv->icons[i]));
410                         priv->icons[i] = NULL;
411                 }
412         }
413 }
414
415 GtkWidget*
416 modest_tny_header_tree_view_new (TnyMsgFolderIface *folder,
417                                  GSList *columns,
418                                  ModestTnyHeaderTreeViewStyle style)
419 {
420         GObject *obj;
421         GtkTreeSelection *sel;
422         ModestTnyHeaderTreeView *self;
423         
424         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_HEADER_TREE_VIEW, NULL));
425         self = MODEST_TNY_HEADER_TREE_VIEW(obj);
426         
427         if (!modest_tny_header_tree_view_set_folder (self, NULL)) {
428                 g_warning ("could not set the folder");
429                 g_object_unref (obj);
430                 return NULL;
431         }
432         
433         modest_tny_header_tree_view_set_style   (self, style);
434         modest_tny_header_tree_view_set_columns (self, columns);
435         
436         /* all cols */
437         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(obj), TRUE);
438         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), TRUE);
439         
440         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
441                                       TRUE); /* alternating row colors */
442
443         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
444         g_signal_connect (sel, "changed",
445                           G_CALLBACK(selection_changed), self);
446
447         return GTK_WIDGET(self);
448 }
449
450 gboolean
451 modest_tny_header_tree_view_set_columns (ModestTnyHeaderTreeView *self, GSList *columns)
452 {
453         ModestTnyHeaderTreeViewPrivate *priv;
454         GSList *cursor;
455
456         g_return_val_if_fail (self, FALSE);
457         
458         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
459
460         g_slist_free (priv->columns);
461         
462         for (cursor = columns; cursor; cursor = cursor->next) {
463                 ModestTnyHeaderTreeViewColumn col = 
464                         (ModestTnyHeaderTreeViewColumn) GPOINTER_TO_INT(cursor->data);
465                 if (0 > col || col >= MODEST_TNY_HEADER_TREE_VIEW_COLUMN_NUM)
466                         g_warning ("invalid column in column list");
467                 else
468                         priv->columns = g_slist_append (priv->columns, cursor->data);
469         }
470
471         init_columns (self); /* redraw them */
472         return TRUE;
473 }
474
475
476
477 const GSList*
478 modest_tny_header_tree_view_get_columns (ModestTnyHeaderTreeView *self)
479 {
480         ModestTnyHeaderTreeViewPrivate *priv;
481         GSList *cursor;
482
483         g_return_val_if_fail (self, FALSE);
484         
485         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
486         return priv->columns;
487 }
488
489
490
491
492 gboolean
493 modest_tny_header_tree_view_set_style (ModestTnyHeaderTreeView *self,
494                                        ModestTnyHeaderTreeViewStyle style)
495 {
496         g_return_val_if_fail (self, FALSE);
497         g_return_val_if_fail (style >= 0 && style < MODEST_TNY_HEADER_TREE_VIEW_STYLE_NUM,
498                               FALSE);
499         
500         MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self)->style = style;
501         
502         return TRUE;
503 }
504
505 ModestTnyHeaderTreeViewStyle
506 modest_tny_header_tree_view_get_style (ModestTnyHeaderTreeView *self)
507 {
508         g_return_val_if_fail (self, FALSE);
509
510         return MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self)->style;
511 }
512
513
514
515 /* get the length of any prefix that should be ignored for sorting */
516 static int 
517 get_prefix_len (const gchar *sub)
518 {
519         int i = 0;
520         const static gchar* prefix[] = {"Re:", "RE:", "Fwd:", "FWD:", NULL};
521
522         if (sub[0] != 'R' && sub[0] != 'F') /* optimization */
523                 return 0;
524         
525         while (prefix[i]) {
526                 if (g_str_has_prefix(sub, prefix[i])) {
527                         int prefix_len = strlen(prefix[i]); 
528                         if (sub[prefix_len + 1] == ' ')
529                                 ++prefix_len; /* ignore space after prefix as well */
530                         return prefix_len; 
531                 }
532         }
533         return 0;
534 }
535
536
537 static gint
538 cmp_normalized_subject (const gchar* s1, const gchar *s2)
539 {
540         /* FIXME: utf8 */
541         return strcmp (s1 + get_prefix_len(s1),
542                        s2 + get_prefix_len(s2));
543 }
544
545
546 static gint
547 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
548           gpointer user_data)
549 {
550         gint col_id = GPOINTER_TO_INT (user_data);
551         gint val1, val2;
552
553         g_return_val_if_fail (GTK_IS_TREE_MODEL(tree_model), -1);
554         
555         switch (col_id) {
556
557         case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_COMPACT_HEADER:
558         case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_RECEIVED_DATE:
559                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
560                                     &val1,-1);
561                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
562                                     &val2,-1);
563                 return val1 - val2;
564
565                 
566         case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_SENT_DATE:
567                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
568                                     &val1,-1);
569                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
570                                     &val2,-1);
571                 return val1 - val2;
572
573         case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_SUBJECT: {
574                 gchar *sub1, *sub2;
575                 int retval;
576                 
577                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
578                                     &sub1,-1);
579                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
580                                     &sub2,-1);
581                 
582                 retval = cmp_normalized_subject(sub1, sub2);
583
584                 g_free (sub1);
585                 g_free (sub2);
586                 
587                 return retval;
588         }
589                 
590         case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_ATTACH:
591                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
592                                     &val1,-1);
593                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
594                                     &val2,-1);
595                 
596                 return (val1 & TNY_MSG_HEADER_FLAG_ATTACHMENTS) - (val2 & TNY_MSG_HEADER_FLAG_ATTACHMENTS);
597         
598
599         case MODEST_TNY_HEADER_TREE_VIEW_COLUMN_MSGTYPE:
600                 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
601                                     &val1,-1);
602                 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
603                                     &val2,-1);
604
605                 g_message ("%p %p", iter1, iter2);
606                 return (val1 & TNY_MSG_HEADER_FLAG_SEEN) - (val2 & TNY_MSG_HEADER_FLAG_SEEN);
607
608         default:
609                 g_message ("%p %p", iter1, iter2);
610                 return &iter1 - &iter2;
611         }
612 }
613
614
615 gboolean
616 modest_tny_header_tree_view_set_folder (ModestTnyHeaderTreeView *self,
617                                         TnyMsgFolderIface *folder)
618 {
619         int i;
620         GtkTreeModel *oldsortable, *sortable, *oldmodel;
621         ModestTnyHeaderTreeViewPrivate *priv;
622         
623         g_return_val_if_fail (self, FALSE);
624
625         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
626         
627         if (folder) {
628                 priv->headers = TNY_LIST_IFACE(tny_msg_header_list_model_new ());
629                 tny_msg_folder_iface_get_headers (folder, priv->headers,
630                                                   FALSE);
631                 tny_msg_header_list_model_set_folder (TNY_MSG_HEADER_LIST_MODEL(priv->headers),
632                                                       folder, TRUE);
633                 
634                 oldsortable = gtk_tree_view_get_model(GTK_TREE_VIEW (self));
635                 if (oldsortable && GTK_IS_TREE_MODEL_SORT(oldsortable)) {
636                         GtkTreeModel *oldmodel = gtk_tree_model_sort_get_model
637                                 (GTK_TREE_MODEL_SORT(oldsortable));
638                         if (oldmodel)
639                                 g_object_unref (G_OBJECT(oldmodel));
640                         g_object_unref (oldsortable);
641                 }
642         
643                 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
644                 
645                 for (i = 0; i != MODEST_TNY_HEADER_TREE_VIEW_COLUMN_NUM; ++i) {
646                         int col_id = priv->sort_columns[i];
647                         if (col_id >= 0) {
648                                 g_message ("%d: %p: %p: %d", i, GTK_TREE_SORTABLE(sortable), sortable, col_id);
649                                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable), col_id,
650                                                                  (GtkTreeIterCompareFunc)cmp_rows,
651                                                                  GINT_TO_POINTER(col_id), NULL);
652                         }
653                 }
654                 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
655                 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), TRUE);
656
657                 /* no need to unref sortable */
658                 
659         } else /* when there is no folder */
660                 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), FALSE);
661                                          
662         return TRUE;
663 }
664
665
666 static void
667 selection_changed (GtkTreeSelection *sel, gpointer user_data)
668 {
669         GtkTreeModel            *model;
670         TnyMsgHeaderIface       *header;
671         GtkTreeIter             iter;
672         ModestTnyHeaderTreeView *tree_view;
673
674         g_return_if_fail (sel);
675         g_return_if_fail (user_data);
676         
677         if (!gtk_tree_selection_get_selected (sel, &model, &iter))
678                 return; /* msg was _un_selected */
679         
680         tree_view = MODEST_TNY_HEADER_TREE_VIEW (user_data);
681         
682         gtk_tree_model_get (model, &iter,
683                             TNY_MSG_HEADER_LIST_MODEL_INSTANCE_COLUMN,
684                             &header, -1);
685         
686         if (header) {
687                 TnyMsgHeaderFlags flags;
688                 const TnyMsgIface *msg;
689                 const TnyMsgFolderIface *folder;
690                 
691                 folder = tny_msg_header_iface_get_folder (TNY_MSG_HEADER_IFACE(header));
692                 if (!folder)
693                         g_message ("cannot find folder");
694                 else {
695                         msg = tny_msg_folder_iface_get_message (TNY_MSG_FOLDER_IFACE(folder),
696                                                                 header);
697                         if (!msg) {
698                                 g_message ("cannot find msg");
699                                 /* FIXME: update display */
700                         }
701                 }
702                                         
703                 g_signal_emit (G_OBJECT(tree_view), signals[MESSAGE_SELECTED_SIGNAL], 0,
704                                msg);
705
706                 /* mark message as seen; _set_flags crashes, bug in tinymail? */
707                 flags = tny_msg_header_iface_get_flags (TNY_MSG_HEADER_IFACE(header));
708                 //tny_msg_header_iface_set_flags (header, flags | TNY_MSG_HEADER_FLAG_SEEN);
709         }
710 }
711
712 static void
713 column_clicked (GtkTreeViewColumn *col, gpointer user_data)
714 {
715         GtkTreeView *treeview;
716         gint id;
717
718         treeview = GTK_TREE_VIEW (user_data);
719         id = gtk_tree_view_column_get_sort_column_id (col);
720         
721         gtk_tree_view_set_search_column (treeview, id);
722 }