934fc37de5b7501a5415d19d4bebbe85c2c12474
[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         /* FIXME: 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 #ifdef MODEST_PLATFORM_MAEMO
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 #ifdef MODEST_PLATFORM_MAEMO
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
548         return TRUE;
549 }
550
551 static void
552 modest_header_view_init (ModestHeaderView *obj)
553 {
554         ModestHeaderViewPrivate *priv;
555         guint i, j;
556
557         priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj); 
558
559         priv->folder  = NULL;
560
561         priv->monitor        = NULL;
562         priv->observers_lock = g_mutex_new ();
563         priv->autoselect_reference = NULL;
564
565         priv->status  = HEADER_VIEW_INIT;
566         priv->status_timeout = 0;
567         priv->notify_status = TRUE;
568
569         priv->observer_list_lock = g_mutex_new();
570         priv->observer_list = NULL;
571
572         priv->clipboard = modest_runtime_get_email_clipboard ();
573         priv->hidding_ids = NULL;
574         priv->n_selected = 0;
575         priv->selection_changed_handler = 0;
576         priv->acc_removed_handler = 0;
577
578         /* Sort parameters */
579         for (j=0; j < 2; j++) {
580                 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
581                         priv->sort_colid[j][i] = -1;
582                         priv->sort_type[j][i] = GTK_SORT_DESCENDING;
583                 }                       
584         }
585
586         setup_drag_and_drop (GTK_WIDGET(obj));
587 }
588
589 static void
590 modest_header_view_dispose (GObject *obj)
591 {
592         ModestHeaderView        *self;
593         ModestHeaderViewPrivate *priv;
594         GtkTreeSelection *sel;
595         
596         self = MODEST_HEADER_VIEW(obj);
597         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
598
599         /* Free in the dispose to avoid unref cycles */
600         if (priv->folder) {
601                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
602                 g_object_unref (G_OBJECT (priv->folder));
603                 priv->folder = NULL;
604         }
605
606         /* We need to do this here in the dispose because the
607            selection won't exist when finalizing */
608         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
609         if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
610                 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
611                 priv->selection_changed_handler = 0;
612         }
613
614         G_OBJECT_CLASS(parent_class)->dispose (obj);
615 }
616
617 static void
618 modest_header_view_finalize (GObject *obj)
619 {
620         ModestHeaderView        *self;
621         ModestHeaderViewPrivate *priv;
622         
623         self = MODEST_HEADER_VIEW(obj);
624         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
625
626         if (g_signal_handler_is_connected (modest_runtime_get_account_store (), 
627                                            priv->acc_removed_handler)) {
628                 g_signal_handler_disconnect (modest_runtime_get_account_store (), 
629                                              priv->acc_removed_handler);
630         }
631
632         /* There is no need to lock because there should not be any
633          * reference to self now. */
634         g_mutex_free(priv->observer_list_lock);
635         g_slist_free(priv->observer_list);
636
637         g_mutex_lock (priv->observers_lock);
638         if (priv->monitor) {
639                 tny_folder_monitor_stop (priv->monitor);
640                 g_object_unref (G_OBJECT (priv->monitor));
641         }
642         g_mutex_unlock (priv->observers_lock);
643         g_mutex_free (priv->observers_lock);
644
645         /* Clear hidding array created by cut operation */
646         _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
647
648         if (priv->autoselect_reference != NULL) {
649                 gtk_tree_row_reference_free (priv->autoselect_reference);
650                 priv->autoselect_reference = NULL;
651         }
652
653         G_OBJECT_CLASS(parent_class)->finalize (obj);
654 }
655
656
657 GtkWidget*
658 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
659 {
660         GObject *obj;
661         GtkTreeSelection *sel;
662         ModestHeaderView *self;
663         ModestHeaderViewPrivate *priv;
664         
665         g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
666                               NULL);
667         
668         obj  = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
669         self = MODEST_HEADER_VIEW(obj);
670         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
671         
672         modest_header_view_set_style   (self, style);
673
674         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
675         gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
676         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
677         
678         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
679                                       TRUE); /* alternating row colors */
680
681         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self)); 
682         priv->selection_changed_handler =
683                 g_signal_connect_after (sel, "changed",
684                                         G_CALLBACK(on_selection_changed), self);
685         
686         g_signal_connect (self, "row-activated",
687                           G_CALLBACK (on_header_row_activated), NULL);
688
689         g_signal_connect (self, "focus-in-event",
690                           G_CALLBACK(on_focus_in), NULL);
691         g_signal_connect (self, "focus-out-event",
692                           G_CALLBACK(on_focus_out), NULL);
693
694         g_signal_connect (self, "button-press-event",
695                           G_CALLBACK(on_button_press_event), NULL);
696         g_signal_connect (self, "button-release-event",
697                           G_CALLBACK(on_button_release_event), NULL);
698         
699         priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
700                                                       "account_removed",
701                                                       G_CALLBACK (on_account_removed),
702                                                       self);
703
704         g_signal_connect (self, "expose-event",
705                         G_CALLBACK(modest_header_view_on_expose_event),
706                         NULL);
707
708         return GTK_WIDGET(self);
709 }
710
711
712 guint
713 modest_header_view_count_selected_headers (ModestHeaderView *self)
714 {
715         GtkTreeSelection *sel;
716         guint selected_rows;
717
718         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
719         
720         /* Get selection object and check selected rows count */
721         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
722         selected_rows = gtk_tree_selection_count_selected_rows (sel);
723         
724         return selected_rows;
725 }
726
727 gboolean
728 modest_header_view_has_selected_headers (ModestHeaderView *self)
729 {
730         GtkTreeSelection *sel;
731         gboolean empty;
732         
733         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
734         
735         /* Get selection object and check selected rows count */
736         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
737         empty = gtk_tree_selection_count_selected_rows (sel) == 0;
738         
739         return !empty;
740 }
741
742
743 TnyList * 
744 modest_header_view_get_selected_headers (ModestHeaderView *self)
745 {
746         GtkTreeSelection *sel;
747         ModestHeaderViewPrivate *priv;
748         TnyList *header_list = NULL;
749         TnyHeader *header;
750         GList *list, *tmp = NULL;
751         GtkTreeModel *tree_model = NULL;
752         GtkTreeIter iter;
753
754         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
755         
756         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
757
758         /* Get selected rows */
759         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
760         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
761
762         if (list) {
763                 header_list = tny_simple_list_new();
764
765                 list = g_list_reverse (list);
766                 tmp = list;
767                 while (tmp) {                   
768                         /* get header from selection */
769                         gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
770                         gtk_tree_model_get (tree_model, &iter,
771                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
772                                             &header, -1);
773                         /* Prepend to list */
774                         tny_list_prepend (header_list, G_OBJECT (header));
775                         g_object_unref (G_OBJECT (header));
776
777                         tmp = g_list_next (tmp);
778                 }
779                 /* Clean up*/
780                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
781                 g_list_free (list);
782         }
783         return header_list;
784 }
785
786
787 /* scroll our list view so the selected item is visible */
788 static void
789 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
790 {
791 #ifdef MODEST_PLATFORM_GNOME 
792
793         GtkTreePath *selected_path;
794         GtkTreePath *start, *end;
795         
796         GtkTreeModel *model;
797         
798         model         = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
799         selected_path = gtk_tree_model_get_path (model, iter);
800
801         start = gtk_tree_path_new ();
802         end   = gtk_tree_path_new ();
803
804         gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
805
806         if (gtk_tree_path_compare (selected_path, start) < 0 ||
807             gtk_tree_path_compare (end, selected_path) < 0)
808                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
809                                               selected_path, NULL, TRUE,
810                                               up ? 0.0 : 1.0,
811                                               up ? 0.0 : 1.0);
812         gtk_tree_path_free (selected_path);
813         gtk_tree_path_free (start);
814         gtk_tree_path_free (end);
815
816 #endif /* MODEST_PLATFORM_GNOME */
817 }
818
819
820 void 
821 modest_header_view_select_next (ModestHeaderView *self)
822 {
823         GtkTreeSelection *sel;
824         GtkTreeIter iter;
825         GtkTreeModel *model;
826         GtkTreePath *path;
827
828         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
829
830         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
831         path = get_selected_row (GTK_TREE_VIEW(self), &model);
832         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
833                 /* Unselect previous path */
834                 gtk_tree_selection_unselect_path (sel, path);
835                 
836                 /* Move path down and selects new one  */
837                 if (gtk_tree_model_iter_next (model, &iter)) {
838                         gtk_tree_selection_select_iter (sel, &iter);
839                         scroll_to_selected (self, &iter, FALSE);        
840                 }
841                 gtk_tree_path_free(path);
842         }
843         
844 }
845
846 void 
847 modest_header_view_select_prev (ModestHeaderView *self)
848 {
849         GtkTreeSelection *sel;
850         GtkTreeIter iter;
851         GtkTreeModel *model;
852         GtkTreePath *path;
853
854         g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
855
856         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
857         path = get_selected_row (GTK_TREE_VIEW(self), &model);
858         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
859                 /* Unselect previous path */
860                 gtk_tree_selection_unselect_path (sel, path);
861
862                 /* Move path up */
863                 if (gtk_tree_path_prev (path)) {
864                         gtk_tree_model_get_iter (model, &iter, path);
865                         
866                         /* Select the new one */
867                         gtk_tree_selection_select_iter (sel, &iter);
868                         scroll_to_selected (self, &iter, TRUE); 
869
870                 }
871                 gtk_tree_path_free (path);
872         }
873 }
874
875 GList*
876 modest_header_view_get_columns (ModestHeaderView *self)
877 {       
878         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
879         
880         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self)); 
881 }
882
883
884
885 gboolean
886 modest_header_view_set_style (ModestHeaderView *self,
887                               ModestHeaderViewStyle style)
888 {
889         ModestHeaderViewPrivate *priv;
890         gboolean show_col_headers = FALSE;
891         ModestHeaderViewStyle old_style;
892         
893         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
894         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
895                               FALSE);
896         
897         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
898         if (priv->style == style)
899                 return TRUE; /* nothing to do */
900         
901         switch (style) {
902         case MODEST_HEADER_VIEW_STYLE_DETAILS:
903                 show_col_headers = TRUE;
904                 break;
905         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
906                 break;
907         default:
908                 g_return_val_if_reached (FALSE);
909         }
910         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
911         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);    
912
913         old_style   = priv->style;
914         priv->style = style;
915
916         return TRUE;
917 }
918
919
920 ModestHeaderViewStyle
921 modest_header_view_get_style (ModestHeaderView *self)
922 {
923         g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
924
925         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
926 }
927
928 /* This is used to automatically select the first header if the user
929  * has not selected any header yet.
930  */
931 static gboolean 
932 modest_header_view_on_expose_event(GtkTreeView *header_view,
933                                    GdkEventExpose *event,
934                                    gpointer user_data)
935 {
936         GtkTreeSelection *sel;
937         GtkTreeModel *model;
938         GtkTreeIter tree_iter;
939         ModestHeaderViewPrivate *priv;
940
941         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
942         model = gtk_tree_view_get_model(header_view);
943
944         if (!model)
945                 return FALSE;
946
947         sel = gtk_tree_view_get_selection(header_view);
948         if(!gtk_tree_selection_count_selected_rows(sel)) {
949                 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
950                         GtkTreePath *tree_iter_path;
951                         /* Prevent the widget from getting the focus
952                            when selecting the first item */
953                         tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
954                         g_object_set(header_view, "can-focus", FALSE, NULL);
955                         gtk_tree_selection_select_iter(sel, &tree_iter);
956                         gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
957                         g_object_set(header_view, "can-focus", TRUE, NULL);
958                         if (priv->autoselect_reference) {
959                                 gtk_tree_row_reference_free (priv->autoselect_reference);
960                         }
961                         priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
962                         gtk_tree_path_free (tree_iter_path);
963                 }
964         } else {
965                 if (priv->autoselect_reference != NULL) {
966                         gboolean moved_selection = FALSE;
967                         GtkTreePath * last_path;
968                         if (gtk_tree_selection_count_selected_rows (sel) != 1) {
969                                 moved_selection = TRUE;
970                         } else {
971                                 GList *rows;
972
973                                 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
974                                 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
975                                 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
976                                         moved_selection = TRUE;
977                                 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
978                                 g_list_free (rows);
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