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