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;
158 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
161 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
162 struct _HeadersCountChangedHelper {
163 ModestHeaderView *self;
164 TnyFolderChange *change;
168 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
169 MODEST_TYPE_HEADER_VIEW, \
170 ModestHeaderViewPrivate))
174 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
177 HEADER_SELECTED_SIGNAL,
178 HEADER_ACTIVATED_SIGNAL,
179 ITEM_NOT_FOUND_SIGNAL,
180 MSG_COUNT_CHANGED_SIGNAL,
181 UPDATING_MSG_LIST_SIGNAL,
186 static GObjectClass *parent_class = NULL;
188 /* uncomment the following if you have defined any signals */
189 static guint signals[LAST_SIGNAL] = {0};
192 modest_header_view_get_type (void)
194 static GType my_type = 0;
196 static const GTypeInfo my_info = {
197 sizeof(ModestHeaderViewClass),
198 NULL, /* base init */
199 NULL, /* base finalize */
200 (GClassInitFunc) modest_header_view_class_init,
201 NULL, /* class finalize */
202 NULL, /* class data */
203 sizeof(ModestHeaderView),
205 (GInstanceInitFunc) modest_header_view_init,
209 static const GInterfaceInfo tny_folder_observer_info =
211 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
212 NULL, /* interface_finalize */
213 NULL /* interface_data */
215 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
219 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
220 &tny_folder_observer_info);
228 modest_header_view_class_init (ModestHeaderViewClass *klass)
230 GObjectClass *gobject_class;
231 gobject_class = (GObjectClass*) klass;
233 parent_class = g_type_class_peek_parent (klass);
234 gobject_class->finalize = modest_header_view_finalize;
235 gobject_class->dispose = modest_header_view_dispose;
237 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
239 signals[HEADER_SELECTED_SIGNAL] =
240 g_signal_new ("header_selected",
241 G_TYPE_FROM_CLASS (gobject_class),
243 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
245 g_cclosure_marshal_VOID__POINTER,
246 G_TYPE_NONE, 1, G_TYPE_POINTER);
248 signals[HEADER_ACTIVATED_SIGNAL] =
249 g_signal_new ("header_activated",
250 G_TYPE_FROM_CLASS (gobject_class),
252 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
254 g_cclosure_marshal_VOID__POINTER,
255 G_TYPE_NONE, 1, G_TYPE_POINTER);
258 signals[ITEM_NOT_FOUND_SIGNAL] =
259 g_signal_new ("item_not_found",
260 G_TYPE_FROM_CLASS (gobject_class),
262 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
264 g_cclosure_marshal_VOID__INT,
265 G_TYPE_NONE, 1, G_TYPE_INT);
267 signals[MSG_COUNT_CHANGED_SIGNAL] =
268 g_signal_new ("msg_count_changed",
269 G_TYPE_FROM_CLASS (gobject_class),
271 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
273 modest_marshal_VOID__POINTER_POINTER,
274 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
276 signals[UPDATING_MSG_LIST_SIGNAL] =
277 g_signal_new ("updating-msg-list",
278 G_TYPE_FROM_CLASS (gobject_class),
280 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
282 g_cclosure_marshal_VOID__BOOLEAN,
283 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
287 tny_folder_observer_init (TnyFolderObserverIface *klass)
289 klass->update = folder_monitor_update;
292 static GtkTreeViewColumn*
293 get_new_column (const gchar *name, GtkCellRenderer *renderer,
294 gboolean resizable, gint sort_col_id, gboolean show_as_text,
295 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
297 GtkTreeViewColumn *column;
299 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
300 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
302 gtk_tree_view_column_set_resizable (column, resizable);
304 gtk_tree_view_column_set_expand (column, TRUE);
307 gtk_tree_view_column_add_attribute (column, renderer, "text",
309 if (sort_col_id >= 0)
310 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
312 gtk_tree_view_column_set_sort_indicator (column, FALSE);
313 gtk_tree_view_column_set_reorderable (column, TRUE);
316 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
323 remove_all_columns (ModestHeaderView *obj)
325 GList *columns, *cursor;
327 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
329 for (cursor = columns; cursor; cursor = cursor->next)
330 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
331 GTK_TREE_VIEW_COLUMN(cursor->data));
332 g_list_free (columns);
336 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
338 GtkTreeModel *tree_filter, *sortable;
339 GtkTreeViewColumn *column=NULL;
340 GtkTreeSelection *selection = NULL;
341 GtkCellRenderer *renderer_msgtype,*renderer_header,
342 *renderer_attach, *renderer_compact_date_or_status;
343 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
344 *renderer_subject, *renderer_subject_box, *renderer_recpt,
346 ModestHeaderViewPrivate *priv;
347 GtkTreeViewColumn *compact_column = NULL;
350 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
351 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
353 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
355 /* FIXME: check whether these renderers need to be freed */
356 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
357 renderer_attach = gtk_cell_renderer_pixbuf_new ();
358 renderer_priority = gtk_cell_renderer_pixbuf_new ();
359 renderer_header = gtk_cell_renderer_text_new ();
361 renderer_compact_header = modest_vbox_cell_renderer_new ();
362 renderer_recpt_box = modest_hbox_cell_renderer_new ();
363 renderer_subject_box = modest_hbox_cell_renderer_new ();
364 renderer_recpt = gtk_cell_renderer_text_new ();
365 renderer_subject = gtk_cell_renderer_text_new ();
366 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
368 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
369 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
370 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
371 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
372 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
373 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
374 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
375 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
376 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
377 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
378 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
379 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
380 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
381 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
383 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
384 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
385 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
386 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
387 g_object_set(G_OBJECT(renderer_header),
388 "ellipsize", PANGO_ELLIPSIZE_END,
390 g_object_set (G_OBJECT (renderer_subject),
391 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
393 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
394 g_object_set (G_OBJECT (renderer_recpt),
395 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
397 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
398 g_object_set(G_OBJECT(renderer_compact_date_or_status),
399 "xalign", 1.0, "yalign", 0.0,
401 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
402 g_object_set (G_OBJECT (renderer_priority),
403 "yalign", 1.0, NULL);
404 g_object_set (G_OBJECT (renderer_attach),
405 "yalign", 0.0, NULL);
407 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
408 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
409 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
411 remove_all_columns (self);
413 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
414 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
415 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
416 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
418 /* Add new columns */
419 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
420 ModestHeaderViewColumn col =
421 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
423 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
424 g_printerr ("modest: invalid column %d in column list\n", col);
430 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
431 column = get_new_column (_("M"), renderer_msgtype, FALSE,
432 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
434 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
436 gtk_tree_view_column_set_fixed_width (column, 45);
439 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
440 column = get_new_column (_("A"), renderer_attach, FALSE,
441 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
443 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
445 gtk_tree_view_column_set_fixed_width (column, 45);
449 case MODEST_HEADER_VIEW_COLUMN_FROM:
450 column = get_new_column (_("From"), renderer_header, TRUE,
451 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
453 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
454 GINT_TO_POINTER(TRUE));
457 case MODEST_HEADER_VIEW_COLUMN_TO:
458 column = get_new_column (_("To"), renderer_header, TRUE,
459 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
461 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
462 GINT_TO_POINTER(FALSE));
465 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
466 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
467 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
469 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
470 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
471 compact_column = column;
474 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
475 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
476 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
478 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
479 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
480 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
481 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
482 compact_column = column;
486 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
487 column = get_new_column (_("Subject"), renderer_header, TRUE,
488 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
490 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
494 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
495 column = get_new_column (_("Received"), renderer_header, TRUE,
496 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
498 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
499 GINT_TO_POINTER(TRUE));
502 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
503 column = get_new_column (_("Sent"), renderer_header, TRUE,
504 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
506 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
507 GINT_TO_POINTER(FALSE));
510 case MODEST_HEADER_VIEW_COLUMN_SIZE:
511 column = get_new_column (_("Size"), renderer_header, TRUE,
512 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
514 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
517 case MODEST_HEADER_VIEW_COLUMN_STATUS:
518 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
519 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
521 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
526 g_return_val_if_reached(FALSE);
529 /* we keep the column id around */
530 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
531 GINT_TO_POINTER(col));
533 /* we need this ptr when sorting the rows */
534 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
536 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
540 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
541 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
542 (GtkTreeIterCompareFunc) cmp_rows,
543 compact_column, NULL);
544 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
545 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
546 (GtkTreeIterCompareFunc) cmp_subject_rows,
547 compact_column, NULL);
555 modest_header_view_init (ModestHeaderView *obj)
557 ModestHeaderViewPrivate *priv;
560 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
564 priv->monitor = NULL;
565 priv->observers_lock = g_mutex_new ();
567 priv->status = HEADER_VIEW_INIT;
568 priv->status_timeout = 0;
569 priv->notify_status = TRUE;
571 priv->observer_list_lock = g_mutex_new();
572 priv->observer_list = NULL;
574 priv->clipboard = modest_runtime_get_email_clipboard ();
575 priv->hidding_ids = NULL;
576 priv->n_selected = 0;
577 priv->selection_changed_handler = 0;
578 priv->acc_removed_handler = 0;
580 /* Sort parameters */
581 for (j=0; j < 2; j++) {
582 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
583 priv->sort_colid[j][i] = -1;
584 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
588 setup_drag_and_drop (GTK_WIDGET(obj));
592 modest_header_view_dispose (GObject *obj)
594 ModestHeaderView *self;
595 ModestHeaderViewPrivate *priv;
596 GtkTreeSelection *sel;
598 self = MODEST_HEADER_VIEW(obj);
599 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
601 /* Free in the dispose to avoid unref cycles */
603 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
604 g_object_unref (G_OBJECT (priv->folder));
608 /* We need to do this here in the dispose because the
609 selection won't exist when finalizing */
610 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
611 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
612 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
613 priv->selection_changed_handler = 0;
616 G_OBJECT_CLASS(parent_class)->dispose (obj);
620 modest_header_view_finalize (GObject *obj)
622 ModestHeaderView *self;
623 ModestHeaderViewPrivate *priv;
625 self = MODEST_HEADER_VIEW(obj);
626 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
628 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
629 priv->acc_removed_handler)) {
630 g_signal_handler_disconnect (modest_runtime_get_account_store (),
631 priv->acc_removed_handler);
634 /* There is no need to lock because there should not be any
635 * reference to self now. */
636 g_mutex_free(priv->observer_list_lock);
637 g_slist_free(priv->observer_list);
639 g_mutex_lock (priv->observers_lock);
641 tny_folder_monitor_stop (priv->monitor);
642 g_object_unref (G_OBJECT (priv->monitor));
644 g_mutex_unlock (priv->observers_lock);
645 g_mutex_free (priv->observers_lock);
647 /* Clear hidding array created by cut operation */
648 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
650 G_OBJECT_CLASS(parent_class)->finalize (obj);
655 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
658 GtkTreeSelection *sel;
659 ModestHeaderView *self;
660 ModestHeaderViewPrivate *priv;
662 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
665 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
666 self = MODEST_HEADER_VIEW(obj);
667 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
669 modest_header_view_set_style (self, style);
671 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
672 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
673 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
675 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
676 TRUE); /* alternating row colors */
678 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
679 priv->selection_changed_handler =
680 g_signal_connect_after (sel, "changed",
681 G_CALLBACK(on_selection_changed), self);
683 g_signal_connect (self, "row-activated",
684 G_CALLBACK (on_header_row_activated), NULL);
686 g_signal_connect (self, "focus-in-event",
687 G_CALLBACK(on_focus_in), NULL);
688 g_signal_connect (self, "focus-out-event",
689 G_CALLBACK(on_focus_out), NULL);
691 g_signal_connect (self, "button-press-event",
692 G_CALLBACK(on_button_press_event), NULL);
693 g_signal_connect (self, "button-release-event",
694 G_CALLBACK(on_button_release_event), NULL);
696 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
698 G_CALLBACK (on_account_removed),
701 g_signal_connect (self, "expose-event",
702 G_CALLBACK(modest_header_view_on_expose_event),
705 return GTK_WIDGET(self);
710 modest_header_view_count_selected_headers (ModestHeaderView *self)
712 GtkTreeSelection *sel;
715 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
717 /* Get selection object and check selected rows count */
718 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
719 selected_rows = gtk_tree_selection_count_selected_rows (sel);
721 return selected_rows;
725 modest_header_view_has_selected_headers (ModestHeaderView *self)
727 GtkTreeSelection *sel;
730 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
732 /* Get selection object and check selected rows count */
733 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
734 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
741 modest_header_view_get_selected_headers (ModestHeaderView *self)
743 GtkTreeSelection *sel;
744 ModestHeaderViewPrivate *priv;
745 TnyList *header_list = NULL;
747 GList *list, *tmp = NULL;
748 GtkTreeModel *tree_model = NULL;
751 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
753 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
755 /* Get selected rows */
756 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
757 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
760 header_list = tny_simple_list_new();
762 list = g_list_reverse (list);
765 /* get header from selection */
766 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
767 gtk_tree_model_get (tree_model, &iter,
768 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
770 /* Prepend to list */
771 tny_list_prepend (header_list, G_OBJECT (header));
772 g_object_unref (G_OBJECT (header));
774 tmp = g_list_next (tmp);
777 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
784 /* scroll our list view so the selected item is visible */
786 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
788 #ifdef MODEST_PLATFORM_GNOME
790 GtkTreePath *selected_path;
791 GtkTreePath *start, *end;
795 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
796 selected_path = gtk_tree_model_get_path (model, iter);
798 start = gtk_tree_path_new ();
799 end = gtk_tree_path_new ();
801 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
803 if (gtk_tree_path_compare (selected_path, start) < 0 ||
804 gtk_tree_path_compare (end, selected_path) < 0)
805 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
806 selected_path, NULL, TRUE,
809 gtk_tree_path_free (selected_path);
810 gtk_tree_path_free (start);
811 gtk_tree_path_free (end);
813 #endif /* MODEST_PLATFORM_GNOME */
818 modest_header_view_select_next (ModestHeaderView *self)
820 GtkTreeSelection *sel;
825 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
827 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
828 path = get_selected_row (GTK_TREE_VIEW(self), &model);
829 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
830 /* Unselect previous path */
831 gtk_tree_selection_unselect_path (sel, path);
833 /* Move path down and selects new one */
834 if (gtk_tree_model_iter_next (model, &iter)) {
835 gtk_tree_selection_select_iter (sel, &iter);
836 scroll_to_selected (self, &iter, FALSE);
838 gtk_tree_path_free(path);
844 modest_header_view_select_prev (ModestHeaderView *self)
846 GtkTreeSelection *sel;
851 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
853 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
854 path = get_selected_row (GTK_TREE_VIEW(self), &model);
855 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
856 /* Unselect previous path */
857 gtk_tree_selection_unselect_path (sel, path);
860 if (gtk_tree_path_prev (path)) {
861 gtk_tree_model_get_iter (model, &iter, path);
863 /* Select the new one */
864 gtk_tree_selection_select_iter (sel, &iter);
865 scroll_to_selected (self, &iter, TRUE);
868 gtk_tree_path_free (path);
873 modest_header_view_get_columns (ModestHeaderView *self)
875 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
877 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
883 modest_header_view_set_style (ModestHeaderView *self,
884 ModestHeaderViewStyle style)
886 ModestHeaderViewPrivate *priv;
887 gboolean show_col_headers = FALSE;
888 ModestHeaderViewStyle old_style;
890 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
891 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
894 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
895 if (priv->style == style)
896 return TRUE; /* nothing to do */
899 case MODEST_HEADER_VIEW_STYLE_DETAILS:
900 show_col_headers = TRUE;
902 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
905 g_return_val_if_reached (FALSE);
907 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
908 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
910 old_style = priv->style;
917 ModestHeaderViewStyle
918 modest_header_view_get_style (ModestHeaderView *self)
920 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
922 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
925 /* This is used to automatically select the first header if the user
926 * has not selected any header yet.
929 modest_header_view_on_expose_event(GtkTreeView *header_view,
930 GdkEventExpose *event,
933 GtkTreeSelection *sel;
935 GtkTreeIter tree_iter;
937 model = gtk_tree_view_get_model(header_view);
942 sel = gtk_tree_view_get_selection(header_view);
943 if(!gtk_tree_selection_count_selected_rows(sel))
944 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
945 /* Prevent the widget from getting the focus
946 when selecting the first item */
947 g_object_set(header_view, "can-focus", FALSE, NULL);
948 gtk_tree_selection_select_iter(sel, &tree_iter);
949 g_object_set(header_view, "can-focus", TRUE, NULL);
956 * This function sets a sortable model in the header view. It's just
957 * used for developing purposes, because it only does a
958 * gtk_tree_view_set_model
961 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
963 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
964 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
965 /* GtkTreeModel *old_model; */
966 /* ModestHeaderViewPrivate *priv; */
967 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
968 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
970 /* /\* Set new model *\/ */
971 /* gtk_tree_view_set_model (header_view, model); */
973 gtk_tree_view_set_model (header_view, model);
977 modest_header_view_get_folder (ModestHeaderView *self)
979 ModestHeaderViewPrivate *priv;
981 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
983 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
986 g_object_ref (priv->folder);
992 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
996 ModestHeaderViewPrivate *priv;
997 GList *cols, *cursor;
998 GtkTreeModel *filter_model, *sortable;
1000 GtkSortType sort_type;
1002 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1004 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1006 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1007 folder, FALSE, NULL, NULL, NULL);
1009 /* Add IDLE observer (monitor) and another folder observer for
1010 new messages (self) */
1011 g_mutex_lock (priv->observers_lock);
1012 if (priv->monitor) {
1013 tny_folder_monitor_stop (priv->monitor);
1014 g_object_unref (G_OBJECT (priv->monitor));
1016 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1017 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1018 tny_folder_monitor_start (priv->monitor);
1019 g_mutex_unlock (priv->observers_lock);
1021 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1022 g_object_unref (G_OBJECT (headers));
1024 /* Init filter_row function to examine empty status */
1025 priv->status = HEADER_VIEW_INIT;
1027 /* Create a tree model filter to hide and show rows for cut operations */
1028 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1029 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1033 g_object_unref (G_OBJECT (sortable));
1035 /* install our special sorting functions */
1036 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1038 /* Restore sort column id */
1040 type = modest_tny_folder_guess_folder_type (folder);
1041 if (type == TNY_FOLDER_TYPE_INVALID)
1042 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1044 sort_colid = modest_header_view_get_sort_column_id (self, type);
1045 sort_type = modest_header_view_get_sort_type (self, type);
1046 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1049 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1050 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1051 (GtkTreeIterCompareFunc) cmp_rows,
1053 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1054 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1055 (GtkTreeIterCompareFunc) cmp_subject_rows,
1060 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1061 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1062 tny_folder_get_id(folder));
1063 g_object_unref (G_OBJECT (filter_model));
1064 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1065 /* g_object_unref (G_OBJECT (sortable)); */
1072 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1074 GtkSortType sort_type)
1076 ModestHeaderViewPrivate *priv = NULL;
1077 GtkTreeModel *tree_filter, *sortable = NULL;
1080 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1081 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1083 /* Get model and private data */
1084 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1085 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1086 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1087 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1089 /* Sort tree model */
1090 type = modest_tny_folder_guess_folder_type (priv->folder);
1091 if (type == TNY_FOLDER_TYPE_INVALID)
1092 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1094 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1097 /* Store new sort parameters */
1098 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1103 modest_header_view_set_sort_params (ModestHeaderView *self,
1105 GtkSortType sort_type,
1108 ModestHeaderViewPrivate *priv;
1109 ModestHeaderViewStyle style;
1111 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1112 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1113 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1115 style = modest_header_view_get_style (self);
1116 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1118 priv->sort_colid[style][type] = sort_colid;
1119 priv->sort_type[style][type] = sort_type;
1123 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1126 ModestHeaderViewPrivate *priv;
1127 ModestHeaderViewStyle style;
1129 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1130 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1132 style = modest_header_view_get_style (self);
1133 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1135 return priv->sort_colid[style][type];
1139 modest_header_view_get_sort_type (ModestHeaderView *self,
1142 ModestHeaderViewPrivate *priv;
1143 ModestHeaderViewStyle style;
1145 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1146 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1148 style = modest_header_view_get_style (self);
1149 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1151 return priv->sort_type[style][type];
1155 ModestHeaderView *header_view;
1156 RefreshAsyncUserCallback cb;
1161 folder_refreshed_cb (ModestMailOperation *mail_op,
1165 ModestHeaderViewPrivate *priv;
1166 SetFolderHelper *info;
1168 info = (SetFolderHelper*) user_data;
1170 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1174 info->cb (mail_op, folder, info->user_data);
1176 /* Start the folder count changes observer. We do not need it
1177 before the refresh. Note that the monitor could still be
1178 called for this refresh but now we know that the callback
1179 was previously called */
1180 g_mutex_lock (priv->observers_lock);
1181 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1182 g_mutex_unlock (priv->observers_lock);
1184 /* Notify the observers that the update is over */
1185 g_signal_emit (G_OBJECT (info->header_view),
1186 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1188 /* Allow filtering notifications from now on if the current
1189 folder is still the same (if not then the user has selected
1190 another one to refresh, we should wait until that refresh
1192 if (priv->folder == folder)
1193 priv->notify_status = TRUE;
1196 g_object_unref (info->header_view);
1201 refresh_folder_error_handler (ModestMailOperation *mail_op,
1204 const GError *error = modest_mail_operation_get_error (mail_op);
1206 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1207 error->code == TNY_IO_ERROR_WRITE ||
1208 error->code == TNY_IO_ERROR_READ) {
1209 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1210 /* If the mail op has been cancelled then it's not an error: don't show any message */
1211 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1212 modest_platform_information_banner (NULL, NULL,
1214 "cerm_device_memory_full"));
1220 modest_header_view_set_folder (ModestHeaderView *self,
1222 RefreshAsyncUserCallback callback,
1225 ModestHeaderViewPrivate *priv;
1226 SetFolderHelper *info;
1227 ModestWindow *main_win;
1229 g_return_if_fail (self);
1231 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1233 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1234 FALSE); /* don't create */
1236 g_warning ("%s: BUG: no main window", __FUNCTION__);
1241 if (priv->status_timeout)
1242 g_source_remove (priv->status_timeout);
1244 g_mutex_lock (priv->observers_lock);
1245 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1246 g_object_unref (priv->folder);
1247 priv->folder = NULL;
1248 g_mutex_unlock (priv->observers_lock);
1252 ModestMailOperation *mail_op = NULL;
1253 GtkTreeSelection *selection;
1255 /* Set folder in the model */
1256 modest_header_view_set_folder_intern (self, folder);
1258 /* Pick my reference. Nothing to do with the mail operation */
1259 priv->folder = g_object_ref (folder);
1261 /* Do not notify about filterings until the refresh finishes */
1262 priv->notify_status = FALSE;
1264 /* Clear the selection if exists */
1265 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1266 gtk_tree_selection_unselect_all(selection);
1267 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1269 /* Notify the observers that the update begins */
1270 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1273 /* create the helper */
1274 info = g_malloc0 (sizeof(SetFolderHelper));
1275 info->header_view = g_object_ref (self);
1276 info->cb = callback;
1277 info->user_data = user_data;
1279 /* Create the mail operation (source will be the parent widget) */
1280 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1281 refresh_folder_error_handler,
1283 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1286 /* Refresh the folder asynchronously */
1287 modest_mail_operation_refresh_folder (mail_op,
1289 folder_refreshed_cb,
1293 g_object_unref (mail_op);
1295 g_mutex_lock (priv->observers_lock);
1297 if (priv->monitor) {
1298 tny_folder_monitor_stop (priv->monitor);
1299 g_object_unref (G_OBJECT (priv->monitor));
1300 priv->monitor = NULL;
1302 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1304 modest_header_view_notify_observers(self, NULL, NULL);
1306 g_mutex_unlock (priv->observers_lock);
1308 /* Notify the observers that the update is over */
1309 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1315 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1316 GtkTreeViewColumn *column, gpointer userdata)
1318 ModestHeaderView *self = NULL;
1319 ModestHeaderViewPrivate *priv = NULL;
1321 GtkTreeModel *model = NULL;
1322 TnyHeader *header = NULL;
1323 TnyHeaderFlags flags;
1325 self = MODEST_HEADER_VIEW (treeview);
1326 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1328 model = gtk_tree_view_get_model (treeview);
1329 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1332 /* get the first selected item */
1333 gtk_tree_model_get (model, &iter,
1334 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1335 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1338 /* Dont open DELETED messages */
1339 if (flags & TNY_HEADER_FLAG_DELETED) {
1342 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1343 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1344 modest_platform_information_banner (NULL, NULL, msg);
1350 g_signal_emit (G_OBJECT(self),
1351 signals[HEADER_ACTIVATED_SIGNAL],
1357 g_object_unref (G_OBJECT (header));
1362 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1364 GtkTreeModel *model;
1365 TnyHeader *header = NULL;
1366 GtkTreePath *path = NULL;
1368 ModestHeaderView *self;
1369 ModestHeaderViewPrivate *priv;
1370 GList *selected = NULL;
1372 g_return_if_fail (sel);
1373 g_return_if_fail (user_data);
1375 self = MODEST_HEADER_VIEW (user_data);
1376 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1378 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1379 if (selected != NULL)
1380 path = (GtkTreePath *) selected->data;
1381 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1382 return; /* msg was _un_selected */
1384 gtk_tree_model_get (model, &iter,
1385 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1389 g_signal_emit (G_OBJECT(self),
1390 signals[HEADER_SELECTED_SIGNAL],
1393 g_object_unref (G_OBJECT (header));
1395 /* free all items in 'selected' */
1396 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1397 g_list_free (selected);
1401 /* PROTECTED method. It's useful when we want to force a given
1402 selection to reload a msg. For example if we have selected a header
1403 in offline mode, when Modest become online, we want to reload the
1404 message automatically without an user click over the header */
1406 _modest_header_view_change_selection (GtkTreeSelection *selection,
1409 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1410 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1412 on_selection_changed (selection, user_data);
1416 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1423 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1427 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1431 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1439 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1446 /* static int counter = 0; */
1448 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1449 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1450 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1454 case TNY_HEADER_FLAG_ATTACHMENTS:
1456 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1457 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1458 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1459 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1461 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1462 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1464 return cmp ? cmp : t1 - t2;
1466 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1467 TnyHeader *header1 = NULL, *header2 = NULL;
1469 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1470 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1471 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1472 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1474 /* This is for making priority values respect the intuitive sort relationship
1475 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1477 if (header1 && header2) {
1478 cmp = compare_priorities (tny_header_get_priority (header1),
1479 tny_header_get_priority (header2));
1480 g_object_unref (header1);
1481 g_object_unref (header2);
1483 return cmp ? cmp : t1 - t2;
1489 return &iter1 - &iter2; /* oughhhh */
1494 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1500 /* static int counter = 0; */
1502 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1504 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1505 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1506 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1507 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1509 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1510 val2 + modest_text_utils_get_subject_prefix_len(val2),
1517 /* Drag and drop stuff */
1519 drag_data_get_cb (GtkWidget *widget,
1520 GdkDragContext *context,
1521 GtkSelectionData *selection_data,
1526 ModestHeaderView *self = NULL;
1527 ModestHeaderViewPrivate *priv = NULL;
1529 self = MODEST_HEADER_VIEW (widget);
1530 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1532 /* Set the data. Do not use the current selection because it
1533 could be different than the selection at the beginning of
1535 modest_dnd_selection_data_set_paths (selection_data,
1536 priv->drag_begin_cached_selected_rows);
1540 * We're caching the selected rows at the beginning because the
1541 * selection could change between drag-begin and drag-data-get, for
1542 * example if we have a set of rows already selected, and then we
1543 * click in one of them (without SHIFT key pressed) and begin a drag,
1544 * the selection at that moment contains all the selected lines, but
1545 * after dropping the selection, the release event provokes that only
1546 * the row used to begin the drag is selected, so at the end the
1547 * drag&drop affects only one rows instead of all the selected ones.
1551 drag_begin_cb (GtkWidget *widget,
1552 GdkDragContext *context,
1555 ModestHeaderView *self = NULL;
1556 ModestHeaderViewPrivate *priv = NULL;
1557 GtkTreeSelection *selection;
1559 self = MODEST_HEADER_VIEW (widget);
1560 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1562 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1563 priv->drag_begin_cached_selected_rows =
1564 gtk_tree_selection_get_selected_rows (selection, NULL);
1568 * We use the drag-end signal to clear the cached selection, we use
1569 * this because this allways happens, whether or not the d&d was a
1573 drag_end_cb (GtkWidget *widget,
1577 ModestHeaderView *self = NULL;
1578 ModestHeaderViewPrivate *priv = NULL;
1580 self = MODEST_HEADER_VIEW (widget);
1581 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1583 /* Free cached data */
1584 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1585 g_list_free (priv->drag_begin_cached_selected_rows);
1586 priv->drag_begin_cached_selected_rows = NULL;
1589 /* Header view drag types */
1590 const GtkTargetEntry header_view_drag_types[] = {
1591 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1595 enable_drag_and_drop (GtkWidget *self)
1597 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1598 header_view_drag_types,
1599 G_N_ELEMENTS (header_view_drag_types),
1600 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1604 disable_drag_and_drop (GtkWidget *self)
1606 gtk_drag_source_unset (self);
1610 setup_drag_and_drop (GtkWidget *self)
1612 enable_drag_and_drop(self);
1613 g_signal_connect(G_OBJECT (self), "drag_data_get",
1614 G_CALLBACK(drag_data_get_cb), NULL);
1616 g_signal_connect(G_OBJECT (self), "drag_begin",
1617 G_CALLBACK(drag_begin_cb), NULL);
1619 g_signal_connect(G_OBJECT (self), "drag_end",
1620 G_CALLBACK(drag_end_cb), NULL);
1623 static GtkTreePath *
1624 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1626 GtkTreePath *path = NULL;
1627 GtkTreeSelection *sel = NULL;
1630 sel = gtk_tree_view_get_selection(self);
1631 rows = gtk_tree_selection_get_selected_rows (sel, model);
1633 if ((rows == NULL) || (g_list_length(rows) != 1))
1636 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1641 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1648 * This function moves the tree view scroll to the current selected
1649 * row when the widget grabs the focus
1652 on_focus_in (GtkWidget *self,
1653 GdkEventFocus *event,
1656 GtkTreeSelection *selection;
1657 GtkTreeModel *model;
1658 GList *selected = NULL;
1659 GtkTreePath *selected_path = NULL;
1661 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1665 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1666 /* If none selected yet, pick the first one */
1667 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1671 /* Return if the model is empty */
1672 if (!gtk_tree_model_get_iter_first (model, &iter))
1675 path = gtk_tree_model_get_path (model, &iter);
1676 gtk_tree_selection_select_path (selection, path);
1677 gtk_tree_path_free (path);
1680 /* Need to get the all the rows because is selection multiple */
1681 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1682 if (selected == NULL) return FALSE;
1683 selected_path = (GtkTreePath *) selected->data;
1685 /* Check if we need to scroll */
1686 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1687 GtkTreePath *start_path = NULL;
1688 GtkTreePath *end_path = NULL;
1689 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1693 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1694 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1696 /* Scroll to first path */
1697 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1706 gtk_tree_path_free (start_path);
1708 gtk_tree_path_free (end_path);
1710 #endif /* GTK_CHECK_VERSION */
1713 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1714 g_list_free (selected);
1720 on_focus_out (GtkWidget *self,
1721 GdkEventFocus *event,
1725 if (!gtk_widget_is_focus (self)) {
1726 GtkTreeSelection *selection = NULL;
1727 GList *selected_rows = NULL;
1728 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1729 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1730 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1731 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1732 gtk_tree_selection_unselect_all (selection);
1733 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1734 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1735 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1736 g_list_free (selected_rows);
1743 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1745 enable_drag_and_drop(self);
1750 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1752 GtkTreeSelection *selection = NULL;
1753 GtkTreePath *path = NULL;
1754 gboolean already_selected = FALSE;
1756 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1757 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1758 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1761 /* Enable drag and drop onlly if the user clicks on a row that
1762 it's already selected. If not, let him select items using
1764 if (!already_selected) {
1765 disable_drag_and_drop(self);
1769 gtk_tree_path_free(path);
1776 folder_monitor_update (TnyFolderObserver *self,
1777 TnyFolderChange *change)
1779 ModestHeaderViewPrivate *priv = NULL;
1780 TnyFolderChangeChanged changed;
1781 TnyFolder *folder = NULL;
1783 changed = tny_folder_change_get_changed (change);
1785 /* Do not notify the observers if the folder of the header
1786 view has changed before this call to the observer
1788 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1789 folder = tny_folder_change_get_folder (change);
1790 if (folder != priv->folder)
1793 MODEST_DEBUG_BLOCK (
1794 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1795 g_print ("ADDED %d/%d (r/t) \n",
1796 tny_folder_change_get_new_unread_count (change),
1797 tny_folder_change_get_new_all_count (change));
1798 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1799 g_print ("ALL COUNT %d\n",
1800 tny_folder_change_get_new_all_count (change));
1801 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1802 g_print ("UNREAD COUNT %d\n",
1803 tny_folder_change_get_new_unread_count (change));
1804 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1805 g_print ("EXPUNGED %d/%d (r/t) \n",
1806 tny_folder_change_get_new_unread_count (change),
1807 tny_folder_change_get_new_all_count (change));
1808 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1809 g_print ("FOLDER RENAME\n");
1810 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1811 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1812 tny_folder_change_get_new_unread_count (change),
1813 tny_folder_change_get_new_all_count (change));
1814 g_print ("---------------------------------------------------\n");
1817 /* Check folder count */
1818 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1819 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1821 g_mutex_lock (priv->observers_lock);
1823 /* Emit signal to evaluate how headers changes affects
1824 to the window view */
1825 g_signal_emit (G_OBJECT(self),
1826 signals[MSG_COUNT_CHANGED_SIGNAL],
1829 /* Added or removed headers, so data stored on cliboard are invalid */
1830 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1831 modest_email_clipboard_clear (priv->clipboard);
1833 g_mutex_unlock (priv->observers_lock);
1839 g_object_unref (folder);
1843 modest_header_view_is_empty (ModestHeaderView *self)
1845 ModestHeaderViewPrivate *priv;
1847 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1849 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1851 return priv->status == HEADER_VIEW_EMPTY;
1855 modest_header_view_clear (ModestHeaderView *self)
1857 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1859 modest_header_view_set_folder (self, NULL, NULL, NULL);
1863 modest_header_view_copy_selection (ModestHeaderView *header_view)
1865 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1867 /* Copy selection */
1868 _clipboard_set_selected_data (header_view, FALSE);
1872 modest_header_view_cut_selection (ModestHeaderView *header_view)
1874 ModestHeaderViewPrivate *priv = NULL;
1875 const gchar **hidding = NULL;
1876 guint i, n_selected;
1878 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1880 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1882 /* Copy selection */
1883 _clipboard_set_selected_data (header_view, TRUE);
1885 /* Get hidding ids */
1886 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1888 /* Clear hidding array created by previous cut operation */
1889 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1891 /* Copy hidding array */
1892 priv->n_selected = n_selected;
1893 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1894 for (i=0; i < n_selected; i++)
1895 priv->hidding_ids[i] = g_strdup(hidding[i]);
1897 /* Hide cut headers */
1898 modest_header_view_refilter (header_view);
1905 _clipboard_set_selected_data (ModestHeaderView *header_view,
1908 ModestHeaderViewPrivate *priv = NULL;
1909 TnyList *headers = NULL;
1911 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1912 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1914 /* Set selected data on clipboard */
1915 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1916 headers = modest_header_view_get_selected_headers (header_view);
1917 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1920 g_object_unref (headers);
1924 ModestHeaderView *self;
1929 notify_filter_change (gpointer data)
1931 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1933 g_signal_emit (info->self,
1934 signals[MSG_COUNT_CHANGED_SIGNAL],
1935 0, info->folder, NULL);
1941 notify_filter_change_destroy (gpointer data)
1943 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1945 g_object_unref (info->self);
1946 g_object_unref (info->folder);
1947 g_slice_free (NotifyFilterInfo, info);
1951 filter_row (GtkTreeModel *model,
1955 ModestHeaderViewPrivate *priv = NULL;
1956 TnyHeaderFlags flags;
1957 TnyHeader *header = NULL;
1960 gboolean visible = TRUE;
1961 gboolean found = FALSE;
1962 GValue value = {0,};
1963 HeaderViewStatus old_status;
1965 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1966 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1968 /* Get header from model */
1969 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
1970 flags = (TnyHeaderFlags) g_value_get_int (&value);
1971 g_value_unset (&value);
1972 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
1973 header = (TnyHeader *) g_value_get_object (&value);
1974 g_value_unset (&value);
1976 /* Hide deleted and mark as deleted heders */
1977 if (flags & TNY_HEADER_FLAG_DELETED ||
1978 flags & TNY_HEADER_FLAG_EXPUNGED) {
1983 /* If no data on clipboard, return always TRUE */
1984 if (modest_email_clipboard_cleared(priv->clipboard)) {
1989 /* Get message id from header (ensure is a valid id) */
1996 if (priv->hidding_ids != NULL) {
1997 id = tny_header_dup_message_id (header);
1998 for (i=0; i < priv->n_selected && !found; i++)
1999 if (priv->hidding_ids[i] != NULL && id != NULL)
2000 found = (!strcmp (priv->hidding_ids[i], id));
2007 old_status = priv->status;
2008 priv->status = ((gboolean) priv->status) && !visible;
2009 if ((priv->notify_status) && (priv->status != old_status)) {
2010 NotifyFilterInfo *info;
2012 if (priv->status_timeout)
2013 g_source_remove (priv->status_timeout);
2015 info = g_slice_new0 (NotifyFilterInfo);
2016 info->self = g_object_ref (G_OBJECT (user_data));
2017 info->folder = tny_header_get_folder (header);
2018 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2019 notify_filter_change,
2021 notify_filter_change_destroy);
2028 _clear_hidding_filter (ModestHeaderView *header_view)
2030 ModestHeaderViewPrivate *priv = NULL;
2033 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2034 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2036 if (priv->hidding_ids != NULL) {
2037 for (i=0; i < priv->n_selected; i++)
2038 g_free (priv->hidding_ids[i]);
2039 g_free(priv->hidding_ids);
2044 modest_header_view_refilter (ModestHeaderView *header_view)
2046 GtkTreeModel *model = NULL;
2047 ModestHeaderViewPrivate *priv = NULL;
2049 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2050 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2052 /* Hide cut headers */
2053 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2054 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2055 priv->status = HEADER_VIEW_INIT;
2056 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2061 * Called when an account is removed. If I'm showing a folder of the
2062 * account that has been removed then clear the view
2065 on_account_removed (TnyAccountStore *self,
2066 TnyAccount *account,
2069 ModestHeaderViewPrivate *priv = NULL;
2071 /* Ignore changes in transport accounts */
2072 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2075 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2078 TnyAccount *my_account;
2080 my_account = tny_folder_get_account (priv->folder);
2081 if (my_account == account)
2082 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2083 g_object_unref (my_account);
2088 modest_header_view_add_observer(ModestHeaderView *header_view,
2089 ModestHeaderViewObserver *observer)
2091 ModestHeaderViewPrivate *priv;
2093 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2094 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2096 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2098 g_mutex_lock(priv->observer_list_lock);
2099 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2100 g_mutex_unlock(priv->observer_list_lock);
2104 modest_header_view_remove_observer(ModestHeaderView *header_view,
2105 ModestHeaderViewObserver *observer)
2107 ModestHeaderViewPrivate *priv;
2109 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2110 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2112 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2114 g_mutex_lock(priv->observer_list_lock);
2115 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2116 g_mutex_unlock(priv->observer_list_lock);
2120 modest_header_view_notify_observers(ModestHeaderView *header_view,
2121 GtkTreeModel *model,
2122 const gchar *tny_folder_id)
2124 ModestHeaderViewPrivate *priv = NULL;
2126 ModestHeaderViewObserver *observer;
2129 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2131 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2133 g_mutex_lock(priv->observer_list_lock);
2134 iter = priv->observer_list;
2135 while(iter != NULL){
2136 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2137 modest_header_view_observer_update(observer, model,
2139 iter = g_slist_next(iter);
2141 g_mutex_unlock(priv->observer_list_lock);