* modest-header-view-render.c:
[modest] / src / widgets / modest-header-view.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <glib/gi18n.h>
31 #include <tny-list.h>
32 #include <tny-simple-list.h>
33 #include <string.h>
34
35 #include <modest-header-view.h>
36 #include <modest-header-view-priv.h>
37
38 #include <modest-marshal.h>
39 #include <modest-text-utils.h>
40 #include <modest-icon-names.h>
41 #include <modest-icon-factory.h>
42
43 static void modest_header_view_class_init  (ModestHeaderViewClass *klass);
44 static void modest_header_view_init        (ModestHeaderView *obj);
45 static void modest_header_view_finalize    (GObject *obj);
46
47 static void on_selection_changed (GtkTreeSelection *sel, gpointer user_data);
48
49 static gint cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
50                       gpointer user_data);
51
52 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
53
54 enum {
55         HEADER_SELECTED_SIGNAL,
56         ITEM_NOT_FOUND_SIGNAL,
57         STATUS_UPDATE_SIGNAL,
58         LAST_SIGNAL
59 };
60
61
62 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
63 struct _ModestHeaderViewPrivate {
64         TnyFolder            *folder;
65         TnyList              *headers;
66         GMutex               *lock;
67         ModestHeaderViewStyle style;
68 };
69
70 #define MODEST_HEADER_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
71                                                 MODEST_TYPE_HEADER_VIEW, \
72                                                 ModestHeaderViewPrivate))
73
74 /* globals */
75 static GObjectClass *parent_class = NULL;
76
77 /* uncomment the following if you have defined any signals */
78 static guint signals[LAST_SIGNAL] = {0};
79
80 GType
81 modest_header_view_get_type (void)
82 {
83         static GType my_type = 0;
84         if (!my_type) {
85                 static const GTypeInfo my_info = {
86                         sizeof(ModestHeaderViewClass),
87                         NULL,           /* base init */
88                         NULL,           /* base finalize */
89                         (GClassInitFunc) modest_header_view_class_init,
90                         NULL,           /* class finalize */
91                         NULL,           /* class data */
92                         sizeof(ModestHeaderView),
93                         1,              /* n_preallocs */
94                         (GInstanceInitFunc) modest_header_view_init,
95                         NULL
96                 };
97                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
98                                                   "ModestHeaderView",
99                                                   &my_info, 0);
100         }
101         return my_type;
102 }
103
104 static void
105 modest_header_view_class_init (ModestHeaderViewClass *klass)
106 {
107         GObjectClass *gobject_class;
108         gobject_class = (GObjectClass*) klass;
109
110         parent_class            = g_type_class_peek_parent (klass);
111         gobject_class->finalize = modest_header_view_finalize;
112         
113         g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
114         
115         signals[HEADER_SELECTED_SIGNAL] = 
116                 g_signal_new ("header_selected",
117                               G_TYPE_FROM_CLASS (gobject_class),
118                               G_SIGNAL_RUN_FIRST,
119                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
120                               NULL, NULL,
121                               g_cclosure_marshal_VOID__POINTER,
122                               G_TYPE_NONE, 1, G_TYPE_POINTER);
123
124         signals[ITEM_NOT_FOUND_SIGNAL] = 
125                 g_signal_new ("item_not_found",
126                               G_TYPE_FROM_CLASS (gobject_class),
127                               G_SIGNAL_RUN_FIRST,
128                               G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
129                               NULL, NULL,
130                               g_cclosure_marshal_VOID__INT,
131                               G_TYPE_NONE, 1, G_TYPE_INT);
132
133         signals[STATUS_UPDATE_SIGNAL] =
134                 g_signal_new ("status_update",
135                               G_TYPE_FROM_CLASS (gobject_class),
136                               G_SIGNAL_RUN_FIRST,
137                               G_STRUCT_OFFSET (ModestHeaderViewClass,status_update),
138                               NULL, NULL,
139                               modest_marshal_VOID__STRING_INT_INT,
140                               G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
141 }
142
143 static GtkTreeViewColumn*
144 get_new_column (const gchar *name, GtkCellRenderer *renderer,
145                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
146                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
147 {
148         GtkTreeViewColumn *column;
149
150         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
151         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
152
153         gtk_tree_view_column_set_resizable (column, resizable);
154         if (resizable) 
155                 gtk_tree_view_column_set_expand (column, TRUE);
156         
157         if (show_as_text) 
158                 gtk_tree_view_column_add_attribute (column, renderer, "text",
159                                                     sort_col_id);
160         if (sort_col_id >= 0)
161                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
162
163         gtk_tree_view_column_set_sort_indicator (column, FALSE);
164         gtk_tree_view_column_set_reorderable (column, TRUE);
165         
166         if (cell_data_func)
167                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
168                                                         user_data, NULL);
169         return column;
170 }
171
172
173
174
175 static void
176 remove_all_columns (ModestHeaderView *obj)
177 {
178         GList *columns, *cursor;
179
180         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
181
182         for (cursor = columns; cursor; cursor = cursor->next)
183                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
184                                              GTK_TREE_VIEW_COLUMN(cursor->data));
185         g_list_free (columns);  
186 }
187
188
189
190 gboolean
191 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns)
192 {
193         GtkTreeModel *sortable;
194         GtkTreeViewColumn *column=NULL;
195         GtkCellRenderer *renderer_msgtype,*renderer_header,
196                 *renderer_attach;
197         ModestHeaderViewPrivate *priv;
198         const GList *cursor;
199         
200         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self); 
201
202         /* FIXME: check whether these renderers need to be freed */
203         renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
204         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
205         renderer_header  = gtk_cell_renderer_text_new ();
206         
207         remove_all_columns (self);
208
209         if (priv->headers)
210                 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
211         else
212                 sortable = NULL;
213         
214         for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
215                 ModestHeaderViewColumn col =
216                         (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
217                 
218                 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
219                         g_printerr ("modest: invalid column %d in column list\n", col);
220                         continue;
221                 }
222                 
223                 switch (col) {
224                         
225                 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
226                         column = get_new_column (_("M"), renderer_msgtype, FALSE,
227                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
228                                                  FALSE,
229                                                  (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
230                                                  NULL);
231                         gtk_tree_view_column_set_fixed_width (column, 45);
232                         break;
233
234                 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
235                         column = get_new_column (_("A"), renderer_attach, FALSE,
236                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
237                                                  FALSE,
238                                                  (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
239                                                  NULL);
240                         gtk_tree_view_column_set_fixed_width (column, 45);
241                         break;
242
243                         
244                 case MODEST_HEADER_VIEW_COLUMN_FROM:
245                         column = get_new_column (_("From"), renderer_header, TRUE,
246                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
247                                                  TRUE,
248                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
249                                                  GINT_TO_POINTER(TRUE));
250                         break;
251
252                 case MODEST_HEADER_VIEW_COLUMN_TO:
253                         column = get_new_column (_("To"), renderer_header, TRUE,
254                                                  TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
255                                                  TRUE,
256                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
257                                                  GINT_TO_POINTER(FALSE));
258                         break;
259                         
260                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
261                         column = get_new_column (_("Header"), renderer_header, TRUE,
262                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
263                                                  TRUE,
264                                                  (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
265                                                  GINT_TO_POINTER(TRUE));
266                         break;
267
268                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
269                         column = get_new_column (_("Header"), renderer_header, TRUE,
270                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
271                                                  TRUE,
272                                                  (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
273                                                  GINT_TO_POINTER(FALSE));
274                         break;
275
276                         
277                 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
278                         column = get_new_column (_("Subject"), renderer_header, TRUE,
279                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
280                                                  TRUE,
281                                                  (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
282                                                  NULL);
283                         break;
284                         
285                 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
286                         column = get_new_column (_("Received"), renderer_header, TRUE,
287                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
288                                                  TRUE,
289                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
290                                                  GINT_TO_POINTER(TRUE));
291                         break;
292                         
293                 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
294                         column = get_new_column (_("Sent"), renderer_header, TRUE,
295                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
296                                                  TRUE,
297                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
298                                                  GINT_TO_POINTER(FALSE));
299                         break;
300                         
301                 case MODEST_HEADER_VIEW_COLUMN_SIZE:
302                         column = get_new_column (_("Size"), renderer_header, TRUE,
303                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
304                                                  FALSE,
305                                                  (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
306                                                  NULL); 
307                         break;
308
309                 default:
310                         g_return_val_if_reached(FALSE);
311                 }
312
313                 if (sortable)
314                         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
315                                                          col, (GtkTreeIterCompareFunc)cmp_rows,
316                                                          column, NULL);
317                 
318                 /* we keep the column id around */
319                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
320                                    GINT_TO_POINTER(col));
321                 
322                 /* we need this ptr when sorting the rows */
323                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
324                                    self);
325                 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);              
326         }       
327         return TRUE;
328 }
329
330 static void
331 modest_header_view_init (ModestHeaderView *obj)
332 {
333         ModestHeaderViewPrivate *priv;
334
335         priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj); 
336
337         priv->lock = g_mutex_new ();
338
339 }
340
341 static void
342 modest_header_view_finalize (GObject *obj)
343 {
344         ModestHeaderView        *self;
345         ModestHeaderViewPrivate *priv;
346         GtkTreeSelection        *sel;
347         
348         self = MODEST_HEADER_VIEW(obj);
349         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
350
351         if (priv->headers)      
352                 g_object_unref (G_OBJECT(priv->headers));
353
354         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
355         
356         if (priv->lock) {
357                 g_mutex_free (priv->lock);
358                 priv->lock = NULL;
359         }
360
361         priv->headers  = NULL;
362         priv->folder   = NULL;
363         
364         G_OBJECT_CLASS(parent_class)->finalize (obj);
365 }
366
367
368 GtkWidget*
369 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
370 {
371         GObject *obj;
372         GtkTreeSelection *sel;
373         ModestHeaderView *self;
374         ModestHeaderViewPrivate *priv;
375         
376         g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
377                               NULL);
378         
379         obj  = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
380         self = MODEST_HEADER_VIEW(obj);
381         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
382         
383         modest_header_view_set_style   (self, style);
384
385         if (!modest_header_view_set_folder (self, NULL)) {
386                 g_printerr ("modest: could not set the folder\n");
387                 g_object_unref (obj);
388                 return NULL;
389         }
390
391         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
392         gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
393
394         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
395                                       TRUE); /* alternating row colors */
396         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
397         
398         g_signal_connect (sel, "changed",
399                           G_CALLBACK(on_selection_changed), self);
400                 
401         return GTK_WIDGET(self);
402 }
403
404
405 TnyList * 
406 modest_header_view_get_selected_headers (ModestHeaderView *self)
407 {
408         GtkTreeSelection *sel;
409         ModestHeaderViewPrivate *priv;
410         TnyList *header_list = NULL;
411         TnyHeader *header;
412         GList *list, *tmp = NULL;
413         GtkTreeModel *tree_model = NULL;
414         GtkTreeIter iter;
415         
416         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
417
418         /* Get selected rows */
419         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
420         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
421
422         if (list) {
423                 header_list = tny_simple_list_new();
424
425                 list = g_list_reverse (list);
426                 tmp = list;
427                 while (tmp) {                   
428                         /* get header from selection */
429                         gtk_tree_model_get_iter (tree_model,
430                                                  &iter,
431                                                  (GtkTreePath *) (tmp->data));
432                                                                           
433                         gtk_tree_model_get (tree_model, &iter,
434                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
435                                             &header, -1);
436
437                         /* Prepend to list */
438                         tny_list_prepend (header_list, G_OBJECT (header));
439                         tmp = g_list_next (tmp);
440                 }
441                 /* Clean up*/
442                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
443                 g_list_free (list);
444         }
445         return header_list;
446 }
447
448 void 
449 modest_header_view_select_next (ModestHeaderView *self)
450 {
451         GtkTreeSelection *sel;
452         GtkTreeIter iter;
453         GtkTreeModel *model;
454
455         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
456         if (sel) {
457                 gtk_tree_selection_get_selected (sel, &model, &iter);
458                 gtk_tree_model_iter_next (model, &iter);
459                 gtk_tree_selection_select_iter (sel, &iter);
460         }
461 }
462 GList*
463 modest_header_view_get_columns (ModestHeaderView *self)
464 {
465         g_return_val_if_fail (self, FALSE);
466         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self)); 
467 }
468
469 gboolean
470 modest_header_view_is_empty (ModestHeaderView *self)
471 {
472         g_return_val_if_fail (self, FALSE);
473         return FALSE; /* FIXME */
474 }
475
476
477 gboolean
478 modest_header_view_set_style (ModestHeaderView *self,
479                               ModestHeaderViewStyle style)
480 {
481         ModestHeaderViewPrivate *priv;
482         gboolean show_col_headers = FALSE;
483         ModestHeaderViewStyle old_style;
484         
485         g_return_val_if_fail (self, FALSE);
486         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
487                               FALSE);
488
489         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
490         if (priv->style == style)
491                 return TRUE; /* nothing to do */
492         
493         switch (style) {
494         case MODEST_HEADER_VIEW_STYLE_DETAILS:
495                 show_col_headers = TRUE;
496                 break;
497         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
498                 break;
499         default:
500                 g_return_val_if_reached (FALSE);
501         }
502         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
503         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);    
504
505         old_style   = priv->style;
506         priv->style = style;
507
508         return TRUE;
509 }
510
511
512 ModestHeaderViewStyle
513 modest_header_view_get_style (ModestHeaderView *self)
514 {
515         g_return_val_if_fail (self, FALSE);
516         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
517 }
518
519
520 static void
521 on_refresh_folder (TnyFolder *folder, gboolean cancelled, GError **err,
522                    gpointer user_data)
523 {
524         GtkTreeModel *sortable; 
525         ModestHeaderView *self;
526         ModestHeaderViewPrivate *priv;
527         GError *error = NULL;
528
529         if (cancelled)
530                 return;
531         
532         self = MODEST_HEADER_VIEW(user_data);
533         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
534
535         priv->folder = folder;
536                 
537         if (folder) { /* it's a new one or a refresh */
538                 GList *cols, *cursor;
539                 
540                 if (priv->headers)
541                         g_object_unref (priv->headers);
542
543                 priv->headers = TNY_LIST(tny_gtk_header_list_model_new ());
544                 tny_folder_get_headers (folder, priv->headers, FALSE, &error); /* FIXME */
545                 if (error) {
546                         g_signal_emit (G_OBJECT(self), signals[ITEM_NOT_FOUND_SIGNAL],
547                                        0, MODEST_ITEM_TYPE_MESSAGE);
548                         g_print (error->message);
549                         g_error_free (error);
550                         return;
551                 }
552
553                 tny_gtk_header_list_model_set_folder
554                         (TNY_GTK_HEADER_LIST_MODEL(priv->headers),folder, TRUE); /*async*/
555
556                 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
557
558                 /* install our special sorting functions */
559                 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
560                 while (cursor) {
561                         gint col_id = GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cursor->data),
562                                                                          MODEST_HEADER_VIEW_COLUMN));
563                         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
564                                                          col_id,
565                                                          (GtkTreeIterCompareFunc)cmp_rows,
566                                                          cursor->data, NULL);
567                         cursor = g_list_next(cursor);
568                 }
569                 g_list_free (cols);             
570                 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
571         }
572 }
573
574
575 static void
576 on_refresh_folder_status_update (TnyFolder *folder, const gchar *msg,
577                                  gint num, gint total,  gpointer user_data)
578 {
579         ModestHeaderView *self;
580         ModestHeaderViewPrivate *priv;
581
582         self = MODEST_HEADER_VIEW(user_data);
583         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
584
585         /* FIXME: this is a hack ==> tinymail gives us this when
586          * it has nothing better to do */
587         if (num == 1 && total == 100)
588                 return;
589         
590         g_signal_emit (G_OBJECT(self), signals[STATUS_UPDATE_SIGNAL],
591                        0, msg, num, total);
592 }
593
594
595 TnyFolder*
596 modest_header_view_get_folder (ModestHeaderView *self)
597 {
598         ModestHeaderViewPrivate *priv;
599         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
600
601         return priv->folder;
602 }
603
604
605 gboolean
606 modest_header_view_set_folder (ModestHeaderView *self, TnyFolder *folder)
607 {
608         ModestHeaderViewPrivate *priv;
609         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
610
611         priv->folder = folder;
612
613         if (folder)
614                 tny_folder_refresh_async (folder,
615                                           on_refresh_folder,
616                                           on_refresh_folder_status_update,
617                                           self);
618         /* no message selected */
619         g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0,
620                        NULL);
621         return TRUE;
622 }
623
624 static void
625 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
626 {
627         GtkTreeModel *model;
628         TnyHeader *header;
629         GtkTreeIter iter;
630         ModestHeaderView *self;
631         ModestHeaderViewPrivate *priv;
632         
633         g_return_if_fail (sel);
634         g_return_if_fail (user_data);
635         
636         self = MODEST_HEADER_VIEW (user_data);
637         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
638         
639         if (!gtk_tree_selection_get_selected (sel, &model, &iter)) 
640                 return; /* msg was _un_selected */
641
642         gtk_tree_model_get (model, &iter,
643                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
644                             &header, -1);
645
646         /* Emit signal */
647         g_signal_emit (G_OBJECT(self), 
648                        signals[HEADER_SELECTED_SIGNAL], 
649                        0, header);
650 }
651
652
653 /* PROTECTED method. It's useful when we want to force a given
654    selection to reload a msg. For example if we have selected a header
655    in offline mode, when Modest become online, we want to reload the
656    message automatically without an user click over the header */
657 void 
658 _modest_header_view_change_selection (GtkTreeSelection *selection,
659                                       gpointer user_data)
660 {
661         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
662         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
663
664         on_selection_changed (selection, user_data);
665 }
666
667
668 static gint
669 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
670           gpointer user_data)
671 {
672         gint col_id;
673         gint t1, t2;
674         gint val1, val2;
675         gchar *s1, *s2;
676         gint cmp;
677         
678         static int counter = 0;
679         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_COLUMN));
680         
681         if (!(++counter % 100)) {
682                 GObject *header_view = g_object_get_data(G_OBJECT(user_data),
683                                                          MODEST_HEADER_VIEW_PTR);
684                 g_signal_emit (header_view,
685                                signals[STATUS_UPDATE_SIGNAL],
686                                0, _("Sorting..."), 0, 0);
687         }       
688         switch (col_id) {
689
690                 /* first one, we decide based on the time */
691         case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
692         case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
693
694                 gtk_tree_model_get (tree_model, iter1,
695                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
696                                     &t1,-1);
697                 gtk_tree_model_get (tree_model, iter2,
698                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
699                                     &t2,-1);
700                 return t1 - t2;
701
702         case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
703         case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
704                 gtk_tree_model_get (tree_model, iter1,
705                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
706                                     &t1,-1);
707                 gtk_tree_model_get (tree_model, iter2,
708                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
709                                     &t2,-1);
710                 return t1 - t2;
711
712                 
713                 /* next ones, we try the search criteria first, if they're the same, then we use 'sent date' */
714                 /* FIXME: what about received-date? */
715         case MODEST_HEADER_VIEW_COLUMN_SUBJECT: {
716
717                 gtk_tree_model_get (tree_model, iter1,
718                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &s1,
719                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
720                                     -1);
721                 gtk_tree_model_get (tree_model, iter2,
722                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &s2,
723                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
724                                     -1);
725
726                 /* the prefix ('Re:', 'Fwd:' etc.) we ignore */ 
727                 cmp = modest_text_utils_utf8_strcmp (s1 + modest_text_utils_get_subject_prefix_len(s1),
728                                                      s2 + modest_text_utils_get_subject_prefix_len(s2),
729                                                      TRUE);
730                 g_free (s1);
731                 g_free (s2);
732                 
733                 return cmp ? cmp : t1 - t2;
734         }
735                 
736         case MODEST_HEADER_VIEW_COLUMN_FROM:
737                 
738                 gtk_tree_model_get (tree_model, iter1,
739                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN, &s1,
740                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
741                                     -1);
742                 gtk_tree_model_get (tree_model, iter2,
743                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN, &s2,
744                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
745                                     -1);
746                 cmp = modest_text_utils_utf8_strcmp (s1, s2, TRUE);
747                 g_free (s1);
748                 g_free (s2);
749                 
750                 return cmp ? cmp : t1 - t2;
751                 
752         case MODEST_HEADER_VIEW_COLUMN_TO: 
753                 
754                 gtk_tree_model_get (tree_model, iter1,
755                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN, &s1,
756                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
757                                     -1);
758                 gtk_tree_model_get (tree_model, iter2,
759                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN, &s2,
760                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
761                                     -1);
762                 cmp = modest_text_utils_utf8_strcmp (s1, s2, TRUE);
763                 g_free (s1);
764                 g_free (s2);
765                 
766                 return cmp ? cmp : t1 - t2;
767
768         case MODEST_HEADER_VIEW_COLUMN_ATTACH:
769
770                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
771                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
772                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
773                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
774                 
775                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
776                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
777
778                 return cmp ? cmp : t1 - t2;
779                 
780         case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
781                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
782                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
783                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
784                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
785                 cmp =  (val1 & TNY_HEADER_FLAG_SEEN) - (val2 & TNY_HEADER_FLAG_SEEN);
786
787                 return cmp ? cmp : t1 - t2;
788
789         default:
790                 return &iter1 - &iter2; /* oughhhh  */
791         }
792 }