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