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 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1198 /* If the mail op has been cancelled then it's not an error: don't show any message */
1199 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1200 modest_platform_information_banner (NULL, NULL,
1202 "cerm_device_memory_full"));
1208 modest_header_view_set_folder (ModestHeaderView *self,
1210 RefreshAsyncUserCallback callback,
1213 ModestHeaderViewPrivate *priv;
1214 SetFolderHelper *info;
1215 ModestWindow *main_win;
1217 g_return_if_fail (self);
1219 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1221 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1222 FALSE); /* don't create */
1224 g_warning ("%s: BUG: no main window", __FUNCTION__);
1229 if (priv->status_timeout)
1230 g_source_remove (priv->status_timeout);
1232 g_mutex_lock (priv->observers_lock);
1233 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1234 g_object_unref (priv->folder);
1235 priv->folder = NULL;
1236 g_mutex_unlock (priv->observers_lock);
1240 ModestMailOperation *mail_op = NULL;
1241 GtkTreeSelection *selection;
1243 /* Set folder in the model */
1244 modest_header_view_set_folder_intern (self, folder);
1246 /* Pick my reference. Nothing to do with the mail operation */
1247 priv->folder = g_object_ref (folder);
1249 /* Clear the selection if exists */
1250 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1251 gtk_tree_selection_unselect_all(selection);
1252 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1254 /* Notify the observers that the update begins */
1255 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1258 /* create the helper */
1259 info = g_malloc0 (sizeof(SetFolderHelper));
1260 info->header_view = g_object_ref (self);
1261 info->cb = callback;
1262 info->user_data = user_data;
1264 /* Create the mail operation (source will be the parent widget) */
1265 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1266 refresh_folder_error_handler,
1268 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1271 /* Refresh the folder asynchronously */
1272 modest_mail_operation_refresh_folder (mail_op,
1274 folder_refreshed_cb,
1278 g_object_unref (mail_op);
1280 g_mutex_lock (priv->observers_lock);
1282 if (priv->monitor) {
1283 tny_folder_monitor_stop (priv->monitor);
1284 g_object_unref (G_OBJECT (priv->monitor));
1285 priv->monitor = NULL;
1287 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1289 modest_header_view_notify_observers(self, NULL, NULL);
1291 g_mutex_unlock (priv->observers_lock);
1293 /* Notify the observers that the update is over */
1294 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1300 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1301 GtkTreeViewColumn *column, gpointer userdata)
1303 ModestHeaderView *self = NULL;
1304 ModestHeaderViewPrivate *priv = NULL;
1306 GtkTreeModel *model = NULL;
1307 TnyHeader *header = NULL;
1308 TnyHeaderFlags flags;
1310 self = MODEST_HEADER_VIEW (treeview);
1311 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1313 model = gtk_tree_view_get_model (treeview);
1314 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1317 /* get the first selected item */
1318 gtk_tree_model_get (model, &iter,
1319 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1320 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1323 /* Dont open DELETED messages */
1324 if (flags & TNY_HEADER_FLAG_DELETED) {
1327 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1328 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1329 modest_platform_information_banner (NULL, NULL, msg);
1335 g_signal_emit (G_OBJECT(self),
1336 signals[HEADER_ACTIVATED_SIGNAL],
1342 g_object_unref (G_OBJECT (header));
1347 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1349 GtkTreeModel *model;
1350 TnyHeader *header = NULL;
1351 GtkTreePath *path = NULL;
1353 ModestHeaderView *self;
1354 ModestHeaderViewPrivate *priv;
1355 GList *selected = NULL;
1357 g_return_if_fail (sel);
1358 g_return_if_fail (user_data);
1360 self = MODEST_HEADER_VIEW (user_data);
1361 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1363 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1364 if (selected != NULL)
1365 path = (GtkTreePath *) selected->data;
1366 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1367 return; /* msg was _un_selected */
1369 gtk_tree_model_get (model, &iter,
1370 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1374 g_signal_emit (G_OBJECT(self),
1375 signals[HEADER_SELECTED_SIGNAL],
1378 g_object_unref (G_OBJECT (header));
1380 /* free all items in 'selected' */
1381 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1382 g_list_free (selected);
1386 /* PROTECTED method. It's useful when we want to force a given
1387 selection to reload a msg. For example if we have selected a header
1388 in offline mode, when Modest become online, we want to reload the
1389 message automatically without an user click over the header */
1391 _modest_header_view_change_selection (GtkTreeSelection *selection,
1394 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1395 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1397 on_selection_changed (selection, user_data);
1401 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1408 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1412 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1416 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1424 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1431 /* static int counter = 0; */
1433 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1434 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1435 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1439 case TNY_HEADER_FLAG_ATTACHMENTS:
1441 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1442 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1443 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1444 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1446 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1447 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1449 return cmp ? cmp : t1 - t2;
1451 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1452 TnyHeader *header1 = NULL, *header2 = NULL;
1454 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1455 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1456 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1457 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1459 /* This is for making priority values respect the intuitive sort relationship
1460 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1462 if (header1 && header2) {
1463 cmp = compare_priorities (tny_header_get_priority (header1),
1464 tny_header_get_priority (header2));
1465 g_object_unref (header1);
1466 g_object_unref (header2);
1468 return cmp ? cmp : t1 - t2;
1474 return &iter1 - &iter2; /* oughhhh */
1479 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1485 /* static int counter = 0; */
1487 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1489 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1490 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1491 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1492 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1494 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1495 val2 + modest_text_utils_get_subject_prefix_len(val2),
1502 /* Drag and drop stuff */
1504 drag_data_get_cb (GtkWidget *widget,
1505 GdkDragContext *context,
1506 GtkSelectionData *selection_data,
1511 ModestHeaderView *self = NULL;
1512 ModestHeaderViewPrivate *priv = NULL;
1514 self = MODEST_HEADER_VIEW (widget);
1515 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1517 /* Set the data. Do not use the current selection because it
1518 could be different than the selection at the beginning of
1520 modest_dnd_selection_data_set_paths (selection_data,
1521 priv->drag_begin_cached_selected_rows);
1525 * We're caching the selected rows at the beginning because the
1526 * selection could change between drag-begin and drag-data-get, for
1527 * example if we have a set of rows already selected, and then we
1528 * click in one of them (without SHIFT key pressed) and begin a drag,
1529 * the selection at that moment contains all the selected lines, but
1530 * after dropping the selection, the release event provokes that only
1531 * the row used to begin the drag is selected, so at the end the
1532 * drag&drop affects only one rows instead of all the selected ones.
1536 drag_begin_cb (GtkWidget *widget,
1537 GdkDragContext *context,
1540 ModestHeaderView *self = NULL;
1541 ModestHeaderViewPrivate *priv = NULL;
1542 GtkTreeSelection *selection;
1544 self = MODEST_HEADER_VIEW (widget);
1545 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1547 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1548 priv->drag_begin_cached_selected_rows =
1549 gtk_tree_selection_get_selected_rows (selection, NULL);
1553 * We use the drag-end signal to clear the cached selection, we use
1554 * this because this allways happens, whether or not the d&d was a
1558 drag_end_cb (GtkWidget *widget,
1562 ModestHeaderView *self = NULL;
1563 ModestHeaderViewPrivate *priv = NULL;
1565 self = MODEST_HEADER_VIEW (widget);
1566 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1568 /* Free cached data */
1569 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1570 g_list_free (priv->drag_begin_cached_selected_rows);
1571 priv->drag_begin_cached_selected_rows = NULL;
1574 /* Header view drag types */
1575 const GtkTargetEntry header_view_drag_types[] = {
1576 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1580 enable_drag_and_drop (GtkWidget *self)
1582 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1583 header_view_drag_types,
1584 G_N_ELEMENTS (header_view_drag_types),
1585 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1589 disable_drag_and_drop (GtkWidget *self)
1591 gtk_drag_source_unset (self);
1595 setup_drag_and_drop (GtkWidget *self)
1597 enable_drag_and_drop(self);
1598 g_signal_connect(G_OBJECT (self), "drag_data_get",
1599 G_CALLBACK(drag_data_get_cb), NULL);
1601 g_signal_connect(G_OBJECT (self), "drag_begin",
1602 G_CALLBACK(drag_begin_cb), NULL);
1604 g_signal_connect(G_OBJECT (self), "drag_end",
1605 G_CALLBACK(drag_end_cb), NULL);
1608 static GtkTreePath *
1609 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1611 GtkTreePath *path = NULL;
1612 GtkTreeSelection *sel = NULL;
1615 sel = gtk_tree_view_get_selection(self);
1616 rows = gtk_tree_selection_get_selected_rows (sel, model);
1618 if ((rows == NULL) || (g_list_length(rows) != 1))
1621 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1626 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1633 * This function moves the tree view scroll to the current selected
1634 * row when the widget grabs the focus
1637 on_focus_in (GtkWidget *self,
1638 GdkEventFocus *event,
1641 GtkTreeSelection *selection;
1642 GtkTreeModel *model;
1643 GList *selected = NULL;
1644 GtkTreePath *selected_path = NULL;
1646 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1650 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1651 /* If none selected yet, pick the first one */
1652 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1656 /* Return if the model is empty */
1657 if (!gtk_tree_model_get_iter_first (model, &iter))
1660 path = gtk_tree_model_get_path (model, &iter);
1661 gtk_tree_selection_select_path (selection, path);
1662 gtk_tree_path_free (path);
1665 /* Need to get the all the rows because is selection multiple */
1666 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1667 if (selected == NULL) return FALSE;
1668 selected_path = (GtkTreePath *) selected->data;
1670 /* Check if we need to scroll */
1671 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1672 GtkTreePath *start_path = NULL;
1673 GtkTreePath *end_path = NULL;
1674 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1678 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1679 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1681 /* Scroll to first path */
1682 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1691 gtk_tree_path_free (start_path);
1693 gtk_tree_path_free (end_path);
1695 #endif /* GTK_CHECK_VERSION */
1698 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1699 g_list_free (selected);
1705 on_focus_out (GtkWidget *self,
1706 GdkEventFocus *event,
1710 if (!gtk_widget_is_focus (self)) {
1711 GtkTreeSelection *selection = NULL;
1712 GList *selected_rows = NULL;
1713 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1714 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1715 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1716 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1717 gtk_tree_selection_unselect_all (selection);
1718 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1719 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1720 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1721 g_list_free (selected_rows);
1728 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1730 enable_drag_and_drop(self);
1735 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1737 GtkTreeSelection *selection = NULL;
1738 GtkTreePath *path = NULL;
1739 gboolean already_selected = FALSE;
1741 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1742 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1743 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1746 /* Enable drag and drop onlly if the user clicks on a row that
1747 it's already selected. If not, let him select items using
1749 if (!already_selected) {
1750 disable_drag_and_drop(self);
1754 gtk_tree_path_free(path);
1761 folder_monitor_update (TnyFolderObserver *self,
1762 TnyFolderChange *change)
1764 ModestHeaderViewPrivate *priv = NULL;
1765 TnyFolderChangeChanged changed;
1766 TnyFolder *folder = NULL;
1768 changed = tny_folder_change_get_changed (change);
1770 /* Do not notify the observers if the folder of the header
1771 view has changed before this call to the observer
1773 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1774 folder = tny_folder_change_get_folder (change);
1775 if (folder != priv->folder)
1778 MODEST_DEBUG_BLOCK (
1779 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1780 g_print ("ADDED %d/%d (r/t) \n",
1781 tny_folder_change_get_new_unread_count (change),
1782 tny_folder_change_get_new_all_count (change));
1783 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1784 g_print ("ALL COUNT %d\n",
1785 tny_folder_change_get_new_all_count (change));
1786 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1787 g_print ("UNREAD COUNT %d\n",
1788 tny_folder_change_get_new_unread_count (change));
1789 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1790 g_print ("EXPUNGED %d/%d (r/t) \n",
1791 tny_folder_change_get_new_unread_count (change),
1792 tny_folder_change_get_new_all_count (change));
1793 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1794 g_print ("FOLDER RENAME\n");
1795 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1796 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1797 tny_folder_change_get_new_unread_count (change),
1798 tny_folder_change_get_new_all_count (change));
1799 g_print ("---------------------------------------------------\n");
1802 /* Check folder count */
1803 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1804 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1806 g_mutex_lock (priv->observers_lock);
1808 /* Emit signal to evaluate how headers changes affects
1809 to the window view */
1810 g_signal_emit (G_OBJECT(self),
1811 signals[MSG_COUNT_CHANGED_SIGNAL],
1814 /* Added or removed headers, so data stored on cliboard are invalid */
1815 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1816 modest_email_clipboard_clear (priv->clipboard);
1818 g_mutex_unlock (priv->observers_lock);
1824 g_object_unref (folder);
1828 modest_header_view_is_empty (ModestHeaderView *self)
1830 ModestHeaderViewPrivate *priv;
1832 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1834 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1836 return priv->status == HEADER_VIEW_EMPTY;
1840 modest_header_view_clear (ModestHeaderView *self)
1842 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1844 modest_header_view_set_folder (self, NULL, NULL, NULL);
1848 modest_header_view_copy_selection (ModestHeaderView *header_view)
1850 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1852 /* Copy selection */
1853 _clipboard_set_selected_data (header_view, FALSE);
1857 modest_header_view_cut_selection (ModestHeaderView *header_view)
1859 ModestHeaderViewPrivate *priv = NULL;
1860 const gchar **hidding = NULL;
1861 guint i, n_selected;
1863 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1865 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1867 /* Copy selection */
1868 _clipboard_set_selected_data (header_view, TRUE);
1870 /* Get hidding ids */
1871 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1873 /* Clear hidding array created by previous cut operation */
1874 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1876 /* Copy hidding array */
1877 priv->n_selected = n_selected;
1878 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1879 for (i=0; i < n_selected; i++)
1880 priv->hidding_ids[i] = g_strdup(hidding[i]);
1882 /* Hide cut headers */
1883 modest_header_view_refilter (header_view);
1890 _clipboard_set_selected_data (ModestHeaderView *header_view,
1893 ModestHeaderViewPrivate *priv = NULL;
1894 TnyList *headers = NULL;
1896 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1897 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1899 /* Set selected data on clipboard */
1900 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1901 headers = modest_header_view_get_selected_headers (header_view);
1902 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1905 g_object_unref (headers);
1909 ModestHeaderView *self;
1914 notify_filter_change (gpointer data)
1916 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1918 g_signal_emit (info->self,
1919 signals[MSG_COUNT_CHANGED_SIGNAL],
1920 0, info->folder, NULL);
1926 notify_filter_change_destroy (gpointer data)
1928 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1930 g_object_unref (info->self);
1931 g_object_unref (info->folder);
1932 g_slice_free (NotifyFilterInfo, info);
1936 filter_row (GtkTreeModel *model,
1940 ModestHeaderViewPrivate *priv = NULL;
1941 TnyHeaderFlags flags;
1942 TnyHeader *header = NULL;
1945 gboolean visible = TRUE;
1946 gboolean found = FALSE;
1947 GValue value = {0,};
1948 HeaderViewStatus old_status;
1950 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1951 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1953 /* Get header from model */
1954 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
1955 flags = (TnyHeaderFlags) g_value_get_int (&value);
1956 g_value_unset (&value);
1957 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
1958 header = (TnyHeader *) g_value_get_object (&value);
1959 g_value_unset (&value);
1961 /* Hide deleted and mark as deleted heders */
1962 if (flags & TNY_HEADER_FLAG_DELETED ||
1963 flags & TNY_HEADER_FLAG_EXPUNGED) {
1968 /* If no data on clipboard, return always TRUE */
1969 if (modest_email_clipboard_cleared(priv->clipboard)) {
1974 /* Get message id from header (ensure is a valid id) */
1981 if (priv->hidding_ids != NULL) {
1982 id = g_strdup(tny_header_get_message_id (header));
1983 for (i=0; i < priv->n_selected && !found; i++)
1984 if (priv->hidding_ids[i] != NULL && id != NULL)
1985 found = (!strcmp (priv->hidding_ids[i], id));
1992 old_status = priv->status;
1993 priv->status = ((gboolean) priv->status) && !visible;
1994 if (priv->status != old_status) {
1995 NotifyFilterInfo *info;
1997 if (priv->status_timeout)
1998 g_source_remove (priv->status_timeout);
2000 info = g_slice_new0 (NotifyFilterInfo);
2001 info->self = g_object_ref (G_OBJECT (user_data));
2002 info->folder = tny_header_get_folder (header);
2003 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2004 notify_filter_change,
2006 notify_filter_change_destroy);
2013 _clear_hidding_filter (ModestHeaderView *header_view)
2015 ModestHeaderViewPrivate *priv = NULL;
2018 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2019 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2021 if (priv->hidding_ids != NULL) {
2022 for (i=0; i < priv->n_selected; i++)
2023 g_free (priv->hidding_ids[i]);
2024 g_free(priv->hidding_ids);
2029 modest_header_view_refilter (ModestHeaderView *header_view)
2031 GtkTreeModel *model = NULL;
2032 ModestHeaderViewPrivate *priv = NULL;
2034 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2035 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2037 /* Hide cut headers */
2038 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2039 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2040 priv->status = HEADER_VIEW_INIT;
2041 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2046 * Called when an account is removed. If I'm showing a folder of the
2047 * account that has been removed then clear the view
2050 on_account_removed (TnyAccountStore *self,
2051 TnyAccount *account,
2054 ModestHeaderViewPrivate *priv = NULL;
2056 /* Ignore changes in transport accounts */
2057 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2060 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2063 TnyAccount *my_account;
2065 my_account = tny_folder_get_account (priv->folder);
2066 if (my_account == account)
2067 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2068 g_object_unref (account);
2073 modest_header_view_add_observer(ModestHeaderView *header_view,
2074 ModestHeaderViewObserver *observer)
2076 ModestHeaderViewPrivate *priv;
2078 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2079 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2081 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2083 g_mutex_lock(priv->observer_list_lock);
2084 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2085 g_mutex_unlock(priv->observer_list_lock);
2089 modest_header_view_remove_observer(ModestHeaderView *header_view,
2090 ModestHeaderViewObserver *observer)
2092 ModestHeaderViewPrivate *priv;
2094 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2095 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2097 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2099 g_mutex_lock(priv->observer_list_lock);
2100 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2101 g_mutex_unlock(priv->observer_list_lock);
2105 modest_header_view_notify_observers(ModestHeaderView *header_view,
2106 GtkTreeModel *model,
2107 const gchar *tny_folder_id)
2109 ModestHeaderViewPrivate *priv = NULL;
2111 ModestHeaderViewObserver *observer;
2114 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2116 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2118 g_mutex_lock(priv->observer_list_lock);
2119 iter = priv->observer_list;
2120 while(iter != NULL){
2121 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2122 modest_header_view_observer_update(observer, model,
2124 iter = g_slist_next(iter);
2126 g_mutex_unlock(priv->observer_list_lock);