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