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