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