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 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
998 ModestHeaderView *self;
999 ModestHeaderViewPrivate *priv;
1001 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1003 self = MODEST_HEADER_VIEW (user_data);
1004 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1006 /* Add IDLE observer (monitor) and another folder observer for
1007 new messages (self) */
1008 g_mutex_lock (priv->observers_lock);
1009 if (priv->monitor) {
1010 tny_folder_monitor_stop (priv->monitor);
1011 g_object_unref (G_OBJECT (priv->monitor));
1013 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1014 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1015 tny_folder_monitor_start (priv->monitor);
1016 g_mutex_unlock (priv->observers_lock);
1020 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1024 ModestHeaderViewPrivate *priv;
1025 GList *cols, *cursor;
1026 GtkTreeModel *filter_model, *sortable;
1028 GtkSortType sort_type;
1030 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1032 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1034 /* Start the monitor in the callback of the
1035 tny_gtk_header_list_model_set_folder call. It's crucial to
1036 do it there and not just after the call because we want the
1037 monitor to observe only the headers returned by the
1038 tny_folder_get_headers_async call that it's inside the
1039 tny_gtk_header_list_model_set_folder call. This way the
1040 monitor infrastructure could successfully cope with
1041 duplicates. For example if a tny_folder_add_msg_async is
1042 happening while tny_gtk_header_list_model_set_folder is
1043 invoked, then the first call could add a header that will
1044 be added again by tny_gtk_header_list_model_set_folder, so
1045 we'd end up with duplicate headers. sergio */
1046 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1048 set_folder_intern_get_headers_async_cb,
1051 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1052 g_object_unref (G_OBJECT (headers));
1054 /* Init filter_row function to examine empty status */
1055 priv->status = HEADER_VIEW_INIT;
1057 /* Create a tree model filter to hide and show rows for cut operations */
1058 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1059 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1063 g_object_unref (G_OBJECT (sortable));
1065 /* install our special sorting functions */
1066 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1068 /* Restore sort column id */
1070 type = modest_tny_folder_guess_folder_type (folder);
1071 if (type == TNY_FOLDER_TYPE_INVALID)
1072 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1074 sort_colid = modest_header_view_get_sort_column_id (self, type);
1075 sort_type = modest_header_view_get_sort_type (self, type);
1076 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1079 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1080 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1081 (GtkTreeIterCompareFunc) cmp_rows,
1083 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1084 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1085 (GtkTreeIterCompareFunc) cmp_subject_rows,
1090 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1091 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1092 tny_folder_get_id(folder));
1093 g_object_unref (G_OBJECT (filter_model));
1094 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1095 /* g_object_unref (G_OBJECT (sortable)); */
1102 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1104 GtkSortType sort_type)
1106 ModestHeaderViewPrivate *priv = NULL;
1107 GtkTreeModel *tree_filter, *sortable = NULL;
1110 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1111 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1113 /* Get model and private data */
1114 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1115 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1116 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1117 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1119 /* Sort tree model */
1120 type = modest_tny_folder_guess_folder_type (priv->folder);
1121 if (type == TNY_FOLDER_TYPE_INVALID)
1122 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1124 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1127 /* Store new sort parameters */
1128 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1133 modest_header_view_set_sort_params (ModestHeaderView *self,
1135 GtkSortType sort_type,
1138 ModestHeaderViewPrivate *priv;
1139 ModestHeaderViewStyle style;
1141 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1142 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1143 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1145 style = modest_header_view_get_style (self);
1146 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1148 priv->sort_colid[style][type] = sort_colid;
1149 priv->sort_type[style][type] = sort_type;
1153 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1156 ModestHeaderViewPrivate *priv;
1157 ModestHeaderViewStyle style;
1159 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1160 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1162 style = modest_header_view_get_style (self);
1163 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1165 return priv->sort_colid[style][type];
1169 modest_header_view_get_sort_type (ModestHeaderView *self,
1172 ModestHeaderViewPrivate *priv;
1173 ModestHeaderViewStyle style;
1175 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1176 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1178 style = modest_header_view_get_style (self);
1179 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1181 return priv->sort_type[style][type];
1185 ModestHeaderView *header_view;
1186 RefreshAsyncUserCallback cb;
1191 folder_refreshed_cb (ModestMailOperation *mail_op,
1195 ModestHeaderViewPrivate *priv;
1196 SetFolderHelper *info;
1198 info = (SetFolderHelper*) user_data;
1200 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1204 info->cb (mail_op, folder, info->user_data);
1206 /* Start the folder count changes observer. We do not need it
1207 before the refresh. Note that the monitor could still be
1208 called for this refresh but now we know that the callback
1209 was previously called */
1210 g_mutex_lock (priv->observers_lock);
1211 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1212 g_mutex_unlock (priv->observers_lock);
1214 /* Notify the observers that the update is over */
1215 g_signal_emit (G_OBJECT (info->header_view),
1216 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1218 /* Allow filtering notifications from now on if the current
1219 folder is still the same (if not then the user has selected
1220 another one to refresh, we should wait until that refresh
1222 if (priv->folder == folder)
1223 priv->notify_status = TRUE;
1226 g_object_unref (info->header_view);
1231 refresh_folder_error_handler (ModestMailOperation *mail_op,
1234 const GError *error = modest_mail_operation_get_error (mail_op);
1236 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1237 error->code == TNY_IO_ERROR_WRITE ||
1238 error->code == TNY_IO_ERROR_READ) {
1239 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1240 /* If the mail op has been cancelled then it's not an error: don't show any message */
1241 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1242 modest_platform_information_banner (NULL, NULL,
1244 "cerm_device_memory_full"));
1250 modest_header_view_set_folder (ModestHeaderView *self,
1252 RefreshAsyncUserCallback callback,
1255 ModestHeaderViewPrivate *priv;
1256 SetFolderHelper *info;
1257 ModestWindow *main_win;
1259 g_return_if_fail (self);
1261 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1263 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1264 FALSE); /* don't create */
1266 g_warning ("%s: BUG: no main window", __FUNCTION__);
1271 if (priv->status_timeout)
1272 g_source_remove (priv->status_timeout);
1274 g_mutex_lock (priv->observers_lock);
1275 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1276 g_object_unref (priv->folder);
1277 priv->folder = NULL;
1278 g_mutex_unlock (priv->observers_lock);
1282 ModestMailOperation *mail_op = NULL;
1283 GtkTreeSelection *selection;
1285 /* Set folder in the model */
1286 modest_header_view_set_folder_intern (self, folder);
1288 /* Pick my reference. Nothing to do with the mail operation */
1289 priv->folder = g_object_ref (folder);
1291 /* Do not notify about filterings until the refresh finishes */
1292 priv->notify_status = FALSE;
1294 /* Clear the selection if exists */
1295 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1296 gtk_tree_selection_unselect_all(selection);
1297 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1299 /* Notify the observers that the update begins */
1300 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1303 /* create the helper */
1304 info = g_malloc0 (sizeof(SetFolderHelper));
1305 info->header_view = g_object_ref (self);
1306 info->cb = callback;
1307 info->user_data = user_data;
1309 /* Create the mail operation (source will be the parent widget) */
1310 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1311 refresh_folder_error_handler,
1313 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1316 /* Refresh the folder asynchronously */
1317 modest_mail_operation_refresh_folder (mail_op,
1319 folder_refreshed_cb,
1323 g_object_unref (mail_op);
1325 g_mutex_lock (priv->observers_lock);
1327 if (priv->monitor) {
1328 tny_folder_monitor_stop (priv->monitor);
1329 g_object_unref (G_OBJECT (priv->monitor));
1330 priv->monitor = NULL;
1332 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1334 modest_header_view_notify_observers(self, NULL, NULL);
1336 g_mutex_unlock (priv->observers_lock);
1338 /* Notify the observers that the update is over */
1339 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1345 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1346 GtkTreeViewColumn *column, gpointer userdata)
1348 ModestHeaderView *self = NULL;
1349 ModestHeaderViewPrivate *priv = NULL;
1351 GtkTreeModel *model = NULL;
1352 TnyHeader *header = NULL;
1353 TnyHeaderFlags flags;
1355 self = MODEST_HEADER_VIEW (treeview);
1356 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1358 model = gtk_tree_view_get_model (treeview);
1359 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1362 /* get the first selected item */
1363 gtk_tree_model_get (model, &iter,
1364 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1365 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1368 /* Dont open DELETED messages */
1369 if (flags & TNY_HEADER_FLAG_DELETED) {
1372 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1373 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1374 modest_platform_information_banner (NULL, NULL, msg);
1380 g_signal_emit (G_OBJECT(self),
1381 signals[HEADER_ACTIVATED_SIGNAL],
1387 g_object_unref (G_OBJECT (header));
1392 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1394 GtkTreeModel *model;
1395 TnyHeader *header = NULL;
1396 GtkTreePath *path = NULL;
1398 ModestHeaderView *self;
1399 ModestHeaderViewPrivate *priv;
1400 GList *selected = NULL;
1402 g_return_if_fail (sel);
1403 g_return_if_fail (user_data);
1405 self = MODEST_HEADER_VIEW (user_data);
1406 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1408 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1409 if (selected != NULL)
1410 path = (GtkTreePath *) selected->data;
1411 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1412 return; /* msg was _un_selected */
1414 gtk_tree_model_get (model, &iter,
1415 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1419 g_signal_emit (G_OBJECT(self),
1420 signals[HEADER_SELECTED_SIGNAL],
1423 g_object_unref (G_OBJECT (header));
1425 /* free all items in 'selected' */
1426 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1427 g_list_free (selected);
1431 /* PROTECTED method. It's useful when we want to force a given
1432 selection to reload a msg. For example if we have selected a header
1433 in offline mode, when Modest become online, we want to reload the
1434 message automatically without an user click over the header */
1436 _modest_header_view_change_selection (GtkTreeSelection *selection,
1439 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1440 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1442 on_selection_changed (selection, user_data);
1446 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1453 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1457 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1461 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1469 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1476 /* static int counter = 0; */
1478 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1479 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1480 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1484 case TNY_HEADER_FLAG_ATTACHMENTS:
1486 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1487 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1488 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1489 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1491 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1492 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1494 return cmp ? cmp : t1 - t2;
1496 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1497 TnyHeader *header1 = NULL, *header2 = NULL;
1499 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1500 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1501 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1502 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1504 /* This is for making priority values respect the intuitive sort relationship
1505 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1507 if (header1 && header2) {
1508 cmp = compare_priorities (tny_header_get_priority (header1),
1509 tny_header_get_priority (header2));
1510 g_object_unref (header1);
1511 g_object_unref (header2);
1513 return cmp ? cmp : t1 - t2;
1519 return &iter1 - &iter2; /* oughhhh */
1524 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1530 /* static int counter = 0; */
1532 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1534 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1535 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1536 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1537 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1539 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1540 val2 + modest_text_utils_get_subject_prefix_len(val2),
1547 /* Drag and drop stuff */
1549 drag_data_get_cb (GtkWidget *widget,
1550 GdkDragContext *context,
1551 GtkSelectionData *selection_data,
1556 ModestHeaderView *self = NULL;
1557 ModestHeaderViewPrivate *priv = NULL;
1559 self = MODEST_HEADER_VIEW (widget);
1560 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1562 /* Set the data. Do not use the current selection because it
1563 could be different than the selection at the beginning of
1565 modest_dnd_selection_data_set_paths (selection_data,
1566 priv->drag_begin_cached_selected_rows);
1570 * We're caching the selected rows at the beginning because the
1571 * selection could change between drag-begin and drag-data-get, for
1572 * example if we have a set of rows already selected, and then we
1573 * click in one of them (without SHIFT key pressed) and begin a drag,
1574 * the selection at that moment contains all the selected lines, but
1575 * after dropping the selection, the release event provokes that only
1576 * the row used to begin the drag is selected, so at the end the
1577 * drag&drop affects only one rows instead of all the selected ones.
1581 drag_begin_cb (GtkWidget *widget,
1582 GdkDragContext *context,
1585 ModestHeaderView *self = NULL;
1586 ModestHeaderViewPrivate *priv = NULL;
1587 GtkTreeSelection *selection;
1589 self = MODEST_HEADER_VIEW (widget);
1590 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1592 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1593 priv->drag_begin_cached_selected_rows =
1594 gtk_tree_selection_get_selected_rows (selection, NULL);
1598 * We use the drag-end signal to clear the cached selection, we use
1599 * this because this allways happens, whether or not the d&d was a
1603 drag_end_cb (GtkWidget *widget,
1607 ModestHeaderView *self = NULL;
1608 ModestHeaderViewPrivate *priv = NULL;
1610 self = MODEST_HEADER_VIEW (widget);
1611 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1613 /* Free cached data */
1614 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1615 g_list_free (priv->drag_begin_cached_selected_rows);
1616 priv->drag_begin_cached_selected_rows = NULL;
1619 /* Header view drag types */
1620 const GtkTargetEntry header_view_drag_types[] = {
1621 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1625 enable_drag_and_drop (GtkWidget *self)
1627 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1628 header_view_drag_types,
1629 G_N_ELEMENTS (header_view_drag_types),
1630 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1634 disable_drag_and_drop (GtkWidget *self)
1636 gtk_drag_source_unset (self);
1640 setup_drag_and_drop (GtkWidget *self)
1642 enable_drag_and_drop(self);
1643 g_signal_connect(G_OBJECT (self), "drag_data_get",
1644 G_CALLBACK(drag_data_get_cb), NULL);
1646 g_signal_connect(G_OBJECT (self), "drag_begin",
1647 G_CALLBACK(drag_begin_cb), NULL);
1649 g_signal_connect(G_OBJECT (self), "drag_end",
1650 G_CALLBACK(drag_end_cb), NULL);
1653 static GtkTreePath *
1654 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1656 GtkTreePath *path = NULL;
1657 GtkTreeSelection *sel = NULL;
1660 sel = gtk_tree_view_get_selection(self);
1661 rows = gtk_tree_selection_get_selected_rows (sel, model);
1663 if ((rows == NULL) || (g_list_length(rows) != 1))
1666 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1671 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1678 * This function moves the tree view scroll to the current selected
1679 * row when the widget grabs the focus
1682 on_focus_in (GtkWidget *self,
1683 GdkEventFocus *event,
1686 GtkTreeSelection *selection;
1687 GtkTreeModel *model;
1688 GList *selected = NULL;
1689 GtkTreePath *selected_path = NULL;
1691 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1695 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1696 /* If none selected yet, pick the first one */
1697 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1701 /* Return if the model is empty */
1702 if (!gtk_tree_model_get_iter_first (model, &iter))
1705 path = gtk_tree_model_get_path (model, &iter);
1706 gtk_tree_selection_select_path (selection, path);
1707 gtk_tree_path_free (path);
1710 /* Need to get the all the rows because is selection multiple */
1711 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1712 if (selected == NULL) return FALSE;
1713 selected_path = (GtkTreePath *) selected->data;
1715 /* Check if we need to scroll */
1716 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1717 GtkTreePath *start_path = NULL;
1718 GtkTreePath *end_path = NULL;
1719 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1723 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1724 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1726 /* Scroll to first path */
1727 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1736 gtk_tree_path_free (start_path);
1738 gtk_tree_path_free (end_path);
1740 #endif /* GTK_CHECK_VERSION */
1743 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1744 g_list_free (selected);
1750 on_focus_out (GtkWidget *self,
1751 GdkEventFocus *event,
1755 if (!gtk_widget_is_focus (self)) {
1756 GtkTreeSelection *selection = NULL;
1757 GList *selected_rows = NULL;
1758 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1759 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1760 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1761 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1762 gtk_tree_selection_unselect_all (selection);
1763 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1764 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1765 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1766 g_list_free (selected_rows);
1773 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1775 enable_drag_and_drop(self);
1780 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1782 GtkTreeSelection *selection = NULL;
1783 GtkTreePath *path = NULL;
1784 gboolean already_selected = FALSE;
1786 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1787 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1788 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1791 /* Enable drag and drop onlly if the user clicks on a row that
1792 it's already selected. If not, let him select items using
1794 if (!already_selected) {
1795 disable_drag_and_drop(self);
1799 gtk_tree_path_free(path);
1806 folder_monitor_update (TnyFolderObserver *self,
1807 TnyFolderChange *change)
1809 ModestHeaderViewPrivate *priv = NULL;
1810 TnyFolderChangeChanged changed;
1811 TnyFolder *folder = NULL;
1813 changed = tny_folder_change_get_changed (change);
1815 /* Do not notify the observers if the folder of the header
1816 view has changed before this call to the observer
1818 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1819 folder = tny_folder_change_get_folder (change);
1820 if (folder != priv->folder)
1823 MODEST_DEBUG_BLOCK (
1824 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1825 g_print ("ADDED %d/%d (r/t) \n",
1826 tny_folder_change_get_new_unread_count (change),
1827 tny_folder_change_get_new_all_count (change));
1828 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1829 g_print ("ALL COUNT %d\n",
1830 tny_folder_change_get_new_all_count (change));
1831 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1832 g_print ("UNREAD COUNT %d\n",
1833 tny_folder_change_get_new_unread_count (change));
1834 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1835 g_print ("EXPUNGED %d/%d (r/t) \n",
1836 tny_folder_change_get_new_unread_count (change),
1837 tny_folder_change_get_new_all_count (change));
1838 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1839 g_print ("FOLDER RENAME\n");
1840 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1841 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1842 tny_folder_change_get_new_unread_count (change),
1843 tny_folder_change_get_new_all_count (change));
1844 g_print ("---------------------------------------------------\n");
1847 /* Check folder count */
1848 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1849 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1851 g_mutex_lock (priv->observers_lock);
1853 /* Emit signal to evaluate how headers changes affects
1854 to the window view */
1855 g_signal_emit (G_OBJECT(self),
1856 signals[MSG_COUNT_CHANGED_SIGNAL],
1859 /* Added or removed headers, so data stored on cliboard are invalid */
1860 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1861 modest_email_clipboard_clear (priv->clipboard);
1863 g_mutex_unlock (priv->observers_lock);
1869 g_object_unref (folder);
1873 modest_header_view_is_empty (ModestHeaderView *self)
1875 ModestHeaderViewPrivate *priv;
1877 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1879 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1881 return priv->status == HEADER_VIEW_EMPTY;
1885 modest_header_view_clear (ModestHeaderView *self)
1887 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1889 modest_header_view_set_folder (self, NULL, NULL, NULL);
1893 modest_header_view_copy_selection (ModestHeaderView *header_view)
1895 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1897 /* Copy selection */
1898 _clipboard_set_selected_data (header_view, FALSE);
1902 modest_header_view_cut_selection (ModestHeaderView *header_view)
1904 ModestHeaderViewPrivate *priv = NULL;
1905 const gchar **hidding = NULL;
1906 guint i, n_selected;
1908 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1910 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1912 /* Copy selection */
1913 _clipboard_set_selected_data (header_view, TRUE);
1915 /* Get hidding ids */
1916 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1918 /* Clear hidding array created by previous cut operation */
1919 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1921 /* Copy hidding array */
1922 priv->n_selected = n_selected;
1923 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1924 for (i=0; i < n_selected; i++)
1925 priv->hidding_ids[i] = g_strdup(hidding[i]);
1927 /* Hide cut headers */
1928 modest_header_view_refilter (header_view);
1935 _clipboard_set_selected_data (ModestHeaderView *header_view,
1938 ModestHeaderViewPrivate *priv = NULL;
1939 TnyList *headers = NULL;
1941 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1942 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1944 /* Set selected data on clipboard */
1945 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1946 headers = modest_header_view_get_selected_headers (header_view);
1947 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1950 g_object_unref (headers);
1954 ModestHeaderView *self;
1959 notify_filter_change (gpointer data)
1961 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1963 g_signal_emit (info->self,
1964 signals[MSG_COUNT_CHANGED_SIGNAL],
1965 0, info->folder, NULL);
1971 notify_filter_change_destroy (gpointer data)
1973 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1975 g_object_unref (info->self);
1976 g_object_unref (info->folder);
1977 g_slice_free (NotifyFilterInfo, info);
1981 filter_row (GtkTreeModel *model,
1985 ModestHeaderViewPrivate *priv = NULL;
1986 TnyHeaderFlags flags;
1987 TnyHeader *header = NULL;
1990 gboolean visible = TRUE;
1991 gboolean found = FALSE;
1992 GValue value = {0,};
1993 HeaderViewStatus old_status;
1995 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1996 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1998 /* Get header from model */
1999 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2000 flags = (TnyHeaderFlags) g_value_get_int (&value);
2001 g_value_unset (&value);
2002 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2003 header = (TnyHeader *) g_value_get_object (&value);
2004 g_value_unset (&value);
2006 /* Hide deleted and mark as deleted heders */
2007 if (flags & TNY_HEADER_FLAG_DELETED ||
2008 flags & TNY_HEADER_FLAG_EXPUNGED) {
2013 /* If no data on clipboard, return always TRUE */
2014 if (modest_email_clipboard_cleared(priv->clipboard)) {
2019 /* Get message id from header (ensure is a valid id) */
2026 if (priv->hidding_ids != NULL) {
2027 id = tny_header_dup_message_id (header);
2028 for (i=0; i < priv->n_selected && !found; i++)
2029 if (priv->hidding_ids[i] != NULL && id != NULL)
2030 found = (!strcmp (priv->hidding_ids[i], id));
2037 old_status = priv->status;
2038 priv->status = ((gboolean) priv->status) && !visible;
2039 if ((priv->notify_status) && (priv->status != old_status)) {
2040 NotifyFilterInfo *info;
2042 if (priv->status_timeout)
2043 g_source_remove (priv->status_timeout);
2045 info = g_slice_new0 (NotifyFilterInfo);
2046 info->self = g_object_ref (G_OBJECT (user_data));
2047 info->folder = tny_header_get_folder (header);
2048 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2049 notify_filter_change,
2051 notify_filter_change_destroy);
2058 _clear_hidding_filter (ModestHeaderView *header_view)
2060 ModestHeaderViewPrivate *priv = NULL;
2063 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2064 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2066 if (priv->hidding_ids != NULL) {
2067 for (i=0; i < priv->n_selected; i++)
2068 g_free (priv->hidding_ids[i]);
2069 g_free(priv->hidding_ids);
2074 modest_header_view_refilter (ModestHeaderView *header_view)
2076 GtkTreeModel *model = NULL;
2077 ModestHeaderViewPrivate *priv = NULL;
2079 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2080 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2082 /* Hide cut headers */
2083 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2084 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2085 priv->status = HEADER_VIEW_INIT;
2086 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2091 * Called when an account is removed. If I'm showing a folder of the
2092 * account that has been removed then clear the view
2095 on_account_removed (TnyAccountStore *self,
2096 TnyAccount *account,
2099 ModestHeaderViewPrivate *priv = NULL;
2101 /* Ignore changes in transport accounts */
2102 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2105 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2108 TnyAccount *my_account;
2110 my_account = tny_folder_get_account (priv->folder);
2111 if (my_account == account)
2112 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2113 g_object_unref (my_account);
2118 modest_header_view_add_observer(ModestHeaderView *header_view,
2119 ModestHeaderViewObserver *observer)
2121 ModestHeaderViewPrivate *priv;
2123 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2124 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2126 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2128 g_mutex_lock(priv->observer_list_lock);
2129 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2130 g_mutex_unlock(priv->observer_list_lock);
2134 modest_header_view_remove_observer(ModestHeaderView *header_view,
2135 ModestHeaderViewObserver *observer)
2137 ModestHeaderViewPrivate *priv;
2139 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2140 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2142 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2144 g_mutex_lock(priv->observer_list_lock);
2145 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2146 g_mutex_unlock(priv->observer_list_lock);
2150 modest_header_view_notify_observers(ModestHeaderView *header_view,
2151 GtkTreeModel *model,
2152 const gchar *tny_folder_id)
2154 ModestHeaderViewPrivate *priv = NULL;
2156 ModestHeaderViewObserver *observer;
2159 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2161 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2163 g_mutex_lock(priv->observer_list_lock);
2164 iter = priv->observer_list;
2165 while(iter != NULL){
2166 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2167 modest_header_view_observer_update(observer, model,
2169 iter = g_slist_next(iter);
2171 g_mutex_unlock(priv->observer_list_lock);