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