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 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
393 g_object_set (G_OBJECT (renderer_recpt),
394 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
396 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
397 g_object_set(G_OBJECT(renderer_compact_date_or_status),
398 "xalign", 1.0, "yalign", 0.0,
400 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
401 g_object_set (G_OBJECT (renderer_priority),
402 "yalign", 1.0, NULL);
403 g_object_set (G_OBJECT (renderer_attach),
404 "yalign", 0.0, NULL);
406 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
407 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
408 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
410 remove_all_columns (self);
412 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
413 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
414 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
415 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
417 /* Add new columns */
418 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
419 ModestHeaderViewColumn col =
420 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
422 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
423 g_printerr ("modest: invalid column %d in column list\n", col);
429 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
430 column = get_new_column (_("M"), renderer_msgtype, FALSE,
431 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
433 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
435 gtk_tree_view_column_set_fixed_width (column, 45);
438 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
439 column = get_new_column (_("A"), renderer_attach, FALSE,
440 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
442 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
444 gtk_tree_view_column_set_fixed_width (column, 45);
448 case MODEST_HEADER_VIEW_COLUMN_FROM:
449 column = get_new_column (_("From"), renderer_header, TRUE,
450 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
452 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
453 GINT_TO_POINTER(TRUE));
456 case MODEST_HEADER_VIEW_COLUMN_TO:
457 column = get_new_column (_("To"), renderer_header, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
461 GINT_TO_POINTER(FALSE));
464 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
465 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
466 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
468 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
469 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
470 compact_column = column;
473 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
474 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
475 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
477 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
478 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
479 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
480 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
481 compact_column = column;
485 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
486 column = get_new_column (_("Subject"), renderer_header, TRUE,
487 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
489 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
493 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
494 column = get_new_column (_("Received"), renderer_header, TRUE,
495 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
497 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
498 GINT_TO_POINTER(TRUE));
501 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
502 column = get_new_column (_("Sent"), renderer_header, TRUE,
503 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
505 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
506 GINT_TO_POINTER(FALSE));
509 case MODEST_HEADER_VIEW_COLUMN_SIZE:
510 column = get_new_column (_("Size"), renderer_header, TRUE,
511 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
513 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
516 case MODEST_HEADER_VIEW_COLUMN_STATUS:
517 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
518 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
520 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
525 g_return_val_if_reached(FALSE);
528 /* we keep the column id around */
529 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
530 GINT_TO_POINTER(col));
532 /* we need this ptr when sorting the rows */
533 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
535 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
539 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
540 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
541 (GtkTreeIterCompareFunc) cmp_rows,
542 compact_column, NULL);
543 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
544 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
545 (GtkTreeIterCompareFunc) cmp_subject_rows,
546 compact_column, NULL);
554 modest_header_view_init (ModestHeaderView *obj)
556 ModestHeaderViewPrivate *priv;
559 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
563 priv->monitor = NULL;
564 priv->observers_lock = g_mutex_new ();
566 priv->status = HEADER_VIEW_INIT;
567 priv->status_timeout = 0;
569 priv->observer_list_lock = g_mutex_new();
570 priv->observer_list = NULL;
572 priv->clipboard = modest_runtime_get_email_clipboard ();
573 priv->hidding_ids = NULL;
574 priv->n_selected = 0;
575 priv->selection_changed_handler = 0;
576 priv->acc_removed_handler = 0;
578 /* Sort parameters */
579 for (j=0; j < 2; j++) {
580 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
581 priv->sort_colid[j][i] = -1;
582 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
586 setup_drag_and_drop (GTK_WIDGET(obj));
590 modest_header_view_dispose (GObject *obj)
592 ModestHeaderView *self;
593 ModestHeaderViewPrivate *priv;
594 GtkTreeSelection *sel;
596 self = MODEST_HEADER_VIEW(obj);
597 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
599 /* Free in the dispose to avoid unref cycles */
601 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
602 g_object_unref (G_OBJECT (priv->folder));
606 /* We need to do this here in the dispose because the
607 selection won't exist when finalizing */
608 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
609 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
610 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
611 priv->selection_changed_handler = 0;
614 G_OBJECT_CLASS(parent_class)->dispose (obj);
618 modest_header_view_finalize (GObject *obj)
620 ModestHeaderView *self;
621 ModestHeaderViewPrivate *priv;
623 self = MODEST_HEADER_VIEW(obj);
624 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
626 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
627 priv->acc_removed_handler)) {
628 g_signal_handler_disconnect (modest_runtime_get_account_store (),
629 priv->acc_removed_handler);
632 /* There is no need to lock because there should not be any
633 * reference to self now. */
634 g_mutex_free(priv->observer_list_lock);
635 g_slist_free(priv->observer_list);
637 g_mutex_lock (priv->observers_lock);
639 tny_folder_monitor_stop (priv->monitor);
640 g_object_unref (G_OBJECT (priv->monitor));
642 g_mutex_unlock (priv->observers_lock);
643 g_mutex_free (priv->observers_lock);
645 /* Clear hidding array created by cut operation */
646 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
648 G_OBJECT_CLASS(parent_class)->finalize (obj);
653 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
656 GtkTreeSelection *sel;
657 ModestHeaderView *self;
658 ModestHeaderViewPrivate *priv;
660 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
663 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
664 self = MODEST_HEADER_VIEW(obj);
665 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
667 modest_header_view_set_style (self, style);
669 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
670 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
671 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
673 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
674 TRUE); /* alternating row colors */
676 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
677 priv->selection_changed_handler =
678 g_signal_connect_after (sel, "changed",
679 G_CALLBACK(on_selection_changed), self);
681 g_signal_connect (self, "row-activated",
682 G_CALLBACK (on_header_row_activated), NULL);
684 g_signal_connect (self, "focus-in-event",
685 G_CALLBACK(on_focus_in), NULL);
686 g_signal_connect (self, "focus-out-event",
687 G_CALLBACK(on_focus_out), NULL);
689 g_signal_connect (self, "button-press-event",
690 G_CALLBACK(on_button_press_event), NULL);
691 g_signal_connect (self, "button-release-event",
692 G_CALLBACK(on_button_release_event), NULL);
694 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
696 G_CALLBACK (on_account_removed),
699 g_signal_connect (self, "expose-event",
700 G_CALLBACK(modest_header_view_on_expose_event),
703 return GTK_WIDGET(self);
708 modest_header_view_count_selected_headers (ModestHeaderView *self)
710 GtkTreeSelection *sel;
713 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
715 /* Get selection object and check selected rows count */
716 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
717 selected_rows = gtk_tree_selection_count_selected_rows (sel);
719 return selected_rows;
723 modest_header_view_has_selected_headers (ModestHeaderView *self)
725 GtkTreeSelection *sel;
728 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
730 /* Get selection object and check selected rows count */
731 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
732 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
739 modest_header_view_get_selected_headers (ModestHeaderView *self)
741 GtkTreeSelection *sel;
742 ModestHeaderViewPrivate *priv;
743 TnyList *header_list = NULL;
745 GList *list, *tmp = NULL;
746 GtkTreeModel *tree_model = NULL;
749 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
751 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
753 /* Get selected rows */
754 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
755 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
758 header_list = tny_simple_list_new();
760 list = g_list_reverse (list);
763 /* get header from selection */
764 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
765 gtk_tree_model_get (tree_model, &iter,
766 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
768 /* Prepend to list */
769 tny_list_prepend (header_list, G_OBJECT (header));
770 g_object_unref (G_OBJECT (header));
772 tmp = g_list_next (tmp);
775 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
782 /* scroll our list view so the selected item is visible */
784 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
786 #ifdef MODEST_PLATFORM_GNOME
788 GtkTreePath *selected_path;
789 GtkTreePath *start, *end;
793 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
794 selected_path = gtk_tree_model_get_path (model, iter);
796 start = gtk_tree_path_new ();
797 end = gtk_tree_path_new ();
799 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
801 if (gtk_tree_path_compare (selected_path, start) < 0 ||
802 gtk_tree_path_compare (end, selected_path) < 0)
803 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
804 selected_path, NULL, TRUE,
807 gtk_tree_path_free (selected_path);
808 gtk_tree_path_free (start);
809 gtk_tree_path_free (end);
811 #endif /* MODEST_PLATFORM_GNOME */
816 modest_header_view_select_next (ModestHeaderView *self)
818 GtkTreeSelection *sel;
823 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
825 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
826 path = get_selected_row (GTK_TREE_VIEW(self), &model);
827 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
828 /* Unselect previous path */
829 gtk_tree_selection_unselect_path (sel, path);
831 /* Move path down and selects new one */
832 if (gtk_tree_model_iter_next (model, &iter)) {
833 gtk_tree_selection_select_iter (sel, &iter);
834 scroll_to_selected (self, &iter, FALSE);
836 gtk_tree_path_free(path);
842 modest_header_view_select_prev (ModestHeaderView *self)
844 GtkTreeSelection *sel;
849 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
851 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
852 path = get_selected_row (GTK_TREE_VIEW(self), &model);
853 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
854 /* Unselect previous path */
855 gtk_tree_selection_unselect_path (sel, path);
858 if (gtk_tree_path_prev (path)) {
859 gtk_tree_model_get_iter (model, &iter, path);
861 /* Select the new one */
862 gtk_tree_selection_select_iter (sel, &iter);
863 scroll_to_selected (self, &iter, TRUE);
866 gtk_tree_path_free (path);
871 modest_header_view_get_columns (ModestHeaderView *self)
873 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
875 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
881 modest_header_view_set_style (ModestHeaderView *self,
882 ModestHeaderViewStyle style)
884 ModestHeaderViewPrivate *priv;
885 gboolean show_col_headers = FALSE;
886 ModestHeaderViewStyle old_style;
888 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
889 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
892 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
893 if (priv->style == style)
894 return TRUE; /* nothing to do */
897 case MODEST_HEADER_VIEW_STYLE_DETAILS:
898 show_col_headers = TRUE;
900 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
903 g_return_val_if_reached (FALSE);
905 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
906 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
908 old_style = priv->style;
915 ModestHeaderViewStyle
916 modest_header_view_get_style (ModestHeaderView *self)
918 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
920 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
923 /* This is used to automatically select the first header if the user
924 * has not selected any header yet.
927 modest_header_view_on_expose_event(GtkTreeView *header_view,
928 GdkEventExpose *event,
931 GtkTreeSelection *sel;
933 GtkTreeIter tree_iter;
935 model = gtk_tree_view_get_model(header_view);
940 sel = gtk_tree_view_get_selection(header_view);
941 if(!gtk_tree_selection_count_selected_rows(sel))
942 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
943 /* Prevent the widget from getting the focus
944 when selecting the first item */
945 g_object_set(header_view, "can-focus", FALSE, NULL);
946 gtk_tree_selection_select_iter(sel, &tree_iter);
947 g_object_set(header_view, "can-focus", TRUE, NULL);
954 * This function sets a sortable model in the header view. It's just
955 * used for developing purposes, because it only does a
956 * gtk_tree_view_set_model
959 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
961 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
962 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
963 /* GtkTreeModel *old_model; */
964 /* ModestHeaderViewPrivate *priv; */
965 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
966 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
968 /* /\* Set new model *\/ */
969 /* gtk_tree_view_set_model (header_view, model); */
971 gtk_tree_view_set_model (header_view, model);
975 modest_header_view_get_folder (ModestHeaderView *self)
977 ModestHeaderViewPrivate *priv;
979 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
981 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
984 g_object_ref (priv->folder);
990 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
994 ModestHeaderViewPrivate *priv;
995 GList *cols, *cursor;
996 GtkTreeModel *filter_model, *sortable;
998 GtkSortType sort_type;
1000 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1002 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1004 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1005 folder, FALSE, NULL, NULL, NULL);
1007 /* Add IDLE observer (monitor) and another folder observer for
1008 new messages (self) */
1009 g_mutex_lock (priv->observers_lock);
1010 if (priv->monitor) {
1011 tny_folder_monitor_stop (priv->monitor);
1012 g_object_unref (G_OBJECT (priv->monitor));
1014 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1015 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1016 tny_folder_monitor_start (priv->monitor);
1017 g_mutex_unlock (priv->observers_lock);
1019 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1020 g_object_unref (G_OBJECT (headers));
1022 /* Init filter_row function to examine empty status */
1023 priv->status = HEADER_VIEW_INIT;
1025 /* Create a tree model filter to hide and show rows for cut operations */
1026 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1027 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1031 g_object_unref (G_OBJECT (sortable));
1033 /* install our special sorting functions */
1034 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1036 /* Restore sort column id */
1038 type = modest_tny_folder_guess_folder_type (folder);
1039 if (type == TNY_FOLDER_TYPE_INVALID)
1040 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1042 sort_colid = modest_header_view_get_sort_column_id (self, type);
1043 sort_type = modest_header_view_get_sort_type (self, type);
1044 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1047 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1048 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1049 (GtkTreeIterCompareFunc) cmp_rows,
1051 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1052 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1053 (GtkTreeIterCompareFunc) cmp_subject_rows,
1058 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1059 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1060 tny_folder_get_id(folder));
1061 g_object_unref (G_OBJECT (filter_model));
1062 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1063 /* g_object_unref (G_OBJECT (sortable)); */
1070 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1072 GtkSortType sort_type)
1074 ModestHeaderViewPrivate *priv = NULL;
1075 GtkTreeModel *tree_filter, *sortable = NULL;
1078 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1079 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1081 /* Get model and private data */
1082 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1083 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1084 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1085 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1087 /* Sort tree model */
1088 type = modest_tny_folder_guess_folder_type (priv->folder);
1089 if (type == TNY_FOLDER_TYPE_INVALID)
1090 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1092 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1095 /* Store new sort parameters */
1096 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1101 modest_header_view_set_sort_params (ModestHeaderView *self,
1103 GtkSortType sort_type,
1106 ModestHeaderViewPrivate *priv;
1107 ModestHeaderViewStyle style;
1109 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1110 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1111 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1113 style = modest_header_view_get_style (self);
1114 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1116 priv->sort_colid[style][type] = sort_colid;
1117 priv->sort_type[style][type] = sort_type;
1121 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1124 ModestHeaderViewPrivate *priv;
1125 ModestHeaderViewStyle style;
1127 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1128 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1130 style = modest_header_view_get_style (self);
1131 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1133 return priv->sort_colid[style][type];
1137 modest_header_view_get_sort_type (ModestHeaderView *self,
1140 ModestHeaderViewPrivate *priv;
1141 ModestHeaderViewStyle style;
1143 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1144 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1146 style = modest_header_view_get_style (self);
1147 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1149 return priv->sort_type[style][type];
1153 ModestHeaderView *header_view;
1154 RefreshAsyncUserCallback cb;
1159 folder_refreshed_cb (ModestMailOperation *mail_op,
1163 ModestHeaderViewPrivate *priv;
1164 SetFolderHelper *info;
1166 info = (SetFolderHelper*) user_data;
1168 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1172 info->cb (mail_op, folder, info->user_data);
1174 /* Start the folder count changes observer. We do not need it
1175 before the refresh. Note that the monitor could still be
1176 called for this refresh but now we know that the callback
1177 was previously called */
1178 g_mutex_lock (priv->observers_lock);
1179 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1180 g_mutex_unlock (priv->observers_lock);
1182 /* Notify the observers that the update is over */
1183 g_signal_emit (G_OBJECT (info->header_view),
1184 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1187 g_object_unref (info->header_view);
1192 refresh_folder_error_handler (ModestMailOperation *mail_op,
1195 const GError *error = modest_mail_operation_get_error (mail_op);
1197 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1198 error->code == TNY_IO_ERROR_WRITE ||
1199 error->code == TNY_IO_ERROR_READ) {
1200 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1201 /* If the mail op has been cancelled then it's not an error: don't show any message */
1202 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1203 modest_platform_information_banner (NULL, NULL,
1205 "cerm_device_memory_full"));
1211 modest_header_view_set_folder (ModestHeaderView *self,
1213 RefreshAsyncUserCallback callback,
1216 ModestHeaderViewPrivate *priv;
1217 SetFolderHelper *info;
1218 ModestWindow *main_win;
1220 g_return_if_fail (self);
1222 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1224 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1225 FALSE); /* don't create */
1227 g_warning ("%s: BUG: no main window", __FUNCTION__);
1232 if (priv->status_timeout)
1233 g_source_remove (priv->status_timeout);
1235 g_mutex_lock (priv->observers_lock);
1236 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1237 g_object_unref (priv->folder);
1238 priv->folder = NULL;
1239 g_mutex_unlock (priv->observers_lock);
1243 ModestMailOperation *mail_op = NULL;
1244 GtkTreeSelection *selection;
1246 /* Set folder in the model */
1247 modest_header_view_set_folder_intern (self, folder);
1249 /* Pick my reference. Nothing to do with the mail operation */
1250 priv->folder = g_object_ref (folder);
1252 /* Clear the selection if exists */
1253 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1254 gtk_tree_selection_unselect_all(selection);
1255 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1257 /* Notify the observers that the update begins */
1258 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1261 /* create the helper */
1262 info = g_malloc0 (sizeof(SetFolderHelper));
1263 info->header_view = g_object_ref (self);
1264 info->cb = callback;
1265 info->user_data = user_data;
1267 /* Create the mail operation (source will be the parent widget) */
1268 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1269 refresh_folder_error_handler,
1271 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1274 /* Refresh the folder asynchronously */
1275 modest_mail_operation_refresh_folder (mail_op,
1277 folder_refreshed_cb,
1281 g_object_unref (mail_op);
1283 g_mutex_lock (priv->observers_lock);
1285 if (priv->monitor) {
1286 tny_folder_monitor_stop (priv->monitor);
1287 g_object_unref (G_OBJECT (priv->monitor));
1288 priv->monitor = NULL;
1290 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1292 modest_header_view_notify_observers(self, NULL, NULL);
1294 g_mutex_unlock (priv->observers_lock);
1296 /* Notify the observers that the update is over */
1297 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1303 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1304 GtkTreeViewColumn *column, gpointer userdata)
1306 ModestHeaderView *self = NULL;
1307 ModestHeaderViewPrivate *priv = NULL;
1309 GtkTreeModel *model = NULL;
1310 TnyHeader *header = NULL;
1311 TnyHeaderFlags flags;
1313 self = MODEST_HEADER_VIEW (treeview);
1314 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1316 model = gtk_tree_view_get_model (treeview);
1317 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1320 /* get the first selected item */
1321 gtk_tree_model_get (model, &iter,
1322 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1323 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1326 /* Dont open DELETED messages */
1327 if (flags & TNY_HEADER_FLAG_DELETED) {
1330 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1331 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1332 modest_platform_information_banner (NULL, NULL, msg);
1338 g_signal_emit (G_OBJECT(self),
1339 signals[HEADER_ACTIVATED_SIGNAL],
1345 g_object_unref (G_OBJECT (header));
1350 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1352 GtkTreeModel *model;
1353 TnyHeader *header = NULL;
1354 GtkTreePath *path = NULL;
1356 ModestHeaderView *self;
1357 ModestHeaderViewPrivate *priv;
1358 GList *selected = NULL;
1360 g_return_if_fail (sel);
1361 g_return_if_fail (user_data);
1363 self = MODEST_HEADER_VIEW (user_data);
1364 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1366 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1367 if (selected != NULL)
1368 path = (GtkTreePath *) selected->data;
1369 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1370 return; /* msg was _un_selected */
1372 gtk_tree_model_get (model, &iter,
1373 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1377 g_signal_emit (G_OBJECT(self),
1378 signals[HEADER_SELECTED_SIGNAL],
1381 g_object_unref (G_OBJECT (header));
1383 /* free all items in 'selected' */
1384 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1385 g_list_free (selected);
1389 /* PROTECTED method. It's useful when we want to force a given
1390 selection to reload a msg. For example if we have selected a header
1391 in offline mode, when Modest become online, we want to reload the
1392 message automatically without an user click over the header */
1394 _modest_header_view_change_selection (GtkTreeSelection *selection,
1397 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1398 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1400 on_selection_changed (selection, user_data);
1404 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1411 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1415 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1419 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1427 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1434 /* static int counter = 0; */
1436 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1437 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1438 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1442 case TNY_HEADER_FLAG_ATTACHMENTS:
1444 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1445 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1446 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1447 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1449 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1450 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1452 return cmp ? cmp : t1 - t2;
1454 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1455 TnyHeader *header1 = NULL, *header2 = NULL;
1457 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1458 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1459 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1460 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1462 /* This is for making priority values respect the intuitive sort relationship
1463 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1465 if (header1 && header2) {
1466 cmp = compare_priorities (tny_header_get_priority (header1),
1467 tny_header_get_priority (header2));
1468 g_object_unref (header1);
1469 g_object_unref (header2);
1471 return cmp ? cmp : t1 - t2;
1477 return &iter1 - &iter2; /* oughhhh */
1482 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1488 /* static int counter = 0; */
1490 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1492 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1493 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1494 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1495 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1497 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1498 val2 + modest_text_utils_get_subject_prefix_len(val2),
1505 /* Drag and drop stuff */
1507 drag_data_get_cb (GtkWidget *widget,
1508 GdkDragContext *context,
1509 GtkSelectionData *selection_data,
1514 ModestHeaderView *self = NULL;
1515 ModestHeaderViewPrivate *priv = NULL;
1517 self = MODEST_HEADER_VIEW (widget);
1518 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1520 /* Set the data. Do not use the current selection because it
1521 could be different than the selection at the beginning of
1523 modest_dnd_selection_data_set_paths (selection_data,
1524 priv->drag_begin_cached_selected_rows);
1528 * We're caching the selected rows at the beginning because the
1529 * selection could change between drag-begin and drag-data-get, for
1530 * example if we have a set of rows already selected, and then we
1531 * click in one of them (without SHIFT key pressed) and begin a drag,
1532 * the selection at that moment contains all the selected lines, but
1533 * after dropping the selection, the release event provokes that only
1534 * the row used to begin the drag is selected, so at the end the
1535 * drag&drop affects only one rows instead of all the selected ones.
1539 drag_begin_cb (GtkWidget *widget,
1540 GdkDragContext *context,
1543 ModestHeaderView *self = NULL;
1544 ModestHeaderViewPrivate *priv = NULL;
1545 GtkTreeSelection *selection;
1547 self = MODEST_HEADER_VIEW (widget);
1548 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1550 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1551 priv->drag_begin_cached_selected_rows =
1552 gtk_tree_selection_get_selected_rows (selection, NULL);
1556 * We use the drag-end signal to clear the cached selection, we use
1557 * this because this allways happens, whether or not the d&d was a
1561 drag_end_cb (GtkWidget *widget,
1565 ModestHeaderView *self = NULL;
1566 ModestHeaderViewPrivate *priv = NULL;
1568 self = MODEST_HEADER_VIEW (widget);
1569 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1571 /* Free cached data */
1572 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1573 g_list_free (priv->drag_begin_cached_selected_rows);
1574 priv->drag_begin_cached_selected_rows = NULL;
1577 /* Header view drag types */
1578 const GtkTargetEntry header_view_drag_types[] = {
1579 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1583 enable_drag_and_drop (GtkWidget *self)
1585 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1586 header_view_drag_types,
1587 G_N_ELEMENTS (header_view_drag_types),
1588 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1592 disable_drag_and_drop (GtkWidget *self)
1594 gtk_drag_source_unset (self);
1598 setup_drag_and_drop (GtkWidget *self)
1600 enable_drag_and_drop(self);
1601 g_signal_connect(G_OBJECT (self), "drag_data_get",
1602 G_CALLBACK(drag_data_get_cb), NULL);
1604 g_signal_connect(G_OBJECT (self), "drag_begin",
1605 G_CALLBACK(drag_begin_cb), NULL);
1607 g_signal_connect(G_OBJECT (self), "drag_end",
1608 G_CALLBACK(drag_end_cb), NULL);
1611 static GtkTreePath *
1612 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1614 GtkTreePath *path = NULL;
1615 GtkTreeSelection *sel = NULL;
1618 sel = gtk_tree_view_get_selection(self);
1619 rows = gtk_tree_selection_get_selected_rows (sel, model);
1621 if ((rows == NULL) || (g_list_length(rows) != 1))
1624 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1629 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1636 * This function moves the tree view scroll to the current selected
1637 * row when the widget grabs the focus
1640 on_focus_in (GtkWidget *self,
1641 GdkEventFocus *event,
1644 GtkTreeSelection *selection;
1645 GtkTreeModel *model;
1646 GList *selected = NULL;
1647 GtkTreePath *selected_path = NULL;
1649 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1653 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1654 /* If none selected yet, pick the first one */
1655 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1659 /* Return if the model is empty */
1660 if (!gtk_tree_model_get_iter_first (model, &iter))
1663 path = gtk_tree_model_get_path (model, &iter);
1664 gtk_tree_selection_select_path (selection, path);
1665 gtk_tree_path_free (path);
1668 /* Need to get the all the rows because is selection multiple */
1669 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1670 if (selected == NULL) return FALSE;
1671 selected_path = (GtkTreePath *) selected->data;
1673 /* Check if we need to scroll */
1674 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1675 GtkTreePath *start_path = NULL;
1676 GtkTreePath *end_path = NULL;
1677 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1681 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1682 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1684 /* Scroll to first path */
1685 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1694 gtk_tree_path_free (start_path);
1696 gtk_tree_path_free (end_path);
1698 #endif /* GTK_CHECK_VERSION */
1701 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1702 g_list_free (selected);
1708 on_focus_out (GtkWidget *self,
1709 GdkEventFocus *event,
1713 if (!gtk_widget_is_focus (self)) {
1714 GtkTreeSelection *selection = NULL;
1715 GList *selected_rows = NULL;
1716 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1717 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1718 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1719 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1720 gtk_tree_selection_unselect_all (selection);
1721 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1722 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1723 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1724 g_list_free (selected_rows);
1731 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1733 enable_drag_and_drop(self);
1738 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1740 GtkTreeSelection *selection = NULL;
1741 GtkTreePath *path = NULL;
1742 gboolean already_selected = FALSE;
1744 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1745 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1746 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1749 /* Enable drag and drop onlly if the user clicks on a row that
1750 it's already selected. If not, let him select items using
1752 if (!already_selected) {
1753 disable_drag_and_drop(self);
1757 gtk_tree_path_free(path);
1764 folder_monitor_update (TnyFolderObserver *self,
1765 TnyFolderChange *change)
1767 ModestHeaderViewPrivate *priv = NULL;
1768 TnyFolderChangeChanged changed;
1769 TnyFolder *folder = NULL;
1771 changed = tny_folder_change_get_changed (change);
1773 /* Do not notify the observers if the folder of the header
1774 view has changed before this call to the observer
1776 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1777 folder = tny_folder_change_get_folder (change);
1778 if (folder != priv->folder)
1781 MODEST_DEBUG_BLOCK (
1782 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1783 g_print ("ADDED %d/%d (r/t) \n",
1784 tny_folder_change_get_new_unread_count (change),
1785 tny_folder_change_get_new_all_count (change));
1786 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1787 g_print ("ALL COUNT %d\n",
1788 tny_folder_change_get_new_all_count (change));
1789 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1790 g_print ("UNREAD COUNT %d\n",
1791 tny_folder_change_get_new_unread_count (change));
1792 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1793 g_print ("EXPUNGED %d/%d (r/t) \n",
1794 tny_folder_change_get_new_unread_count (change),
1795 tny_folder_change_get_new_all_count (change));
1796 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1797 g_print ("FOLDER RENAME\n");
1798 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1799 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1800 tny_folder_change_get_new_unread_count (change),
1801 tny_folder_change_get_new_all_count (change));
1802 g_print ("---------------------------------------------------\n");
1805 /* Check folder count */
1806 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1807 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1809 g_mutex_lock (priv->observers_lock);
1811 /* Emit signal to evaluate how headers changes affects
1812 to the window view */
1813 g_signal_emit (G_OBJECT(self),
1814 signals[MSG_COUNT_CHANGED_SIGNAL],
1817 /* Added or removed headers, so data stored on cliboard are invalid */
1818 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1819 modest_email_clipboard_clear (priv->clipboard);
1821 g_mutex_unlock (priv->observers_lock);
1827 g_object_unref (folder);
1831 modest_header_view_is_empty (ModestHeaderView *self)
1833 ModestHeaderViewPrivate *priv;
1835 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1837 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1839 return priv->status == HEADER_VIEW_EMPTY;
1843 modest_header_view_clear (ModestHeaderView *self)
1845 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1847 modest_header_view_set_folder (self, NULL, NULL, NULL);
1851 modest_header_view_copy_selection (ModestHeaderView *header_view)
1853 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1855 /* Copy selection */
1856 _clipboard_set_selected_data (header_view, FALSE);
1860 modest_header_view_cut_selection (ModestHeaderView *header_view)
1862 ModestHeaderViewPrivate *priv = NULL;
1863 const gchar **hidding = NULL;
1864 guint i, n_selected;
1866 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1868 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1870 /* Copy selection */
1871 _clipboard_set_selected_data (header_view, TRUE);
1873 /* Get hidding ids */
1874 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1876 /* Clear hidding array created by previous cut operation */
1877 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1879 /* Copy hidding array */
1880 priv->n_selected = n_selected;
1881 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1882 for (i=0; i < n_selected; i++)
1883 priv->hidding_ids[i] = g_strdup(hidding[i]);
1885 /* Hide cut headers */
1886 modest_header_view_refilter (header_view);
1893 _clipboard_set_selected_data (ModestHeaderView *header_view,
1896 ModestHeaderViewPrivate *priv = NULL;
1897 TnyList *headers = NULL;
1899 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1900 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1902 /* Set selected data on clipboard */
1903 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1904 headers = modest_header_view_get_selected_headers (header_view);
1905 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1908 g_object_unref (headers);
1912 ModestHeaderView *self;
1917 notify_filter_change (gpointer data)
1919 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1921 g_signal_emit (info->self,
1922 signals[MSG_COUNT_CHANGED_SIGNAL],
1923 0, info->folder, NULL);
1929 notify_filter_change_destroy (gpointer data)
1931 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1933 g_object_unref (info->self);
1934 g_object_unref (info->folder);
1935 g_slice_free (NotifyFilterInfo, info);
1939 filter_row (GtkTreeModel *model,
1943 ModestHeaderViewPrivate *priv = NULL;
1944 TnyHeaderFlags flags;
1945 TnyHeader *header = NULL;
1948 gboolean visible = TRUE;
1949 gboolean found = FALSE;
1950 GValue value = {0,};
1951 HeaderViewStatus old_status;
1953 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1954 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1956 /* Get header from model */
1957 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
1958 flags = (TnyHeaderFlags) g_value_get_int (&value);
1959 g_value_unset (&value);
1960 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
1961 header = (TnyHeader *) g_value_get_object (&value);
1962 g_value_unset (&value);
1964 /* Hide deleted and mark as deleted heders */
1965 if (flags & TNY_HEADER_FLAG_DELETED ||
1966 flags & TNY_HEADER_FLAG_EXPUNGED) {
1971 /* If no data on clipboard, return always TRUE */
1972 if (modest_email_clipboard_cleared(priv->clipboard)) {
1977 /* Get message id from header (ensure is a valid id) */
1984 if (priv->hidding_ids != NULL) {
1985 id = g_strdup(tny_header_get_message_id (header));
1986 for (i=0; i < priv->n_selected && !found; i++)
1987 if (priv->hidding_ids[i] != NULL && id != NULL)
1988 found = (!strcmp (priv->hidding_ids[i], id));
1995 old_status = priv->status;
1996 priv->status = ((gboolean) priv->status) && !visible;
1997 if (priv->status != old_status) {
1998 NotifyFilterInfo *info;
2000 if (priv->status_timeout)
2001 g_source_remove (priv->status_timeout);
2003 info = g_slice_new0 (NotifyFilterInfo);
2004 info->self = g_object_ref (G_OBJECT (user_data));
2005 info->folder = tny_header_get_folder (header);
2006 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2007 notify_filter_change,
2009 notify_filter_change_destroy);
2016 _clear_hidding_filter (ModestHeaderView *header_view)
2018 ModestHeaderViewPrivate *priv = NULL;
2021 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2022 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2024 if (priv->hidding_ids != NULL) {
2025 for (i=0; i < priv->n_selected; i++)
2026 g_free (priv->hidding_ids[i]);
2027 g_free(priv->hidding_ids);
2032 modest_header_view_refilter (ModestHeaderView *header_view)
2034 GtkTreeModel *model = NULL;
2035 ModestHeaderViewPrivate *priv = NULL;
2037 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2038 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2040 /* Hide cut headers */
2041 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2042 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2043 priv->status = HEADER_VIEW_INIT;
2044 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2049 * Called when an account is removed. If I'm showing a folder of the
2050 * account that has been removed then clear the view
2053 on_account_removed (TnyAccountStore *self,
2054 TnyAccount *account,
2057 ModestHeaderViewPrivate *priv = NULL;
2059 /* Ignore changes in transport accounts */
2060 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2063 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2066 TnyAccount *my_account;
2068 my_account = tny_folder_get_account (priv->folder);
2069 if (my_account == account)
2070 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2071 g_object_unref (account);
2076 modest_header_view_add_observer(ModestHeaderView *header_view,
2077 ModestHeaderViewObserver *observer)
2079 ModestHeaderViewPrivate *priv;
2081 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2082 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2084 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2086 g_mutex_lock(priv->observer_list_lock);
2087 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2088 g_mutex_unlock(priv->observer_list_lock);
2092 modest_header_view_remove_observer(ModestHeaderView *header_view,
2093 ModestHeaderViewObserver *observer)
2095 ModestHeaderViewPrivate *priv;
2097 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2098 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2100 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2102 g_mutex_lock(priv->observer_list_lock);
2103 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2104 g_mutex_unlock(priv->observer_list_lock);
2108 modest_header_view_notify_observers(ModestHeaderView *header_view,
2109 GtkTreeModel *model,
2110 const gchar *tny_folder_id)
2112 ModestHeaderViewPrivate *priv = NULL;
2114 ModestHeaderViewObserver *observer;
2117 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2119 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2121 g_mutex_lock(priv->observer_list_lock);
2122 iter = priv->observer_list;
2123 while(iter != NULL){
2124 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2125 modest_header_view_observer_update(observer, model,
2127 iter = g_slist_next(iter);
2129 g_mutex_unlock(priv->observer_list_lock);