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