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