* modest-folder-view.[ch]:
[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         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
394         
395         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
396                                       TRUE); /* alternating row colors */
397         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
398         
399         g_signal_connect (sel, "changed",
400                           G_CALLBACK(on_selection_changed), self);
401                 
402         return GTK_WIDGET(self);
403 }
404
405
406 TnyList * 
407 modest_header_view_get_selected_headers (ModestHeaderView *self)
408 {
409         GtkTreeSelection *sel;
410         ModestHeaderViewPrivate *priv;
411         TnyList *header_list = NULL;
412         TnyHeader *header;
413         GList *list, *tmp = NULL;
414         GtkTreeModel *tree_model = NULL;
415         GtkTreeIter iter;
416         
417         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
418
419         /* Get selected rows */
420         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
421         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
422
423         if (list) {
424                 header_list = tny_simple_list_new();
425
426                 list = g_list_reverse (list);
427                 tmp = list;
428                 while (tmp) {                   
429                         /* get header from selection */
430                         gtk_tree_model_get_iter (tree_model,
431                                                  &iter,
432                                                  (GtkTreePath *) (tmp->data));
433                                                                           
434                         gtk_tree_model_get (tree_model, &iter,
435                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
436                                             &header, -1);
437
438                         /* Prepend to list */
439                         tny_list_prepend (header_list, G_OBJECT (header));
440                         tmp = g_list_next (tmp);
441                 }
442                 /* Clean up*/
443                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
444                 g_list_free (list);
445         }
446         return header_list;
447 }
448
449 void 
450 modest_header_view_select_next (ModestHeaderView *self)
451 {
452         GtkTreeSelection *sel;
453         GtkTreeIter iter;
454         GtkTreeModel *model;
455
456         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
457         if (sel) {
458                 gtk_tree_selection_get_selected (sel, &model, &iter);
459                 gtk_tree_model_iter_next (model, &iter);
460                 gtk_tree_selection_select_iter (sel, &iter);
461         }
462 }
463 GList*
464 modest_header_view_get_columns (ModestHeaderView *self)
465 {
466         g_return_val_if_fail (self, FALSE);
467         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self)); 
468 }
469
470 gboolean
471 modest_header_view_is_empty (ModestHeaderView *self)
472 {
473         g_return_val_if_fail (self, FALSE);
474         return FALSE; /* FIXME */
475 }
476
477
478 gboolean
479 modest_header_view_set_style (ModestHeaderView *self,
480                               ModestHeaderViewStyle style)
481 {
482         ModestHeaderViewPrivate *priv;
483         gboolean show_col_headers = FALSE;
484         ModestHeaderViewStyle old_style;
485         
486         g_return_val_if_fail (self, FALSE);
487         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
488                               FALSE);
489
490         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
491         if (priv->style == style)
492                 return TRUE; /* nothing to do */
493         
494         switch (style) {
495         case MODEST_HEADER_VIEW_STYLE_DETAILS:
496                 show_col_headers = TRUE;
497                 break;
498         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
499                 break;
500         default:
501                 g_return_val_if_reached (FALSE);
502         }
503         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
504         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);    
505
506         old_style   = priv->style;
507         priv->style = style;
508
509         return TRUE;
510 }
511
512
513 ModestHeaderViewStyle
514 modest_header_view_get_style (ModestHeaderView *self)
515 {
516         g_return_val_if_fail (self, FALSE);
517         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
518 }
519
520
521 static void
522 on_refresh_folder (TnyFolder *folder, gboolean cancelled, GError **err,
523                    gpointer user_data)
524 {
525         GtkTreeModel *sortable; 
526         ModestHeaderView *self;
527         ModestHeaderViewPrivate *priv;
528         GError *error = NULL;
529
530         if (cancelled)
531                 return;
532         
533         self = MODEST_HEADER_VIEW(user_data);
534         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
535
536         priv->folder = folder;
537                 
538         if (folder) { /* it's a new one or a refresh */
539                 GList *cols, *cursor;
540                 
541                 if (priv->headers)
542                         g_object_unref (priv->headers);
543
544                 priv->headers = TNY_LIST(tny_gtk_header_list_model_new ());
545                 tny_folder_get_headers (folder, priv->headers, FALSE, &error); /* FIXME */
546                 if (error) {
547                         g_signal_emit (G_OBJECT(self), signals[ITEM_NOT_FOUND_SIGNAL],
548                                        0, MODEST_ITEM_TYPE_MESSAGE);
549                         g_print (error->message);
550                         g_error_free (error);
551                         return;
552                 }
553
554                 tny_gtk_header_list_model_set_folder
555                         (TNY_GTK_HEADER_LIST_MODEL(priv->headers),folder, TRUE); /*async*/
556
557                 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
558
559                 /* install our special sorting functions */
560                 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
561                 while (cursor) {
562                         gint col_id = GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cursor->data),
563                                                                          MODEST_HEADER_VIEW_COLUMN));
564                         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
565                                                          col_id,
566                                                          (GtkTreeIterCompareFunc)cmp_rows,
567                                                          cursor->data, NULL);
568                         cursor = g_list_next(cursor);
569                 }
570                 g_list_free (cols);             
571                 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
572         }
573 }
574
575
576 static void
577 on_refresh_folder_status_update (TnyFolder *folder, const gchar *msg,
578                                  gint num, gint total,  gpointer user_data)
579 {
580         ModestHeaderView *self;
581         ModestHeaderViewPrivate *priv;
582
583         self = MODEST_HEADER_VIEW(user_data);
584         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
585
586         /* FIXME: this is a hack ==> tinymail gives us this when
587          * it has nothing better to do */
588         if (num == 1 && total == 100)
589                 return;
590         
591         g_signal_emit (G_OBJECT(self), signals[STATUS_UPDATE_SIGNAL],
592                        0, msg, num, total);
593 }
594
595
596 TnyFolder*
597 modest_header_view_get_folder (ModestHeaderView *self)
598 {
599         ModestHeaderViewPrivate *priv;
600         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
601
602         return priv->folder;
603 }
604
605
606 gboolean
607 modest_header_view_set_folder (ModestHeaderView *self, TnyFolder *folder)
608 {
609         ModestHeaderViewPrivate *priv;
610         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
611
612         priv->folder = folder;
613
614         if (folder)
615                 tny_folder_refresh_async (folder,
616                                           on_refresh_folder,
617                                           on_refresh_folder_status_update,
618                                           self);
619
620         /* no message selected */
621         g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0,
622                        NULL);
623         return TRUE;
624 }
625
626 static void
627 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
628 {
629         GtkTreeModel *model;
630         TnyHeader *header;
631         GtkTreeIter iter;
632         ModestHeaderView *self;
633         ModestHeaderViewPrivate *priv;
634         
635         g_return_if_fail (sel);
636         g_return_if_fail (user_data);
637         
638         self = MODEST_HEADER_VIEW (user_data);
639         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
640         
641         if (!gtk_tree_selection_get_selected (sel, &model, &iter)) 
642                 return; /* msg was _un_selected */
643
644         gtk_tree_model_get (model, &iter,
645                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
646                             &header, -1);
647
648         /* Emit signal */
649         g_signal_emit (G_OBJECT(self), 
650                        signals[HEADER_SELECTED_SIGNAL], 
651                        0, header);
652 }
653
654
655 /* PROTECTED method. It's useful when we want to force a given
656    selection to reload a msg. For example if we have selected a header
657    in offline mode, when Modest become online, we want to reload the
658    message automatically without an user click over the header */
659 void 
660 _modest_header_view_change_selection (GtkTreeSelection *selection,
661                                       gpointer user_data)
662 {
663         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
664         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
665
666         on_selection_changed (selection, user_data);
667 }
668
669
670 static gint
671 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
672           gpointer user_data)
673 {
674         gint col_id;
675         gint t1, t2;
676         gint val1, val2;
677         gchar *s1, *s2;
678         gint cmp;
679         
680         static int counter = 0;
681         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_COLUMN));
682         
683         if (!(++counter % 100)) {
684                 GObject *header_view = g_object_get_data(G_OBJECT(user_data),
685                                                          MODEST_HEADER_VIEW_PTR);
686                 g_signal_emit (header_view,
687                                signals[STATUS_UPDATE_SIGNAL],
688                                0, _("Sorting..."), 0, 0);
689         }       
690         switch (col_id) {
691
692                 /* first one, we decide based on the time */
693         case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
694         case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
695
696                 gtk_tree_model_get (tree_model, iter1,
697                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
698                                     &t1,-1);
699                 gtk_tree_model_get (tree_model, iter2,
700                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
701                                     &t2,-1);
702                 return t1 - t2;
703
704         case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
705         case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
706                 gtk_tree_model_get (tree_model, iter1,
707                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
708                                     &t1,-1);
709                 gtk_tree_model_get (tree_model, iter2,
710                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
711                                     &t2,-1);
712                 return t1 - t2;
713
714                 
715                 /* next ones, we try the search criteria first, if they're the same, then we use 'sent date' */
716                 /* FIXME: what about received-date? */
717         case MODEST_HEADER_VIEW_COLUMN_SUBJECT: {
718
719                 gtk_tree_model_get (tree_model, iter1,
720                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &s1,
721                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
722                                     -1);
723                 gtk_tree_model_get (tree_model, iter2,
724                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &s2,
725                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
726                                     -1);
727
728                 /* the prefix ('Re:', 'Fwd:' etc.) we ignore */ 
729                 cmp = modest_text_utils_utf8_strcmp (s1 + modest_text_utils_get_subject_prefix_len(s1),
730                                                      s2 + modest_text_utils_get_subject_prefix_len(s2),
731                                                      TRUE);
732                 g_free (s1);
733                 g_free (s2);
734                 
735                 return cmp ? cmp : t1 - t2;
736         }
737                 
738         case MODEST_HEADER_VIEW_COLUMN_FROM:
739                 
740                 gtk_tree_model_get (tree_model, iter1,
741                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN, &s1,
742                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
743                                     -1);
744                 gtk_tree_model_get (tree_model, iter2,
745                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN, &s2,
746                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
747                                     -1);
748                 cmp = modest_text_utils_utf8_strcmp (s1, s2, TRUE);
749                 g_free (s1);
750                 g_free (s2);
751                 
752                 return cmp ? cmp : t1 - t2;
753                 
754         case MODEST_HEADER_VIEW_COLUMN_TO: 
755                 
756                 gtk_tree_model_get (tree_model, iter1,
757                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN, &s1,
758                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,
759                                     -1);
760                 gtk_tree_model_get (tree_model, iter2,
761                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN, &s2,
762                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,
763                                     -1);
764                 cmp = modest_text_utils_utf8_strcmp (s1, s2, TRUE);
765                 g_free (s1);
766                 g_free (s2);
767                 
768                 return cmp ? cmp : t1 - t2;
769
770         case MODEST_HEADER_VIEW_COLUMN_ATTACH:
771
772                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
773                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
774                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
775                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
776                 
777                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
778                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
779
780                 return cmp ? cmp : t1 - t2;
781                 
782         case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
783                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
784                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
785                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
786                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
787                 cmp =  (val1 & TNY_HEADER_FLAG_SEEN) - (val2 & TNY_HEADER_FLAG_SEEN);
788
789                 return cmp ? cmp : t1 - t2;
790
791         default:
792                 return &iter1 - &iter2; /* oughhhh  */
793         }
794 }