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