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