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