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