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