* modest-header-view.c: Remove some commented and unsed code.
[modest] / src / widgets / modest-header-view.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <glib/gi18n.h>
31 #include <tny-list.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <string.h>
36
37 #include <modest-header-view.h>
38 #include <modest-header-view-priv.h>
39 #include <modest-dnd.h>
40 #include <modest-tny-folder.h>
41
42 #include <modest-main-window.h>
43
44 #include <modest-marshal.h>
45 #include <modest-text-utils.h>
46 #include <modest-icon-names.h>
47 #include <modest-runtime.h>
48 #include "modest-platform.h"
49 #include <modest-hbox-cell-renderer.h>
50 #include <modest-vbox-cell-renderer.h>
51
52 static void modest_header_view_class_init  (ModestHeaderViewClass *klass);
53 static void modest_header_view_init        (ModestHeaderView *obj);
54 static void modest_header_view_finalize    (GObject *obj);
55 static void modest_header_view_dispose     (GObject *obj);
56
57 static void          on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
58                                               GtkTreeViewColumn *column, gpointer userdata);
59
60 static gint          cmp_rows               (GtkTreeModel *tree_model,
61                                              GtkTreeIter *iter1,
62                                              GtkTreeIter *iter2,
63                                              gpointer user_data);
64
65 static gint          cmp_subject_rows       (GtkTreeModel *tree_model,
66                                              GtkTreeIter *iter1,
67                                              GtkTreeIter *iter2,
68                                              gpointer user_data);
69
70 static gboolean     filter_row             (GtkTreeModel *model,
71                                             GtkTreeIter *iter,
72                                             gpointer data);
73
74 static void         on_account_removed     (TnyAccountStore *self, 
75                                             TnyAccount *account,
76                                             gpointer user_data);
77
78 static void          on_selection_changed   (GtkTreeSelection *sel,
79                                              gpointer user_data);
80
81 static void          setup_drag_and_drop    (GtkTreeView *self);
82
83 static GtkTreePath * get_selected_row       (GtkTreeView *self, GtkTreeModel **model);
84
85 static gboolean      on_focus_in            (GtkWidget     *sef,
86                                              GdkEventFocus *event,
87                                              gpointer       user_data);
88
89 static void          folder_monitor_update  (TnyFolderObserver *self, 
90                                              TnyFolderChange *change);
91
92 static void          tny_folder_observer_init (TnyFolderObserverIface *klass);
93
94 static void          _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
95
96 static void          _clear_hidding_filter (ModestHeaderView *header_view);
97
98
99 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
100 struct _ModestHeaderViewPrivate {
101         TnyFolder            *folder;
102         ModestHeaderViewStyle style;
103
104         TnyFolderMonitor     *monitor;
105         GMutex               *observers_lock;
106
107         /* not unref this object, its a singlenton */
108         ModestEmailClipboard *clipboard;
109
110         /* Filter tree model */
111         gchar **hidding_ids;
112         guint   n_selected;
113
114         gint    sort_colid[2][TNY_FOLDER_TYPE_NUM];
115         gint    sort_type[2][TNY_FOLDER_TYPE_NUM];
116
117         gulong  selection_changed_handler;
118         gulong  acc_removed_handler;
119 };
120
121 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
122 struct _HeadersCountChangedHelper {
123         ModestHeaderView *self;
124         TnyFolderChange  *change;       
125 };
126
127
128 #define MODEST_HEADER_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
129                                                 MODEST_TYPE_HEADER_VIEW, \
130                                                 ModestHeaderViewPrivate))
131
132
133
134 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
135
136 enum {
137         HEADER_SELECTED_SIGNAL,
138         HEADER_ACTIVATED_SIGNAL,
139         ITEM_NOT_FOUND_SIGNAL,
140         MSG_COUNT_CHANGED_SIGNAL,
141         LAST_SIGNAL
142 };
143
144 /* globals */
145 static GObjectClass *parent_class = NULL;
146
147 /* uncomment the following if you have defined any signals */
148 static guint signals[LAST_SIGNAL] = {0};
149
150 GType
151 modest_header_view_get_type (void)
152 {
153         static GType my_type = 0;
154         if (!my_type) {
155                 static const GTypeInfo my_info = {
156                         sizeof(ModestHeaderViewClass),
157                         NULL,           /* base init */
158                         NULL,           /* base finalize */
159                         (GClassInitFunc) modest_header_view_class_init,
160                         NULL,           /* class finalize */
161                         NULL,           /* class data */
162                         sizeof(ModestHeaderView),
163                         1,              /* n_preallocs */
164                         (GInstanceInitFunc) modest_header_view_init,
165                         NULL
166                 };
167
168                 static const GInterfaceInfo tny_folder_observer_info = 
169                 {
170                         (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
171                         NULL,         /* interface_finalize */
172                         NULL          /* interface_data */
173                 };
174                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
175                                                   "ModestHeaderView",
176                                                   &my_info, 0);
177
178                 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
179                                              &tny_folder_observer_info);
180
181
182         }
183         return my_type;
184 }
185
186 static void
187 modest_header_view_class_init (ModestHeaderViewClass *klass)
188 {
189         GObjectClass *gobject_class;
190         gobject_class = (GObjectClass*) klass;
191
192         parent_class            = g_type_class_peek_parent (klass);
193         gobject_class->finalize = modest_header_view_finalize;
194         gobject_class->dispose = modest_header_view_dispose;
195         
196         g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
197         
198         signals[HEADER_SELECTED_SIGNAL] = 
199                 g_signal_new ("header_selected",
200                               G_TYPE_FROM_CLASS (gobject_class),
201                               G_SIGNAL_RUN_FIRST,
202                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
203                               NULL, NULL,
204                               g_cclosure_marshal_VOID__POINTER,
205                               G_TYPE_NONE, 1, G_TYPE_POINTER);
206
207         signals[HEADER_ACTIVATED_SIGNAL] = 
208                 g_signal_new ("header_activated",
209                               G_TYPE_FROM_CLASS (gobject_class),
210                               G_SIGNAL_RUN_FIRST,
211                               G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
212                               NULL, NULL,
213                               g_cclosure_marshal_VOID__POINTER,
214                               G_TYPE_NONE, 1, G_TYPE_POINTER);
215         
216         
217         signals[ITEM_NOT_FOUND_SIGNAL] = 
218                 g_signal_new ("item_not_found",
219                               G_TYPE_FROM_CLASS (gobject_class),
220                               G_SIGNAL_RUN_FIRST,
221                               G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
222                               NULL, NULL,
223                               g_cclosure_marshal_VOID__INT,
224                               G_TYPE_NONE, 1, G_TYPE_INT);
225
226         signals[MSG_COUNT_CHANGED_SIGNAL] =
227                 g_signal_new ("msg_count_changed",
228                               G_TYPE_FROM_CLASS (gobject_class),
229                               G_SIGNAL_RUN_FIRST,
230                               G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
231                               NULL, NULL,
232                               modest_marshal_VOID__POINTER_POINTER,
233                               G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
234 }
235
236 static void
237 tny_folder_observer_init (TnyFolderObserverIface *klass)
238 {
239         klass->update_func = folder_monitor_update;
240 }
241
242 static GtkTreeViewColumn*
243 get_new_column (const gchar *name, GtkCellRenderer *renderer,
244                 gboolean resizable, gint sort_col_id, gboolean show_as_text,
245                 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
246 {
247         GtkTreeViewColumn *column;
248
249         column =  gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
250         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
251
252         gtk_tree_view_column_set_resizable (column, resizable);
253         if (resizable) 
254                 gtk_tree_view_column_set_expand (column, TRUE);
255         
256         if (show_as_text)
257                 gtk_tree_view_column_add_attribute (column, renderer, "text",
258                                                     sort_col_id);
259         if (sort_col_id >= 0)
260                 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
261
262         gtk_tree_view_column_set_sort_indicator (column, FALSE);
263         gtk_tree_view_column_set_reorderable (column, TRUE);
264         
265         if (cell_data_func)
266                 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
267                                                         user_data, NULL);
268         return column;
269 }
270
271
272 static void
273 remove_all_columns (ModestHeaderView *obj)
274 {
275         GList *columns, *cursor;
276         
277         columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
278
279         for (cursor = columns; cursor; cursor = cursor->next)
280                 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
281                                              GTK_TREE_VIEW_COLUMN(cursor->data));
282         g_list_free (columns);  
283 }
284
285 gboolean
286 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
287 {
288         GtkTreeModel *tree_filter, *sortable;
289         GtkTreeViewColumn *column=NULL;
290         GtkTreeSelection *selection = NULL;
291         GtkCellRenderer *renderer_msgtype,*renderer_header,
292                 *renderer_attach, *renderer_compact_date_or_status;
293         GtkCellRenderer *renderer_compact_header, *renderer_recpt_box, 
294                 *renderer_subject, *renderer_subject_box, *renderer_recpt,
295                 *renderer_priority;
296         ModestHeaderViewPrivate *priv;
297         GtkTreeViewColumn *compact_column = NULL;
298         const GList *cursor;
299         
300         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self); 
301
302         /* FIXME: check whether these renderers need to be freed */
303         renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
304         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
305         renderer_priority  = gtk_cell_renderer_pixbuf_new ();
306         renderer_header  = gtk_cell_renderer_text_new ();
307
308         renderer_compact_header = modest_vbox_cell_renderer_new ();
309         renderer_recpt_box = modest_hbox_cell_renderer_new ();
310         renderer_subject_box = modest_hbox_cell_renderer_new ();
311         renderer_recpt = gtk_cell_renderer_text_new ();
312         renderer_subject = gtk_cell_renderer_text_new ();
313         renderer_compact_date_or_status  = gtk_cell_renderer_text_new ();
314
315         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
316         g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
317         modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
318         g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
319         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
320         g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
321         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
322         g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
323         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
324         g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
325         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
326         g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
327         modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
328         g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
329
330         g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
331         gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
332         gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
333         g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
334         g_object_set(G_OBJECT(renderer_header),
335                      "ellipsize", PANGO_ELLIPSIZE_END,
336                      NULL);
337         g_object_set (G_OBJECT (renderer_subject),
338                       "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
339                       NULL);
340         g_object_set (G_OBJECT (renderer_recpt),
341                       "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
342                       NULL);
343         g_object_set(G_OBJECT(renderer_compact_date_or_status),
344                      "xalign", 1.0, "yalign", 0.0,
345                      NULL);
346         g_object_set (G_OBJECT (renderer_priority),
347                       "yalign", 1.0, NULL);
348         g_object_set (G_OBJECT (renderer_attach),
349                       "yalign", 0.0, NULL);
350
351         gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
352         gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
353         gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
354         
355         remove_all_columns (self);
356
357         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
358         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
359         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
360         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
361
362         /* Add new columns */
363         for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
364                 ModestHeaderViewColumn col =
365                         (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
366                 
367                 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
368                         g_printerr ("modest: invalid column %d in column list\n", col);
369                         continue;
370                 }
371                 
372                 switch (col) {
373                         
374                 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
375                         column = get_new_column (_("M"), renderer_msgtype, FALSE,
376                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
377                                                  FALSE,
378                                                  (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
379                                                  NULL);
380                         gtk_tree_view_column_set_fixed_width (column, 45);
381                         break;
382
383                 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
384                         column = get_new_column (_("A"), renderer_attach, FALSE,
385                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
386                                                  FALSE,
387                                                  (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
388                                                  NULL);
389                         gtk_tree_view_column_set_fixed_width (column, 45);
390                         break;
391
392                         
393                 case MODEST_HEADER_VIEW_COLUMN_FROM:
394                         column = get_new_column (_("From"), renderer_header, TRUE,
395                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
396                                                  TRUE,
397                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
398                                                  GINT_TO_POINTER(TRUE));
399                         break;
400
401                 case MODEST_HEADER_VIEW_COLUMN_TO:
402                         column = get_new_column (_("To"), renderer_header, TRUE,
403                                                  TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
404                                                  TRUE,
405                                                  (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
406                                                  GINT_TO_POINTER(FALSE));
407                         break;
408                         
409                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
410                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
411                                                      TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
412                                                      FALSE,
413                                                      (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
414                                                      GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
415                         compact_column = column;
416                         break;
417
418                 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
419                         column = get_new_column (_("Header"), renderer_compact_header, TRUE,
420                                                  TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
421                                                  FALSE,
422                                                  (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
423                                                  GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
424                                                                  MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
425                                                                  MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
426                         compact_column = column;
427                         break;
428
429                         
430                 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
431                         column = get_new_column (_("Subject"), renderer_header, TRUE,
432                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
433                                                  TRUE,
434                                                  (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
435                                                  NULL);
436                         break;
437                         
438                 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
439                         column = get_new_column (_("Received"), renderer_header, TRUE,
440                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
441                                                  TRUE,
442                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
443                                                  GINT_TO_POINTER(TRUE));
444                         break;
445                         
446                 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:  
447                         column = get_new_column (_("Sent"), renderer_header, TRUE,
448                                                  TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
449                                                  TRUE,
450                                                  (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
451                                                  GINT_TO_POINTER(FALSE));
452                         break;
453                         
454                 case MODEST_HEADER_VIEW_COLUMN_SIZE:
455                         column = get_new_column (_("Size"), renderer_header, TRUE,
456                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
457                                                  FALSE,
458                                                  (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
459                                                  NULL); 
460                         break;
461                 case MODEST_HEADER_VIEW_COLUMN_STATUS:
462                         column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
463                                                  TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
464                                                  FALSE,
465                                                  (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
466                                                  NULL); 
467                         break;
468
469                 default:
470                         g_return_val_if_reached(FALSE);
471                 }
472
473                 /* we keep the column id around */
474                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
475                                    GINT_TO_POINTER(col));
476                 
477                 /* we need this ptr when sorting the rows */
478                 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
479                                    self);
480                 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);              
481         }               
482
483         if (sortable) {
484                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
485                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
486                                                  (GtkTreeIterCompareFunc) cmp_rows,
487                                                  compact_column, NULL);
488                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
489                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
490                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
491                                                  compact_column, NULL);
492         }
493
494
495         return TRUE;
496 }
497
498 static void
499 modest_header_view_init (ModestHeaderView *obj)
500 {
501         ModestHeaderViewPrivate *priv;
502         guint i, j;
503
504         priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj); 
505
506         priv->folder  = NULL;
507
508         priv->monitor        = NULL;
509         priv->observers_lock = g_mutex_new ();
510
511         priv->clipboard = modest_runtime_get_email_clipboard ();
512         priv->hidding_ids = NULL;
513         priv->n_selected = 0;
514         priv->selection_changed_handler = 0;
515         priv->acc_removed_handler = 0;
516
517         /* Sort parameters */
518         for (j=0; j < 2; j++) {
519                 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
520                         priv->sort_colid[j][i] = -1;
521                         priv->sort_type[j][i] = GTK_SORT_DESCENDING;
522                 }                       
523         }
524
525         setup_drag_and_drop (GTK_TREE_VIEW (obj));
526 }
527
528 static void
529 modest_header_view_dispose (GObject *obj)
530 {
531         ModestHeaderView        *self;
532         ModestHeaderViewPrivate *priv;
533         GtkTreeSelection *sel;
534         
535         self = MODEST_HEADER_VIEW(obj);
536         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
537
538         /* Free in the dispose to avoid unref cycles */
539         if (priv->folder) {
540                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
541                 g_object_unref (G_OBJECT (priv->folder));
542                 priv->folder = NULL;
543         }
544
545         /* We need to do this here in the dispose because the
546            selection won't exist when finalizing */
547         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
548         if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
549                 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
550                 priv->selection_changed_handler = 0;
551         }
552
553         G_OBJECT_CLASS(parent_class)->dispose (obj);
554 }
555
556 static void
557 modest_header_view_finalize (GObject *obj)
558 {
559         ModestHeaderView        *self;
560         ModestHeaderViewPrivate *priv;
561         
562         self = MODEST_HEADER_VIEW(obj);
563         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
564
565         if (g_signal_handler_is_connected (modest_runtime_get_account_store (), 
566                                            priv->acc_removed_handler)) {
567                 g_signal_handler_disconnect (modest_runtime_get_account_store (), 
568                                              priv->acc_removed_handler);
569         }
570
571         g_mutex_lock (priv->observers_lock);
572         if (priv->monitor) {
573                 tny_folder_monitor_stop (priv->monitor);
574                 g_object_unref (G_OBJECT (priv->monitor));
575         }
576         g_mutex_unlock (priv->observers_lock);
577         g_mutex_free (priv->observers_lock);
578
579         /* Clear hidding array created by cut operation */
580         _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
581
582         G_OBJECT_CLASS(parent_class)->finalize (obj);
583 }
584
585
586 GtkWidget*
587 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
588 {
589         GObject *obj;
590         GtkTreeSelection *sel;
591         ModestHeaderView *self;
592         ModestHeaderViewPrivate *priv;
593         
594         g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
595                               NULL);
596         
597         obj  = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
598         self = MODEST_HEADER_VIEW(obj);
599         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
600         
601         modest_header_view_set_style   (self, style);
602 /*      modest_header_view_set_folder (self, NULL, NULL, NULL); */
603
604         gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
605         gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
606         gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
607         
608         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
609                                       TRUE); /* alternating row colors */
610
611         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self)); 
612         priv->selection_changed_handler =
613                 g_signal_connect_after (sel, "changed",
614                                         G_CALLBACK(on_selection_changed), self);
615         
616         g_signal_connect (self, "row-activated",
617                           G_CALLBACK (on_header_row_activated), NULL);
618
619         g_signal_connect (self, "focus-in-event",
620                           G_CALLBACK(on_focus_in), NULL);
621         
622         priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
623                                                       "account_removed",
624                                                       G_CALLBACK (on_account_removed),
625                                                       self);
626
627         return GTK_WIDGET(self);
628 }
629
630
631 guint
632 modest_header_view_count_selected_headers (ModestHeaderView *self)
633 {
634         GtkTreeSelection *sel;
635         guint selected_rows;
636
637         g_return_val_if_fail (self, 0);
638         
639         /* Get selection object and check selected rows count */
640         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
641         selected_rows = gtk_tree_selection_count_selected_rows (sel);
642         
643         return selected_rows;
644 }
645
646 gboolean
647 modest_header_view_has_selected_headers (ModestHeaderView *self)
648 {
649         GtkTreeSelection *sel;
650         gboolean empty;
651
652         g_return_val_if_fail (self, FALSE);
653         
654         /* Get selection object and check selected rows count */
655         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
656         empty = gtk_tree_selection_count_selected_rows (sel) == 0;
657         
658         return !empty;
659 }
660
661
662 TnyList * 
663 modest_header_view_get_selected_headers (ModestHeaderView *self)
664 {
665         GtkTreeSelection *sel;
666         ModestHeaderViewPrivate *priv;
667         TnyList *header_list = NULL;
668         TnyHeader *header;
669         GList *list, *tmp = NULL;
670         GtkTreeModel *tree_model = NULL;
671         GtkTreeIter iter;
672
673         g_return_val_if_fail (self, NULL);
674         
675         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
676
677         /* Get selected rows */
678         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
679         list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
680
681         if (list) {
682                 header_list = tny_simple_list_new();
683
684                 list = g_list_reverse (list);
685                 tmp = list;
686                 while (tmp) {                   
687                         /* get header from selection */
688                         gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
689                         gtk_tree_model_get (tree_model, &iter,
690                                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
691                                             &header, -1);
692                         /* Prepend to list */
693                         tny_list_prepend (header_list, G_OBJECT (header));
694                         g_object_unref (G_OBJECT (header));
695
696                         tmp = g_list_next (tmp);
697                 }
698                 /* Clean up*/
699                 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
700                 g_list_free (list);
701         }
702         return header_list;
703 }
704
705
706 /* scroll our list view so the selected item is visible */
707 static void
708 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
709 {
710 #ifdef MODEST_PLATFORM_GNOME 
711
712         GtkTreePath *selected_path;
713         GtkTreePath *start, *end;
714         
715         GtkTreeModel *model;
716         
717         model         = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
718         selected_path = gtk_tree_model_get_path (model, iter);
719
720         start = gtk_tree_path_new ();
721         end   = gtk_tree_path_new ();
722
723         gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
724
725         if (gtk_tree_path_compare (selected_path, start) < 0 ||
726             gtk_tree_path_compare (end, selected_path) < 0)
727                 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
728                                               selected_path, NULL, TRUE,
729                                               up ? 0.0 : 1.0,
730                                               up ? 0.0 : 1.0);
731         gtk_tree_path_free (selected_path);
732         gtk_tree_path_free (start);
733         gtk_tree_path_free (end);
734
735 #endif /* MODEST_PLATFORM_GNOME */
736 }
737
738
739 void 
740 modest_header_view_select_next (ModestHeaderView *self)
741 {
742         GtkTreeSelection *sel;
743         GtkTreeIter iter;
744         GtkTreeModel *model;
745         GtkTreePath *path;
746
747         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
748         path = get_selected_row (GTK_TREE_VIEW(self), &model);
749         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
750                 /* Unselect previous path */
751                 gtk_tree_selection_unselect_path (sel, path);
752                 
753                 /* Move path down and selects new one  */
754                 if (gtk_tree_model_iter_next (model, &iter)) {
755                         gtk_tree_selection_select_iter (sel, &iter);
756                         scroll_to_selected (self, &iter, FALSE);        
757                 }
758                 gtk_tree_path_free(path);
759         }
760         
761 }
762
763 void 
764 modest_header_view_select_prev (ModestHeaderView *self)
765 {
766         GtkTreeSelection *sel;
767         GtkTreeIter iter;
768         GtkTreeModel *model;
769         GtkTreePath *path;
770
771         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
772         path = get_selected_row (GTK_TREE_VIEW(self), &model);
773         if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
774                 /* Unselect previous path */
775                 gtk_tree_selection_unselect_path (sel, path);
776
777                 /* Move path up */
778                 if (gtk_tree_path_prev (path)) {
779                         gtk_tree_model_get_iter (model, &iter, path);
780                         
781                         /* Select the new one */
782                         gtk_tree_selection_select_iter (sel, &iter);
783                         scroll_to_selected (self, &iter, TRUE); 
784
785                 }
786                 gtk_tree_path_free (path);
787         }
788 }
789
790 GList*
791 modest_header_view_get_columns (ModestHeaderView *self)
792 {
793         g_return_val_if_fail (self, FALSE);
794         return gtk_tree_view_get_columns (GTK_TREE_VIEW(self)); 
795 }
796
797
798 gboolean
799 modest_header_view_is_empty (ModestHeaderView *self)
800 {
801         g_return_val_if_fail (self, FALSE);
802         return FALSE; /* FIXME */
803 }
804
805
806 gboolean
807 modest_header_view_set_style (ModestHeaderView *self,
808                               ModestHeaderViewStyle style)
809 {
810         ModestHeaderViewPrivate *priv;
811         gboolean show_col_headers = FALSE;
812         ModestHeaderViewStyle old_style;
813         
814         g_return_val_if_fail (self, FALSE);
815         g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
816                               FALSE);
817
818         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
819         if (priv->style == style)
820                 return TRUE; /* nothing to do */
821         
822         switch (style) {
823         case MODEST_HEADER_VIEW_STYLE_DETAILS:
824                 show_col_headers = TRUE;
825                 break;
826         case MODEST_HEADER_VIEW_STYLE_TWOLINES:
827                 break;
828         default:
829                 g_return_val_if_reached (FALSE);
830         }
831         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(self), show_col_headers);
832         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);    
833
834         old_style   = priv->style;
835         priv->style = style;
836
837         return TRUE;
838 }
839
840
841 ModestHeaderViewStyle
842 modest_header_view_get_style (ModestHeaderView *self)
843 {
844         g_return_val_if_fail (self, FALSE);
845         return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
846 }
847
848 /* 
849  * This function sets a sortable model in the header view. It's just
850  * used for developing purposes, because it only does a
851  * gtk_tree_view_set_model
852  */
853 static void
854 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
855 {
856 /*      GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
857 /*      if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
858 /*              GtkTreeModel *old_model; */
859 /*              ModestHeaderViewPrivate *priv; */
860 /*              priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
861 /*              old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
862
863 /*              /\* Set new model *\/ */
864 /*              gtk_tree_view_set_model (header_view, model); */
865 /*      } else */
866         gtk_tree_view_set_model (header_view, model);
867 }
868
869 TnyFolder*
870 modest_header_view_get_folder (ModestHeaderView *self)
871 {
872         ModestHeaderViewPrivate *priv;
873         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
874
875         if (priv->folder)
876                 g_object_ref (priv->folder);
877
878         return priv->folder;
879 }
880
881 static void
882 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
883 {
884         TnyFolderType type;
885         TnyList *headers;
886         ModestHeaderViewPrivate *priv;
887         GList *cols, *cursor;
888         GtkTreeModel *filter_model, *sortable; 
889         guint sort_colid;
890         GtkSortType sort_type;
891
892         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
893
894         headers = TNY_LIST (tny_gtk_header_list_model_new ());
895
896         tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
897                                               folder, FALSE);
898
899         /* Add IDLE observer (monitor) and another folder observer for
900            new messages (self) */
901         g_mutex_lock (priv->observers_lock);
902         if (priv->monitor) {
903                 tny_folder_monitor_stop (priv->monitor);
904                 g_object_unref (G_OBJECT (priv->monitor));
905         }
906         priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
907         tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
908         tny_folder_monitor_start (priv->monitor);
909         g_mutex_unlock (priv->observers_lock);
910
911         sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
912         g_object_unref (G_OBJECT (headers));
913
914         /* Create a tree model filter to hide and show rows for cut operations  */
915         filter_model = gtk_tree_model_filter_new (sortable, NULL);
916         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
917                                                 filter_row,
918                                                 self,
919                                                 NULL);
920         g_object_unref (G_OBJECT (sortable));
921
922         /* install our special sorting functions */
923         cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
924
925         /* Restore sort column id */
926         if (cols) {
927                 type  = modest_tny_folder_guess_folder_type (folder);
928                 sort_colid = modest_header_view_get_sort_column_id (self, type); 
929                 sort_type = modest_header_view_get_sort_type (self, type); 
930                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
931                                                       sort_colid,
932                                                       sort_type);
933                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
934                                                  TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
935                                                  (GtkTreeIterCompareFunc) cmp_rows,
936                                                  cols->data, NULL);
937                 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
938                                                  TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
939                                                  (GtkTreeIterCompareFunc) cmp_subject_rows,
940                                                  cols->data, NULL);
941         }
942
943         /* Set new model */
944         modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
945         g_object_unref (G_OBJECT (filter_model));
946 /*      modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
947 /*      g_object_unref (G_OBJECT (sortable)); */
948
949         /* Free */
950         g_list_free (cols);
951 }
952
953 void
954 modest_header_view_sort_by_column_id (ModestHeaderView *self, 
955                                       guint sort_colid,
956                                       GtkSortType sort_type)
957 {
958         ModestHeaderViewPrivate *priv = NULL;
959         GtkTreeModel *tree_filter, *sortable = NULL; 
960         TnyFolderType type;
961
962         /* Get model and private data */
963         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);            
964         tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
965         sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
966 /*      sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
967         
968         /* Sort tree model */
969         type  = modest_tny_folder_guess_folder_type (priv->folder);
970         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
971                                               sort_colid,
972                                               sort_type);
973         /* Store new sort parameters */
974         modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
975
976         /* Save GConf parameters */
977 /*      modest_widget_memory_save (modest_runtime_get_conf(), */
978 /*                                 G_OBJECT(self), "header-view"); */
979         
980 }
981
982 void
983 modest_header_view_set_sort_params (ModestHeaderView *self, 
984                                     guint sort_colid, 
985                                     GtkSortType sort_type,
986                                     TnyFolderType type)
987 {
988         ModestHeaderViewPrivate *priv;
989         ModestHeaderViewStyle style;
990
991         style = modest_header_view_get_style   (self);
992         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
993
994         priv->sort_colid[style][type] = sort_colid;
995         priv->sort_type[style][type] = sort_type;
996 }
997
998 gint
999 modest_header_view_get_sort_column_id (ModestHeaderView *self, 
1000                                        TnyFolderType type)
1001 {
1002         ModestHeaderViewPrivate *priv;
1003         ModestHeaderViewStyle style;
1004
1005         style = modest_header_view_get_style   (self);
1006         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1007
1008         return priv->sort_colid[style][type];
1009 }
1010
1011 GtkSortType
1012 modest_header_view_get_sort_type (ModestHeaderView *self, 
1013                                   TnyFolderType type)
1014 {
1015         ModestHeaderViewPrivate *priv;
1016         ModestHeaderViewStyle style;
1017
1018         style = modest_header_view_get_style   (self);
1019         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1020
1021         return priv->sort_type[style][type];
1022 }
1023
1024 typedef struct {
1025         ModestHeaderView *header_view;
1026         RefreshAsyncUserCallback cb;
1027         gpointer user_data;
1028 } SetFolderHelper;
1029
1030 static void
1031 folder_refreshed_cb (ModestMailOperation *mail_op,
1032                      TnyFolder *folder,
1033                      gpointer user_data)
1034 {
1035         ModestHeaderViewPrivate *priv;
1036         SetFolderHelper *info;
1037  
1038         info = (SetFolderHelper*) user_data;
1039
1040         priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1041
1042         /* User callback */
1043         if (info->cb)
1044                 info->cb (mail_op, folder, info->user_data);
1045
1046         /* Start the folder count changes observer. We do not need it
1047            before the refresh. Note that the monitor could still be
1048            called for this refresh but now we know that the callback
1049            was previously called */
1050         g_mutex_lock (priv->observers_lock);
1051         tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1052         g_mutex_unlock (priv->observers_lock);
1053
1054         /* Frees */
1055         g_free (info);
1056 }
1057
1058 void
1059 modest_header_view_set_folder (ModestHeaderView *self, 
1060                                TnyFolder *folder,
1061                                RefreshAsyncUserCallback callback,
1062                                gpointer user_data)
1063 {
1064         ModestHeaderViewPrivate *priv;
1065         ModestWindowMgr *mgr = NULL;
1066         GObject *source = NULL;
1067         SetFolderHelper *info;
1068  
1069         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1070
1071         if (priv->folder) {
1072                 g_mutex_lock (priv->observers_lock);
1073                 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1074                 g_object_unref (priv->folder);
1075                 priv->folder = NULL;
1076                 g_mutex_unlock (priv->observers_lock);
1077         }
1078
1079         if (folder) {
1080                 ModestMailOperation *mail_op = NULL;
1081                 GtkTreeSelection *selection;
1082
1083                 /* Get main window to use it as source of mail operation */
1084                 mgr = modest_runtime_get_window_mgr ();
1085                 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1086
1087                 /* Set folder in the model */
1088                 modest_header_view_set_folder_intern (self, folder);
1089
1090                 /* Pick my reference. Nothing to do with the mail operation */
1091                 priv->folder = g_object_ref (folder);
1092
1093                 /* no message selected */
1094                 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1095
1096                 info = g_malloc0 (sizeof(SetFolderHelper));
1097                 info->header_view = self;
1098                 info->cb = callback;
1099                 info->user_data = user_data;
1100
1101                 /* bug 57631: Clear the selection if exists */
1102                 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1103                 gtk_tree_selection_unselect_all(selection);
1104
1105                 /* Create the mail operation (source will be the parent widget) */
1106                 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1107                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1108                                                  mail_op);
1109
1110                 /* Refresh the folder asynchronously */
1111                 modest_mail_operation_refresh_folder (mail_op,
1112                                                       folder,
1113                                                       folder_refreshed_cb,
1114                                                       info);
1115
1116                 /* Free */
1117                 g_object_unref (mail_op);
1118         } else {
1119                 g_mutex_lock (priv->observers_lock);
1120
1121                 if (priv->monitor) {
1122                         tny_folder_monitor_stop (priv->monitor);
1123                         g_object_unref (G_OBJECT (priv->monitor));
1124                         priv->monitor = NULL;
1125                 }
1126                 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL); 
1127
1128                 g_mutex_unlock (priv->observers_lock);
1129         }
1130 }
1131
1132 static void
1133 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1134                          GtkTreeViewColumn *column, gpointer userdata)
1135 {
1136         ModestHeaderView *self = NULL;
1137         ModestHeaderViewPrivate *priv = NULL;
1138         GtkTreeIter iter;
1139         GtkTreeModel *model = NULL;
1140         TnyHeader *header = NULL;
1141         TnyHeaderFlags flags;
1142
1143         self = MODEST_HEADER_VIEW (treeview);
1144         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1145
1146         model = gtk_tree_view_get_model (treeview);     
1147         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path))) 
1148                 goto frees;
1149
1150         /* get the first selected item */
1151         gtk_tree_model_get (model, &iter,
1152                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1153                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, 
1154                             -1);
1155
1156         /* Dont open DELETED messages */
1157         if (flags & TNY_HEADER_FLAG_DELETED) {
1158                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1159                 goto frees;
1160         }
1161
1162         /* Emit signal */
1163         g_signal_emit (G_OBJECT(self), 
1164                        signals[HEADER_ACTIVATED_SIGNAL], 
1165                        0, header);
1166
1167         /* Free */
1168  frees:
1169         if (header != NULL) 
1170                 g_object_unref (G_OBJECT (header));     
1171
1172 }
1173
1174 static void
1175 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1176 {
1177         GtkTreeModel *model;
1178         TnyHeader *header = NULL;
1179         GtkTreePath *path = NULL;       
1180         GtkTreeIter iter;
1181         ModestHeaderView *self;
1182         ModestHeaderViewPrivate *priv;
1183         GList *selected = NULL;
1184         
1185         g_return_if_fail (sel);
1186         g_return_if_fail (user_data);
1187         
1188         self = MODEST_HEADER_VIEW (user_data);
1189         priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);    
1190
1191         selected = gtk_tree_selection_get_selected_rows (sel, &model);
1192         if (selected != NULL) 
1193                 path = (GtkTreePath *) selected->data;
1194         if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1195                 return; /* msg was _un_selected */
1196
1197         gtk_tree_model_get (model, &iter,
1198                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1199                             &header, -1);
1200
1201         /* Emit signal */
1202         g_signal_emit (G_OBJECT(self), 
1203                        signals[HEADER_SELECTED_SIGNAL], 
1204                        0, header);
1205
1206         g_object_unref (G_OBJECT (header));
1207
1208         /* free all items in 'selected' */
1209         g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1210         g_list_free (selected);
1211 }
1212
1213
1214 /* PROTECTED method. It's useful when we want to force a given
1215    selection to reload a msg. For example if we have selected a header
1216    in offline mode, when Modest become online, we want to reload the
1217    message automatically without an user click over the header */
1218 void 
1219 _modest_header_view_change_selection (GtkTreeSelection *selection,
1220                                       gpointer user_data)
1221 {
1222         g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1223         g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1224
1225         on_selection_changed (selection, user_data);
1226 }
1227
1228 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1229 {
1230         p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1231         p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1232         if (p1 == 0) 
1233                 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1234         if (p2 == 0) 
1235                 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1236         return p1 - p2;
1237 }
1238
1239 static gint
1240 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1241           gpointer user_data)
1242 {
1243         gint col_id;
1244         gint t1, t2;
1245         gint val1, val2;
1246         gint cmp;
1247 /*      static int counter = 0; */
1248
1249         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1250 /*      col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1251         col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1252
1253         
1254         switch (col_id) {
1255         case TNY_HEADER_FLAG_ATTACHMENTS:
1256
1257                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1258                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1259                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1260                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1261
1262                 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1263                         (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1264
1265                 return cmp ? cmp : t1 - t2;
1266                 
1267         case TNY_HEADER_FLAG_PRIORITY:
1268                 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1269                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1270                 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1271                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1272
1273                 /* This is for making priority values respect the intuitive sort relationship 
1274                  * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1275                 cmp =  compare_priorities (val1, val2);
1276
1277                 return cmp ? cmp : t1 - t2;
1278
1279         default:
1280                 return &iter1 - &iter2; /* oughhhh  */
1281         }
1282 }
1283
1284 static gint
1285 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1286                   gpointer user_data)
1287 {
1288         gint t1, t2;
1289         gchar *val1, *val2;
1290         gint cmp;
1291 /*      static int counter = 0; */
1292
1293         g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1294
1295         gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1296                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1297         gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1298                             TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1299
1300         cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1301                                              val2 + modest_text_utils_get_subject_prefix_len(val2),
1302                                              TRUE);
1303         g_free (val1);
1304         g_free (val2);
1305         return cmp;
1306 }
1307
1308 /* Drag and drop stuff */
1309 static void
1310 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context, 
1311                   GtkSelectionData *selection_data, 
1312                   guint info,  guint time, gpointer data)
1313 {
1314         GtkTreeModel *model = NULL;
1315         GtkTreeIter iter;
1316         GtkTreePath *source_row = NULL;
1317         GtkTreeSelection *sel = NULL;   
1318         
1319         source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1320         if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1321
1322         switch (info) {
1323         case MODEST_HEADER_ROW:
1324                 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1325                 break;
1326         case MODEST_MSG: {
1327                 TnyHeader *hdr = NULL;
1328                 gtk_tree_model_get (model, &iter,
1329                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1330                                     -1);
1331                 if (hdr) {
1332                         g_object_unref (G_OBJECT(hdr));
1333                 }
1334                 break;
1335         }
1336         default:
1337                 g_message ("%s: default switch case.", __FUNCTION__);
1338         }
1339
1340         /* Set focus on next header */
1341         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1342         gtk_tree_path_next (source_row);
1343         gtk_tree_selection_select_path (sel, source_row);
1344
1345         gtk_tree_path_free (source_row);
1346 }
1347
1348 /* Header view drag types */
1349 const GtkTargetEntry header_view_drag_types[] = {
1350         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1351         { "text/uri-list",      0,                   MODEST_MSG }, 
1352 };
1353
1354 static void
1355 setup_drag_and_drop (GtkTreeView *self)
1356 {
1357         gtk_drag_source_set (GTK_WIDGET (self),
1358                              GDK_BUTTON1_MASK,
1359                              header_view_drag_types,
1360                              G_N_ELEMENTS (header_view_drag_types),
1361                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
1362
1363         g_signal_connect(G_OBJECT (self), "drag_data_get",
1364                          G_CALLBACK(drag_data_get_cb), NULL);
1365 }
1366
1367 static GtkTreePath *
1368 get_selected_row (GtkTreeView *self, GtkTreeModel **model) 
1369 {
1370         GtkTreePath *path = NULL;
1371         GtkTreeSelection *sel = NULL;   
1372         GList *rows = NULL;
1373
1374         sel   = gtk_tree_view_get_selection(self);
1375         rows = gtk_tree_selection_get_selected_rows (sel, model);
1376         
1377         if ((rows == NULL) || (g_list_length(rows) != 1))
1378                 goto frees;
1379
1380         path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1381         
1382
1383         /* Free */
1384  frees:
1385         g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1386         g_list_free(rows);
1387
1388         return path;
1389 }
1390
1391 /*
1392  * This function moves the tree view scroll to the current selected
1393  * row when the widget grabs the focus 
1394  */
1395 static gboolean 
1396 on_focus_in (GtkWidget     *self,
1397              GdkEventFocus *event,
1398              gpointer       user_data)
1399 {
1400         GtkTreeSelection *selection;
1401         GtkTreeModel *model;
1402         GList *selected = NULL;
1403         GtkTreePath *selected_path = NULL;
1404
1405         model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1406         if (!model)
1407                 return FALSE;
1408
1409         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1410         /* If none selected yet, pick the first one */
1411         if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1412                 GtkTreeIter iter;
1413                 GtkTreePath *path;
1414
1415                 /* Return if the model is empty */
1416                 if (!gtk_tree_model_get_iter_first (model, &iter))
1417                         return FALSE;
1418
1419                 path = gtk_tree_model_get_path (model, &iter);
1420                 gtk_tree_selection_select_path (selection, path);
1421                 gtk_tree_path_free (path);
1422         }
1423
1424         /* Need to get the all the rows because is selection multiple */
1425         selected = gtk_tree_selection_get_selected_rows (selection, &model);
1426         if (selected == NULL) return FALSE;
1427         selected_path = (GtkTreePath *) selected->data;
1428
1429         /* Check if we need to scroll */
1430         #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1431         GtkTreePath *start_path = NULL;
1432         GtkTreePath *end_path = NULL;
1433         if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1434                                              &start_path,
1435                                              &end_path)) {
1436
1437                 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1438                     (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1439
1440                         /* Scroll to first path */
1441                         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1442                                                       selected_path,
1443                                                       NULL,
1444                                                       TRUE,
1445                                                       0.5,
1446                                                       0.0);
1447                 }
1448         }
1449         if (start_path)
1450                 gtk_tree_path_free (start_path);
1451         if (end_path)
1452                 gtk_tree_path_free (end_path);
1453
1454         #endif /* GTK_CHECK_VERSION */
1455
1456         /* Frees */     
1457         g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1458         g_list_free (selected);
1459
1460         return FALSE;
1461 }
1462
1463 static void
1464 idle_notify_headers_count_changed_destroy (gpointer data)
1465 {
1466         HeadersCountChangedHelper *helper = NULL;
1467
1468         g_return_if_fail (data != NULL);
1469         helper = (HeadersCountChangedHelper *) data; 
1470
1471         g_object_unref (helper->change);
1472         g_slice_free (HeadersCountChangedHelper, helper);
1473 }
1474
1475 static gboolean
1476 idle_notify_headers_count_changed (gpointer data)
1477 {
1478         TnyFolder *folder = NULL;
1479         ModestHeaderViewPrivate *priv = NULL;
1480         HeadersCountChangedHelper *helper = NULL;
1481
1482         g_return_val_if_fail (data != NULL, FALSE);
1483         helper = (HeadersCountChangedHelper *) data; 
1484         g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1485         g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1486
1487         folder = tny_folder_change_get_folder (helper->change);
1488
1489         priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1490
1491         g_mutex_lock (priv->observers_lock);
1492
1493         /* Emit signal to evaluate how headers changes affects to the window view  */
1494         g_signal_emit (G_OBJECT(helper->self), 
1495                        signals[MSG_COUNT_CHANGED_SIGNAL], 
1496                        0, folder, helper->change);
1497                 
1498         /* Added or removed headers, so data stored on cliboard are invalid  */
1499         if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1500             modest_email_clipboard_clear (priv->clipboard);
1501             
1502         g_mutex_unlock (priv->observers_lock);
1503
1504         return FALSE;
1505 }
1506
1507 static void
1508 folder_monitor_update (TnyFolderObserver *self, 
1509                        TnyFolderChange *change)
1510 {
1511         ModestHeaderViewPrivate *priv;
1512         TnyFolderChangeChanged changed;
1513         HeadersCountChangedHelper *helper = NULL;
1514
1515         changed = tny_folder_change_get_changed (change);
1516         
1517         /* Do not notify the observers if the folder of the header
1518            view has changed before this call to the observer
1519            happens */
1520         priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1521         if (tny_folder_change_get_folder (change) != priv->folder)
1522                 return;
1523
1524         /* Check folder count */
1525         if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1526             (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1527                 helper = g_slice_new0 (HeadersCountChangedHelper);
1528                 helper->self = MODEST_HEADER_VIEW(self);
1529                 helper->change = g_object_ref(change);
1530                 
1531                 g_idle_add_full (G_PRIORITY_DEFAULT, 
1532                                  idle_notify_headers_count_changed, 
1533                                  helper,
1534                                  idle_notify_headers_count_changed_destroy);
1535         }       
1536 }
1537
1538 void
1539 modest_header_view_clear (ModestHeaderView *self)
1540 {
1541         modest_header_view_set_folder (self, NULL, NULL, NULL);
1542 }
1543
1544 void 
1545 modest_header_view_copy_selection (ModestHeaderView *header_view)
1546 {
1547         /* Copy selection */
1548         _clipboard_set_selected_data (header_view, FALSE);
1549 }
1550
1551 void 
1552 modest_header_view_cut_selection (ModestHeaderView *header_view)
1553 {
1554         ModestHeaderViewPrivate *priv = NULL;
1555         const gchar **hidding = NULL;
1556         guint i, n_selected;
1557
1558         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1559         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1560
1561         /* Copy selection */
1562         _clipboard_set_selected_data (header_view, TRUE);
1563
1564         /* Get hidding ids */
1565         hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected); 
1566         
1567         /* Clear hidding array created by previous cut operation */
1568         _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1569
1570         /* Copy hidding array */
1571         priv->n_selected = n_selected;
1572         priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1573         for (i=0; i < n_selected; i++) 
1574                 priv->hidding_ids[i] = g_strdup(hidding[i]);            
1575
1576         /* Hide cut headers */
1577         modest_header_view_refilter (header_view);
1578 }
1579
1580
1581  
1582
1583 static void
1584 _clipboard_set_selected_data (ModestHeaderView *header_view,
1585                               gboolean delete)
1586 {
1587         ModestHeaderViewPrivate *priv = NULL;
1588         TnyList *headers = NULL;
1589
1590         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1591         priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1592                 
1593         /* Set selected data on clipboard   */
1594         g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1595         headers = modest_header_view_get_selected_headers (header_view);
1596         modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1597
1598         /* Free */
1599         g_object_unref (headers);
1600 }
1601
1602
1603
1604 static gboolean
1605 filter_row (GtkTreeModel *model,
1606             GtkTreeIter *iter,
1607             gpointer user_data)
1608 {
1609         ModestHeaderViewPrivate *priv = NULL;
1610         TnyHeaderFlags flags;
1611         TnyHeader *header = NULL;
1612         guint i;
1613         gchar *id = NULL;
1614         gboolean visible = TRUE;
1615         gboolean found = FALSE;
1616         
1617         g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1618         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1619
1620         /* Get header from model */
1621         gtk_tree_model_get (model, iter,
1622                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1623                             TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1624                             -1);
1625         
1626         /* Hide mark as deleted heders */
1627         if (flags & TNY_HEADER_FLAG_DELETED) {
1628                 visible = FALSE;
1629                 goto frees;
1630         }
1631
1632         /* If no data on clipboard, return always TRUE */
1633         if (modest_email_clipboard_cleared(priv->clipboard)) {
1634                 visible = TRUE;
1635                 goto frees;
1636         }               
1637
1638         /* Get message id from header (ensure is a valid id) */
1639         if (!header) return FALSE;
1640         id = g_strdup(tny_header_get_message_id (header));
1641         
1642         /* Check hiding */
1643         if (priv->hidding_ids != NULL) {
1644                 for (i=0; i < priv->n_selected && !found; i++)
1645                         if (priv->hidding_ids[i] != NULL && id != NULL)
1646                                 found = (!strcmp (priv->hidding_ids[i], id));
1647         
1648                 visible = !found;
1649         }
1650
1651         /* Free */
1652  frees:
1653         if (header)
1654                 g_object_unref (header);
1655         g_free(id);
1656
1657         return visible;
1658 }
1659
1660 static void
1661 _clear_hidding_filter (ModestHeaderView *header_view) 
1662 {
1663         ModestHeaderViewPrivate *priv;
1664         guint i;
1665         
1666         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view)); 
1667         priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1668
1669         if (priv->hidding_ids != NULL) {
1670                 for (i=0; i < priv->n_selected; i++) 
1671                         g_free (priv->hidding_ids[i]);
1672                 g_free(priv->hidding_ids);
1673         }       
1674 }
1675
1676 void 
1677 modest_header_view_refilter (ModestHeaderView *header_view)
1678 {
1679         GtkTreeModel *model;
1680
1681         g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1682
1683         /* Hide cut headers */
1684         model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1685         if (GTK_IS_TREE_MODEL_FILTER (model))
1686                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1687 }
1688
1689 /* 
1690  * Called when an account is removed. If I'm showing a folder of the
1691  * account that has been removed then clear the view
1692  */
1693 static void
1694 on_account_removed (TnyAccountStore *self, 
1695                     TnyAccount *account,
1696                     gpointer user_data)
1697 {
1698         ModestHeaderViewPrivate *priv = NULL;
1699
1700         /* Ignore changes in transport accounts */
1701         if (TNY_IS_TRANSPORT_ACCOUNT (account))
1702                 return;
1703
1704         g_print ("--------------------- HEADER ---------------\n");
1705
1706         priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1707
1708         if (priv->folder) {
1709                 TnyAccount *my_account;
1710
1711                 my_account = tny_folder_get_account (priv->folder);
1712                 if (my_account == account)
1713                         modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1714                 g_object_unref (account);
1715         }
1716 }