beea9b5c08d053674ad9e711df1e03c1a0b6d217
[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         /* I'm invalidating this method because it causes an annoying
920            efect, the focus changes to the header view when selecting
921            a folder in the folder view because of this code and it
922            shouldn't. We need to find another way to set the passive
923            focus on it. Sergio. */
924         return FALSE;
925
926         model = gtk_tree_view_get_model(header_view);
927
928         sel = gtk_tree_view_get_selection(header_view);
929         if(!gtk_tree_selection_count_selected_rows(sel))
930                 if (gtk_tree_model_get_iter_first(model, &tree_iter))
931                         gtk_tree_selection_select_iter(sel, &tree_iter);
932
933         return FALSE;
934 }
935
936 /* 
937  * This function sets a sortable model in the header view. It's just
938  * used for developing purposes, because it only does a
939  * gtk_tree_view_set_model
940  */
941 static void
942 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
943 {
944 /*      GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
945 /*      if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
946 /*              GtkTreeModel *old_model; */
947 /*              ModestHeaderViewPrivate *priv; */
948 /*              priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
949 /*              old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
950
951 /*              /\* Set new model *\/ */
952 /*              gtk_tree_view_set_model (header_view, model); */
953 /*      } else */
954         gtk_tree_view_set_model (header_view, model);
955 }
956
957 TnyFolder*
958 modest_header_view_get_folder (ModestHeaderView *self)
959 {
960         ModestHeaderViewPrivate *priv;
961         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
962
963         if (priv->folder)
964                 g_object_ref (priv->folder);
965
966         return priv->folder;
967 }
968
969 static void
970 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
971 {
972         TnyFolderType type;
973         TnyList *headers;
974         ModestHeaderViewPrivate *priv;
975         GList *cols, *cursor;
976         GtkTreeModel *filter_model, *sortable; 
977         guint sort_colid;
978         GtkSortType sort_type;
979
980         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
981
982         headers = TNY_LIST (tny_gtk_header_list_model_new ());
983
984         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
985                                               folder, FALSE, NULL, NULL, NULL);
986
987         /* Add IDLE observer (monitor) and another folder observer for
988            new messages (self) */
989         g_mutex_lock (priv->observers_lock);
990         if (priv->monitor) {
991                 tny_folder_monitor_stop (priv->monitor);
992                 g_object_unref (G_OBJECT (priv->monitor));
993         }
994         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
995         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
996         tny_folder_monitor_start (priv->monitor);
997         g_mutex_unlock (priv->observers_lock);
998
999         sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1000         g_object_unref (G_OBJECT (headers));
1001
1002         /* Init filter_row function to examine empty status */
1003         priv->status  = HEADER_VIEW_INIT;
1004
1005         /* Create a tree model filter to hide and show rows for cut operations  */
1006         filter_model = gtk_tree_model_filter_new (sortable, NULL);
1007         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1008                                                 filter_row,
1009                                                 self,
1010                                                 NULL);
1011         g_object_unref (G_OBJECT (sortable));
1012
1013         /* install our special sorting functions */
1014         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1015
1016         /* Restore sort column id */
1017         if (cols) {
1018                 type  = modest_tny_folder_guess_folder_type (folder);
1019                 sort_colid = modest_header_view_get_sort_column_id (self, type); 
1020                 sort_type = modest_header_view_get_sort_type (self, type); 
1021                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1022                                                       sort_colid,
1023                                                       sort_type);
1024                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1025                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1026                                                  (GtkTreeIterCompareFunc) cmp_rows,
1027                                                  cols->data, NULL);
1028                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1029                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1030                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
1031                                                  cols->data, NULL);
1032         }
1033
1034         /* Set new model */
1035         modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1036         modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1037                         tny_folder_get_id(folder));
1038         g_object_unref (G_OBJECT (filter_model));
1039 /*      modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1040 /*      g_object_unref (G_OBJECT (sortable)); */
1041
1042         /* Free */
1043         g_list_free (cols);
1044 }
1045
1046 void
1047 modest_header_view_sort_by_column_id (ModestHeaderView *self, 
1048                                       guint sort_colid,
1049                                       GtkSortType sort_type)
1050 {
1051         ModestHeaderViewPrivate *priv = NULL;
1052         GtkTreeModel *tree_filter, *sortable = NULL; 
1053         TnyFolderType type;
1054
1055         /* Get model and private data */
1056         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);            
1057         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1058         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1059 /*      sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1060         
1061         /* Sort tree model */
1062         type  = modest_tny_folder_guess_folder_type (priv->folder);
1063         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1064                                               sort_colid,
1065                                               sort_type);
1066         /* Store new sort parameters */
1067         modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1068
1069         /* Save GConf parameters */
1070 /*      modest_widget_memory_save (modest_runtime_get_conf(), */
1071 /*                                 G_OBJECT(self), "header-view"); */
1072         
1073 }
1074
1075 void
1076 modest_header_view_set_sort_params (ModestHeaderView *self, 
1077                                     guint sort_colid, 
1078                                     GtkSortType sort_type,
1079                                     TnyFolderType type)
1080 {
1081         ModestHeaderViewPrivate *priv;
1082         ModestHeaderViewStyle style;
1083
1084         style = modest_header_view_get_style   (self);
1085         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1086
1087         priv->sort_colid[style][type] = sort_colid;
1088         priv->sort_type[style][type] = sort_type;
1089 }
1090
1091 gint
1092 modest_header_view_get_sort_column_id (ModestHeaderView *self, 
1093                                        TnyFolderType type)
1094 {
1095         ModestHeaderViewPrivate *priv;
1096         ModestHeaderViewStyle style;
1097
1098         style = modest_header_view_get_style   (self);
1099         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1100
1101         return priv->sort_colid[style][type];
1102 }
1103
1104 GtkSortType
1105 modest_header_view_get_sort_type (ModestHeaderView *self, 
1106                                   TnyFolderType type)
1107 {
1108         ModestHeaderViewPrivate *priv;
1109         ModestHeaderViewStyle style;
1110
1111         style = modest_header_view_get_style   (self);
1112         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1113
1114         return priv->sort_type[style][type];
1115 }
1116
1117 typedef struct {
1118         ModestHeaderView *header_view;
1119         RefreshAsyncUserCallback cb;
1120         gpointer user_data;
1121 } SetFolderHelper;
1122
1123 static void
1124 folder_refreshed_cb (ModestMailOperation *mail_op,
1125                      TnyFolder *folder,
1126                      gpointer user_data)
1127 {
1128         ModestHeaderViewPrivate *priv;
1129         SetFolderHelper *info;
1130  
1131         info = (SetFolderHelper*) user_data;
1132
1133         priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1134
1135         /* User callback */
1136         if (info->cb)
1137                 info->cb (mail_op, folder, info->user_data);
1138
1139         /* Start the folder count changes observer. We do not need it
1140            before the refresh. Note that the monitor could still be
1141            called for this refresh but now we know that the callback
1142            was previously called */
1143         g_mutex_lock (priv->observers_lock);
1144         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1145         g_mutex_unlock (priv->observers_lock);
1146
1147         /* Notify the observers that the update is over */
1148         g_signal_emit (G_OBJECT (info->header_view), 
1149                        signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1150
1151         /* Frees */
1152         g_free (info);
1153 }
1154
1155 void
1156 modest_header_view_set_folder (ModestHeaderView *self, 
1157                                TnyFolder *folder,
1158                                RefreshAsyncUserCallback callback,
1159                                gpointer user_data)
1160 {
1161         ModestHeaderViewPrivate *priv;
1162         ModestWindowMgr *mgr = NULL;
1163         GObject *source = NULL;
1164         SetFolderHelper *info;
1165  
1166         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1167
1168         if (priv->folder) {
1169                 g_mutex_lock (priv->observers_lock);
1170                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1171                 g_object_unref (priv->folder);
1172                 priv->folder = NULL;
1173                 g_mutex_unlock (priv->observers_lock);
1174         }
1175
1176         if (folder) {
1177                 ModestMailOperation *mail_op = NULL;
1178                 GtkTreeSelection *selection;
1179
1180                 /* Get main window to use it as source of mail operation */
1181                 mgr = modest_runtime_get_window_mgr ();
1182                 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1183
1184                 /* Set folder in the model */
1185                 modest_header_view_set_folder_intern (self, folder);
1186
1187                 /* Pick my reference. Nothing to do with the mail operation */
1188                 priv->folder = g_object_ref (folder);
1189
1190                 /* Clear the selection if exists */
1191                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1192                 gtk_tree_selection_unselect_all(selection);
1193                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1194
1195                 /* Notify the observers that the update begins */
1196                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1197                                0, TRUE, NULL);
1198
1199                 /* create the helper */
1200                 info = g_malloc0 (sizeof(SetFolderHelper));
1201                 info->header_view = self;
1202                 info->cb = callback;
1203                 info->user_data = user_data;
1204
1205                 /* Create the mail operation (source will be the parent widget) */
1206                 mail_op = modest_mail_operation_new (source);
1207                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1208                                                  mail_op);
1209
1210                 /* Refresh the folder asynchronously */
1211                 modest_mail_operation_refresh_folder (mail_op,
1212                                                       folder,
1213                                                       folder_refreshed_cb,
1214                                                       info);
1215
1216                 /* Free */
1217                 g_object_unref (mail_op);
1218         } else {
1219                 g_mutex_lock (priv->observers_lock);
1220
1221                 if (priv->monitor) {
1222                         tny_folder_monitor_stop (priv->monitor);
1223                         g_object_unref (G_OBJECT (priv->monitor));
1224                         priv->monitor = NULL;
1225                 }
1226                 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL); 
1227
1228                 modest_header_view_notify_observers(self, NULL, NULL);
1229
1230                 g_mutex_unlock (priv->observers_lock);
1231
1232                 /* Notify the observers that the update is over */
1233                 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL], 
1234                                0, FALSE, NULL);
1235         }
1236 }
1237
1238 static void
1239 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1240                          GtkTreeViewColumn *column, gpointer userdata)
1241 {
1242         ModestHeaderView *self = NULL;
1243         ModestHeaderViewPrivate *priv = NULL;
1244         GtkTreeIter iter;
1245         GtkTreeModel *model = NULL;
1246         TnyHeader *header = NULL;
1247         TnyHeaderFlags flags;
1248
1249         self = MODEST_HEADER_VIEW (treeview);
1250         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1251
1252         model = gtk_tree_view_get_model (treeview);     
1253         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
1254                 goto frees;
1255
1256         /* get the first selected item */
1257         gtk_tree_model_get (model, &iter,
1258                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1259                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, 
1260                             -1);
1261
1262         /* Dont open DELETED messages */
1263         if (flags & TNY_HEADER_FLAG_DELETED) {
1264                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1265                 goto frees;
1266         }
1267
1268         /* Emit signal */
1269         g_signal_emit (G_OBJECT(self), 
1270                        signals[HEADER_ACTIVATED_SIGNAL], 
1271                        0, header);
1272
1273         /* Free */
1274  frees:
1275         if (header != NULL) 
1276                 g_object_unref (G_OBJECT (header));     
1277
1278 }
1279
1280 static void
1281 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1282 {
1283         GtkTreeModel *model;
1284         TnyHeader *header = NULL;
1285         GtkTreePath *path = NULL;       
1286         GtkTreeIter iter;
1287         ModestHeaderView *self;
1288         ModestHeaderViewPrivate *priv;
1289         GList *selected = NULL;
1290         
1291         g_return_if_fail (sel);
1292         g_return_if_fail (user_data);
1293         
1294         self = MODEST_HEADER_VIEW (user_data);
1295         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
1296
1297         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1298         if (selected != NULL) 
1299                 path = (GtkTreePath *) selected->data;
1300         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1301                 return; /* msg was _un_selected */
1302
1303         gtk_tree_model_get (model, &iter,
1304                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1305                             &header, -1);
1306
1307         /* Emit signal */
1308         g_signal_emit (G_OBJECT(self), 
1309                        signals[HEADER_SELECTED_SIGNAL], 
1310                        0, header);
1311
1312         g_object_unref (G_OBJECT (header));
1313
1314         /* free all items in 'selected' */
1315         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1316         g_list_free (selected);
1317 }
1318
1319
1320 /* PROTECTED method. It's useful when we want to force a given
1321    selection to reload a msg. For example if we have selected a header
1322    in offline mode, when Modest become online, we want to reload the
1323    message automatically without an user click over the header */
1324 void 
1325 _modest_header_view_change_selection (GtkTreeSelection *selection,
1326                                       gpointer user_data)
1327 {
1328         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1329         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1330
1331         on_selection_changed (selection, user_data);
1332 }
1333
1334 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1335 {
1336         p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1337         p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1338         if (p1 == 0) 
1339                 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1340         if (p2 == 0) 
1341                 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1342         return p1 - p2;
1343 }
1344
1345 static gint
1346 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1347           gpointer user_data)
1348 {
1349         gint col_id;
1350         gint t1, t2;
1351         gint val1, val2;
1352         gint cmp;
1353 /*      static int counter = 0; */
1354
1355         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1356 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1357         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1358
1359         
1360         switch (col_id) {
1361         case TNY_HEADER_FLAG_ATTACHMENTS:
1362
1363                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1364                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1365                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1366                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1367
1368                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1369                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1370
1371                 return cmp ? cmp : t1 - t2;
1372                 
1373         case TNY_HEADER_FLAG_PRIORITY:
1374                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1375                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1376                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1377                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1378
1379                 /* This is for making priority values respect the intuitive sort relationship 
1380                  * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1381                 cmp =  compare_priorities (val1, val2);
1382
1383                 return cmp ? cmp : t1 - t2;
1384
1385         default:
1386                 return &iter1 - &iter2; /* oughhhh  */
1387         }
1388 }
1389
1390 static gint
1391 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1392                   gpointer user_data)
1393 {
1394         gint t1, t2;
1395         gchar *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
1401         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1402                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1403         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1404                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1405
1406         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1407                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1408                                              TRUE);
1409         g_free (val1);
1410         g_free (val2);
1411         return cmp;
1412 }
1413
1414 /* Drag and drop stuff */
1415 static void
1416 drag_data_get_cb (GtkWidget *widget, 
1417                   GdkDragContext *context, 
1418                   GtkSelectionData *selection_data, 
1419                   guint info,  
1420                   guint time, 
1421                   gpointer data)
1422 {
1423         ModestHeaderView *self = NULL;
1424         ModestHeaderViewPrivate *priv = NULL;
1425
1426         self = MODEST_HEADER_VIEW (widget);
1427         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1428
1429         /* Set the data. Do not use the current selection because it
1430            could be different than the selection at the beginning of
1431            the d&d */
1432         modest_dnd_selection_data_set_paths (selection_data, 
1433                                              priv->drag_begin_cached_selected_rows);
1434 }
1435
1436 /**
1437  * We're caching the selected rows at the beginning because the
1438  * selection could change between drag-begin and drag-data-get, for
1439  * example if we have a set of rows already selected, and then we
1440  * click in one of them (without SHIFT key pressed) and begin a drag,
1441  * the selection at that moment contains all the selected lines, but
1442  * after dropping the selection, the release event provokes that only
1443  * the row used to begin the drag is selected, so at the end the
1444  * drag&drop affects only one rows instead of all the selected ones.
1445  *
1446  */
1447 static void
1448 drag_begin_cb (GtkWidget *widget, 
1449                GdkDragContext *context, 
1450                gpointer data)
1451 {
1452         ModestHeaderView *self = NULL;
1453         ModestHeaderViewPrivate *priv = NULL;
1454         GtkTreeSelection *selection;
1455
1456         self = MODEST_HEADER_VIEW (widget);
1457         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1458
1459         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1460         priv->drag_begin_cached_selected_rows = 
1461                 gtk_tree_selection_get_selected_rows (selection, NULL);
1462 }
1463
1464 /**
1465  * We use the drag-end signal to clear the cached selection, we use
1466  * this because this allways happens, whether or not the d&d was a
1467  * success
1468  */
1469 static void
1470 drag_end_cb (GtkWidget *widget, 
1471              GdkDragContext *dc, 
1472              gpointer data)
1473 {
1474         ModestHeaderView *self = NULL;
1475         ModestHeaderViewPrivate *priv = NULL;
1476
1477         self = MODEST_HEADER_VIEW (widget);
1478         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1479
1480         /* Free cached data */
1481         g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1482         g_list_free (priv->drag_begin_cached_selected_rows);
1483         priv->drag_begin_cached_selected_rows = NULL;
1484 }
1485
1486 /* Header view drag types */
1487 const GtkTargetEntry header_view_drag_types[] = {
1488         { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1489 };
1490
1491 static void
1492 enable_drag_and_drop (GtkWidget *self)
1493 {
1494         gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1495                              header_view_drag_types,
1496                              G_N_ELEMENTS (header_view_drag_types),
1497                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1498 }
1499
1500 static void
1501 disable_drag_and_drop (GtkWidget *self)
1502 {
1503         gtk_drag_source_unset (self);
1504 }
1505
1506 static void
1507 setup_drag_and_drop (GtkWidget *self)
1508 {
1509         enable_drag_and_drop(self);
1510         g_signal_connect(G_OBJECT (self), "drag_data_get",
1511                          G_CALLBACK(drag_data_get_cb), NULL);
1512
1513         g_signal_connect(G_OBJECT (self), "drag_begin",
1514                          G_CALLBACK(drag_begin_cb), NULL);
1515
1516         g_signal_connect(G_OBJECT (self), "drag_end",
1517                          G_CALLBACK(drag_end_cb), NULL);
1518 }
1519
1520 static GtkTreePath *
1521 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1522 {
1523         GtkTreePath *path = NULL;
1524         GtkTreeSelection *sel = NULL;   
1525         GList *rows = NULL;
1526
1527         sel   = gtk_tree_view_get_selection(self);
1528         rows = gtk_tree_selection_get_selected_rows (sel, model);
1529         
1530         if ((rows == NULL) || (g_list_length(rows) != 1))
1531                 goto frees;
1532
1533         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1534         
1535
1536         /* Free */
1537  frees:
1538         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1539         g_list_free(rows);
1540
1541         return path;
1542 }
1543
1544 /*
1545  * This function moves the tree view scroll to the current selected
1546  * row when the widget grabs the focus 
1547  */
1548 static gboolean 
1549 on_focus_in (GtkWidget     *self,
1550              GdkEventFocus *event,
1551              gpointer       user_data)
1552 {
1553         GtkTreeSelection *selection;
1554         GtkTreeModel *model;
1555         GList *selected = NULL;
1556         GtkTreePath *selected_path = NULL;
1557
1558         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1559         if (!model)
1560                 return FALSE;
1561
1562         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1563         /* If none selected yet, pick the first one */
1564         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1565                 GtkTreeIter iter;
1566                 GtkTreePath *path;
1567
1568                 /* Return if the model is empty */
1569                 if (!gtk_tree_model_get_iter_first (model, &iter))
1570                         return FALSE;
1571
1572                 path = gtk_tree_model_get_path (model, &iter);
1573                 gtk_tree_selection_select_path (selection, path);
1574                 gtk_tree_path_free (path);
1575         }
1576
1577         /* Need to get the all the rows because is selection multiple */
1578         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1579         if (selected == NULL) return FALSE;
1580         selected_path = (GtkTreePath *) selected->data;
1581
1582         /* Check if we need to scroll */
1583         #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1584         GtkTreePath *start_path = NULL;
1585         GtkTreePath *end_path = NULL;
1586         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1587                                              &start_path,
1588                                              &end_path)) {
1589
1590                 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1591                     (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1592
1593                         /* Scroll to first path */
1594                         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1595                                                       selected_path,
1596                                                       NULL,
1597                                                       TRUE,
1598                                                       0.5,
1599                                                       0.0);
1600                 }
1601         }
1602         if (start_path)
1603                 gtk_tree_path_free (start_path);
1604         if (end_path)
1605                 gtk_tree_path_free (end_path);
1606
1607         #endif /* GTK_CHECK_VERSION */
1608
1609         /* Frees */     
1610         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1611         g_list_free (selected);
1612
1613         return FALSE;
1614 }
1615
1616 static gboolean 
1617 on_focus_out (GtkWidget     *self,
1618              GdkEventFocus *event,
1619              gpointer       user_data)
1620 {
1621
1622         if (!gtk_widget_is_focus (self)) {
1623                 GtkTreeSelection *selection = NULL;
1624                 GList *selected_rows = NULL;
1625                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1626                 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1627                         selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1628                         g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1629                         gtk_tree_selection_unselect_all (selection);
1630                         gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1631                         g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1632                         g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1633                         g_list_free (selected_rows);
1634                 }
1635         }
1636         return FALSE;
1637 }
1638
1639 static gboolean
1640 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1641 {
1642         enable_drag_and_drop(self);
1643         return FALSE;
1644 }
1645
1646 static gboolean
1647 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1648 {
1649         GtkTreeSelection *selection = NULL;
1650         GtkTreePath *path = NULL;
1651         gboolean already_selected = FALSE;
1652
1653         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1654                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1655                 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1656         }
1657
1658         /* Enable drag and drop onlly if the user clicks on a row that
1659            it's already selected. If not, let him select items using
1660            the pointer */
1661         if (!already_selected) {
1662                 disable_drag_and_drop(self);
1663         }
1664
1665         if (path != NULL) {
1666                 gtk_tree_path_free(path);
1667         }
1668
1669         return FALSE;
1670 }
1671
1672 static void
1673 folder_monitor_update (TnyFolderObserver *self, 
1674                        TnyFolderChange *change)
1675 {
1676         ModestHeaderViewPrivate *priv = NULL;
1677         TnyFolderChangeChanged changed;
1678         TnyFolder *folder = NULL;
1679
1680         changed = tny_folder_change_get_changed (change);
1681         
1682         /* Do not notify the observers if the folder of the header
1683            view has changed before this call to the observer
1684            happens */
1685         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1686         folder = tny_folder_change_get_folder (change);
1687         if (folder != priv->folder)
1688                 goto frees;
1689
1690         /* Check folder count */
1691         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1692             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1693
1694                 g_mutex_lock (priv->observers_lock);
1695
1696                 /* Emit signal to evaluate how headers changes affects
1697                    to the window view  */
1698                 g_signal_emit (G_OBJECT(self), 
1699                                signals[MSG_COUNT_CHANGED_SIGNAL], 
1700                                0, folder, change);
1701                 
1702                 /* Added or removed headers, so data stored on cliboard are invalid  */
1703                 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1704                         modest_email_clipboard_clear (priv->clipboard);
1705             
1706                 g_mutex_unlock (priv->observers_lock);
1707         }       
1708
1709         /* Free */
1710  frees:
1711         if (folder != NULL)
1712                 g_object_unref (folder);
1713 }
1714
1715 gboolean
1716 modest_header_view_is_empty (ModestHeaderView *self)
1717 {
1718         ModestHeaderViewPrivate *priv = NULL;
1719                 
1720         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1721
1722         return priv->status == HEADER_VIEW_EMPTY;
1723 }
1724
1725 void
1726 modest_header_view_clear (ModestHeaderView *self)
1727 {
1728         modest_header_view_set_folder (self, NULL, NULL, NULL);
1729 }
1730
1731 void 
1732 modest_header_view_copy_selection (ModestHeaderView *header_view)
1733 {
1734         /* Copy selection */
1735         _clipboard_set_selected_data (header_view, FALSE);
1736 }
1737
1738 void 
1739 modest_header_view_cut_selection (ModestHeaderView *header_view)
1740 {
1741         ModestHeaderViewPrivate *priv = NULL;
1742         const gchar **hidding = NULL;
1743         guint i, n_selected;
1744
1745         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1746         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1747
1748         /* Copy selection */
1749         _clipboard_set_selected_data (header_view, TRUE);
1750
1751         /* Get hidding ids */
1752         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1753         
1754         /* Clear hidding array created by previous cut operation */
1755         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1756
1757         /* Copy hidding array */
1758         priv->n_selected = n_selected;
1759         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1760         for (i=0; i < n_selected; i++) 
1761                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1762
1763         /* Hide cut headers */
1764         modest_header_view_refilter (header_view);
1765 }
1766
1767
1768  
1769
1770 static void
1771 _clipboard_set_selected_data (ModestHeaderView *header_view,
1772                               gboolean delete)
1773 {
1774         ModestHeaderViewPrivate *priv = NULL;
1775         TnyList *headers = NULL;
1776
1777         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1778         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1779                 
1780         /* Set selected data on clipboard   */
1781         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1782         headers = modest_header_view_get_selected_headers (header_view);
1783         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1784
1785         /* Free */
1786         g_object_unref (headers);
1787 }
1788
1789
1790
1791 static gboolean
1792 filter_row (GtkTreeModel *model,
1793             GtkTreeIter *iter,
1794             gpointer user_data)
1795 {
1796         ModestHeaderViewPrivate *priv = NULL;
1797         TnyHeaderFlags flags;
1798         TnyHeader *header = NULL;
1799         guint i;
1800         gchar *id = NULL;
1801         gboolean visible = TRUE;
1802         gboolean found = FALSE;
1803         
1804         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1805         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1806
1807         /* Get header from model */
1808         gtk_tree_model_get (model, iter,
1809                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1810                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1811                             -1);
1812         
1813         /* Hide mark as deleted heders */
1814         if (flags & TNY_HEADER_FLAG_DELETED) {
1815                 visible = FALSE;
1816                 goto frees;
1817         }
1818
1819         /* If no data on clipboard, return always TRUE */
1820         if (modest_email_clipboard_cleared(priv->clipboard)) {
1821                 visible = TRUE;
1822                 goto frees;
1823         }               
1824
1825         /* Get message id from header (ensure is a valid id) */
1826         if (!header) return FALSE;
1827         id = g_strdup(tny_header_get_message_id (header));
1828         
1829         /* Check hiding */
1830         if (priv->hidding_ids != NULL) {
1831                 for (i=0; i < priv->n_selected && !found; i++)
1832                         if (priv->hidding_ids[i] != NULL && id != NULL)
1833                                 found = (!strcmp (priv->hidding_ids[i], id));
1834         
1835                 visible = !found;
1836         }
1837
1838  frees:
1839         priv->status = ((gboolean) priv->status) && !visible;
1840         
1841         /* Free */
1842         if (header)
1843                 g_object_unref (header);
1844         g_free(id);
1845
1846         return visible;
1847 }
1848
1849 static void
1850 _clear_hidding_filter (ModestHeaderView *header_view) 
1851 {
1852         ModestHeaderViewPrivate *priv = NULL;
1853         guint i;
1854         
1855         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
1856         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1857
1858         if (priv->hidding_ids != NULL) {
1859                 for (i=0; i < priv->n_selected; i++) 
1860                         g_free (priv->hidding_ids[i]);
1861                 g_free(priv->hidding_ids);
1862         }       
1863 }
1864
1865 void 
1866 modest_header_view_refilter (ModestHeaderView *header_view)
1867 {
1868         GtkTreeModel *model = NULL;
1869         ModestHeaderViewPrivate *priv = NULL;
1870
1871         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1872         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1873
1874         /* Hide cut headers */
1875         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1876         if (GTK_IS_TREE_MODEL_FILTER (model)) {
1877                 priv->status = 0;
1878                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1879         }
1880 }
1881
1882 /* 
1883  * Called when an account is removed. If I'm showing a folder of the
1884  * account that has been removed then clear the view
1885  */
1886 static void
1887 on_account_removed (TnyAccountStore *self, 
1888                     TnyAccount *account,
1889                     gpointer user_data)
1890 {
1891         ModestHeaderViewPrivate *priv = NULL;
1892
1893         /* Ignore changes in transport accounts */
1894         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1895                 return;
1896
1897         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1898
1899         if (priv->folder) {
1900                 TnyAccount *my_account;
1901
1902                 my_account = tny_folder_get_account (priv->folder);
1903                 if (my_account == account)
1904                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1905                 g_object_unref (account);
1906         }
1907 }
1908
1909 void modest_header_view_add_observer(
1910                 ModestHeaderView *header_view,
1911                 ModestHeaderViewObserver *observer)
1912 {
1913         ModestHeaderViewPrivate *priv = NULL;
1914
1915         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1916         g_assert(observer != NULL);
1917         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1918
1919         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1920
1921         g_mutex_lock(priv->observer_list_lock);
1922         priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1923         g_mutex_unlock(priv->observer_list_lock);
1924 }
1925
1926 void 
1927 modest_header_view_remove_observer(ModestHeaderView *header_view,
1928                                    ModestHeaderViewObserver *observer)
1929 {
1930         ModestHeaderViewPrivate *priv = NULL;
1931
1932         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1933         g_assert(observer != NULL);
1934         g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1935
1936         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1937
1938         g_mutex_lock(priv->observer_list_lock);
1939         priv->observer_list = g_slist_remove(priv->observer_list, observer);
1940         g_mutex_unlock(priv->observer_list_lock);
1941 }
1942
1943 static void 
1944 modest_header_view_notify_observers(ModestHeaderView *header_view,
1945                                     GtkTreeModel *model,
1946                                     const gchar *tny_folder_id)
1947 {
1948         ModestHeaderViewPrivate *priv = NULL;
1949         GSList *iter;
1950         ModestHeaderViewObserver *observer;
1951
1952         g_assert(MODEST_IS_HEADER_VIEW(header_view));
1953
1954         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1955
1956         g_mutex_lock(priv->observer_list_lock);
1957         iter = priv->observer_list;
1958         while(iter != NULL){
1959                 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1960                 modest_header_view_observer_update(observer, model,
1961                                 tny_folder_id);
1962                 iter = g_slist_next(iter);
1963         }
1964         g_mutex_unlock(priv->observer_list_lock);
1965 }
1966