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