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