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