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