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