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