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