* Allow opening multiple viewers in nested messages
[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 <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <tny-error.h>
36 #include <string.h>
37
38 #include <modest-header-view.h>
39 #include <modest-header-view-priv.h>
40 #include <modest-dnd.h>
41 #include <modest-tny-folder.h>
42 #include <modest-debug.h>
43 #include <modest-main-window.h>
44 #include <modest-ui-actions.h>
45 #include <modest-marshal.h>
46 #include <modest-text-utils.h>
47 #include <modest-icon-names.h>
48 #include <modest-runtime.h>
49 #include "modest-platform.h"
50 #include <modest-hbox-cell-renderer.h>
51 #include <modest-vbox-cell-renderer.h>
52 #include <modest-datetime-formatter.h>
53
54 static void modest_header_view_class_init  (ModestHeaderViewClass *klass);
55 static void modest_header_view_init        (ModestHeaderView *obj);
56 static void modest_header_view_finalize    (GObject *obj);
57 static void modest_header_view_dispose     (GObject *obj);
58
59 static void          on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
60                                               GtkTreeViewColumn *column, gpointer userdata);
61
62 static gint          cmp_rows               (GtkTreeModel *tree_model,
63                                              GtkTreeIter *iter1,
64                                              GtkTreeIter *iter2,
65                                              gpointer user_data);
66
67 static gint          cmp_subject_rows       (GtkTreeModel *tree_model,
68                                              GtkTreeIter *iter1,
69                                              GtkTreeIter *iter2,
70                                              gpointer user_data);
71
72 static gboolean     filter_row             (GtkTreeModel *model,
73                                             GtkTreeIter *iter,
74                                             gpointer data);
75
76 static void         on_account_removed     (TnyAccountStore *self, 
77                                             TnyAccount *account,
78                                             gpointer user_data);
79
80 static void          on_selection_changed   (GtkTreeSelection *sel,
81                                              gpointer user_data);
82
83 static gboolean      on_button_press_event  (GtkWidget * self, GdkEventButton * event,
84                                              gpointer userdata);
85
86 static gboolean      on_button_release_event(GtkWidget * self, GdkEventButton * event,
87                                              gpointer userdata);
88
89 static void          setup_drag_and_drop    (GtkWidget *self);
90
91 static void          enable_drag_and_drop   (GtkWidget *self);
92
93 static void          disable_drag_and_drop  (GtkWidget *self);
94
95 static GtkTreePath * get_selected_row       (GtkTreeView *self, GtkTreeModel **model);
96
97 static gboolean      on_focus_in            (GtkWidget     *sef,
98                                              GdkEventFocus *event,
99                                              gpointer       user_data);
100
101 static gboolean      on_focus_out            (GtkWidget     *self,
102                                               GdkEventFocus *event,
103                                               gpointer       user_data);
104
105 static void          folder_monitor_update  (TnyFolderObserver *self, 
106                                              TnyFolderChange *change);
107
108 static void          tny_folder_observer_init (TnyFolderObserverIface *klass);
109
110 static void          _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
111
112 static void          _clear_hidding_filter (ModestHeaderView *header_view);
113
114 static void          modest_header_view_notify_observers(ModestHeaderView *header_view,
115                                                          GtkTreeModel *model,
116                                                          const gchar *tny_folder_id);
117
118 static gboolean      modest_header_view_on_expose_event (GtkTreeView *header_view,
119                                                          GdkEventExpose *event,
120                                                          gpointer user_data);
121
122 typedef enum {
123         HEADER_VIEW_NON_EMPTY,
124         HEADER_VIEW_EMPTY,
125         HEADER_VIEW_INIT
126 } HeaderViewStatus;
127
128 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
129 struct _ModestHeaderViewPrivate {
130         TnyFolder            *folder;
131         ModestHeaderViewStyle style;
132
133         TnyFolderMonitor     *monitor;
134         GMutex               *observers_lock;
135
136         /*header-view-observer observer*/
137         GMutex *observer_list_lock;
138         GSList *observer_list;
139
140         /* not unref this object, its a singlenton */
141         ModestEmailClipboard *clipboard;
142
143         /* Filter tree model */
144         gchar **hidding_ids;
145         guint   n_selected;
146         GtkTreeRowReference *autoselect_reference;
147
148         gint    sort_colid[2][TNY_FOLDER_TYPE_NUM];
149         gint    sort_type[2][TNY_FOLDER_TYPE_NUM];
150
151         gulong  selection_changed_handler;
152         gulong  acc_removed_handler;
153
154         GList *drag_begin_cached_selected_rows;
155
156         HeaderViewStatus status;
157         guint status_timeout;
158         gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
159
160         ModestDatetimeFormatter *datetime_formatter;
161 };
162
163 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
164 struct _HeadersCountChangedHelper {
165         ModestHeaderView *self;
166         TnyFolderChange  *change;       
167 };
168
169
170 #define MODEST_HEADER_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
171                                                 MODEST_TYPE_HEADER_VIEW, \
172                                                 ModestHeaderViewPrivate))
173
174
175
176 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
177
178 enum {
179         HEADER_SELECTED_SIGNAL,
180         HEADER_ACTIVATED_SIGNAL,
181         ITEM_NOT_FOUND_SIGNAL,
182         MSG_COUNT_CHANGED_SIGNAL,
183         UPDATING_MSG_LIST_SIGNAL,
184         LAST_SIGNAL
185 };
186
187 /* globals */
188 static GObjectClass *parent_class = NULL;
189
190 /* uncomment the following if you have defined any signals */
191 static guint signals[LAST_SIGNAL] = {0};
192
193 GType
194 modest_header_view_get_type (void)
195 {
196         static GType my_type = 0;
197         if (!my_type) {
198                 static const GTypeInfo my_info = {
199                         sizeof(ModestHeaderViewClass),
200                         NULL,           /* base init */
201                         NULL,           /* base finalize */
202                         (GClassInitFunc) modest_header_view_class_init,
203                         NULL,           /* class finalize */
204                         NULL,           /* class data */
205                         sizeof(ModestHeaderView),
206                         1,              /* n_preallocs */
207                         (GInstanceInitFunc) modest_header_view_init,
208                         NULL
209                 };
210
211                 static const GInterfaceInfo tny_folder_observer_info = 
212                 {
213                         (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
214                         NULL,         /* interface_finalize */
215                         NULL          /* interface_data */
216                 };
217                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
218                                                   "ModestHeaderView",
219                                                   &my_info, 0);
220
221                 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
222                                              &tny_folder_observer_info);
223
224
225         }
226         return my_type;
227 }
228
229 static void
230 modest_header_view_class_init (ModestHeaderViewClass *klass)
231 {
232         GObjectClass *gobject_class;
233         gobject_class = (GObjectClass*) klass;
234
235         parent_class            = g_type_class_peek_parent (klass);
236         gobject_class->finalize = modest_header_view_finalize;
237         gobject_class->dispose = modest_header_view_dispose;
238         
239         g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
240         
241         signals[HEADER_SELECTED_SIGNAL] = 
242                 g_signal_new ("header_selected",
243                               G_TYPE_FROM_CLASS (gobject_class),
244                               G_SIGNAL_RUN_FIRST,
245                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
246                               NULL, NULL,
247                               g_cclosure_marshal_VOID__POINTER,
248                               G_TYPE_NONE, 1, G_TYPE_POINTER);
249
250         signals[HEADER_ACTIVATED_SIGNAL] = 
251                 g_signal_new ("header_activated",
252                               G_TYPE_FROM_CLASS (gobject_class),
253                               G_SIGNAL_RUN_FIRST,
254                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
255                               NULL, NULL,
256                               gtk_marshal_VOID__POINTER_POINTER,
257                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
258         
259         
260         signals[ITEM_NOT_FOUND_SIGNAL] = 
261                 g_signal_new ("item_not_found",
262                               G_TYPE_FROM_CLASS (gobject_class),
263                               G_SIGNAL_RUN_FIRST,
264                               G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
265                               NULL, NULL,
266                               g_cclosure_marshal_VOID__INT,
267                               G_TYPE_NONE, 1, G_TYPE_INT);
268
269         signals[MSG_COUNT_CHANGED_SIGNAL] =
270                 g_signal_new ("msg_count_changed",
271                               G_TYPE_FROM_CLASS (gobject_class),
272                               G_SIGNAL_RUN_FIRST,
273                               G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
274                               NULL, NULL,
275                               modest_marshal_VOID__POINTER_POINTER,
276                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
277
278         signals[UPDATING_MSG_LIST_SIGNAL] =
279                 g_signal_new ("updating-msg-list",
280                               G_TYPE_FROM_CLASS (gobject_class),
281                               G_SIGNAL_RUN_FIRST,
282                               G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
283                               NULL, NULL,
284                               g_cclosure_marshal_VOID__BOOLEAN,
285                               G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
286
287 #ifdef MODEST_TOOLKIT_HILDON2
288         gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
289         
290 #endif
291 }
292
293 static void
294 tny_folder_observer_init (TnyFolderObserverIface *klass)
295 {
296         klass->update = folder_monitor_update;
297 }
298
299 static GtkTreeViewColumn*
300 get_new_column (const gchar *name, GtkCellRenderer *renderer,
301                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
302                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
303 {
304         GtkTreeViewColumn *column;
305
306         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
307         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
308
309         gtk_tree_view_column_set_resizable (column, resizable);
310         if (resizable) 
311                 gtk_tree_view_column_set_expand (column, TRUE);
312         
313         if (show_as_text)
314                 gtk_tree_view_column_add_attribute (column, renderer, "text",
315                                                     sort_col_id);
316         if (sort_col_id >= 0)
317                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
318
319         gtk_tree_view_column_set_sort_indicator (column, FALSE);
320         gtk_tree_view_column_set_reorderable (column, TRUE);
321         
322         if (cell_data_func)
323                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
324                                                         user_data, NULL);
325         return column;
326 }
327
328
329 static void
330 remove_all_columns (ModestHeaderView *obj)
331 {
332         GList *columns, *cursor;
333         
334         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
335
336         for (cursor = columns; cursor; cursor = cursor->next)
337                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
338                                              GTK_TREE_VIEW_COLUMN(cursor->data));
339         g_list_free (columns);  
340 }
341
342 gboolean
343 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
344 {
345         GtkTreeModel *tree_filter, *sortable;
346         GtkTreeViewColumn *column=NULL;
347         GtkTreeSelection *selection = NULL;
348         GtkCellRenderer *renderer_header,
349                 *renderer_attach, *renderer_compact_date_or_status;
350         GtkCellRenderer *renderer_compact_header, *renderer_recpt_box, 
351                 *renderer_subject, *renderer_subject_box, *renderer_recpt,
352                 *renderer_priority;
353         ModestHeaderViewPrivate *priv;
354         GtkTreeViewColumn *compact_column = NULL;
355         const GList *cursor;
356
357         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
358         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
359         
360         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self); 
361
362         /* TODO: check whether these renderers need to be freed */
363         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
364         renderer_priority  = gtk_cell_renderer_pixbuf_new ();
365         renderer_header  = gtk_cell_renderer_text_new ();
366
367         renderer_compact_header = modest_vbox_cell_renderer_new ();
368         renderer_recpt_box = modest_hbox_cell_renderer_new ();
369         renderer_subject_box = modest_hbox_cell_renderer_new ();
370         renderer_recpt = gtk_cell_renderer_text_new ();
371         renderer_subject = gtk_cell_renderer_text_new ();
372         renderer_compact_date_or_status  = gtk_cell_renderer_text_new ();
373
374         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
375         g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
376         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
377         g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
378         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
379         g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
380         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
381         g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
382         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
383         g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
384         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
385         g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
386         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
387         g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
388
389         g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
390 #ifndef MODEST_TOOLKIT_GTK
391         gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
392         gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
393 #endif
394         g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
395         g_object_set(G_OBJECT(renderer_header),
396                      "ellipsize", PANGO_ELLIPSIZE_END,
397                      NULL);
398         g_object_set (G_OBJECT (renderer_subject),
399                       "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
400                       NULL);
401         gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
402         g_object_set (G_OBJECT (renderer_recpt),
403                       "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
404                       NULL);
405         gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
406         g_object_set(G_OBJECT(renderer_compact_date_or_status),
407                      "xalign", 1.0, "yalign", 0.0,
408                      NULL);
409         gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
410         g_object_set (G_OBJECT (renderer_priority),
411                       "yalign", 1.0, NULL);
412         g_object_set (G_OBJECT (renderer_attach),
413                       "yalign", 0.0, NULL);
414
415 #ifndef MODEST_TOOLKIT_GTK
416         gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
417         gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
418         gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
419 #else
420         gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
421         gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
422         /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
423 #endif
424         
425         remove_all_columns (self);
426
427         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
428         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
429         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
430         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
431
432         /* Add new columns */
433         for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
434                 ModestHeaderViewColumn col =
435                         (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
436                 
437                 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
438                         g_printerr ("modest: invalid column %d in column list\n", col);
439                         continue;
440                 }
441                 
442                 switch (col) {
443                         
444                 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
445                         column = get_new_column (_("A"), renderer_attach, FALSE,
446                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
447                                                  FALSE,
448                                                  (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
449                                                  NULL);
450                         gtk_tree_view_column_set_fixed_width (column, 45);
451                         break;
452
453                         
454                 case MODEST_HEADER_VIEW_COLUMN_FROM:
455                         column = get_new_column (_("From"), renderer_header, TRUE,
456                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
457                                                  TRUE,
458                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
459                                                  GINT_TO_POINTER(TRUE));
460                         break;
461
462                 case MODEST_HEADER_VIEW_COLUMN_TO:
463                         column = get_new_column (_("To"), renderer_header, TRUE,
464                                                  TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
465                                                  TRUE,
466                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
467                                                  GINT_TO_POINTER(FALSE));
468                         break;
469                         
470                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
471                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
472                                                      TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
473                                                      FALSE,
474                                                      (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
475                                                      GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
476                         compact_column = column;
477                         break;
478
479                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
480                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
481                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
482                                                  FALSE,
483                                                  (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
484                                                  GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
485                                                                  MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
486                                                                  MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
487                         compact_column = column;
488                         break;
489
490                         
491                 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
492                         column = get_new_column (_("Subject"), renderer_header, TRUE,
493                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
494                                                  TRUE,
495                                                  (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
496                                                  NULL);
497                         break;
498                         
499                 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
500                         column = get_new_column (_("Received"), renderer_header, TRUE,
501                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
502                                                  TRUE,
503                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
504                                                  GINT_TO_POINTER(TRUE));
505                         break;
506                         
507                 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:  
508                         column = get_new_column (_("Sent"), renderer_header, TRUE,
509                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
510                                                  TRUE,
511                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
512                                                  GINT_TO_POINTER(FALSE));
513                         break;
514                         
515                 case MODEST_HEADER_VIEW_COLUMN_SIZE:
516                         column = get_new_column (_("Size"), renderer_header, TRUE,
517                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
518                                                  FALSE,
519                                                  (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
520                                                  NULL); 
521                         break;
522                 case MODEST_HEADER_VIEW_COLUMN_STATUS:
523                         column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
524                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
525                                                  FALSE,
526                                                  (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
527                                                  NULL); 
528                         break;
529
530                 default:
531                         g_return_val_if_reached(FALSE);
532                 }
533
534                 /* we keep the column id around */
535                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
536                                    GINT_TO_POINTER(col));
537                 
538                 /* we need this ptr when sorting the rows */
539                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
540                                    self);
541                 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);              
542         }               
543
544         if (sortable) {
545                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
546                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
547                                                  (GtkTreeIterCompareFunc) cmp_rows,
548                                                  compact_column, NULL);
549                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
550                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
551                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
552                                                  compact_column, NULL);
553         }
554
555         return TRUE;
556 }
557
558 static void
559 datetime_format_changed (ModestDatetimeFormatter *formatter,
560                          ModestHeaderView *self)
561 {
562         gtk_widget_queue_draw (GTK_WIDGET (self));
563 }
564
565 static void
566 modest_header_view_init (ModestHeaderView *obj)
567 {
568         ModestHeaderViewPrivate *priv;
569         guint i, j;
570
571         priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj); 
572
573         priv->folder  = NULL;
574
575         priv->monitor        = NULL;
576         priv->observers_lock = g_mutex_new ();
577         priv->autoselect_reference = NULL;
578
579         priv->status  = HEADER_VIEW_INIT;
580         priv->status_timeout = 0;
581         priv->notify_status = TRUE;
582
583         priv->observer_list_lock = g_mutex_new();
584         priv->observer_list = NULL;
585
586         priv->clipboard = modest_runtime_get_email_clipboard ();
587         priv->hidding_ids = NULL;
588         priv->n_selected = 0;
589         priv->selection_changed_handler = 0;
590         priv->acc_removed_handler = 0;
591
592         /* Sort parameters */
593         for (j=0; j < 2; j++) {
594                 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
595                         priv->sort_colid[j][i] = -1;
596                         priv->sort_type[j][i] = GTK_SORT_DESCENDING;
597                 }                       
598         }
599
600         priv->datetime_formatter = modest_datetime_formatter_new ();
601         g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed", 
602                           G_CALLBACK (datetime_format_changed), (gpointer) obj);
603
604         setup_drag_and_drop (GTK_WIDGET(obj));
605 }
606
607 static void
608 modest_header_view_dispose (GObject *obj)
609 {
610         ModestHeaderView        *self;
611         ModestHeaderViewPrivate *priv;
612         GtkTreeSelection *sel;
613         
614         self = MODEST_HEADER_VIEW(obj);
615         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
616
617         if (priv->datetime_formatter) {
618                 g_object_unref (priv->datetime_formatter);
619                 priv->datetime_formatter = NULL;
620         }
621
622         /* Free in the dispose to avoid unref cycles */
623         if (priv->folder) {
624                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
625                 g_object_unref (G_OBJECT (priv->folder));
626                 priv->folder = NULL;
627         }
628
629         /* We need to do this here in the dispose because the
630            selection won't exist when finalizing */
631         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
632         if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
633                 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
634                 priv->selection_changed_handler = 0;
635         }
636
637         G_OBJECT_CLASS(parent_class)->dispose (obj);
638 }
639
640 static void
641 modest_header_view_finalize (GObject *obj)
642 {
643         ModestHeaderView        *self;
644         ModestHeaderViewPrivate *priv;
645         
646         self = MODEST_HEADER_VIEW(obj);
647         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
648
649         if (g_signal_handler_is_connected (modest_runtime_get_account_store (), 
650                                            priv->acc_removed_handler)) {
651                 g_signal_handler_disconnect (modest_runtime_get_account_store (), 
652                                              priv->acc_removed_handler);
653         }
654
655         /* There is no need to lock because there should not be any
656          * reference to self now. */
657         g_mutex_free(priv->observer_list_lock);
658         g_slist_free(priv->observer_list);
659
660         g_mutex_lock (priv->observers_lock);
661         if (priv->monitor) {
662                 tny_folder_monitor_stop (priv->monitor);
663                 g_object_unref (G_OBJECT (priv->monitor));
664         }
665         g_mutex_unlock (priv->observers_lock);
666         g_mutex_free (priv->observers_lock);
667
668         /* Clear hidding array created by cut operation */
669         _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
670
671         if (priv->autoselect_reference != NULL) {
672                 gtk_tree_row_reference_free (priv->autoselect_reference);
673                 priv->autoselect_reference = NULL;
674         }
675
676         G_OBJECT_CLASS(parent_class)->finalize (obj);
677 }
678
679
680 GtkWidget*
681 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
682 {
683         GObject *obj;
684         GtkTreeSelection *sel;
685         ModestHeaderView *self;
686         ModestHeaderViewPrivate *priv;
687         
688         g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
689                               NULL);
690         
691         obj  = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
692         self = MODEST_HEADER_VIEW(obj);
693         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
694         
695         modest_header_view_set_style   (self, style);
696
697         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
698         gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
699         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
700         
701         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
702                                       TRUE); /* alternating row colors */
703
704         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self)); 
705         priv->selection_changed_handler =
706                 g_signal_connect_after (sel, "changed",
707                                         G_CALLBACK(on_selection_changed), self);
708         
709         g_signal_connect (self, "row-activated",
710                           G_CALLBACK (on_header_row_activated), NULL);
711
712         g_signal_connect (self, "focus-in-event",
713                           G_CALLBACK(on_focus_in), NULL);
714         g_signal_connect (self, "focus-out-event",
715                           G_CALLBACK(on_focus_out), NULL);
716
717         g_signal_connect (self, "button-press-event",
718                           G_CALLBACK(on_button_press_event), NULL);
719         g_signal_connect (self, "button-release-event",
720                           G_CALLBACK(on_button_release_event), NULL);
721         
722         priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
723                                                       "account_removed",
724                                                       G_CALLBACK (on_account_removed),
725                                                       self);
726
727         g_signal_connect (self, "expose-event",
728                         G_CALLBACK(modest_header_view_on_expose_event),
729                         NULL);
730
731         return GTK_WIDGET(self);
732 }
733
734
735 guint
736 modest_header_view_count_selected_headers (ModestHeaderView *self)
737 {
738         GtkTreeSelection *sel;
739         guint selected_rows;
740
741         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
742         
743         /* Get selection object and check selected rows count */
744         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
745         selected_rows = gtk_tree_selection_count_selected_rows (sel);
746         
747         return selected_rows;
748 }
749
750 gboolean
751 modest_header_view_has_selected_headers (ModestHeaderView *self)
752 {
753         GtkTreeSelection *sel;
754         gboolean empty;
755         
756         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
757         
758         /* Get selection object and check selected rows count */
759         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
760         empty = gtk_tree_selection_count_selected_rows (sel) == 0;
761         
762         return !empty;
763 }
764
765
766 TnyList * 
767 modest_header_view_get_selected_headers (ModestHeaderView *self)
768 {
769         GtkTreeSelection *sel;
770         ModestHeaderViewPrivate *priv;
771         TnyList *header_list = NULL;
772         TnyHeader *header;
773         GList *list, *tmp = NULL;
774         GtkTreeModel *tree_model = NULL;
775         GtkTreeIter iter;
776
777         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
778         
779         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
780
781         /* Get selected rows */
782         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
783         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
784
785         if (list) {
786                 header_list = tny_simple_list_new();
787
788                 list = g_list_reverse (list);
789                 tmp = list;
790                 while (tmp) {                   
791                         /* get header from selection */
792                         gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
793                         gtk_tree_model_get (tree_model, &iter,
794                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
795                                             &header, -1);
796                         /* Prepend to list */
797                         tny_list_prepend (header_list, G_OBJECT (header));
798                         g_object_unref (G_OBJECT (header));
799
800                         tmp = g_list_next (tmp);
801                 }
802                 /* Clean up*/
803                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
804                 g_list_free (list);
805         }
806         return header_list;
807 }
808
809
810 /* scroll our list view so the selected item is visible */
811 static void
812 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
813 {
814 #ifdef MODEST_TOOLKIT_GTK 
815
816         GtkTreePath *selected_path;
817         GtkTreePath *start, *end;
818         
819         GtkTreeModel *model;
820         
821         model         = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
822         selected_path = gtk_tree_model_get_path (model, iter);
823
824         start = gtk_tree_path_new ();
825         end   = gtk_tree_path_new ();
826
827         gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
828
829         if (gtk_tree_path_compare (selected_path, start) < 0 ||
830             gtk_tree_path_compare (end, selected_path) < 0)
831                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
832                                               selected_path, NULL, TRUE,
833                                               up ? 0.0 : 1.0,
834                                               up ? 0.0 : 1.0);
835         gtk_tree_path_free (selected_path);
836         gtk_tree_path_free (start);
837         gtk_tree_path_free (end);
838
839 #endif /* MODEST_TOOLKIT_GTK */
840 }
841
842
843 void 
844 modest_header_view_select_next (ModestHeaderView *self)
845 {
846         GtkTreeSelection *sel;
847         GtkTreeIter iter;
848         GtkTreeModel *model;
849         GtkTreePath *path;
850
851         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
852
853         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
854         path = get_selected_row (GTK_TREE_VIEW(self), &model);
855         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
856                 /* Unselect previous path */
857                 gtk_tree_selection_unselect_path (sel, path);
858                 
859                 /* Move path down and selects new one  */
860                 if (gtk_tree_model_iter_next (model, &iter)) {
861                         gtk_tree_selection_select_iter (sel, &iter);
862                         scroll_to_selected (self, &iter, FALSE);        
863                 }
864                 gtk_tree_path_free(path);
865         }
866         
867 }
868
869 void 
870 modest_header_view_select_prev (ModestHeaderView *self)
871 {
872         GtkTreeSelection *sel;
873         GtkTreeIter iter;
874         GtkTreeModel *model;
875         GtkTreePath *path;
876
877         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
878
879         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
880         path = get_selected_row (GTK_TREE_VIEW(self), &model);
881         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
882                 /* Unselect previous path */
883                 gtk_tree_selection_unselect_path (sel, path);
884
885                 /* Move path up */
886                 if (gtk_tree_path_prev (path)) {
887                         gtk_tree_model_get_iter (model, &iter, path);
888                         
889                         /* Select the new one */
890                         gtk_tree_selection_select_iter (sel, &iter);
891                         scroll_to_selected (self, &iter, TRUE); 
892
893                 }
894                 gtk_tree_path_free (path);
895         }
896 }
897
898 GList*
899 modest_header_view_get_columns (ModestHeaderView *self)
900 {       
901         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
902         
903         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self)); 
904 }
905
906
907
908 gboolean
909 modest_header_view_set_style (ModestHeaderView *self,
910                               ModestHeaderViewStyle style)
911 {
912         ModestHeaderViewPrivate *priv;
913         gboolean show_col_headers = FALSE;
914         ModestHeaderViewStyle old_style;
915         
916         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
917         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
918                               FALSE);
919         
920         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
921         if (priv->style == style)
922                 return TRUE; /* nothing to do */
923         
924         switch (style) {
925         case MODEST_HEADER_VIEW_STYLE_DETAILS:
926                 show_col_headers = TRUE;
927                 break;
928         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
929                 break;
930         default:
931                 g_return_val_if_reached (FALSE);
932         }
933         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
934         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);    
935
936         old_style   = priv->style;
937         priv->style = style;
938
939         return TRUE;
940 }
941
942
943 ModestHeaderViewStyle
944 modest_header_view_get_style (ModestHeaderView *self)
945 {
946         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
947
948         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
949 }
950
951 /* This is used to automatically select the first header if the user
952  * has not selected any header yet.
953  */
954 static gboolean 
955 modest_header_view_on_expose_event(GtkTreeView *header_view,
956                                    GdkEventExpose *event,
957                                    gpointer user_data)
958 {
959         GtkTreeSelection *sel;
960         GtkTreeModel *model;
961         GtkTreeIter tree_iter;
962         ModestHeaderViewPrivate *priv;
963
964         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
965         model = gtk_tree_view_get_model(header_view);
966
967         if (!model)
968                 return FALSE;
969
970 #ifdef MODEST_TOOLKIT_HILDON2
971         HildonUIMode ui_mode;
972         g_object_get (G_OBJECT (header_view), "hildon-ui-mode", &ui_mode, NULL);
973         if (ui_mode == HILDON_UI_MODE_NORMAL)
974                 /* As in hildon 2.2 normal mode there's no selection, we just simply return */
975                 return FALSE;
976 #endif
977         sel = gtk_tree_view_get_selection(header_view);
978         if(!gtk_tree_selection_count_selected_rows(sel)) {
979                 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
980                         GtkTreePath *tree_iter_path;
981                         /* Prevent the widget from getting the focus
982                            when selecting the first item */
983                         tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
984                         g_object_set(header_view, "can-focus", FALSE, NULL);
985                         gtk_tree_selection_select_iter(sel, &tree_iter);
986                         gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
987                         g_object_set(header_view, "can-focus", TRUE, NULL);
988                         if (priv->autoselect_reference) {
989                                 gtk_tree_row_reference_free (priv->autoselect_reference);
990                         }
991                         priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
992                         gtk_tree_path_free (tree_iter_path);
993                 }
994         } else {
995                 if (priv->autoselect_reference != NULL) {
996                         gboolean moved_selection = FALSE;
997                         GtkTreePath * last_path;
998                         if (gtk_tree_selection_count_selected_rows (sel) != 1) {
999                                 moved_selection = TRUE;
1000                         } else {
1001                                 GList *rows;
1002
1003                                 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1004                                 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1005                                 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1006                                         moved_selection = TRUE;
1007                                 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1008                                 g_list_free (rows);
1009                                 gtk_tree_path_free (last_path);
1010                         }
1011                         if (moved_selection) {
1012                                 gtk_tree_row_reference_free (priv->autoselect_reference);
1013                                 priv->autoselect_reference = NULL;
1014                         } else {
1015
1016                                 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1017                                         GtkTreePath *current_path;
1018                                         current_path = gtk_tree_model_get_path (model, &tree_iter);
1019                                         last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1020                                         if (gtk_tree_path_compare (current_path, last_path) != 0) {
1021                                                 g_object_set(header_view, "can-focus", FALSE, NULL);
1022                                                 gtk_tree_selection_unselect_all (sel);
1023                                                 gtk_tree_selection_select_iter(sel, &tree_iter);
1024                                                 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1025                                                 g_object_set(header_view, "can-focus", TRUE, NULL);
1026                                                 gtk_tree_row_reference_free (priv->autoselect_reference);
1027                                                 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1028                                         }
1029                                         gtk_tree_path_free (current_path);
1030                                         gtk_tree_path_free (last_path);
1031                                 }
1032                         }
1033                 }
1034         }
1035
1036         return FALSE;
1037 }
1038
1039 TnyFolder*
1040 modest_header_view_get_folder (ModestHeaderView *self)
1041 {
1042         ModestHeaderViewPrivate *priv;
1043
1044         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1045
1046         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1047
1048         if (priv->folder)
1049                 g_object_ref (priv->folder);
1050
1051         return priv->folder;
1052 }
1053
1054 static void
1055 set_folder_intern_get_headers_async_cb (TnyFolder *folder, 
1056                                         gboolean cancelled, 
1057                                         TnyList *headers, 
1058                                         GError *err, 
1059                                         gpointer user_data)
1060 {
1061         ModestHeaderView *self;
1062         ModestHeaderViewPrivate *priv;
1063
1064         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1065
1066         self = MODEST_HEADER_VIEW (user_data);
1067         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1068
1069         if (cancelled || err)
1070                 return;
1071
1072         /* Add IDLE observer (monitor) and another folder observer for
1073            new messages (self) */
1074         g_mutex_lock (priv->observers_lock);
1075         if (priv->monitor) {
1076                 tny_folder_monitor_stop (priv->monitor);
1077                 g_object_unref (G_OBJECT (priv->monitor));
1078         }
1079         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1080         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1081         tny_folder_monitor_start (priv->monitor);
1082         g_mutex_unlock (priv->observers_lock);
1083 }
1084
1085 static void
1086 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1087 {
1088         TnyFolderType type;
1089         TnyList *headers;
1090         ModestHeaderViewPrivate *priv;
1091         GList *cols, *cursor;
1092         GtkTreeModel *filter_model, *sortable; 
1093         guint sort_colid;
1094         GtkSortType sort_type;
1095
1096         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1097
1098         headers = TNY_LIST (tny_gtk_header_list_model_new ());
1099
1100         /* Start the monitor in the callback of the
1101            tny_gtk_header_list_model_set_folder call. It's crucial to
1102            do it there and not just after the call because we want the
1103            monitor to observe only the headers returned by the
1104            tny_folder_get_headers_async call that it's inside the
1105            tny_gtk_header_list_model_set_folder call. This way the
1106            monitor infrastructure could successfully cope with
1107            duplicates. For example if a tny_folder_add_msg_async is
1108            happening while tny_gtk_header_list_model_set_folder is
1109            invoked, then the first call could add a header that will
1110            be added again by tny_gtk_header_list_model_set_folder, so
1111            we'd end up with duplicate headers. sergio */
1112         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1113                                               folder, FALSE, 
1114                                               set_folder_intern_get_headers_async_cb, 
1115                                               NULL, self);
1116
1117         sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1118         g_object_unref (G_OBJECT (headers));
1119
1120         /* Init filter_row function to examine empty status */
1121         priv->status  = HEADER_VIEW_INIT;
1122
1123         /* Create a tree model filter to hide and show rows for cut operations  */
1124         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1125         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1126                                                 filter_row,
1127                                                 self,
1128                                                 NULL);
1129         g_object_unref (G_OBJECT (sortable));
1130
1131         /* install our special sorting functions */
1132         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1133
1134         /* Restore sort column id */
1135         if (cols) {
1136                 type  = modest_tny_folder_guess_folder_type (folder);
1137                 if (type == TNY_FOLDER_TYPE_INVALID)
1138                         g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1139                 
1140                 sort_colid = modest_header_view_get_sort_column_id (self, type); 
1141                 sort_type = modest_header_view_get_sort_type (self, type); 
1142                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1143                                                       sort_colid,
1144                                                       sort_type);
1145                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1146                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1147                                                  (GtkTreeIterCompareFunc) cmp_rows,
1148                                                  cols->data, NULL);
1149                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1150                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1151                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
1152                                                  cols->data, NULL);
1153         }
1154
1155         /* Set new model */
1156         gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1157         modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1158                         tny_folder_get_id(folder));
1159         g_object_unref (G_OBJECT (filter_model));
1160
1161         /* Free */
1162         g_list_free (cols);
1163 }
1164
1165 void
1166 modest_header_view_sort_by_column_id (ModestHeaderView *self, 
1167                                       guint sort_colid,
1168                                       GtkSortType sort_type)
1169 {
1170         ModestHeaderViewPrivate *priv = NULL;
1171         GtkTreeModel *tree_filter, *sortable = NULL; 
1172         TnyFolderType type;
1173
1174         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1175         g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1176         
1177         /* Get model and private data */
1178         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);            
1179         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1180         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1181 /*      sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1182         
1183         /* Sort tree model */
1184         type  = modest_tny_folder_guess_folder_type (priv->folder);
1185         if (type == TNY_FOLDER_TYPE_INVALID)
1186                 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1187         else {
1188                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1189                                               sort_colid,
1190                                               sort_type);
1191                 /* Store new sort parameters */
1192                 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1193         }       
1194 }
1195
1196 void
1197 modest_header_view_set_sort_params (ModestHeaderView *self, 
1198                                     guint sort_colid, 
1199                                     GtkSortType sort_type,
1200                                     TnyFolderType type)
1201 {
1202         ModestHeaderViewPrivate *priv;
1203         ModestHeaderViewStyle style;
1204         
1205         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1206         g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1207         g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1208         
1209         style = modest_header_view_get_style   (self);
1210         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1211
1212         priv->sort_colid[style][type] = sort_colid;
1213         priv->sort_type[style][type] = sort_type;
1214 }
1215
1216 gint
1217 modest_header_view_get_sort_column_id (ModestHeaderView *self, 
1218                                        TnyFolderType type)
1219 {
1220         ModestHeaderViewPrivate *priv;
1221         ModestHeaderViewStyle style;
1222
1223         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1224         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1225         
1226         style = modest_header_view_get_style   (self);
1227         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1228
1229         return priv->sort_colid[style][type];
1230 }
1231
1232 GtkSortType
1233 modest_header_view_get_sort_type (ModestHeaderView *self, 
1234                                   TnyFolderType type)
1235 {
1236         ModestHeaderViewPrivate *priv;
1237         ModestHeaderViewStyle style;
1238         
1239         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1240         g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1241         
1242         style = modest_header_view_get_style   (self);
1243         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1244
1245         return priv->sort_type[style][type];
1246 }
1247
1248 typedef struct {
1249         ModestHeaderView *header_view;
1250         RefreshAsyncUserCallback cb;
1251         gpointer user_data;
1252 } SetFolderHelper;
1253
1254 static void
1255 folder_refreshed_cb (ModestMailOperation *mail_op,
1256                      TnyFolder *folder,
1257                      gpointer user_data)
1258 {
1259         ModestHeaderViewPrivate *priv;
1260         SetFolderHelper *info;
1261  
1262         info = (SetFolderHelper*) user_data;
1263
1264         priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1265
1266         /* User callback */
1267         if (info->cb)
1268                 info->cb (mail_op, folder, info->user_data);
1269
1270         /* Start the folder count changes observer. We do not need it
1271            before the refresh. Note that the monitor could still be
1272            called for this refresh but now we know that the callback
1273            was previously called */
1274         g_mutex_lock (priv->observers_lock);
1275         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1276         g_mutex_unlock (priv->observers_lock);
1277
1278         /* Notify the observers that the update is over */
1279         g_signal_emit (G_OBJECT (info->header_view), 
1280                        signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1281
1282         /* Allow filtering notifications from now on if the current
1283            folder is still the same (if not then the user has selected
1284            another one to refresh, we should wait until that refresh
1285            finishes) */
1286         if (priv->folder == folder)
1287                 priv->notify_status = TRUE;
1288
1289         /* Frees */
1290         g_object_unref (info->header_view);
1291         g_free (info);
1292 }
1293
1294 static void
1295 refresh_folder_error_handler (ModestMailOperation *mail_op, 
1296                               gpointer user_data)
1297 {
1298         const GError *error = modest_mail_operation_get_error (mail_op);
1299
1300         if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1301             error->code == TNY_IO_ERROR_WRITE ||
1302             error->code == TNY_IO_ERROR_READ) {
1303                 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1304                 /* If the mail op has been cancelled then it's not an error: don't show any message */
1305                 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1306                         modest_platform_information_banner (NULL, NULL,
1307                                                             dgettext("ke-recv",
1308                                                                      "cerm_device_memory_full"));
1309                 }
1310         }
1311 }
1312
1313 void
1314 modest_header_view_set_folder (ModestHeaderView *self, 
1315                                TnyFolder *folder,
1316                                gboolean refresh,
1317                                RefreshAsyncUserCallback callback,
1318                                gpointer user_data)
1319 {
1320         ModestHeaderViewPrivate *priv;
1321         ModestWindow *main_win;
1322         
1323         g_return_if_fail (self);
1324
1325         priv =     MODEST_HEADER_VIEW_GET_PRIVATE(self);
1326
1327         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1328                                                       FALSE); /* don't create */
1329         if (!main_win) {
1330                 g_warning ("%s: BUG: no main window", __FUNCTION__);
1331                 return;
1332         }
1333                                                       
1334         if (priv->folder) {
1335                 if (priv->status_timeout) {
1336                         g_source_remove (priv->status_timeout);
1337                         priv->status_timeout = 0;
1338                 }
1339
1340                 g_mutex_lock (priv->observers_lock);
1341                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1342                 g_object_unref (priv->folder);
1343                 priv->folder = NULL;
1344                 g_mutex_unlock (priv->observers_lock);
1345         }
1346
1347         if (folder) {
1348                 GtkTreeSelection *selection;
1349                 SetFolderHelper *info;
1350                 ModestMailOperation *mail_op = NULL;
1351
1352                 /* Set folder in the model */
1353                 modest_header_view_set_folder_intern (self, folder);
1354                 
1355                 /* Pick my reference. Nothing to do with the mail operation */
1356                 priv->folder = g_object_ref (folder);
1357
1358                 /* Do not notify about filterings until the refresh finishes */
1359                 priv->notify_status = FALSE;
1360
1361                 /* Clear the selection if exists */
1362                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1363                 gtk_tree_selection_unselect_all(selection);
1364                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1365
1366                 /* Notify the observers that the update begins */
1367                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1368                                0, TRUE, NULL);
1369
1370                 /* create the helper */
1371                 info = g_malloc0 (sizeof (SetFolderHelper));
1372                 info->header_view = g_object_ref (self);
1373                 info->cb = callback;
1374                 info->user_data = user_data;
1375
1376                 /* Create the mail operation (source will be the parent widget) */
1377                 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1378                                                                          refresh_folder_error_handler,
1379                                                                          NULL, NULL);
1380                 if (refresh) {                  
1381                         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1382                                                          mail_op);
1383                         
1384                         /* Refresh the folder asynchronously */
1385                         modest_mail_operation_refresh_folder (mail_op,
1386                                                               folder,
1387                                                               folder_refreshed_cb,
1388                                                               info);
1389                 } else {
1390                         folder_refreshed_cb (mail_op, folder, info);
1391                 }
1392                 /* Free */
1393                 g_object_unref (mail_op);
1394         } else {
1395                 g_mutex_lock (priv->observers_lock);
1396
1397                 if (priv->monitor) {
1398                         tny_folder_monitor_stop (priv->monitor);
1399                         g_object_unref (G_OBJECT (priv->monitor));
1400                         priv->monitor = NULL;
1401                 }
1402
1403                 if (priv->autoselect_reference) {
1404                         gtk_tree_row_reference_free (priv->autoselect_reference);
1405                         priv->autoselect_reference = NULL;
1406                 }
1407
1408                 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL); 
1409
1410                 modest_header_view_notify_observers(self, NULL, NULL);
1411
1412                 g_mutex_unlock (priv->observers_lock);
1413
1414                 /* Notify the observers that the update is over */
1415                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1416                                0, FALSE, NULL);
1417         }
1418 }
1419
1420 static void
1421 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1422                          GtkTreeViewColumn *column, gpointer userdata)
1423 {
1424         ModestHeaderView *self = NULL;
1425         ModestHeaderViewPrivate *priv = NULL;
1426         GtkTreeIter iter;
1427         GtkTreeModel *model = NULL;
1428         TnyHeader *header = NULL;
1429         TnyHeaderFlags flags;
1430
1431         self = MODEST_HEADER_VIEW (treeview);
1432         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1433
1434         model = gtk_tree_view_get_model (treeview);     
1435         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
1436                 goto frees;
1437
1438         /* get the first selected item */
1439         gtk_tree_model_get (model, &iter,
1440                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1441                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, 
1442                             -1);
1443
1444         /* Dont open DELETED messages */
1445         if (flags & TNY_HEADER_FLAG_DELETED) {
1446                 GtkWidget *win;
1447                 gchar *msg;
1448                 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1449                 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1450                 modest_platform_information_banner (NULL, NULL, msg);
1451                 g_free (msg);
1452                 goto frees;
1453         }
1454
1455         /* Emit signal */
1456         g_signal_emit (G_OBJECT(self), 
1457                        signals[HEADER_ACTIVATED_SIGNAL], 
1458                        0, header, path);
1459
1460         /* Free */
1461  frees:
1462         if (header != NULL) 
1463                 g_object_unref (G_OBJECT (header));     
1464
1465 }
1466
1467 static void
1468 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1469 {
1470         GtkTreeModel *model;
1471         TnyHeader *header = NULL;
1472         GtkTreePath *path = NULL;       
1473         GtkTreeIter iter;
1474         ModestHeaderView *self;
1475         ModestHeaderViewPrivate *priv;
1476         GList *selected = NULL;
1477         
1478         g_return_if_fail (sel);
1479         g_return_if_fail (user_data);
1480         
1481         self = MODEST_HEADER_VIEW (user_data);
1482         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
1483
1484         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1485         if (selected != NULL) 
1486                 path = (GtkTreePath *) selected->data;
1487         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1488                 return; /* msg was _un_selected */
1489
1490         gtk_tree_model_get (model, &iter,
1491                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1492                             &header, -1);
1493
1494         /* Emit signal */
1495         g_signal_emit (G_OBJECT(self), 
1496                        signals[HEADER_SELECTED_SIGNAL], 
1497                        0, header);
1498
1499         g_object_unref (G_OBJECT (header));
1500
1501         /* free all items in 'selected' */
1502         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1503         g_list_free (selected);
1504 }
1505
1506
1507 /* PROTECTED method. It's useful when we want to force a given
1508    selection to reload a msg. For example if we have selected a header
1509    in offline mode, when Modest become online, we want to reload the
1510    message automatically without an user click over the header */
1511 void 
1512 _modest_header_view_change_selection (GtkTreeSelection *selection,
1513                                       gpointer user_data)
1514 {
1515         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1516         g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1517         
1518         on_selection_changed (selection, user_data);
1519 }
1520
1521 static gint
1522 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1523 {
1524         /* HH, LL, NN */
1525         if (p1 == p2)
1526                 return 0;
1527
1528         /* HL HN */
1529         if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1530                 return 1;
1531
1532         /* LH LN */
1533         if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1534                 return -1;
1535
1536         /* NH */
1537         if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1538                 return -1;
1539
1540         /* NL */
1541         return 1;
1542 }
1543
1544 static gint
1545 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1546           gpointer user_data)
1547 {
1548         gint col_id;
1549         gint t1, t2;
1550         gint val1, val2;
1551         gint cmp;
1552 /*      static int counter = 0; */
1553
1554         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1555 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1556         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1557
1558         
1559         switch (col_id) {
1560         case TNY_HEADER_FLAG_ATTACHMENTS:
1561
1562                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1563                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1564                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1565                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1566
1567                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1568                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1569
1570                 return cmp ? cmp : t1 - t2;
1571                 
1572         case TNY_HEADER_FLAG_PRIORITY_MASK: {
1573                 TnyHeader *header1 = NULL, *header2 = NULL;
1574
1575                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1576                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1577                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1578                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1579
1580                 /* This is for making priority values respect the intuitive sort relationship 
1581                  * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1582
1583                 if (header1 && header2) {
1584                         cmp =  compare_priorities (tny_header_get_priority (header1), 
1585                                 tny_header_get_priority (header2));
1586                         g_object_unref (header1);
1587                         g_object_unref (header2);
1588
1589                         return cmp ? cmp : t1 - t2;
1590                 }
1591
1592                 return t1 - t2;
1593         }
1594         default:
1595                 return &iter1 - &iter2; /* oughhhh  */
1596         }
1597 }
1598
1599 static gint
1600 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1601                   gpointer user_data)
1602 {
1603         gint t1, t2;
1604         gchar *val1, *val2;
1605         gint cmp;
1606 /*      static int counter = 0; */
1607
1608         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1609
1610         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1611                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1612         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1613                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1614
1615         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1616                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1617                                              TRUE);
1618         g_free (val1);
1619         g_free (val2);
1620         return cmp;
1621 }
1622
1623 /* Drag and drop stuff */
1624 static void
1625 drag_data_get_cb (GtkWidget *widget, 
1626                   GdkDragContext *context, 
1627                   GtkSelectionData *selection_data, 
1628                   guint info,  
1629                   guint time, 
1630                   gpointer data)
1631 {
1632         ModestHeaderView *self = NULL;
1633         ModestHeaderViewPrivate *priv = NULL;
1634
1635         self = MODEST_HEADER_VIEW (widget);
1636         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1637
1638         /* Set the data. Do not use the current selection because it
1639            could be different than the selection at the beginning of
1640            the d&d */
1641         modest_dnd_selection_data_set_paths (selection_data, 
1642                                              priv->drag_begin_cached_selected_rows);
1643 }
1644
1645 /**
1646  * We're caching the selected rows at the beginning because the
1647  * selection could change between drag-begin and drag-data-get, for
1648  * example if we have a set of rows already selected, and then we
1649  * click in one of them (without SHIFT key pressed) and begin a drag,
1650  * the selection at that moment contains all the selected lines, but
1651  * after dropping the selection, the release event provokes that only
1652  * the row used to begin the drag is selected, so at the end the
1653  * drag&drop affects only one rows instead of all the selected ones.
1654  *
1655  */
1656 static void
1657 drag_begin_cb (GtkWidget *widget, 
1658                GdkDragContext *context, 
1659                gpointer data)
1660 {
1661         ModestHeaderView *self = NULL;
1662         ModestHeaderViewPrivate *priv = NULL;
1663         GtkTreeSelection *selection;
1664
1665         self = MODEST_HEADER_VIEW (widget);
1666         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1667
1668         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1669         priv->drag_begin_cached_selected_rows = 
1670                 gtk_tree_selection_get_selected_rows (selection, NULL);
1671 }
1672
1673 /**
1674  * We use the drag-end signal to clear the cached selection, we use
1675  * this because this allways happens, whether or not the d&d was a
1676  * success
1677  */
1678 static void
1679 drag_end_cb (GtkWidget *widget, 
1680              GdkDragContext *dc, 
1681              gpointer data)
1682 {
1683         ModestHeaderView *self = NULL;
1684         ModestHeaderViewPrivate *priv = NULL;
1685
1686         self = MODEST_HEADER_VIEW (widget);
1687         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1688
1689         /* Free cached data */
1690         g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1691         g_list_free (priv->drag_begin_cached_selected_rows);
1692         priv->drag_begin_cached_selected_rows = NULL;
1693 }
1694
1695 /* Header view drag types */
1696 const GtkTargetEntry header_view_drag_types[] = {
1697         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1698 };
1699
1700 static void
1701 enable_drag_and_drop (GtkWidget *self)
1702 {
1703         gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1704                              header_view_drag_types,
1705                              G_N_ELEMENTS (header_view_drag_types),
1706                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1707 }
1708
1709 static void
1710 disable_drag_and_drop (GtkWidget *self)
1711 {
1712         gtk_drag_source_unset (self);
1713 }
1714
1715 static void
1716 setup_drag_and_drop (GtkWidget *self)
1717 {
1718         enable_drag_and_drop(self);
1719         g_signal_connect(G_OBJECT (self), "drag_data_get",
1720                          G_CALLBACK(drag_data_get_cb), NULL);
1721
1722         g_signal_connect(G_OBJECT (self), "drag_begin",
1723                          G_CALLBACK(drag_begin_cb), NULL);
1724
1725         g_signal_connect(G_OBJECT (self), "drag_end",
1726                          G_CALLBACK(drag_end_cb), NULL);
1727 }
1728
1729 static GtkTreePath *
1730 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1731 {
1732         GtkTreePath *path = NULL;
1733         GtkTreeSelection *sel = NULL;   
1734         GList *rows = NULL;
1735
1736         sel   = gtk_tree_view_get_selection(self);
1737         rows = gtk_tree_selection_get_selected_rows (sel, model);
1738         
1739         if ((rows == NULL) || (g_list_length(rows) != 1))
1740                 goto frees;
1741
1742         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1743         
1744
1745         /* Free */
1746  frees:
1747         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1748         g_list_free(rows);
1749
1750         return path;
1751 }
1752
1753 /*
1754  * This function moves the tree view scroll to the current selected
1755  * row when the widget grabs the focus 
1756  */
1757 static gboolean 
1758 on_focus_in (GtkWidget     *self,
1759              GdkEventFocus *event,
1760              gpointer       user_data)
1761 {
1762         GtkTreeSelection *selection;
1763         GtkTreeModel *model;
1764         GList *selected = NULL;
1765         GtkTreePath *selected_path = NULL;
1766
1767         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1768         if (!model)
1769                 return FALSE;
1770
1771         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1772         /* If none selected yet, pick the first one */
1773         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1774                 GtkTreeIter iter;
1775                 GtkTreePath *path;
1776
1777                 /* Return if the model is empty */
1778                 if (!gtk_tree_model_get_iter_first (model, &iter))
1779                         return FALSE;
1780
1781                 path = gtk_tree_model_get_path (model, &iter);
1782                 gtk_tree_selection_select_path (selection, path);
1783                 gtk_tree_path_free (path);
1784         }
1785
1786         /* Need to get the all the rows because is selection multiple */
1787         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1788         if (selected == NULL) return FALSE;
1789         selected_path = (GtkTreePath *) selected->data;
1790
1791         /* Frees */     
1792         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1793         g_list_free (selected);
1794
1795         return FALSE;
1796 }
1797
1798 static gboolean 
1799 on_focus_out (GtkWidget     *self,
1800              GdkEventFocus *event,
1801              gpointer       user_data)
1802 {
1803
1804         if (!gtk_widget_is_focus (self)) {
1805                 GtkTreeSelection *selection = NULL;
1806                 GList *selected_rows = NULL;
1807                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1808                 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1809                         selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1810                         g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1811                         gtk_tree_selection_unselect_all (selection);
1812                         gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1813                         g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1814                         g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1815                         g_list_free (selected_rows);
1816                 }
1817         }
1818         return FALSE;
1819 }
1820
1821 static gboolean
1822 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1823 {
1824         enable_drag_and_drop(self);
1825         return FALSE;
1826 }
1827
1828 static gboolean
1829 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1830 {
1831         GtkTreeSelection *selection = NULL;
1832         GtkTreePath *path = NULL;
1833         gboolean already_selected = FALSE, already_opened = FALSE;
1834         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1835
1836         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1837                 GtkTreeIter iter;
1838                 GtkTreeModel *model;
1839
1840                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1841                 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1842
1843                 /* Get header from model */
1844                 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1845                 if (gtk_tree_model_get_iter (model, &iter, path)) {
1846                         GValue value = {0,};
1847                         TnyHeader *header;
1848
1849                         gtk_tree_model_get_value (model, &iter, 
1850                                                   TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, 
1851                                                   &value);
1852                         header = (TnyHeader *) g_value_get_object (&value);
1853                         if (TNY_IS_HEADER (header)) {
1854                                 status = modest_tny_all_send_queues_get_msg_status (header);
1855                                 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (), 
1856                                                                                            header, NULL);
1857                         }
1858                         g_value_unset (&value);
1859                 }
1860         }
1861
1862         /* Enable drag and drop only if the user clicks on a row that
1863            it's already selected. If not, let him select items using
1864            the pointer. If the message is in an OUTBOX and in sending
1865            status disable drag and drop as well */
1866         if (!already_selected ||
1867             status == MODEST_TNY_SEND_QUEUE_SENDING ||
1868             already_opened)
1869                 disable_drag_and_drop(self);
1870
1871         if (path != NULL)
1872                 gtk_tree_path_free(path);
1873
1874         /* If it's already opened then do not let the button-press
1875            event go on because it'll perform a message open because
1876            we're clicking on to an already selected header */
1877         return FALSE;
1878 }
1879
1880 static void
1881 folder_monitor_update (TnyFolderObserver *self, 
1882                        TnyFolderChange *change)
1883 {
1884         ModestHeaderViewPrivate *priv = NULL;
1885         TnyFolderChangeChanged changed;
1886         TnyFolder *folder = NULL;
1887
1888         changed = tny_folder_change_get_changed (change);
1889         
1890         /* Do not notify the observers if the folder of the header
1891            view has changed before this call to the observer
1892            happens */
1893         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1894         folder = tny_folder_change_get_folder (change);
1895         if (folder != priv->folder)
1896                 goto frees;
1897
1898         MODEST_DEBUG_BLOCK (
1899                             if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1900                                     g_print ("ADDED %d/%d (r/t) \n", 
1901                                              tny_folder_change_get_new_unread_count (change),
1902                                              tny_folder_change_get_new_all_count (change));
1903                             if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1904                                     g_print ("ALL COUNT %d\n", 
1905                                              tny_folder_change_get_new_all_count (change));
1906                             if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1907                                     g_print ("UNREAD COUNT %d\n", 
1908                                              tny_folder_change_get_new_unread_count (change));
1909                             if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1910                                     g_print ("EXPUNGED %d/%d (r/t) \n", 
1911                                              tny_folder_change_get_new_unread_count (change),
1912                                              tny_folder_change_get_new_all_count (change));
1913                             if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1914                                     g_print ("FOLDER RENAME\n");
1915                             if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1916                                     g_print ("MSG RECEIVED %d/%d (r/t) \n", 
1917                                              tny_folder_change_get_new_unread_count (change),
1918                                              tny_folder_change_get_new_all_count (change));
1919                             g_print ("---------------------------------------------------\n");
1920                             );
1921
1922         /* Check folder count */
1923         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1924             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1925
1926                 g_mutex_lock (priv->observers_lock);
1927
1928                 /* Emit signal to evaluate how headers changes affects
1929                    to the window view  */
1930                 g_signal_emit (G_OBJECT(self), 
1931                                signals[MSG_COUNT_CHANGED_SIGNAL], 
1932                                0, folder, change);
1933                 
1934                 /* Added or removed headers, so data stored on cliboard are invalid  */
1935                 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1936                         modest_email_clipboard_clear (priv->clipboard);
1937             
1938                 g_mutex_unlock (priv->observers_lock);
1939         }       
1940
1941         /* Free */
1942  frees:
1943         if (folder != NULL)
1944                 g_object_unref (folder);
1945 }
1946
1947 gboolean
1948 modest_header_view_is_empty (ModestHeaderView *self)
1949 {
1950         ModestHeaderViewPrivate *priv;
1951         
1952         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1953         
1954         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1955
1956         return priv->status == HEADER_VIEW_EMPTY;
1957 }
1958
1959 void
1960 modest_header_view_clear (ModestHeaderView *self)
1961 {
1962         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1963         
1964         modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1965 }
1966
1967 void 
1968 modest_header_view_copy_selection (ModestHeaderView *header_view)
1969 {
1970         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1971         
1972         /* Copy selection */
1973         _clipboard_set_selected_data (header_view, FALSE);
1974 }
1975
1976 void 
1977 modest_header_view_cut_selection (ModestHeaderView *header_view)
1978 {
1979         ModestHeaderViewPrivate *priv = NULL;
1980         const gchar **hidding = NULL;
1981         guint i, n_selected;
1982
1983         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1984         
1985         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1986
1987         /* Copy selection */
1988         _clipboard_set_selected_data (header_view, TRUE);
1989
1990         /* Get hidding ids */
1991         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1992         
1993         /* Clear hidding array created by previous cut operation */
1994         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1995
1996         /* Copy hidding array */
1997         priv->n_selected = n_selected;
1998         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1999         for (i=0; i < n_selected; i++) 
2000                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
2001
2002         /* Hide cut headers */
2003         modest_header_view_refilter (header_view);
2004 }
2005
2006
2007  
2008
2009 static void
2010 _clipboard_set_selected_data (ModestHeaderView *header_view,
2011                               gboolean delete)
2012 {
2013         ModestHeaderViewPrivate *priv = NULL;
2014         TnyList *headers = NULL;
2015
2016         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2017         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2018                 
2019         /* Set selected data on clipboard   */
2020         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2021         headers = modest_header_view_get_selected_headers (header_view);
2022         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2023
2024         /* Free */
2025         g_object_unref (headers);
2026 }
2027
2028 typedef struct {
2029         ModestHeaderView *self;
2030         TnyFolder *folder;
2031 } NotifyFilterInfo;
2032
2033 static gboolean
2034 notify_filter_change (gpointer data)
2035 {
2036         NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2037
2038         g_signal_emit (info->self, 
2039                        signals[MSG_COUNT_CHANGED_SIGNAL], 
2040                        0, info->folder, NULL);
2041
2042         return FALSE;
2043 }
2044
2045 static void
2046 notify_filter_change_destroy (gpointer data)
2047 {
2048         NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2049         ModestHeaderViewPrivate *priv;
2050
2051         priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2052         priv->status_timeout = 0;
2053
2054         g_object_unref (info->self);
2055         g_object_unref (info->folder);
2056         g_slice_free (NotifyFilterInfo, info);
2057 }
2058
2059 static gboolean
2060 filter_row (GtkTreeModel *model,
2061             GtkTreeIter *iter,
2062             gpointer user_data)
2063 {
2064         ModestHeaderViewPrivate *priv = NULL;
2065         TnyHeaderFlags flags;
2066         TnyHeader *header = NULL;
2067         guint i;
2068         gchar *id = NULL;
2069         gboolean visible = TRUE;
2070         gboolean found = FALSE;
2071         GValue value = {0,};
2072         HeaderViewStatus old_status;
2073
2074         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2075         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2076
2077         /* Get header from model */
2078         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2079         flags = (TnyHeaderFlags) g_value_get_int (&value);
2080         g_value_unset (&value);
2081         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2082         header = (TnyHeader *) g_value_get_object (&value);
2083         g_value_unset (&value);
2084         
2085         /* Hide deleted and mark as deleted heders */
2086         if (flags & TNY_HEADER_FLAG_DELETED ||
2087             flags & TNY_HEADER_FLAG_EXPUNGED) {
2088                 visible = FALSE;
2089                 goto frees;
2090         }
2091
2092         /* If no data on clipboard, return always TRUE */
2093         if (modest_email_clipboard_cleared(priv->clipboard)) {
2094                 visible = TRUE;
2095                 goto frees;
2096         }               
2097
2098         /* Get message id from header (ensure is a valid id) */
2099         if (!header) {
2100                 visible = FALSE;
2101                 goto frees;
2102         }
2103         
2104         /* Check hiding */
2105         if (priv->hidding_ids != NULL) {
2106                 id = tny_header_dup_message_id (header);
2107                 for (i=0; i < priv->n_selected && !found; i++)
2108                         if (priv->hidding_ids[i] != NULL && id != NULL)
2109                                 found = (!strcmp (priv->hidding_ids[i], id));
2110         
2111                 visible = !found;
2112                 g_free(id);
2113         }
2114
2115  frees:
2116         old_status = priv->status;
2117         priv->status = ((gboolean) priv->status) && !visible;
2118         if ((priv->notify_status) && (priv->status != old_status)) {
2119                 NotifyFilterInfo *info;
2120
2121                 if (priv->status_timeout)
2122                         g_source_remove (priv->status_timeout);
2123
2124                 info = g_slice_new0 (NotifyFilterInfo);
2125                 info->self = g_object_ref (G_OBJECT (user_data));
2126                 info->folder = tny_header_get_folder (header);
2127                 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2128                                                            notify_filter_change,
2129                                                            info,
2130                                                            notify_filter_change_destroy);
2131         }
2132
2133         return visible;
2134 }
2135
2136 static void
2137 _clear_hidding_filter (ModestHeaderView *header_view) 
2138 {
2139         ModestHeaderViewPrivate *priv = NULL;
2140         guint i;
2141         
2142         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
2143         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2144
2145         if (priv->hidding_ids != NULL) {
2146                 for (i=0; i < priv->n_selected; i++) 
2147                         g_free (priv->hidding_ids[i]);
2148                 g_free(priv->hidding_ids);
2149         }       
2150 }
2151
2152 void 
2153 modest_header_view_refilter (ModestHeaderView *header_view)
2154 {
2155         GtkTreeModel *model = NULL;
2156         ModestHeaderViewPrivate *priv = NULL;
2157
2158         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2159         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2160         
2161         /* Hide cut headers */
2162         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2163         if (GTK_IS_TREE_MODEL_FILTER (model)) {
2164                 priv->status = HEADER_VIEW_INIT;
2165                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2166         }
2167 }
2168
2169 /* 
2170  * Called when an account is removed. If I'm showing a folder of the
2171  * account that has been removed then clear the view
2172  */
2173 static void
2174 on_account_removed (TnyAccountStore *self, 
2175                     TnyAccount *account,
2176                     gpointer user_data)
2177 {
2178         ModestHeaderViewPrivate *priv = NULL;
2179
2180         /* Ignore changes in transport accounts */
2181         if (TNY_IS_TRANSPORT_ACCOUNT (account))
2182                 return;
2183
2184         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2185
2186         if (priv->folder) {
2187                 TnyAccount *my_account;
2188
2189                 my_account = tny_folder_get_account (priv->folder);
2190                 if (my_account == account)
2191                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2192                 g_object_unref (my_account);
2193         }
2194 }
2195
2196 void
2197 modest_header_view_add_observer(ModestHeaderView *header_view,
2198                                      ModestHeaderViewObserver *observer)
2199 {
2200         ModestHeaderViewPrivate *priv;
2201         
2202         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2203         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2204
2205         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2206
2207         g_mutex_lock(priv->observer_list_lock);
2208         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2209         g_mutex_unlock(priv->observer_list_lock);
2210 }
2211
2212 void 
2213 modest_header_view_remove_observer(ModestHeaderView *header_view,
2214                                    ModestHeaderViewObserver *observer)
2215 {
2216         ModestHeaderViewPrivate *priv;
2217
2218         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2219         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2220
2221         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2222
2223         g_mutex_lock(priv->observer_list_lock);
2224         priv->observer_list = g_slist_remove(priv->observer_list, observer);
2225         g_mutex_unlock(priv->observer_list_lock);
2226 }
2227
2228 static void 
2229 modest_header_view_notify_observers(ModestHeaderView *header_view,
2230                                     GtkTreeModel *model,
2231                                     const gchar *tny_folder_id)
2232 {
2233         ModestHeaderViewPrivate *priv = NULL;
2234         GSList *iter;
2235         ModestHeaderViewObserver *observer;
2236
2237
2238         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2239         
2240         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2241
2242         g_mutex_lock(priv->observer_list_lock);
2243         iter = priv->observer_list;
2244         while(iter != NULL){
2245                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2246                 modest_header_view_observer_update(observer, model,
2247                                 tny_folder_id);
2248                 iter = g_slist_next(iter);
2249         }
2250         g_mutex_unlock(priv->observer_list_lock);
2251 }
2252
2253 const gchar *
2254 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2255 {
2256         ModestHeaderViewPrivate *priv = NULL;
2257         
2258         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2259         return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2260 }