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