* all:
[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
42 static void modest_header_view_class_init  (ModestHeaderViewClass *klass);
43 static void modest_header_view_init        (ModestHeaderView *obj);
44 static void modest_header_view_finalize    (GObject *obj);
45
46 static void on_selection_changed (GtkTreeSelection *sel, gpointer user_data);
47
48 static gint cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
49                       gpointer user_data);
50
51 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
52
53 enum {
54         HEADER_SELECTED_SIGNAL,
55         ITEM_NOT_FOUND_SIGNAL,
56         STATUS_UPDATE_SIGNAL,
57         LAST_SIGNAL
58 };
59
60
61 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
62 struct _ModestHeaderViewPrivate {
63         TnyFolder            *folder;
64         TnyList              *headers;
65         GMutex               *lock;
66         ModestHeaderViewStyle style;
67 };
68
69 #define MODEST_HEADER_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
70                                                 MODEST_TYPE_HEADER_VIEW, \
71                                                 ModestHeaderViewPrivate))
72
73 /* globals */
74 static GObjectClass *parent_class = NULL;
75
76 /* uncomment the following if you have defined any signals */
77 static guint signals[LAST_SIGNAL] = {0};
78
79 GType
80 modest_header_view_get_type (void)
81 {
82         static GType my_type = 0;
83         if (!my_type) {
84                 static const GTypeInfo my_info = {
85                         sizeof(ModestHeaderViewClass),
86                         NULL,           /* base init */
87                         NULL,           /* base finalize */
88                         (GClassInitFunc) modest_header_view_class_init,
89                         NULL,           /* class finalize */
90                         NULL,           /* class data */
91                         sizeof(ModestHeaderView),
92                         1,              /* n_preallocs */
93                         (GInstanceInitFunc) modest_header_view_init,
94                         NULL
95                 };
96                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
97                                                   "ModestHeaderView",
98                                                   &my_info, 0);
99         }
100         return my_type;
101 }
102
103 static void
104 modest_header_view_class_init (ModestHeaderViewClass *klass)
105 {
106         GObjectClass *gobject_class;
107         gobject_class = (GObjectClass*) klass;
108
109         parent_class            = g_type_class_peek_parent (klass);
110         gobject_class->finalize = modest_header_view_finalize;
111         
112         g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
113         
114         signals[HEADER_SELECTED_SIGNAL] = 
115                 g_signal_new ("header_selected",
116                               G_TYPE_FROM_CLASS (gobject_class),
117                               G_SIGNAL_RUN_FIRST,
118                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
119                               NULL, NULL,
120                               g_cclosure_marshal_VOID__POINTER,
121                               G_TYPE_NONE, 1, G_TYPE_POINTER);
122
123         signals[ITEM_NOT_FOUND_SIGNAL] = 
124                 g_signal_new ("item_not_found",
125                               G_TYPE_FROM_CLASS (gobject_class),
126                               G_SIGNAL_RUN_FIRST,
127                               G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
128                               NULL, NULL,
129                               g_cclosure_marshal_VOID__INT,
130                               G_TYPE_NONE, 1, G_TYPE_INT);
131
132         signals[STATUS_UPDATE_SIGNAL] =
133                 g_signal_new ("status_update",
134                               G_TYPE_FROM_CLASS (gobject_class),
135                               G_SIGNAL_RUN_FIRST,
136                               G_STRUCT_OFFSET (ModestHeaderViewClass,status_update),
137                               NULL, NULL,
138                               modest_marshal_VOID__STRING_INT_INT,
139                               G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
140 }
141
142 static GtkTreeViewColumn*
143 get_new_column (const gchar *name, GtkCellRenderer *renderer,
144                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
145                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
146 {
147         GtkTreeViewColumn *column;
148
149         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
150         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
151
152         gtk_tree_view_column_set_resizable (column, resizable);
153         if (resizable) 
154                 gtk_tree_view_column_set_expand (column, TRUE);
155         
156         if (show_as_text) 
157                 gtk_tree_view_column_add_attribute (column, renderer, "text",
158                                                     sort_col_id);
159         if (sort_col_id >= 0)
160                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
161
162         gtk_tree_view_column_set_sort_indicator (column, FALSE);
163         gtk_tree_view_column_set_reorderable (column, TRUE);
164         
165         if (cell_data_func)
166                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
167                                                         user_data, NULL);
168         return column;
169 }
170
171
172
173
174 static void
175 remove_all_columns (ModestHeaderView *obj)
176 {
177         GList *columns, *cursor;
178
179         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
180
181         for (cursor = columns; cursor; cursor = cursor->next)
182                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
183                                              GTK_TREE_VIEW_COLUMN(cursor->data));
184         g_list_free (columns);  
185 }
186
187
188
189 gboolean
190 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns)
191 {
192         GtkTreeModel *sortable;
193         GtkTreeViewColumn *column=NULL;
194         GtkCellRenderer *renderer_msgtype,*renderer_header,
195                 *renderer_attach;
196         ModestHeaderViewPrivate *priv;
197         const GList *cursor;
198         
199         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self); 
200
201         /* FIXME: check whether these renderers need to be freed */
202         renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
203         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
204         renderer_header  = gtk_cell_renderer_text_new ();
205         
206         remove_all_columns (self);
207
208         if (priv->headers)
209                 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
210         else
211                 sortable = NULL;
212         
213         for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
214                 ModestHeaderViewColumn col =
215                         (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
216                 
217                 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
218                         g_printerr ("modest: invalid column %d in column list\n", col);
219                         continue;
220                 }
221                 
222                 switch (col) {
223                         
224                 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
225                         column = get_new_column (_("M"), renderer_msgtype, FALSE,
226                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
227                                                  FALSE,
228                                                  (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
229                                                  NULL);
230                         gtk_tree_view_column_set_fixed_width (column, 45);
231                         break;
232
233                 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
234                         column = get_new_column (_("A"), renderer_attach, FALSE,
235                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
236                                                  FALSE,
237                                                  (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
238                                                  NULL);
239                         gtk_tree_view_column_set_fixed_width (column, 45);
240                         break;
241
242                         
243                 case MODEST_HEADER_VIEW_COLUMN_FROM:
244                         column = get_new_column (_("From"), renderer_header, TRUE,
245                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
246                                                  TRUE,
247                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
248                                                  GINT_TO_POINTER(TRUE));
249                         break;
250
251                 case MODEST_HEADER_VIEW_COLUMN_TO:
252                         column = get_new_column (_("To"), renderer_header, TRUE,
253                                                  TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
254                                                  TRUE,
255                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
256                                                  GINT_TO_POINTER(FALSE));
257                         break;
258                         
259                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
260                         column = get_new_column (_("Header"), renderer_header, TRUE,
261                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
262                                                  TRUE,
263                                                  (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
264                                                  GINT_TO_POINTER(TRUE));
265                         break;
266
267                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
268                         column = get_new_column (_("Header"), renderer_header, TRUE,
269                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
270                                                  TRUE,
271                                                  (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
272                                                  GINT_TO_POINTER(FALSE));
273                         break;
274
275                         
276                 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
277                         column = get_new_column (_("Subject"), renderer_header, TRUE,
278                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
279                                                  TRUE,
280                                                  (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
281                                                  NULL);
282                         break;
283                         
284                 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
285                         column = get_new_column (_("Received"), renderer_header, TRUE,
286                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
287                                                  TRUE,
288                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
289                                                  GINT_TO_POINTER(TRUE));
290                         break;
291                         
292                 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
293                         column = get_new_column (_("Sent"), renderer_header, TRUE,
294                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
295                                                  TRUE,
296                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
297                                                  GINT_TO_POINTER(FALSE));
298                         break;
299                         
300                 case MODEST_HEADER_VIEW_COLUMN_SIZE:
301                         column = get_new_column (_("Size"), renderer_header, TRUE,
302                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
303                                                  FALSE,
304                                                  (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
305                                                  NULL); 
306                         break;
307
308                 default:
309                         g_return_val_if_reached(FALSE);
310                 }
311
312                 if (sortable)
313                         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
314                                                          col, (GtkTreeIterCompareFunc)cmp_rows,
315                                                          column, NULL);
316                 
317                 /* we keep the column id around */
318                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
319                                    GINT_TO_POINTER(col));
320                 
321                 /* we need this ptr when sorting the rows */
322                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
323                                    self);
324                 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);              
325         }       
326         return TRUE;
327 }
328
329 static void
330 modest_header_view_init (ModestHeaderView *obj)
331 {
332         ModestHeaderViewPrivate *priv;
333
334         priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj); 
335
336         priv->lock = g_mutex_new ();
337
338 }
339
340 static void
341 modest_header_view_finalize (GObject *obj)
342 {
343         ModestHeaderView        *self;
344         ModestHeaderViewPrivate *priv;
345         GtkTreeSelection        *sel;
346         
347         self = MODEST_HEADER_VIEW(obj);
348         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
349
350         if (priv->headers)      
351                 g_object_unref (G_OBJECT(priv->headers));
352
353         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
354         
355         if (priv->lock) {
356                 g_mutex_free (priv->lock);
357                 priv->lock = NULL;
358         }
359
360         priv->headers  = NULL;
361         priv->folder   = NULL;
362         
363         G_OBJECT_CLASS(parent_class)->finalize (obj);
364 }
365
366
367 GtkWidget*
368 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
369 {
370         GObject *obj;
371         GtkTreeSelection *sel;
372         ModestHeaderView *self;
373         ModestHeaderViewPrivate *priv;
374         
375         g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
376                               NULL);
377         
378         obj  = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
379         self = MODEST_HEADER_VIEW(obj);
380         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
381         
382         modest_header_view_set_style   (self, style);
383
384         if (!modest_header_view_set_folder (self, NULL)) {
385                 g_printerr ("modest: could not set the folder\n");
386                 g_object_unref (obj);
387                 return NULL;
388         }
389
390         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
391         gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
392         gtk_tree_view_set_enable_search (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
619         /* no message selected */
620         g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0,
621                        NULL);
622         return TRUE;
623 }
624
625 static void
626 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
627 {
628         GtkTreeModel *model;
629         TnyHeader *header;
630         GtkTreeIter iter;
631         ModestHeaderView *self;
632         ModestHeaderViewPrivate *priv;
633         
634         g_return_if_fail (sel);
635         g_return_if_fail (user_data);
636         
637         self = MODEST_HEADER_VIEW (user_data);
638         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
639         
640         if (!gtk_tree_selection_get_selected (sel, &model, &iter)) 
641                 return; /* msg was _un_selected */
642
643         gtk_tree_model_get (model, &iter,
644                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
645                             &header, -1);
646
647         /* Emit signal */
648         g_signal_emit (G_OBJECT(self), 
649                        signals[HEADER_SELECTED_SIGNAL], 
650                        0, header);
651 }
652
653
654 /* PROTECTED method. It's useful when we want to force a given
655    selection to reload a msg. For example if we have selected a header
656    in offline mode, when Modest become online, we want to reload the
657    message automatically without an user click over the header */
658 void 
659 _modest_header_view_change_selection (GtkTreeSelection *selection,
660                                       gpointer user_data)
661 {
662         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
663         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
664
665         on_selection_changed (selection, user_data);
666 }
667
668
669 static gint
670 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
671           gpointer user_data)
672 {
673         gint col_id;
674         gint t1, t2;
675         gint val1, val2;
676         gchar *s1, *s2;
677         gint cmp;
678         
679         static int counter = 0;
680         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_COLUMN));
681         
682         if (!(++counter % 100)) {
683                 GObject *header_view = g_object_get_data(G_OBJECT(user_data),
684                                                          MODEST_HEADER_VIEW_PTR);
685                 g_signal_emit (header_view,
686                                signals[STATUS_UPDATE_SIGNAL],
687                                0, _("Sorting..."), 0, 0);
688         }       
689         switch (col_id) {
690
691                 /* first one, we decide based on the time */
692         case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
693         case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
694
695                 gtk_tree_model_get (tree_model, iter1,
696                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
697                                     &t1,-1);
698                 gtk_tree_model_get (tree_model, iter2,
699                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
700                                     &t2,-1);
701                 return t1 - t2;
702
703         case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
704         case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
705                 gtk_tree_model_get (tree_model, iter1,
706                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
707                                     &t1,-1);
708                 gtk_tree_model_get (tree_model, iter2,
709                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
710                                     &t2,-1);
711                 return t1 - t2;
712
713                 
714                 /* next ones, we try the search criteria first, if they're the same, then we use 'sent date' */
715                 /* FIXME: what about received-date? */
716         case MODEST_HEADER_VIEW_COLUMN_SUBJECT: {
717
718                 gtk_tree_model_get (tree_model, iter1,
719                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &s1,
720                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
721                                     -1);
722                 gtk_tree_model_get (tree_model, iter2,
723                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &s2,
724                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
725                                     -1);
726
727                 /* the prefix ('Re:', 'Fwd:' etc.) we ignore */ 
728                 cmp = modest_text_utils_utf8_strcmp (s1 + modest_text_utils_get_subject_prefix_len(s1),
729                                                      s2 + modest_text_utils_get_subject_prefix_len(s2),
730                                                      TRUE);
731                 g_free (s1);
732                 g_free (s2);
733                 
734                 return cmp ? cmp : t1 - t2;
735         }
736                 
737         case MODEST_HEADER_VIEW_COLUMN_FROM:
738                 
739                 gtk_tree_model_get (tree_model, iter1,
740                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN, &s1,
741                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
742                                     -1);
743                 gtk_tree_model_get (tree_model, iter2,
744                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN, &s2,
745                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
746                                     -1);
747                 cmp = modest_text_utils_utf8_strcmp (s1, s2, TRUE);
748                 g_free (s1);
749                 g_free (s2);
750                 
751                 return cmp ? cmp : t1 - t2;
752                 
753         case MODEST_HEADER_VIEW_COLUMN_TO: 
754                 
755                 gtk_tree_model_get (tree_model, iter1,
756                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN, &s1,
757                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
758                                     -1);
759                 gtk_tree_model_get (tree_model, iter2,
760                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN, &s2,
761                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
762                                     -1);
763                 cmp = modest_text_utils_utf8_strcmp (s1, s2, TRUE);
764                 g_free (s1);
765                 g_free (s2);
766                 
767                 return cmp ? cmp : t1 - t2;
768
769         case MODEST_HEADER_VIEW_COLUMN_ATTACH:
770
771                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
772                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
773                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
774                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
775                 
776                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
777                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
778
779                 return cmp ? cmp : t1 - t2;
780                 
781         case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
782                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
783                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
784                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
785                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
786                 cmp =  (val1 & TNY_HEADER_FLAG_SEEN) - (val2 & TNY_HEADER_FLAG_SEEN);
787
788                 return cmp ? cmp : t1 - t2;
789
790         default:
791                 return &iter1 - &iter2; /* oughhhh  */
792         }
793 }