aeb0a9be6500aa8c5fc4da135be0f69899566f77
[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_free (info);
1181 }
1182
1183 void
1184 modest_header_view_set_folder (ModestHeaderView *self, 
1185                                TnyFolder *folder,
1186                                RefreshAsyncUserCallback callback,
1187                                gpointer user_data)
1188 {
1189         ModestHeaderViewPrivate *priv;
1190         SetFolderHelper *info;
1191         ModestWindow *main_win;
1192         
1193         g_return_if_fail (self);
1194
1195         priv =     MODEST_HEADER_VIEW_GET_PRIVATE(self);
1196
1197         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1198                                                       FALSE); /* don't create */
1199         if (!main_win) {
1200                 g_warning ("%s: BUG: no main window", __FUNCTION__);
1201                 return;
1202         }
1203                                                       
1204         if (priv->folder) {
1205                 g_mutex_lock (priv->observers_lock);
1206                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1207                 g_object_unref (priv->folder);
1208                 priv->folder = NULL;
1209                 g_mutex_unlock (priv->observers_lock);
1210         }
1211
1212         if (folder) {
1213                 ModestMailOperation *mail_op = NULL;
1214                 GtkTreeSelection *selection;
1215
1216                 /* Set folder in the model */
1217                 modest_header_view_set_folder_intern (self, folder);
1218                 
1219                 /* Pick my reference. Nothing to do with the mail operation */
1220                 priv->folder = g_object_ref (folder);
1221
1222                 /* Clear the selection if exists */
1223                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1224                 gtk_tree_selection_unselect_all(selection);
1225                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1226
1227                 /* Notify the observers that the update begins */
1228                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1229                                0, TRUE, NULL);
1230
1231                 /* create the helper */
1232                 info = g_malloc0 (sizeof(SetFolderHelper));
1233                 info->header_view = self;
1234                 info->cb = callback;
1235                 info->user_data = user_data;
1236
1237                 /* Create the mail operation (source will be the parent widget) */
1238                 mail_op = modest_mail_operation_new (G_OBJECT(main_win));
1239                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1240                                                  mail_op);
1241
1242                 /* Refresh the folder asynchronously */
1243                 modest_mail_operation_refresh_folder (mail_op,
1244                                                       folder,
1245                                                       folder_refreshed_cb,
1246                                                       info);
1247
1248                 /* Free */
1249                 g_object_unref (mail_op);
1250         } else {
1251                 g_mutex_lock (priv->observers_lock);
1252
1253                 if (priv->monitor) {
1254                         tny_folder_monitor_stop (priv->monitor);
1255                         g_object_unref (G_OBJECT (priv->monitor));
1256                         priv->monitor = NULL;
1257                 }
1258                 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL); 
1259
1260                 modest_header_view_notify_observers(self, NULL, NULL);
1261
1262                 g_mutex_unlock (priv->observers_lock);
1263
1264                 /* Notify the observers that the update is over */
1265                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1266                                0, FALSE, NULL);
1267         }
1268 }
1269
1270 static void
1271 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1272                          GtkTreeViewColumn *column, gpointer userdata)
1273 {
1274         ModestHeaderView *self = NULL;
1275         ModestHeaderViewPrivate *priv = NULL;
1276         GtkTreeIter iter;
1277         GtkTreeModel *model = NULL;
1278         TnyHeader *header = NULL;
1279         TnyHeaderFlags flags;
1280
1281         self = MODEST_HEADER_VIEW (treeview);
1282         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1283
1284         model = gtk_tree_view_get_model (treeview);     
1285         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
1286                 goto frees;
1287
1288         /* get the first selected item */
1289         gtk_tree_model_get (model, &iter,
1290                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1291                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, 
1292                             -1);
1293
1294         /* Dont open DELETED messages */
1295         if (flags & TNY_HEADER_FLAG_DELETED) {
1296                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1297                 goto frees;
1298         }
1299
1300         /* Emit signal */
1301         g_signal_emit (G_OBJECT(self), 
1302                        signals[HEADER_ACTIVATED_SIGNAL], 
1303                        0, header);
1304
1305         /* Free */
1306  frees:
1307         if (header != NULL) 
1308                 g_object_unref (G_OBJECT (header));     
1309
1310 }
1311
1312 static void
1313 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1314 {
1315         GtkTreeModel *model;
1316         TnyHeader *header = NULL;
1317         GtkTreePath *path = NULL;       
1318         GtkTreeIter iter;
1319         ModestHeaderView *self;
1320         ModestHeaderViewPrivate *priv;
1321         GList *selected = NULL;
1322         
1323         g_return_if_fail (sel);
1324         g_return_if_fail (user_data);
1325         
1326         self = MODEST_HEADER_VIEW (user_data);
1327         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
1328
1329         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1330         if (selected != NULL) 
1331                 path = (GtkTreePath *) selected->data;
1332         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1333                 return; /* msg was _un_selected */
1334
1335         gtk_tree_model_get (model, &iter,
1336                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1337                             &header, -1);
1338
1339         /* Emit signal */
1340         g_signal_emit (G_OBJECT(self), 
1341                        signals[HEADER_SELECTED_SIGNAL], 
1342                        0, header);
1343
1344         g_object_unref (G_OBJECT (header));
1345
1346         /* free all items in 'selected' */
1347         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1348         g_list_free (selected);
1349 }
1350
1351
1352 /* PROTECTED method. It's useful when we want to force a given
1353    selection to reload a msg. For example if we have selected a header
1354    in offline mode, when Modest become online, we want to reload the
1355    message automatically without an user click over the header */
1356 void 
1357 _modest_header_view_change_selection (GtkTreeSelection *selection,
1358                                       gpointer user_data)
1359 {
1360         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1361         g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1362         
1363         on_selection_changed (selection, user_data);
1364 }
1365
1366 static gint
1367 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1368 {
1369         /* HH, LL, NN */
1370         if (p1 == p2)
1371                 return 0;
1372
1373         /* HL HN */
1374         if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1375                 return 1;
1376
1377         /* LH LN */
1378         if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1379                 return -1;
1380
1381         /* NH */
1382         if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1383                 return -1;
1384
1385         /* NL */
1386         return 1;
1387 }
1388
1389 static gint
1390 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1391           gpointer user_data)
1392 {
1393         gint col_id;
1394         gint t1, t2;
1395         gint val1, val2;
1396         gint cmp;
1397 /*      static int counter = 0; */
1398
1399         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1400 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1401         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1402
1403         
1404         switch (col_id) {
1405         case TNY_HEADER_FLAG_ATTACHMENTS:
1406
1407                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1408                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1409                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1410                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1411
1412                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1413                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1414
1415                 return cmp ? cmp : t1 - t2;
1416                 
1417         case TNY_HEADER_FLAG_PRIORITY_MASK: {
1418                 TnyHeader *header1 = NULL, *header2 = NULL;
1419
1420                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1421                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1422                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1423                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1424
1425                 /* This is for making priority values respect the intuitive sort relationship 
1426                  * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1427
1428                 if (header1 && header2) {
1429                         cmp =  compare_priorities (tny_header_get_priority (header1), 
1430                                 tny_header_get_priority (header2));
1431                         g_object_unref (header1);
1432                         g_object_unref (header2);
1433
1434                         return cmp ? cmp : t1 - t2;
1435                 }
1436
1437                 return t1 - t2;
1438         }
1439         default:
1440                 return &iter1 - &iter2; /* oughhhh  */
1441         }
1442 }
1443
1444 static gint
1445 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1446                   gpointer user_data)
1447 {
1448         gint t1, t2;
1449         gchar *val1, *val2;
1450         gint cmp;
1451 /*      static int counter = 0; */
1452
1453         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1454
1455         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1456                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1457         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1458                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1459
1460         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1461                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1462                                              TRUE);
1463         g_free (val1);
1464         g_free (val2);
1465         return cmp;
1466 }
1467
1468 /* Drag and drop stuff */
1469 static void
1470 drag_data_get_cb (GtkWidget *widget, 
1471                   GdkDragContext *context, 
1472                   GtkSelectionData *selection_data, 
1473                   guint info,  
1474                   guint time, 
1475                   gpointer data)
1476 {
1477         ModestHeaderView *self = NULL;
1478         ModestHeaderViewPrivate *priv = NULL;
1479
1480         self = MODEST_HEADER_VIEW (widget);
1481         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1482
1483         /* Set the data. Do not use the current selection because it
1484            could be different than the selection at the beginning of
1485            the d&d */
1486         modest_dnd_selection_data_set_paths (selection_data, 
1487                                              priv->drag_begin_cached_selected_rows);
1488 }
1489
1490 /**
1491  * We're caching the selected rows at the beginning because the
1492  * selection could change between drag-begin and drag-data-get, for
1493  * example if we have a set of rows already selected, and then we
1494  * click in one of them (without SHIFT key pressed) and begin a drag,
1495  * the selection at that moment contains all the selected lines, but
1496  * after dropping the selection, the release event provokes that only
1497  * the row used to begin the drag is selected, so at the end the
1498  * drag&drop affects only one rows instead of all the selected ones.
1499  *
1500  */
1501 static void
1502 drag_begin_cb (GtkWidget *widget, 
1503                GdkDragContext *context, 
1504                gpointer data)
1505 {
1506         ModestHeaderView *self = NULL;
1507         ModestHeaderViewPrivate *priv = NULL;
1508         GtkTreeSelection *selection;
1509
1510         self = MODEST_HEADER_VIEW (widget);
1511         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1512
1513         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1514         priv->drag_begin_cached_selected_rows = 
1515                 gtk_tree_selection_get_selected_rows (selection, NULL);
1516 }
1517
1518 /**
1519  * We use the drag-end signal to clear the cached selection, we use
1520  * this because this allways happens, whether or not the d&d was a
1521  * success
1522  */
1523 static void
1524 drag_end_cb (GtkWidget *widget, 
1525              GdkDragContext *dc, 
1526              gpointer data)
1527 {
1528         ModestHeaderView *self = NULL;
1529         ModestHeaderViewPrivate *priv = NULL;
1530
1531         self = MODEST_HEADER_VIEW (widget);
1532         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1533
1534         /* Free cached data */
1535         g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1536         g_list_free (priv->drag_begin_cached_selected_rows);
1537         priv->drag_begin_cached_selected_rows = NULL;
1538 }
1539
1540 /* Header view drag types */
1541 const GtkTargetEntry header_view_drag_types[] = {
1542         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1543 };
1544
1545 static void
1546 enable_drag_and_drop (GtkWidget *self)
1547 {
1548         gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1549                              header_view_drag_types,
1550                              G_N_ELEMENTS (header_view_drag_types),
1551                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1552 }
1553
1554 static void
1555 disable_drag_and_drop (GtkWidget *self)
1556 {
1557         gtk_drag_source_unset (self);
1558 }
1559
1560 static void
1561 setup_drag_and_drop (GtkWidget *self)
1562 {
1563         enable_drag_and_drop(self);
1564         g_signal_connect(G_OBJECT (self), "drag_data_get",
1565                          G_CALLBACK(drag_data_get_cb), NULL);
1566
1567         g_signal_connect(G_OBJECT (self), "drag_begin",
1568                          G_CALLBACK(drag_begin_cb), NULL);
1569
1570         g_signal_connect(G_OBJECT (self), "drag_end",
1571                          G_CALLBACK(drag_end_cb), NULL);
1572 }
1573
1574 static GtkTreePath *
1575 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1576 {
1577         GtkTreePath *path = NULL;
1578         GtkTreeSelection *sel = NULL;   
1579         GList *rows = NULL;
1580
1581         sel   = gtk_tree_view_get_selection(self);
1582         rows = gtk_tree_selection_get_selected_rows (sel, model);
1583         
1584         if ((rows == NULL) || (g_list_length(rows) != 1))
1585                 goto frees;
1586
1587         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1588         
1589
1590         /* Free */
1591  frees:
1592         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1593         g_list_free(rows);
1594
1595         return path;
1596 }
1597
1598 /*
1599  * This function moves the tree view scroll to the current selected
1600  * row when the widget grabs the focus 
1601  */
1602 static gboolean 
1603 on_focus_in (GtkWidget     *self,
1604              GdkEventFocus *event,
1605              gpointer       user_data)
1606 {
1607         GtkTreeSelection *selection;
1608         GtkTreeModel *model;
1609         GList *selected = NULL;
1610         GtkTreePath *selected_path = NULL;
1611
1612         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1613         if (!model)
1614                 return FALSE;
1615
1616         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1617         /* If none selected yet, pick the first one */
1618         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1619                 GtkTreeIter iter;
1620                 GtkTreePath *path;
1621
1622                 /* Return if the model is empty */
1623                 if (!gtk_tree_model_get_iter_first (model, &iter))
1624                         return FALSE;
1625
1626                 path = gtk_tree_model_get_path (model, &iter);
1627                 gtk_tree_selection_select_path (selection, path);
1628                 gtk_tree_path_free (path);
1629         }
1630
1631         /* Need to get the all the rows because is selection multiple */
1632         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1633         if (selected == NULL) return FALSE;
1634         selected_path = (GtkTreePath *) selected->data;
1635
1636         /* Check if we need to scroll */
1637         #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1638         GtkTreePath *start_path = NULL;
1639         GtkTreePath *end_path = NULL;
1640         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1641                                              &start_path,
1642                                              &end_path)) {
1643
1644                 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1645                     (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1646
1647                         /* Scroll to first path */
1648                         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1649                                                       selected_path,
1650                                                       NULL,
1651                                                       TRUE,
1652                                                       0.5,
1653                                                       0.0);
1654                 }
1655         }
1656         if (start_path)
1657                 gtk_tree_path_free (start_path);
1658         if (end_path)
1659                 gtk_tree_path_free (end_path);
1660
1661         #endif /* GTK_CHECK_VERSION */
1662
1663         /* Frees */     
1664         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1665         g_list_free (selected);
1666
1667         return FALSE;
1668 }
1669
1670 static gboolean 
1671 on_focus_out (GtkWidget     *self,
1672              GdkEventFocus *event,
1673              gpointer       user_data)
1674 {
1675
1676         if (!gtk_widget_is_focus (self)) {
1677                 GtkTreeSelection *selection = NULL;
1678                 GList *selected_rows = NULL;
1679                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1680                 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1681                         selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1682                         g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1683                         gtk_tree_selection_unselect_all (selection);
1684                         gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1685                         g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1686                         g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1687                         g_list_free (selected_rows);
1688                 }
1689         }
1690         return FALSE;
1691 }
1692
1693 static gboolean
1694 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1695 {
1696         enable_drag_and_drop(self);
1697         return FALSE;
1698 }
1699
1700 static gboolean
1701 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1702 {
1703         GtkTreeSelection *selection = NULL;
1704         GtkTreePath *path = NULL;
1705         gboolean already_selected = FALSE;
1706
1707         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1708                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1709                 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1710         }
1711
1712         /* Enable drag and drop onlly if the user clicks on a row that
1713            it's already selected. If not, let him select items using
1714            the pointer */
1715         if (!already_selected) {
1716                 disable_drag_and_drop(self);
1717         }
1718
1719         if (path != NULL) {
1720                 gtk_tree_path_free(path);
1721         }
1722
1723         return FALSE;
1724 }
1725
1726 static void
1727 folder_monitor_update (TnyFolderObserver *self, 
1728                        TnyFolderChange *change)
1729 {
1730         ModestHeaderViewPrivate *priv = NULL;
1731         TnyFolderChangeChanged changed;
1732         TnyFolder *folder = NULL;
1733
1734         changed = tny_folder_change_get_changed (change);
1735         
1736         /* Do not notify the observers if the folder of the header
1737            view has changed before this call to the observer
1738            happens */
1739         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1740         folder = tny_folder_change_get_folder (change);
1741         if (folder != priv->folder)
1742                 goto frees;
1743
1744         /* Check folder count */
1745         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1746             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1747
1748                 g_mutex_lock (priv->observers_lock);
1749
1750                 /* Emit signal to evaluate how headers changes affects
1751                    to the window view  */
1752                 g_signal_emit (G_OBJECT(self), 
1753                                signals[MSG_COUNT_CHANGED_SIGNAL], 
1754                                0, folder, change);
1755                 
1756                 /* Added or removed headers, so data stored on cliboard are invalid  */
1757                 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1758                         modest_email_clipboard_clear (priv->clipboard);
1759             
1760                 g_mutex_unlock (priv->observers_lock);
1761         }       
1762
1763         /* Free */
1764  frees:
1765         if (folder != NULL)
1766                 g_object_unref (folder);
1767 }
1768
1769 gboolean
1770 modest_header_view_is_empty (ModestHeaderView *self)
1771 {
1772         ModestHeaderViewPrivate *priv;
1773         
1774         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1775         
1776         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1777
1778         return priv->status == HEADER_VIEW_EMPTY;
1779 }
1780
1781 void
1782 modest_header_view_clear (ModestHeaderView *self)
1783 {
1784         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1785         
1786         modest_header_view_set_folder (self, NULL, NULL, NULL);
1787 }
1788
1789 void 
1790 modest_header_view_copy_selection (ModestHeaderView *header_view)
1791 {
1792         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1793         
1794         /* Copy selection */
1795         _clipboard_set_selected_data (header_view, FALSE);
1796 }
1797
1798 void 
1799 modest_header_view_cut_selection (ModestHeaderView *header_view)
1800 {
1801         ModestHeaderViewPrivate *priv = NULL;
1802         const gchar **hidding = NULL;
1803         guint i, n_selected;
1804
1805         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1806         
1807         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1808
1809         /* Copy selection */
1810         _clipboard_set_selected_data (header_view, TRUE);
1811
1812         /* Get hidding ids */
1813         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1814         
1815         /* Clear hidding array created by previous cut operation */
1816         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1817
1818         /* Copy hidding array */
1819         priv->n_selected = n_selected;
1820         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1821         for (i=0; i < n_selected; i++) 
1822                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1823
1824         /* Hide cut headers */
1825         modest_header_view_refilter (header_view);
1826 }
1827
1828
1829  
1830
1831 static void
1832 _clipboard_set_selected_data (ModestHeaderView *header_view,
1833                               gboolean delete)
1834 {
1835         ModestHeaderViewPrivate *priv = NULL;
1836         TnyList *headers = NULL;
1837
1838         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1839         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1840                 
1841         /* Set selected data on clipboard   */
1842         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1843         headers = modest_header_view_get_selected_headers (header_view);
1844         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1845
1846         /* Free */
1847         g_object_unref (headers);
1848 }
1849
1850
1851
1852 static gboolean
1853 filter_row (GtkTreeModel *model,
1854             GtkTreeIter *iter,
1855             gpointer user_data)
1856 {
1857         ModestHeaderViewPrivate *priv = NULL;
1858         TnyHeaderFlags flags;
1859         TnyHeader *header = NULL;
1860         guint i;
1861         gchar *id = NULL;
1862         gboolean visible = TRUE;
1863         gboolean found = FALSE;
1864         GValue value = {0,};
1865         
1866         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1867         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1868
1869         /* Get header from model */
1870         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
1871         flags = (TnyHeaderFlags) g_value_get_int (&value);
1872         g_value_unset (&value);
1873         gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
1874         header = (TnyHeader *) g_value_get_object (&value);
1875         g_value_unset (&value);
1876         
1877         /* Hide mark as deleted heders */
1878         if (flags & TNY_HEADER_FLAG_DELETED) {
1879                 visible = FALSE;
1880                 goto frees;
1881         }
1882
1883         /* If no data on clipboard, return always TRUE */
1884         if (modest_email_clipboard_cleared(priv->clipboard)) {
1885                 visible = TRUE;
1886                 goto frees;
1887         }               
1888
1889         /* Get message id from header (ensure is a valid id) */
1890         if (!header) return FALSE;
1891         
1892         /* Check hiding */
1893         if (priv->hidding_ids != NULL) {
1894                 id = g_strdup(tny_header_get_message_id (header));
1895                 for (i=0; i < priv->n_selected && !found; i++)
1896                         if (priv->hidding_ids[i] != NULL && id != NULL)
1897                                 found = (!strcmp (priv->hidding_ids[i], id));
1898         
1899                 visible = !found;
1900                 g_free(id);
1901         }
1902
1903  frees:
1904         priv->status = ((gboolean) priv->status) && !visible;
1905         
1906         return visible;
1907 }
1908
1909 static void
1910 _clear_hidding_filter (ModestHeaderView *header_view) 
1911 {
1912         ModestHeaderViewPrivate *priv = NULL;
1913         guint i;
1914         
1915         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
1916         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1917
1918         if (priv->hidding_ids != NULL) {
1919                 for (i=0; i < priv->n_selected; i++) 
1920                         g_free (priv->hidding_ids[i]);
1921                 g_free(priv->hidding_ids);
1922         }       
1923 }
1924
1925 void 
1926 modest_header_view_refilter (ModestHeaderView *header_view)
1927 {
1928         GtkTreeModel *model = NULL;
1929         ModestHeaderViewPrivate *priv = NULL;
1930
1931         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1932         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1933         
1934         /* Hide cut headers */
1935         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1936         if (GTK_IS_TREE_MODEL_FILTER (model)) {
1937                 priv->status = HEADER_VIEW_INIT;
1938                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1939         }
1940 }
1941
1942 /* 
1943  * Called when an account is removed. If I'm showing a folder of the
1944  * account that has been removed then clear the view
1945  */
1946 static void
1947 on_account_removed (TnyAccountStore *self, 
1948                     TnyAccount *account,
1949                     gpointer user_data)
1950 {
1951         ModestHeaderViewPrivate *priv = NULL;
1952
1953         /* Ignore changes in transport accounts */
1954         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1955                 return;
1956
1957         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1958
1959         if (priv->folder) {
1960                 TnyAccount *my_account;
1961
1962                 my_account = tny_folder_get_account (priv->folder);
1963                 if (my_account == account)
1964                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1965                 g_object_unref (account);
1966         }
1967 }
1968
1969 void
1970 modest_header_view_add_observer(ModestHeaderView *header_view,
1971                                      ModestHeaderViewObserver *observer)
1972 {
1973         ModestHeaderViewPrivate *priv;
1974         
1975         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1976         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1977
1978         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1979
1980         g_mutex_lock(priv->observer_list_lock);
1981         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1982         g_mutex_unlock(priv->observer_list_lock);
1983 }
1984
1985 void 
1986 modest_header_view_remove_observer(ModestHeaderView *header_view,
1987                                    ModestHeaderViewObserver *observer)
1988 {
1989         ModestHeaderViewPrivate *priv;
1990
1991         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1992         g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1993
1994         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1995
1996         g_mutex_lock(priv->observer_list_lock);
1997         priv->observer_list = g_slist_remove(priv->observer_list, observer);
1998         g_mutex_unlock(priv->observer_list_lock);
1999 }
2000
2001 static void 
2002 modest_header_view_notify_observers(ModestHeaderView *header_view,
2003                                     GtkTreeModel *model,
2004                                     const gchar *tny_folder_id)
2005 {
2006         ModestHeaderViewPrivate *priv = NULL;
2007         GSList *iter;
2008         ModestHeaderViewObserver *observer;
2009
2010
2011         g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2012         
2013         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2014
2015         g_mutex_lock(priv->observer_list_lock);
2016         iter = priv->observer_list;
2017         while(iter != NULL){
2018                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2019                 modest_header_view_observer_update(observer, model,
2020                                 tny_folder_id);
2021                 iter = g_slist_next(iter);
2022         }
2023         g_mutex_unlock(priv->observer_list_lock);
2024 }
2025