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