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