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