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