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