1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
30 #include <glib/gi18n.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <tny-error.h>
38 #include <modest-header-view.h>
39 #include <modest-header-view-priv.h>
40 #include <modest-dnd.h>
41 #include <modest-tny-folder.h>
42 #include <modest-debug.h>
43 #include <modest-main-window.h>
44 #include <modest-ui-actions.h>
45 #include <modest-marshal.h>
46 #include <modest-text-utils.h>
47 #include <modest-icon-names.h>
48 #include <modest-runtime.h>
49 #include "modest-platform.h"
50 #include <modest-hbox-cell-renderer.h>
51 #include <modest-vbox-cell-renderer.h>
53 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
54 static void modest_header_view_init (ModestHeaderView *obj);
55 static void modest_header_view_finalize (GObject *obj);
56 static void modest_header_view_dispose (GObject *obj);
58 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
59 GtkTreeViewColumn *column, gpointer userdata);
61 static gint cmp_rows (GtkTreeModel *tree_model,
66 static gint cmp_subject_rows (GtkTreeModel *tree_model,
71 static gboolean filter_row (GtkTreeModel *model,
75 static void on_account_removed (TnyAccountStore *self,
79 static void on_selection_changed (GtkTreeSelection *sel,
82 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
85 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
88 static void setup_drag_and_drop (GtkWidget *self);
90 static void enable_drag_and_drop (GtkWidget *self);
92 static void disable_drag_and_drop (GtkWidget *self);
94 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
96 static gboolean on_focus_in (GtkWidget *sef,
100 static gboolean on_focus_out (GtkWidget *self,
101 GdkEventFocus *event,
104 static void folder_monitor_update (TnyFolderObserver *self,
105 TnyFolderChange *change);
107 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
109 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
111 static void _clear_hidding_filter (ModestHeaderView *header_view);
113 static void modest_header_view_notify_observers(
114 ModestHeaderView *header_view,
116 const gchar *tny_folder_id);
118 static gboolean modest_header_view_on_expose_event(
119 GtkTreeView *header_view,
120 GdkEventExpose *event,
124 HEADER_VIEW_NON_EMPTY,
129 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
130 struct _ModestHeaderViewPrivate {
132 ModestHeaderViewStyle style;
134 TnyFolderMonitor *monitor;
135 GMutex *observers_lock;
137 /*header-view-observer observer*/
138 GMutex *observer_list_lock;
139 GSList *observer_list;
141 /* not unref this object, its a singlenton */
142 ModestEmailClipboard *clipboard;
144 /* Filter tree model */
148 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
149 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
151 gulong selection_changed_handler;
152 gulong acc_removed_handler;
154 GList *drag_begin_cached_selected_rows;
156 HeaderViewStatus status;
157 guint status_timeout;
160 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
161 struct _HeadersCountChangedHelper {
162 ModestHeaderView *self;
163 TnyFolderChange *change;
167 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
168 MODEST_TYPE_HEADER_VIEW, \
169 ModestHeaderViewPrivate))
173 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
176 HEADER_SELECTED_SIGNAL,
177 HEADER_ACTIVATED_SIGNAL,
178 ITEM_NOT_FOUND_SIGNAL,
179 MSG_COUNT_CHANGED_SIGNAL,
180 UPDATING_MSG_LIST_SIGNAL,
185 static GObjectClass *parent_class = NULL;
187 /* uncomment the following if you have defined any signals */
188 static guint signals[LAST_SIGNAL] = {0};
191 modest_header_view_get_type (void)
193 static GType my_type = 0;
195 static const GTypeInfo my_info = {
196 sizeof(ModestHeaderViewClass),
197 NULL, /* base init */
198 NULL, /* base finalize */
199 (GClassInitFunc) modest_header_view_class_init,
200 NULL, /* class finalize */
201 NULL, /* class data */
202 sizeof(ModestHeaderView),
204 (GInstanceInitFunc) modest_header_view_init,
208 static const GInterfaceInfo tny_folder_observer_info =
210 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
211 NULL, /* interface_finalize */
212 NULL /* interface_data */
214 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
218 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
219 &tny_folder_observer_info);
227 modest_header_view_class_init (ModestHeaderViewClass *klass)
229 GObjectClass *gobject_class;
230 gobject_class = (GObjectClass*) klass;
232 parent_class = g_type_class_peek_parent (klass);
233 gobject_class->finalize = modest_header_view_finalize;
234 gobject_class->dispose = modest_header_view_dispose;
236 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
238 signals[HEADER_SELECTED_SIGNAL] =
239 g_signal_new ("header_selected",
240 G_TYPE_FROM_CLASS (gobject_class),
242 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
244 g_cclosure_marshal_VOID__POINTER,
245 G_TYPE_NONE, 1, G_TYPE_POINTER);
247 signals[HEADER_ACTIVATED_SIGNAL] =
248 g_signal_new ("header_activated",
249 G_TYPE_FROM_CLASS (gobject_class),
251 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
253 g_cclosure_marshal_VOID__POINTER,
254 G_TYPE_NONE, 1, G_TYPE_POINTER);
257 signals[ITEM_NOT_FOUND_SIGNAL] =
258 g_signal_new ("item_not_found",
259 G_TYPE_FROM_CLASS (gobject_class),
261 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
263 g_cclosure_marshal_VOID__INT,
264 G_TYPE_NONE, 1, G_TYPE_INT);
266 signals[MSG_COUNT_CHANGED_SIGNAL] =
267 g_signal_new ("msg_count_changed",
268 G_TYPE_FROM_CLASS (gobject_class),
270 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
272 modest_marshal_VOID__POINTER_POINTER,
273 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
275 signals[UPDATING_MSG_LIST_SIGNAL] =
276 g_signal_new ("updating-msg-list",
277 G_TYPE_FROM_CLASS (gobject_class),
279 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
281 g_cclosure_marshal_VOID__BOOLEAN,
282 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
286 tny_folder_observer_init (TnyFolderObserverIface *klass)
288 klass->update = folder_monitor_update;
291 static GtkTreeViewColumn*
292 get_new_column (const gchar *name, GtkCellRenderer *renderer,
293 gboolean resizable, gint sort_col_id, gboolean show_as_text,
294 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
296 GtkTreeViewColumn *column;
298 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
299 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
301 gtk_tree_view_column_set_resizable (column, resizable);
303 gtk_tree_view_column_set_expand (column, TRUE);
306 gtk_tree_view_column_add_attribute (column, renderer, "text",
308 if (sort_col_id >= 0)
309 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
311 gtk_tree_view_column_set_sort_indicator (column, FALSE);
312 gtk_tree_view_column_set_reorderable (column, TRUE);
315 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
322 remove_all_columns (ModestHeaderView *obj)
324 GList *columns, *cursor;
326 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
328 for (cursor = columns; cursor; cursor = cursor->next)
329 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
330 GTK_TREE_VIEW_COLUMN(cursor->data));
331 g_list_free (columns);
335 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
337 GtkTreeModel *tree_filter, *sortable;
338 GtkTreeViewColumn *column=NULL;
339 GtkTreeSelection *selection = NULL;
340 GtkCellRenderer *renderer_msgtype,*renderer_header,
341 *renderer_attach, *renderer_compact_date_or_status;
342 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
343 *renderer_subject, *renderer_subject_box, *renderer_recpt,
345 ModestHeaderViewPrivate *priv;
346 GtkTreeViewColumn *compact_column = NULL;
349 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
350 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
352 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
354 /* FIXME: check whether these renderers need to be freed */
355 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
356 renderer_attach = gtk_cell_renderer_pixbuf_new ();
357 renderer_priority = gtk_cell_renderer_pixbuf_new ();
358 renderer_header = gtk_cell_renderer_text_new ();
360 renderer_compact_header = modest_vbox_cell_renderer_new ();
361 renderer_recpt_box = modest_hbox_cell_renderer_new ();
362 renderer_subject_box = modest_hbox_cell_renderer_new ();
363 renderer_recpt = gtk_cell_renderer_text_new ();
364 renderer_subject = gtk_cell_renderer_text_new ();
365 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
367 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
368 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
369 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
370 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
371 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
372 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
373 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
374 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
375 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
376 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
377 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
378 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
379 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
380 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
382 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
383 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
384 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
385 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
386 g_object_set(G_OBJECT(renderer_header),
387 "ellipsize", PANGO_ELLIPSIZE_END,
389 g_object_set (G_OBJECT (renderer_subject),
390 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
392 g_object_set (G_OBJECT (renderer_recpt),
393 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
395 g_object_set(G_OBJECT(renderer_compact_date_or_status),
396 "xalign", 1.0, "yalign", 0.0,
398 g_object_set (G_OBJECT (renderer_priority),
399 "yalign", 1.0, NULL);
400 g_object_set (G_OBJECT (renderer_attach),
401 "yalign", 0.0, NULL);
403 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
404 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
405 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
407 remove_all_columns (self);
409 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
410 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
411 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
412 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
414 /* Add new columns */
415 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
416 ModestHeaderViewColumn col =
417 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
419 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
420 g_printerr ("modest: invalid column %d in column list\n", col);
426 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
427 column = get_new_column (_("M"), renderer_msgtype, FALSE,
428 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
430 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
432 gtk_tree_view_column_set_fixed_width (column, 45);
435 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
436 column = get_new_column (_("A"), renderer_attach, FALSE,
437 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
439 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
441 gtk_tree_view_column_set_fixed_width (column, 45);
445 case MODEST_HEADER_VIEW_COLUMN_FROM:
446 column = get_new_column (_("From"), renderer_header, TRUE,
447 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
449 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
450 GINT_TO_POINTER(TRUE));
453 case MODEST_HEADER_VIEW_COLUMN_TO:
454 column = get_new_column (_("To"), renderer_header, TRUE,
455 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
457 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
458 GINT_TO_POINTER(FALSE));
461 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
462 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
463 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
465 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
466 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
467 compact_column = column;
470 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
471 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
472 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
474 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
475 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
476 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
477 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
478 compact_column = column;
482 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
483 column = get_new_column (_("Subject"), renderer_header, TRUE,
484 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
486 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
490 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
491 column = get_new_column (_("Received"), renderer_header, TRUE,
492 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
494 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
495 GINT_TO_POINTER(TRUE));
498 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
499 column = get_new_column (_("Sent"), renderer_header, TRUE,
500 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
502 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
503 GINT_TO_POINTER(FALSE));
506 case MODEST_HEADER_VIEW_COLUMN_SIZE:
507 column = get_new_column (_("Size"), renderer_header, TRUE,
508 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
510 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
513 case MODEST_HEADER_VIEW_COLUMN_STATUS:
514 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
515 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
517 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
522 g_return_val_if_reached(FALSE);
525 /* we keep the column id around */
526 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
527 GINT_TO_POINTER(col));
529 /* we need this ptr when sorting the rows */
530 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
532 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
536 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
537 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
538 (GtkTreeIterCompareFunc) cmp_rows,
539 compact_column, NULL);
540 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
541 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
542 (GtkTreeIterCompareFunc) cmp_subject_rows,
543 compact_column, NULL);
551 modest_header_view_init (ModestHeaderView *obj)
553 ModestHeaderViewPrivate *priv;
556 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
560 priv->monitor = NULL;
561 priv->observers_lock = g_mutex_new ();
563 priv->status = HEADER_VIEW_INIT;
564 priv->status_timeout = 0;
566 priv->observer_list_lock = g_mutex_new();
567 priv->observer_list = NULL;
569 priv->clipboard = modest_runtime_get_email_clipboard ();
570 priv->hidding_ids = NULL;
571 priv->n_selected = 0;
572 priv->selection_changed_handler = 0;
573 priv->acc_removed_handler = 0;
575 /* Sort parameters */
576 for (j=0; j < 2; j++) {
577 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
578 priv->sort_colid[j][i] = -1;
579 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
583 setup_drag_and_drop (GTK_WIDGET(obj));
587 modest_header_view_dispose (GObject *obj)
589 ModestHeaderView *self;
590 ModestHeaderViewPrivate *priv;
591 GtkTreeSelection *sel;
593 self = MODEST_HEADER_VIEW(obj);
594 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
596 /* Free in the dispose to avoid unref cycles */
598 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
599 g_object_unref (G_OBJECT (priv->folder));
603 /* We need to do this here in the dispose because the
604 selection won't exist when finalizing */
605 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
606 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
607 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
608 priv->selection_changed_handler = 0;
611 G_OBJECT_CLASS(parent_class)->dispose (obj);
615 modest_header_view_finalize (GObject *obj)
617 ModestHeaderView *self;
618 ModestHeaderViewPrivate *priv;
620 self = MODEST_HEADER_VIEW(obj);
621 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
623 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
624 priv->acc_removed_handler)) {
625 g_signal_handler_disconnect (modest_runtime_get_account_store (),
626 priv->acc_removed_handler);
629 /* There is no need to lock because there should not be any
630 * reference to self now. */
631 g_mutex_free(priv->observer_list_lock);
632 g_slist_free(priv->observer_list);
634 g_mutex_lock (priv->observers_lock);
636 tny_folder_monitor_stop (priv->monitor);
637 g_object_unref (G_OBJECT (priv->monitor));
639 g_mutex_unlock (priv->observers_lock);
640 g_mutex_free (priv->observers_lock);
642 /* Clear hidding array created by cut operation */
643 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
645 G_OBJECT_CLASS(parent_class)->finalize (obj);
650 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
653 GtkTreeSelection *sel;
654 ModestHeaderView *self;
655 ModestHeaderViewPrivate *priv;
657 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
660 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
661 self = MODEST_HEADER_VIEW(obj);
662 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
664 modest_header_view_set_style (self, style);
666 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
667 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
668 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
670 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
671 TRUE); /* alternating row colors */
673 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
674 priv->selection_changed_handler =
675 g_signal_connect_after (sel, "changed",
676 G_CALLBACK(on_selection_changed), self);
678 g_signal_connect (self, "row-activated",
679 G_CALLBACK (on_header_row_activated), NULL);
681 g_signal_connect (self, "focus-in-event",
682 G_CALLBACK(on_focus_in), NULL);
683 g_signal_connect (self, "focus-out-event",
684 G_CALLBACK(on_focus_out), NULL);
686 g_signal_connect (self, "button-press-event",
687 G_CALLBACK(on_button_press_event), NULL);
688 g_signal_connect (self, "button-release-event",
689 G_CALLBACK(on_button_release_event), NULL);
691 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
693 G_CALLBACK (on_account_removed),
696 g_signal_connect (self, "expose-event",
697 G_CALLBACK(modest_header_view_on_expose_event),
700 return GTK_WIDGET(self);
705 modest_header_view_count_selected_headers (ModestHeaderView *self)
707 GtkTreeSelection *sel;
710 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
712 /* Get selection object and check selected rows count */
713 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
714 selected_rows = gtk_tree_selection_count_selected_rows (sel);
716 return selected_rows;
720 modest_header_view_has_selected_headers (ModestHeaderView *self)
722 GtkTreeSelection *sel;
725 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
727 /* Get selection object and check selected rows count */
728 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
729 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
736 modest_header_view_get_selected_headers (ModestHeaderView *self)
738 GtkTreeSelection *sel;
739 ModestHeaderViewPrivate *priv;
740 TnyList *header_list = NULL;
742 GList *list, *tmp = NULL;
743 GtkTreeModel *tree_model = NULL;
746 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
748 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
750 /* Get selected rows */
751 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
752 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
755 header_list = tny_simple_list_new();
757 list = g_list_reverse (list);
760 /* get header from selection */
761 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
762 gtk_tree_model_get (tree_model, &iter,
763 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
765 /* Prepend to list */
766 tny_list_prepend (header_list, G_OBJECT (header));
767 g_object_unref (G_OBJECT (header));
769 tmp = g_list_next (tmp);
772 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
779 /* scroll our list view so the selected item is visible */
781 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
783 #ifdef MODEST_PLATFORM_GNOME
785 GtkTreePath *selected_path;
786 GtkTreePath *start, *end;
790 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
791 selected_path = gtk_tree_model_get_path (model, iter);
793 start = gtk_tree_path_new ();
794 end = gtk_tree_path_new ();
796 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
798 if (gtk_tree_path_compare (selected_path, start) < 0 ||
799 gtk_tree_path_compare (end, selected_path) < 0)
800 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
801 selected_path, NULL, TRUE,
804 gtk_tree_path_free (selected_path);
805 gtk_tree_path_free (start);
806 gtk_tree_path_free (end);
808 #endif /* MODEST_PLATFORM_GNOME */
813 modest_header_view_select_next (ModestHeaderView *self)
815 GtkTreeSelection *sel;
820 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
822 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
823 path = get_selected_row (GTK_TREE_VIEW(self), &model);
824 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
825 /* Unselect previous path */
826 gtk_tree_selection_unselect_path (sel, path);
828 /* Move path down and selects new one */
829 if (gtk_tree_model_iter_next (model, &iter)) {
830 gtk_tree_selection_select_iter (sel, &iter);
831 scroll_to_selected (self, &iter, FALSE);
833 gtk_tree_path_free(path);
839 modest_header_view_select_prev (ModestHeaderView *self)
841 GtkTreeSelection *sel;
846 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
848 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
849 path = get_selected_row (GTK_TREE_VIEW(self), &model);
850 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
851 /* Unselect previous path */
852 gtk_tree_selection_unselect_path (sel, path);
855 if (gtk_tree_path_prev (path)) {
856 gtk_tree_model_get_iter (model, &iter, path);
858 /* Select the new one */
859 gtk_tree_selection_select_iter (sel, &iter);
860 scroll_to_selected (self, &iter, TRUE);
863 gtk_tree_path_free (path);
868 modest_header_view_get_columns (ModestHeaderView *self)
870 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
872 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
878 modest_header_view_set_style (ModestHeaderView *self,
879 ModestHeaderViewStyle style)
881 ModestHeaderViewPrivate *priv;
882 gboolean show_col_headers = FALSE;
883 ModestHeaderViewStyle old_style;
885 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
886 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
889 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
890 if (priv->style == style)
891 return TRUE; /* nothing to do */
894 case MODEST_HEADER_VIEW_STYLE_DETAILS:
895 show_col_headers = TRUE;
897 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
900 g_return_val_if_reached (FALSE);
902 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
903 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
905 old_style = priv->style;
912 ModestHeaderViewStyle
913 modest_header_view_get_style (ModestHeaderView *self)
915 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
917 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
920 /* This is used to automatically select the first header if the user
921 * has not selected any header yet.
924 modest_header_view_on_expose_event(GtkTreeView *header_view,
925 GdkEventExpose *event,
928 GtkTreeSelection *sel;
930 GtkTreeIter tree_iter;
932 model = gtk_tree_view_get_model(header_view);
937 sel = gtk_tree_view_get_selection(header_view);
938 if(!gtk_tree_selection_count_selected_rows(sel))
939 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
940 /* Prevent the widget from getting the focus
941 when selecting the first item */
942 g_object_set(header_view, "can-focus", FALSE, NULL);
943 gtk_tree_selection_select_iter(sel, &tree_iter);
944 g_object_set(header_view, "can-focus", TRUE, NULL);
951 * This function sets a sortable model in the header view. It's just
952 * used for developing purposes, because it only does a
953 * gtk_tree_view_set_model
956 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
958 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
959 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
960 /* GtkTreeModel *old_model; */
961 /* ModestHeaderViewPrivate *priv; */
962 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
963 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
965 /* /\* Set new model *\/ */
966 /* gtk_tree_view_set_model (header_view, model); */
968 gtk_tree_view_set_model (header_view, model);
972 modest_header_view_get_folder (ModestHeaderView *self)
974 ModestHeaderViewPrivate *priv;
976 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
978 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
981 g_object_ref (priv->folder);
987 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
991 ModestHeaderViewPrivate *priv;
992 GList *cols, *cursor;
993 GtkTreeModel *filter_model, *sortable;
995 GtkSortType sort_type;
997 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
999 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1001 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1002 folder, FALSE, NULL, NULL, NULL);
1004 /* Add IDLE observer (monitor) and another folder observer for
1005 new messages (self) */
1006 g_mutex_lock (priv->observers_lock);
1007 if (priv->monitor) {
1008 tny_folder_monitor_stop (priv->monitor);
1009 g_object_unref (G_OBJECT (priv->monitor));
1011 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1012 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1013 tny_folder_monitor_start (priv->monitor);
1014 g_mutex_unlock (priv->observers_lock);
1016 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1017 g_object_unref (G_OBJECT (headers));
1019 /* Init filter_row function to examine empty status */
1020 priv->status = HEADER_VIEW_INIT;
1022 /* Create a tree model filter to hide and show rows for cut operations */
1023 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1024 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1028 g_object_unref (G_OBJECT (sortable));
1030 /* install our special sorting functions */
1031 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1033 /* Restore sort column id */
1035 type = modest_tny_folder_guess_folder_type (folder);
1036 if (type == TNY_FOLDER_TYPE_INVALID)
1037 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1039 sort_colid = modest_header_view_get_sort_column_id (self, type);
1040 sort_type = modest_header_view_get_sort_type (self, type);
1041 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1044 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1045 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1046 (GtkTreeIterCompareFunc) cmp_rows,
1048 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1049 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1050 (GtkTreeIterCompareFunc) cmp_subject_rows,
1055 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1056 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1057 tny_folder_get_id(folder));
1058 g_object_unref (G_OBJECT (filter_model));
1059 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1060 /* g_object_unref (G_OBJECT (sortable)); */
1067 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1069 GtkSortType sort_type)
1071 ModestHeaderViewPrivate *priv = NULL;
1072 GtkTreeModel *tree_filter, *sortable = NULL;
1075 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1076 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1078 /* Get model and private data */
1079 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1080 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1081 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1082 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1084 /* Sort tree model */
1085 type = modest_tny_folder_guess_folder_type (priv->folder);
1086 if (type == TNY_FOLDER_TYPE_INVALID)
1087 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1089 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1092 /* Store new sort parameters */
1093 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1098 modest_header_view_set_sort_params (ModestHeaderView *self,
1100 GtkSortType sort_type,
1103 ModestHeaderViewPrivate *priv;
1104 ModestHeaderViewStyle style;
1106 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1107 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1108 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1110 style = modest_header_view_get_style (self);
1111 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1113 priv->sort_colid[style][type] = sort_colid;
1114 priv->sort_type[style][type] = sort_type;
1118 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1121 ModestHeaderViewPrivate *priv;
1122 ModestHeaderViewStyle style;
1124 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1125 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1127 style = modest_header_view_get_style (self);
1128 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1130 return priv->sort_colid[style][type];
1134 modest_header_view_get_sort_type (ModestHeaderView *self,
1137 ModestHeaderViewPrivate *priv;
1138 ModestHeaderViewStyle style;
1140 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1141 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1143 style = modest_header_view_get_style (self);
1144 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1146 return priv->sort_type[style][type];
1150 ModestHeaderView *header_view;
1151 RefreshAsyncUserCallback cb;
1156 folder_refreshed_cb (ModestMailOperation *mail_op,
1160 ModestHeaderViewPrivate *priv;
1161 SetFolderHelper *info;
1163 info = (SetFolderHelper*) user_data;
1165 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1169 info->cb (mail_op, folder, info->user_data);
1171 /* Start the folder count changes observer. We do not need it
1172 before the refresh. Note that the monitor could still be
1173 called for this refresh but now we know that the callback
1174 was previously called */
1175 g_mutex_lock (priv->observers_lock);
1176 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1177 g_mutex_unlock (priv->observers_lock);
1179 /* Notify the observers that the update is over */
1180 g_signal_emit (G_OBJECT (info->header_view),
1181 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1184 g_object_unref (info->header_view);
1189 refresh_folder_error_handler (ModestMailOperation *mail_op,
1192 const GError *error = modest_mail_operation_get_error (mail_op);
1194 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1195 error->code == TNY_IO_ERROR_WRITE ||
1196 error->code == TNY_IO_ERROR_READ) {
1197 modest_platform_information_banner (NULL, NULL,
1199 "cerm_device_memory_full"));
1204 modest_header_view_set_folder (ModestHeaderView *self,
1206 RefreshAsyncUserCallback callback,
1209 ModestHeaderViewPrivate *priv;
1210 SetFolderHelper *info;
1211 ModestWindow *main_win;
1213 g_return_if_fail (self);
1215 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1217 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1218 FALSE); /* don't create */
1220 g_warning ("%s: BUG: no main window", __FUNCTION__);
1225 if (priv->status_timeout)
1226 g_source_remove (priv->status_timeout);
1228 g_mutex_lock (priv->observers_lock);
1229 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1230 g_object_unref (priv->folder);
1231 priv->folder = NULL;
1232 g_mutex_unlock (priv->observers_lock);
1236 ModestMailOperation *mail_op = NULL;
1237 GtkTreeSelection *selection;
1239 /* Set folder in the model */
1240 modest_header_view_set_folder_intern (self, folder);
1242 /* Pick my reference. Nothing to do with the mail operation */
1243 priv->folder = g_object_ref (folder);
1245 /* Clear the selection if exists */
1246 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1247 gtk_tree_selection_unselect_all(selection);
1248 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1250 /* Notify the observers that the update begins */
1251 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1254 /* create the helper */
1255 info = g_malloc0 (sizeof(SetFolderHelper));
1256 info->header_view = g_object_ref (self);
1257 info->cb = callback;
1258 info->user_data = user_data;
1260 /* Create the mail operation (source will be the parent widget) */
1261 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1262 refresh_folder_error_handler,
1264 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1267 /* Refresh the folder asynchronously */
1268 modest_mail_operation_refresh_folder (mail_op,
1270 folder_refreshed_cb,
1274 g_object_unref (mail_op);
1276 g_mutex_lock (priv->observers_lock);
1278 if (priv->monitor) {
1279 tny_folder_monitor_stop (priv->monitor);
1280 g_object_unref (G_OBJECT (priv->monitor));
1281 priv->monitor = NULL;
1283 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1285 modest_header_view_notify_observers(self, NULL, NULL);
1287 g_mutex_unlock (priv->observers_lock);
1289 /* Notify the observers that the update is over */
1290 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1296 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1297 GtkTreeViewColumn *column, gpointer userdata)
1299 ModestHeaderView *self = NULL;
1300 ModestHeaderViewPrivate *priv = NULL;
1302 GtkTreeModel *model = NULL;
1303 TnyHeader *header = NULL;
1304 TnyHeaderFlags flags;
1306 self = MODEST_HEADER_VIEW (treeview);
1307 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1309 model = gtk_tree_view_get_model (treeview);
1310 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1313 /* get the first selected item */
1314 gtk_tree_model_get (model, &iter,
1315 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1316 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1319 /* Dont open DELETED messages */
1320 if (flags & TNY_HEADER_FLAG_DELETED) {
1323 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1324 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1325 modest_platform_information_banner (NULL, NULL, msg);
1331 g_signal_emit (G_OBJECT(self),
1332 signals[HEADER_ACTIVATED_SIGNAL],
1338 g_object_unref (G_OBJECT (header));
1343 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1345 GtkTreeModel *model;
1346 TnyHeader *header = NULL;
1347 GtkTreePath *path = NULL;
1349 ModestHeaderView *self;
1350 ModestHeaderViewPrivate *priv;
1351 GList *selected = NULL;
1353 g_return_if_fail (sel);
1354 g_return_if_fail (user_data);
1356 self = MODEST_HEADER_VIEW (user_data);
1357 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1359 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1360 if (selected != NULL)
1361 path = (GtkTreePath *) selected->data;
1362 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1363 return; /* msg was _un_selected */
1365 gtk_tree_model_get (model, &iter,
1366 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1370 g_signal_emit (G_OBJECT(self),
1371 signals[HEADER_SELECTED_SIGNAL],
1374 g_object_unref (G_OBJECT (header));
1376 /* free all items in 'selected' */
1377 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1378 g_list_free (selected);
1382 /* PROTECTED method. It's useful when we want to force a given
1383 selection to reload a msg. For example if we have selected a header
1384 in offline mode, when Modest become online, we want to reload the
1385 message automatically without an user click over the header */
1387 _modest_header_view_change_selection (GtkTreeSelection *selection,
1390 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1391 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1393 on_selection_changed (selection, user_data);
1397 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1404 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1408 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1412 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1420 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1427 /* static int counter = 0; */
1429 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1430 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1431 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1435 case TNY_HEADER_FLAG_ATTACHMENTS:
1437 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1438 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1439 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1440 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1442 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1443 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1445 return cmp ? cmp : t1 - t2;
1447 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1448 TnyHeader *header1 = NULL, *header2 = NULL;
1450 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1451 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1452 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1453 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1455 /* This is for making priority values respect the intuitive sort relationship
1456 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1458 if (header1 && header2) {
1459 cmp = compare_priorities (tny_header_get_priority (header1),
1460 tny_header_get_priority (header2));
1461 g_object_unref (header1);
1462 g_object_unref (header2);
1464 return cmp ? cmp : t1 - t2;
1470 return &iter1 - &iter2; /* oughhhh */
1475 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1481 /* static int counter = 0; */
1483 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1485 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1486 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1487 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1488 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1490 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1491 val2 + modest_text_utils_get_subject_prefix_len(val2),
1498 /* Drag and drop stuff */
1500 drag_data_get_cb (GtkWidget *widget,
1501 GdkDragContext *context,
1502 GtkSelectionData *selection_data,
1507 ModestHeaderView *self = NULL;
1508 ModestHeaderViewPrivate *priv = NULL;
1510 self = MODEST_HEADER_VIEW (widget);
1511 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1513 /* Set the data. Do not use the current selection because it
1514 could be different than the selection at the beginning of
1516 modest_dnd_selection_data_set_paths (selection_data,
1517 priv->drag_begin_cached_selected_rows);
1521 * We're caching the selected rows at the beginning because the
1522 * selection could change between drag-begin and drag-data-get, for
1523 * example if we have a set of rows already selected, and then we
1524 * click in one of them (without SHIFT key pressed) and begin a drag,
1525 * the selection at that moment contains all the selected lines, but
1526 * after dropping the selection, the release event provokes that only
1527 * the row used to begin the drag is selected, so at the end the
1528 * drag&drop affects only one rows instead of all the selected ones.
1532 drag_begin_cb (GtkWidget *widget,
1533 GdkDragContext *context,
1536 ModestHeaderView *self = NULL;
1537 ModestHeaderViewPrivate *priv = NULL;
1538 GtkTreeSelection *selection;
1540 self = MODEST_HEADER_VIEW (widget);
1541 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1543 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1544 priv->drag_begin_cached_selected_rows =
1545 gtk_tree_selection_get_selected_rows (selection, NULL);
1549 * We use the drag-end signal to clear the cached selection, we use
1550 * this because this allways happens, whether or not the d&d was a
1554 drag_end_cb (GtkWidget *widget,
1558 ModestHeaderView *self = NULL;
1559 ModestHeaderViewPrivate *priv = NULL;
1561 self = MODEST_HEADER_VIEW (widget);
1562 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1564 /* Free cached data */
1565 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1566 g_list_free (priv->drag_begin_cached_selected_rows);
1567 priv->drag_begin_cached_selected_rows = NULL;
1570 /* Header view drag types */
1571 const GtkTargetEntry header_view_drag_types[] = {
1572 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1576 enable_drag_and_drop (GtkWidget *self)
1578 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1579 header_view_drag_types,
1580 G_N_ELEMENTS (header_view_drag_types),
1581 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1585 disable_drag_and_drop (GtkWidget *self)
1587 gtk_drag_source_unset (self);
1591 setup_drag_and_drop (GtkWidget *self)
1593 enable_drag_and_drop(self);
1594 g_signal_connect(G_OBJECT (self), "drag_data_get",
1595 G_CALLBACK(drag_data_get_cb), NULL);
1597 g_signal_connect(G_OBJECT (self), "drag_begin",
1598 G_CALLBACK(drag_begin_cb), NULL);
1600 g_signal_connect(G_OBJECT (self), "drag_end",
1601 G_CALLBACK(drag_end_cb), NULL);
1604 static GtkTreePath *
1605 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1607 GtkTreePath *path = NULL;
1608 GtkTreeSelection *sel = NULL;
1611 sel = gtk_tree_view_get_selection(self);
1612 rows = gtk_tree_selection_get_selected_rows (sel, model);
1614 if ((rows == NULL) || (g_list_length(rows) != 1))
1617 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1622 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1629 * This function moves the tree view scroll to the current selected
1630 * row when the widget grabs the focus
1633 on_focus_in (GtkWidget *self,
1634 GdkEventFocus *event,
1637 GtkTreeSelection *selection;
1638 GtkTreeModel *model;
1639 GList *selected = NULL;
1640 GtkTreePath *selected_path = NULL;
1642 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1646 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1647 /* If none selected yet, pick the first one */
1648 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1652 /* Return if the model is empty */
1653 if (!gtk_tree_model_get_iter_first (model, &iter))
1656 path = gtk_tree_model_get_path (model, &iter);
1657 gtk_tree_selection_select_path (selection, path);
1658 gtk_tree_path_free (path);
1661 /* Need to get the all the rows because is selection multiple */
1662 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1663 if (selected == NULL) return FALSE;
1664 selected_path = (GtkTreePath *) selected->data;
1666 /* Check if we need to scroll */
1667 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1668 GtkTreePath *start_path = NULL;
1669 GtkTreePath *end_path = NULL;
1670 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1674 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1675 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1677 /* Scroll to first path */
1678 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1687 gtk_tree_path_free (start_path);
1689 gtk_tree_path_free (end_path);
1691 #endif /* GTK_CHECK_VERSION */
1694 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1695 g_list_free (selected);
1701 on_focus_out (GtkWidget *self,
1702 GdkEventFocus *event,
1706 if (!gtk_widget_is_focus (self)) {
1707 GtkTreeSelection *selection = NULL;
1708 GList *selected_rows = NULL;
1709 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1710 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1711 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1712 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1713 gtk_tree_selection_unselect_all (selection);
1714 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1715 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1716 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1717 g_list_free (selected_rows);
1724 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1726 enable_drag_and_drop(self);
1731 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1733 GtkTreeSelection *selection = NULL;
1734 GtkTreePath *path = NULL;
1735 gboolean already_selected = FALSE;
1737 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1738 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1739 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1742 /* Enable drag and drop onlly if the user clicks on a row that
1743 it's already selected. If not, let him select items using
1745 if (!already_selected) {
1746 disable_drag_and_drop(self);
1750 gtk_tree_path_free(path);
1757 folder_monitor_update (TnyFolderObserver *self,
1758 TnyFolderChange *change)
1760 ModestHeaderViewPrivate *priv = NULL;
1761 TnyFolderChangeChanged changed;
1762 TnyFolder *folder = NULL;
1764 changed = tny_folder_change_get_changed (change);
1766 /* Do not notify the observers if the folder of the header
1767 view has changed before this call to the observer
1769 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1770 folder = tny_folder_change_get_folder (change);
1771 if (folder != priv->folder)
1774 MODEST_DEBUG_BLOCK (
1775 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1776 g_print ("ADDED %d/%d (r/t) \n",
1777 tny_folder_change_get_new_unread_count (change),
1778 tny_folder_change_get_new_all_count (change));
1779 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1780 g_print ("ALL COUNT %d\n",
1781 tny_folder_change_get_new_all_count (change));
1782 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1783 g_print ("UNREAD COUNT %d\n",
1784 tny_folder_change_get_new_unread_count (change));
1785 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1786 g_print ("EXPUNGED %d/%d (r/t) \n",
1787 tny_folder_change_get_new_unread_count (change),
1788 tny_folder_change_get_new_all_count (change));
1789 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1790 g_print ("FOLDER RENAME\n");
1791 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1792 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1793 tny_folder_change_get_new_unread_count (change),
1794 tny_folder_change_get_new_all_count (change));
1795 g_print ("---------------------------------------------------\n");
1798 /* Check folder count */
1799 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1800 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1802 g_mutex_lock (priv->observers_lock);
1804 /* Emit signal to evaluate how headers changes affects
1805 to the window view */
1806 g_signal_emit (G_OBJECT(self),
1807 signals[MSG_COUNT_CHANGED_SIGNAL],
1810 /* Added or removed headers, so data stored on cliboard are invalid */
1811 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1812 modest_email_clipboard_clear (priv->clipboard);
1814 g_mutex_unlock (priv->observers_lock);
1820 g_object_unref (folder);
1824 modest_header_view_is_empty (ModestHeaderView *self)
1826 ModestHeaderViewPrivate *priv;
1828 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1830 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1832 return priv->status == HEADER_VIEW_EMPTY;
1836 modest_header_view_clear (ModestHeaderView *self)
1838 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1840 modest_header_view_set_folder (self, NULL, NULL, NULL);
1844 modest_header_view_copy_selection (ModestHeaderView *header_view)
1846 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1848 /* Copy selection */
1849 _clipboard_set_selected_data (header_view, FALSE);
1853 modest_header_view_cut_selection (ModestHeaderView *header_view)
1855 ModestHeaderViewPrivate *priv = NULL;
1856 const gchar **hidding = NULL;
1857 guint i, n_selected;
1859 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1861 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1863 /* Copy selection */
1864 _clipboard_set_selected_data (header_view, TRUE);
1866 /* Get hidding ids */
1867 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1869 /* Clear hidding array created by previous cut operation */
1870 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1872 /* Copy hidding array */
1873 priv->n_selected = n_selected;
1874 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1875 for (i=0; i < n_selected; i++)
1876 priv->hidding_ids[i] = g_strdup(hidding[i]);
1878 /* Hide cut headers */
1879 modest_header_view_refilter (header_view);
1886 _clipboard_set_selected_data (ModestHeaderView *header_view,
1889 ModestHeaderViewPrivate *priv = NULL;
1890 TnyList *headers = NULL;
1892 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1893 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1895 /* Set selected data on clipboard */
1896 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1897 headers = modest_header_view_get_selected_headers (header_view);
1898 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1901 g_object_unref (headers);
1905 ModestHeaderView *self;
1910 notify_filter_change (gpointer data)
1912 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1914 g_signal_emit (info->self,
1915 signals[MSG_COUNT_CHANGED_SIGNAL],
1916 0, info->folder, NULL);
1922 notify_filter_change_destroy (gpointer data)
1924 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1926 g_object_unref (info->self);
1927 g_object_unref (info->folder);
1928 g_slice_free (NotifyFilterInfo, info);
1932 filter_row (GtkTreeModel *model,
1936 ModestHeaderViewPrivate *priv = NULL;
1937 TnyHeaderFlags flags;
1938 TnyHeader *header = NULL;
1941 gboolean visible = TRUE;
1942 gboolean found = FALSE;
1943 GValue value = {0,};
1944 HeaderViewStatus old_status;
1946 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1947 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1949 /* Get header from model */
1950 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
1951 flags = (TnyHeaderFlags) g_value_get_int (&value);
1952 g_value_unset (&value);
1953 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
1954 header = (TnyHeader *) g_value_get_object (&value);
1955 g_value_unset (&value);
1957 /* Hide deleted and mark as deleted heders */
1958 if (flags & TNY_HEADER_FLAG_DELETED ||
1959 flags & TNY_HEADER_FLAG_EXPUNGED) {
1964 /* If no data on clipboard, return always TRUE */
1965 if (modest_email_clipboard_cleared(priv->clipboard)) {
1970 /* Get message id from header (ensure is a valid id) */
1977 if (priv->hidding_ids != NULL) {
1978 id = g_strdup(tny_header_get_message_id (header));
1979 for (i=0; i < priv->n_selected && !found; i++)
1980 if (priv->hidding_ids[i] != NULL && id != NULL)
1981 found = (!strcmp (priv->hidding_ids[i], id));
1988 old_status = priv->status;
1989 priv->status = ((gboolean) priv->status) && !visible;
1990 if (priv->status != old_status) {
1991 NotifyFilterInfo *info;
1993 if (priv->status_timeout)
1994 g_source_remove (priv->status_timeout);
1996 info = g_slice_new0 (NotifyFilterInfo);
1997 info->self = g_object_ref (G_OBJECT (user_data));
1998 info->folder = tny_header_get_folder (header);
1999 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2000 notify_filter_change,
2002 notify_filter_change_destroy);
2009 _clear_hidding_filter (ModestHeaderView *header_view)
2011 ModestHeaderViewPrivate *priv = NULL;
2014 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2015 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2017 if (priv->hidding_ids != NULL) {
2018 for (i=0; i < priv->n_selected; i++)
2019 g_free (priv->hidding_ids[i]);
2020 g_free(priv->hidding_ids);
2025 modest_header_view_refilter (ModestHeaderView *header_view)
2027 GtkTreeModel *model = NULL;
2028 ModestHeaderViewPrivate *priv = NULL;
2030 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2031 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2033 /* Hide cut headers */
2034 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2035 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2036 priv->status = HEADER_VIEW_INIT;
2037 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2042 * Called when an account is removed. If I'm showing a folder of the
2043 * account that has been removed then clear the view
2046 on_account_removed (TnyAccountStore *self,
2047 TnyAccount *account,
2050 ModestHeaderViewPrivate *priv = NULL;
2052 /* Ignore changes in transport accounts */
2053 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2056 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2059 TnyAccount *my_account;
2061 my_account = tny_folder_get_account (priv->folder);
2062 if (my_account == account)
2063 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2064 g_object_unref (account);
2069 modest_header_view_add_observer(ModestHeaderView *header_view,
2070 ModestHeaderViewObserver *observer)
2072 ModestHeaderViewPrivate *priv;
2074 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2075 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2077 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2079 g_mutex_lock(priv->observer_list_lock);
2080 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2081 g_mutex_unlock(priv->observer_list_lock);
2085 modest_header_view_remove_observer(ModestHeaderView *header_view,
2086 ModestHeaderViewObserver *observer)
2088 ModestHeaderViewPrivate *priv;
2090 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2091 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2093 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2095 g_mutex_lock(priv->observer_list_lock);
2096 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2097 g_mutex_unlock(priv->observer_list_lock);
2101 modest_header_view_notify_observers(ModestHeaderView *header_view,
2102 GtkTreeModel *model,
2103 const gchar *tny_folder_id)
2105 ModestHeaderViewPrivate *priv = NULL;
2107 ModestHeaderViewObserver *observer;
2110 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2112 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2114 g_mutex_lock(priv->observer_list_lock);
2115 iter = priv->observer_list;
2116 while(iter != NULL){
2117 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2118 modest_header_view_observer_update(observer, model,
2120 iter = g_slist_next(iter);
2122 g_mutex_unlock(priv->observer_list_lock);