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 (GtkTreeView *header_view,
119 GdkEventExpose *event,
123 HEADER_VIEW_NON_EMPTY,
128 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
129 struct _ModestHeaderViewPrivate {
131 ModestHeaderViewStyle style;
133 TnyFolderMonitor *monitor;
134 GMutex *observers_lock;
136 /*header-view-observer observer*/
137 GMutex *observer_list_lock;
138 GSList *observer_list;
140 /* not unref this object, its a singlenton */
141 ModestEmailClipboard *clipboard;
143 /* Filter tree model */
147 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
148 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
150 gulong selection_changed_handler;
151 gulong acc_removed_handler;
153 GList *drag_begin_cached_selected_rows;
155 HeaderViewStatus status;
156 guint status_timeout;
157 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
160 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
161 struct _HeadersCountChangedHelper {
162 ModestHeaderView *self;
163 TnyFolderChange *change;
167 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
168 MODEST_TYPE_HEADER_VIEW, \
169 ModestHeaderViewPrivate))
173 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
176 HEADER_SELECTED_SIGNAL,
177 HEADER_ACTIVATED_SIGNAL,
178 ITEM_NOT_FOUND_SIGNAL,
179 MSG_COUNT_CHANGED_SIGNAL,
180 UPDATING_MSG_LIST_SIGNAL,
185 static GObjectClass *parent_class = NULL;
187 /* uncomment the following if you have defined any signals */
188 static guint signals[LAST_SIGNAL] = {0};
191 modest_header_view_get_type (void)
193 static GType my_type = 0;
195 static const GTypeInfo my_info = {
196 sizeof(ModestHeaderViewClass),
197 NULL, /* base init */
198 NULL, /* base finalize */
199 (GClassInitFunc) modest_header_view_class_init,
200 NULL, /* class finalize */
201 NULL, /* class data */
202 sizeof(ModestHeaderView),
204 (GInstanceInitFunc) modest_header_view_init,
208 static const GInterfaceInfo tny_folder_observer_info =
210 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
211 NULL, /* interface_finalize */
212 NULL /* interface_data */
214 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
218 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
219 &tny_folder_observer_info);
227 modest_header_view_class_init (ModestHeaderViewClass *klass)
229 GObjectClass *gobject_class;
230 gobject_class = (GObjectClass*) klass;
232 parent_class = g_type_class_peek_parent (klass);
233 gobject_class->finalize = modest_header_view_finalize;
234 gobject_class->dispose = modest_header_view_dispose;
236 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
238 signals[HEADER_SELECTED_SIGNAL] =
239 g_signal_new ("header_selected",
240 G_TYPE_FROM_CLASS (gobject_class),
242 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
244 g_cclosure_marshal_VOID__POINTER,
245 G_TYPE_NONE, 1, G_TYPE_POINTER);
247 signals[HEADER_ACTIVATED_SIGNAL] =
248 g_signal_new ("header_activated",
249 G_TYPE_FROM_CLASS (gobject_class),
251 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
253 g_cclosure_marshal_VOID__POINTER,
254 G_TYPE_NONE, 1, G_TYPE_POINTER);
257 signals[ITEM_NOT_FOUND_SIGNAL] =
258 g_signal_new ("item_not_found",
259 G_TYPE_FROM_CLASS (gobject_class),
261 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
263 g_cclosure_marshal_VOID__INT,
264 G_TYPE_NONE, 1, G_TYPE_INT);
266 signals[MSG_COUNT_CHANGED_SIGNAL] =
267 g_signal_new ("msg_count_changed",
268 G_TYPE_FROM_CLASS (gobject_class),
270 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
272 modest_marshal_VOID__POINTER_POINTER,
273 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
275 signals[UPDATING_MSG_LIST_SIGNAL] =
276 g_signal_new ("updating-msg-list",
277 G_TYPE_FROM_CLASS (gobject_class),
279 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
281 g_cclosure_marshal_VOID__BOOLEAN,
282 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
286 tny_folder_observer_init (TnyFolderObserverIface *klass)
288 klass->update = folder_monitor_update;
291 static GtkTreeViewColumn*
292 get_new_column (const gchar *name, GtkCellRenderer *renderer,
293 gboolean resizable, gint sort_col_id, gboolean show_as_text,
294 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
296 GtkTreeViewColumn *column;
298 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
299 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
301 gtk_tree_view_column_set_resizable (column, resizable);
303 gtk_tree_view_column_set_expand (column, TRUE);
306 gtk_tree_view_column_add_attribute (column, renderer, "text",
308 if (sort_col_id >= 0)
309 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
311 gtk_tree_view_column_set_sort_indicator (column, FALSE);
312 gtk_tree_view_column_set_reorderable (column, TRUE);
315 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
322 remove_all_columns (ModestHeaderView *obj)
324 GList *columns, *cursor;
326 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
328 for (cursor = columns; cursor; cursor = cursor->next)
329 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
330 GTK_TREE_VIEW_COLUMN(cursor->data));
331 g_list_free (columns);
335 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
337 GtkTreeModel *tree_filter, *sortable;
338 GtkTreeViewColumn *column=NULL;
339 GtkTreeSelection *selection = NULL;
340 GtkCellRenderer *renderer_msgtype,*renderer_header,
341 *renderer_attach, *renderer_compact_date_or_status;
342 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
343 *renderer_subject, *renderer_subject_box, *renderer_recpt,
345 ModestHeaderViewPrivate *priv;
346 GtkTreeViewColumn *compact_column = NULL;
349 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
350 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
352 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
354 /* FIXME: check whether these renderers need to be freed */
355 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
356 renderer_attach = gtk_cell_renderer_pixbuf_new ();
357 renderer_priority = gtk_cell_renderer_pixbuf_new ();
358 renderer_header = gtk_cell_renderer_text_new ();
360 renderer_compact_header = modest_vbox_cell_renderer_new ();
361 renderer_recpt_box = modest_hbox_cell_renderer_new ();
362 renderer_subject_box = modest_hbox_cell_renderer_new ();
363 renderer_recpt = gtk_cell_renderer_text_new ();
364 renderer_subject = gtk_cell_renderer_text_new ();
365 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
367 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
368 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
369 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
370 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
371 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
372 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
373 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
374 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
375 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
376 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
377 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
378 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
379 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
380 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
382 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
383 #ifdef MAEMO_PLATFORM
384 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
385 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
387 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
388 g_object_set(G_OBJECT(renderer_header),
389 "ellipsize", PANGO_ELLIPSIZE_END,
391 g_object_set (G_OBJECT (renderer_subject),
392 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
394 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
395 g_object_set (G_OBJECT (renderer_recpt),
396 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
398 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
399 g_object_set(G_OBJECT(renderer_compact_date_or_status),
400 "xalign", 1.0, "yalign", 0.0,
402 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
403 g_object_set (G_OBJECT (renderer_priority),
404 "yalign", 1.0, NULL);
405 g_object_set (G_OBJECT (renderer_attach),
406 "yalign", 0.0, NULL);
408 #ifdef MAEMO_PLATFORM
409 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
410 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
411 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
413 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
414 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
415 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
418 remove_all_columns (self);
420 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
421 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
422 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
423 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
425 /* Add new columns */
426 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
427 ModestHeaderViewColumn col =
428 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
430 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
431 g_printerr ("modest: invalid column %d in column list\n", col);
437 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
438 column = get_new_column (_("M"), renderer_msgtype, FALSE,
439 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
441 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
443 gtk_tree_view_column_set_fixed_width (column, 45);
446 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
447 column = get_new_column (_("A"), renderer_attach, FALSE,
448 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
450 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
452 gtk_tree_view_column_set_fixed_width (column, 45);
456 case MODEST_HEADER_VIEW_COLUMN_FROM:
457 column = get_new_column (_("From"), renderer_header, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
461 GINT_TO_POINTER(TRUE));
464 case MODEST_HEADER_VIEW_COLUMN_TO:
465 column = get_new_column (_("To"), renderer_header, TRUE,
466 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
468 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
469 GINT_TO_POINTER(FALSE));
472 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
473 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
474 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
476 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
477 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
478 compact_column = column;
481 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
482 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
483 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
485 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
486 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
487 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
488 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
489 compact_column = column;
493 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
494 column = get_new_column (_("Subject"), renderer_header, TRUE,
495 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
497 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
501 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
502 column = get_new_column (_("Received"), renderer_header, TRUE,
503 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
505 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
506 GINT_TO_POINTER(TRUE));
509 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
510 column = get_new_column (_("Sent"), renderer_header, TRUE,
511 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
513 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
514 GINT_TO_POINTER(FALSE));
517 case MODEST_HEADER_VIEW_COLUMN_SIZE:
518 column = get_new_column (_("Size"), renderer_header, TRUE,
519 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
521 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
524 case MODEST_HEADER_VIEW_COLUMN_STATUS:
525 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
526 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
528 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
533 g_return_val_if_reached(FALSE);
536 /* we keep the column id around */
537 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
538 GINT_TO_POINTER(col));
540 /* we need this ptr when sorting the rows */
541 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
543 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
547 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
548 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
549 (GtkTreeIterCompareFunc) cmp_rows,
550 compact_column, NULL);
551 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
552 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
553 (GtkTreeIterCompareFunc) cmp_subject_rows,
554 compact_column, NULL);
562 modest_header_view_init (ModestHeaderView *obj)
564 ModestHeaderViewPrivate *priv;
567 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
571 priv->monitor = NULL;
572 priv->observers_lock = g_mutex_new ();
574 priv->status = HEADER_VIEW_INIT;
575 priv->status_timeout = 0;
576 priv->notify_status = TRUE;
578 priv->observer_list_lock = g_mutex_new();
579 priv->observer_list = NULL;
581 priv->clipboard = modest_runtime_get_email_clipboard ();
582 priv->hidding_ids = NULL;
583 priv->n_selected = 0;
584 priv->selection_changed_handler = 0;
585 priv->acc_removed_handler = 0;
587 /* Sort parameters */
588 for (j=0; j < 2; j++) {
589 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
590 priv->sort_colid[j][i] = -1;
591 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
595 setup_drag_and_drop (GTK_WIDGET(obj));
599 modest_header_view_dispose (GObject *obj)
601 ModestHeaderView *self;
602 ModestHeaderViewPrivate *priv;
603 GtkTreeSelection *sel;
605 self = MODEST_HEADER_VIEW(obj);
606 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
608 /* Free in the dispose to avoid unref cycles */
610 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
611 g_object_unref (G_OBJECT (priv->folder));
615 /* We need to do this here in the dispose because the
616 selection won't exist when finalizing */
617 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
618 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
619 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
620 priv->selection_changed_handler = 0;
623 G_OBJECT_CLASS(parent_class)->dispose (obj);
627 modest_header_view_finalize (GObject *obj)
629 ModestHeaderView *self;
630 ModestHeaderViewPrivate *priv;
632 self = MODEST_HEADER_VIEW(obj);
633 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
635 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
636 priv->acc_removed_handler)) {
637 g_signal_handler_disconnect (modest_runtime_get_account_store (),
638 priv->acc_removed_handler);
641 /* There is no need to lock because there should not be any
642 * reference to self now. */
643 g_mutex_free(priv->observer_list_lock);
644 g_slist_free(priv->observer_list);
646 g_mutex_lock (priv->observers_lock);
648 tny_folder_monitor_stop (priv->monitor);
649 g_object_unref (G_OBJECT (priv->monitor));
651 g_mutex_unlock (priv->observers_lock);
652 g_mutex_free (priv->observers_lock);
654 /* Clear hidding array created by cut operation */
655 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
657 G_OBJECT_CLASS(parent_class)->finalize (obj);
662 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
665 GtkTreeSelection *sel;
666 ModestHeaderView *self;
667 ModestHeaderViewPrivate *priv;
669 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
672 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
673 self = MODEST_HEADER_VIEW(obj);
674 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
676 modest_header_view_set_style (self, style);
678 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
679 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
680 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
682 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
683 TRUE); /* alternating row colors */
685 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
686 priv->selection_changed_handler =
687 g_signal_connect_after (sel, "changed",
688 G_CALLBACK(on_selection_changed), self);
690 g_signal_connect (self, "row-activated",
691 G_CALLBACK (on_header_row_activated), NULL);
693 g_signal_connect (self, "focus-in-event",
694 G_CALLBACK(on_focus_in), NULL);
695 g_signal_connect (self, "focus-out-event",
696 G_CALLBACK(on_focus_out), NULL);
698 g_signal_connect (self, "button-press-event",
699 G_CALLBACK(on_button_press_event), NULL);
700 g_signal_connect (self, "button-release-event",
701 G_CALLBACK(on_button_release_event), NULL);
703 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
705 G_CALLBACK (on_account_removed),
708 g_signal_connect (self, "expose-event",
709 G_CALLBACK(modest_header_view_on_expose_event),
712 return GTK_WIDGET(self);
717 modest_header_view_count_selected_headers (ModestHeaderView *self)
719 GtkTreeSelection *sel;
722 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
724 /* Get selection object and check selected rows count */
725 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
726 selected_rows = gtk_tree_selection_count_selected_rows (sel);
728 return selected_rows;
732 modest_header_view_has_selected_headers (ModestHeaderView *self)
734 GtkTreeSelection *sel;
737 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
739 /* Get selection object and check selected rows count */
740 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
741 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
748 modest_header_view_get_selected_headers (ModestHeaderView *self)
750 GtkTreeSelection *sel;
751 ModestHeaderViewPrivate *priv;
752 TnyList *header_list = NULL;
754 GList *list, *tmp = NULL;
755 GtkTreeModel *tree_model = NULL;
758 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
760 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
762 /* Get selected rows */
763 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
764 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
767 header_list = tny_simple_list_new();
769 list = g_list_reverse (list);
772 /* get header from selection */
773 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
774 gtk_tree_model_get (tree_model, &iter,
775 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
777 /* Prepend to list */
778 tny_list_prepend (header_list, G_OBJECT (header));
779 g_object_unref (G_OBJECT (header));
781 tmp = g_list_next (tmp);
784 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
791 /* scroll our list view so the selected item is visible */
793 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
795 #ifdef MODEST_PLATFORM_GNOME
797 GtkTreePath *selected_path;
798 GtkTreePath *start, *end;
802 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
803 selected_path = gtk_tree_model_get_path (model, iter);
805 start = gtk_tree_path_new ();
806 end = gtk_tree_path_new ();
808 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
810 if (gtk_tree_path_compare (selected_path, start) < 0 ||
811 gtk_tree_path_compare (end, selected_path) < 0)
812 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
813 selected_path, NULL, TRUE,
816 gtk_tree_path_free (selected_path);
817 gtk_tree_path_free (start);
818 gtk_tree_path_free (end);
820 #endif /* MODEST_PLATFORM_GNOME */
825 modest_header_view_select_next (ModestHeaderView *self)
827 GtkTreeSelection *sel;
832 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
834 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
835 path = get_selected_row (GTK_TREE_VIEW(self), &model);
836 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
837 /* Unselect previous path */
838 gtk_tree_selection_unselect_path (sel, path);
840 /* Move path down and selects new one */
841 if (gtk_tree_model_iter_next (model, &iter)) {
842 gtk_tree_selection_select_iter (sel, &iter);
843 scroll_to_selected (self, &iter, FALSE);
845 gtk_tree_path_free(path);
851 modest_header_view_select_prev (ModestHeaderView *self)
853 GtkTreeSelection *sel;
858 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
860 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
861 path = get_selected_row (GTK_TREE_VIEW(self), &model);
862 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
863 /* Unselect previous path */
864 gtk_tree_selection_unselect_path (sel, path);
867 if (gtk_tree_path_prev (path)) {
868 gtk_tree_model_get_iter (model, &iter, path);
870 /* Select the new one */
871 gtk_tree_selection_select_iter (sel, &iter);
872 scroll_to_selected (self, &iter, TRUE);
875 gtk_tree_path_free (path);
880 modest_header_view_get_columns (ModestHeaderView *self)
882 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
884 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
890 modest_header_view_set_style (ModestHeaderView *self,
891 ModestHeaderViewStyle style)
893 ModestHeaderViewPrivate *priv;
894 gboolean show_col_headers = FALSE;
895 ModestHeaderViewStyle old_style;
897 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
898 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
901 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
902 if (priv->style == style)
903 return TRUE; /* nothing to do */
906 case MODEST_HEADER_VIEW_STYLE_DETAILS:
907 show_col_headers = TRUE;
909 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
912 g_return_val_if_reached (FALSE);
914 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
915 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
917 old_style = priv->style;
924 ModestHeaderViewStyle
925 modest_header_view_get_style (ModestHeaderView *self)
927 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
929 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
932 /* This is used to automatically select the first header if the user
933 * has not selected any header yet.
936 modest_header_view_on_expose_event(GtkTreeView *header_view,
937 GdkEventExpose *event,
940 GtkTreeSelection *sel;
942 GtkTreeIter tree_iter;
944 model = gtk_tree_view_get_model(header_view);
949 sel = gtk_tree_view_get_selection(header_view);
950 if(!gtk_tree_selection_count_selected_rows(sel))
951 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
952 /* Prevent the widget from getting the focus
953 when selecting the first item */
954 g_object_set(header_view, "can-focus", FALSE, NULL);
955 gtk_tree_selection_select_iter(sel, &tree_iter);
956 g_object_set(header_view, "can-focus", TRUE, NULL);
963 modest_header_view_get_folder (ModestHeaderView *self)
965 ModestHeaderViewPrivate *priv;
967 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
969 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
972 g_object_ref (priv->folder);
978 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
984 ModestHeaderView *self;
985 ModestHeaderViewPrivate *priv;
987 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
989 self = MODEST_HEADER_VIEW (user_data);
990 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
992 if (cancelled || err)
995 /* Add IDLE observer (monitor) and another folder observer for
996 new messages (self) */
997 g_mutex_lock (priv->observers_lock);
999 tny_folder_monitor_stop (priv->monitor);
1000 g_object_unref (G_OBJECT (priv->monitor));
1002 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1003 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1004 tny_folder_monitor_start (priv->monitor);
1005 g_mutex_unlock (priv->observers_lock);
1009 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1013 ModestHeaderViewPrivate *priv;
1014 GList *cols, *cursor;
1015 GtkTreeModel *filter_model, *sortable;
1017 GtkSortType sort_type;
1019 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1021 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1023 /* Start the monitor in the callback of the
1024 tny_gtk_header_list_model_set_folder call. It's crucial to
1025 do it there and not just after the call because we want the
1026 monitor to observe only the headers returned by the
1027 tny_folder_get_headers_async call that it's inside the
1028 tny_gtk_header_list_model_set_folder call. This way the
1029 monitor infrastructure could successfully cope with
1030 duplicates. For example if a tny_folder_add_msg_async is
1031 happening while tny_gtk_header_list_model_set_folder is
1032 invoked, then the first call could add a header that will
1033 be added again by tny_gtk_header_list_model_set_folder, so
1034 we'd end up with duplicate headers. sergio */
1035 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1037 set_folder_intern_get_headers_async_cb,
1040 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1041 g_object_unref (G_OBJECT (headers));
1043 /* Init filter_row function to examine empty status */
1044 priv->status = HEADER_VIEW_INIT;
1046 /* Create a tree model filter to hide and show rows for cut operations */
1047 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1048 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1052 g_object_unref (G_OBJECT (sortable));
1054 /* install our special sorting functions */
1055 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1057 /* Restore sort column id */
1059 type = modest_tny_folder_guess_folder_type (folder);
1060 if (type == TNY_FOLDER_TYPE_INVALID)
1061 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1063 sort_colid = modest_header_view_get_sort_column_id (self, type);
1064 sort_type = modest_header_view_get_sort_type (self, type);
1065 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1068 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1069 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1070 (GtkTreeIterCompareFunc) cmp_rows,
1072 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1073 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1074 (GtkTreeIterCompareFunc) cmp_subject_rows,
1079 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1080 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1081 tny_folder_get_id(folder));
1082 g_object_unref (G_OBJECT (filter_model));
1089 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1091 GtkSortType sort_type)
1093 ModestHeaderViewPrivate *priv = NULL;
1094 GtkTreeModel *tree_filter, *sortable = NULL;
1097 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1098 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1100 /* Get model and private data */
1101 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1102 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1103 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1104 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1106 /* Sort tree model */
1107 type = modest_tny_folder_guess_folder_type (priv->folder);
1108 if (type == TNY_FOLDER_TYPE_INVALID)
1109 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1111 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1114 /* Store new sort parameters */
1115 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1120 modest_header_view_set_sort_params (ModestHeaderView *self,
1122 GtkSortType sort_type,
1125 ModestHeaderViewPrivate *priv;
1126 ModestHeaderViewStyle style;
1128 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1129 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1130 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1132 style = modest_header_view_get_style (self);
1133 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1135 priv->sort_colid[style][type] = sort_colid;
1136 priv->sort_type[style][type] = sort_type;
1140 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1143 ModestHeaderViewPrivate *priv;
1144 ModestHeaderViewStyle style;
1146 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1147 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1149 style = modest_header_view_get_style (self);
1150 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1152 return priv->sort_colid[style][type];
1156 modest_header_view_get_sort_type (ModestHeaderView *self,
1159 ModestHeaderViewPrivate *priv;
1160 ModestHeaderViewStyle style;
1162 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1163 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1165 style = modest_header_view_get_style (self);
1166 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1168 return priv->sort_type[style][type];
1172 ModestHeaderView *header_view;
1173 RefreshAsyncUserCallback cb;
1178 folder_refreshed_cb (ModestMailOperation *mail_op,
1182 ModestHeaderViewPrivate *priv;
1183 SetFolderHelper *info;
1185 info = (SetFolderHelper*) user_data;
1187 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1191 info->cb (mail_op, folder, info->user_data);
1193 /* Start the folder count changes observer. We do not need it
1194 before the refresh. Note that the monitor could still be
1195 called for this refresh but now we know that the callback
1196 was previously called */
1197 g_mutex_lock (priv->observers_lock);
1198 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1199 g_mutex_unlock (priv->observers_lock);
1201 /* Notify the observers that the update is over */
1202 g_signal_emit (G_OBJECT (info->header_view),
1203 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1205 /* Allow filtering notifications from now on if the current
1206 folder is still the same (if not then the user has selected
1207 another one to refresh, we should wait until that refresh
1209 if (priv->folder == folder)
1210 priv->notify_status = TRUE;
1213 g_object_unref (info->header_view);
1218 refresh_folder_error_handler (ModestMailOperation *mail_op,
1221 const GError *error = modest_mail_operation_get_error (mail_op);
1223 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1224 error->code == TNY_IO_ERROR_WRITE ||
1225 error->code == TNY_IO_ERROR_READ) {
1226 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1227 /* If the mail op has been cancelled then it's not an error: don't show any message */
1228 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1229 modest_platform_information_banner (NULL, NULL,
1231 "cerm_device_memory_full"));
1237 modest_header_view_set_folder (ModestHeaderView *self,
1240 RefreshAsyncUserCallback callback,
1243 ModestHeaderViewPrivate *priv;
1244 ModestWindow *main_win;
1246 g_return_if_fail (self);
1248 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1250 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1251 FALSE); /* don't create */
1253 g_warning ("%s: BUG: no main window", __FUNCTION__);
1258 if (priv->status_timeout) {
1259 g_source_remove (priv->status_timeout);
1260 priv->status_timeout = 0;
1263 g_mutex_lock (priv->observers_lock);
1264 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1265 g_object_unref (priv->folder);
1266 priv->folder = NULL;
1267 g_mutex_unlock (priv->observers_lock);
1271 GtkTreeSelection *selection;
1272 SetFolderHelper *info;
1273 ModestMailOperation *mail_op = NULL;
1275 /* Set folder in the model */
1276 modest_header_view_set_folder_intern (self, folder);
1278 /* Pick my reference. Nothing to do with the mail operation */
1279 priv->folder = g_object_ref (folder);
1281 /* Do not notify about filterings until the refresh finishes */
1282 priv->notify_status = FALSE;
1284 /* Clear the selection if exists */
1285 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1286 gtk_tree_selection_unselect_all(selection);
1287 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1289 /* Notify the observers that the update begins */
1290 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1293 /* create the helper */
1294 info = g_malloc0 (sizeof (SetFolderHelper));
1295 info->header_view = g_object_ref (self);
1296 info->cb = callback;
1297 info->user_data = user_data;
1299 /* Create the mail operation (source will be the parent widget) */
1300 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1301 refresh_folder_error_handler,
1304 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1307 /* Refresh the folder asynchronously */
1308 modest_mail_operation_refresh_folder (mail_op,
1310 folder_refreshed_cb,
1313 folder_refreshed_cb (mail_op, folder, info);
1316 g_object_unref (mail_op);
1318 g_mutex_lock (priv->observers_lock);
1320 if (priv->monitor) {
1321 tny_folder_monitor_stop (priv->monitor);
1322 g_object_unref (G_OBJECT (priv->monitor));
1323 priv->monitor = NULL;
1325 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1327 modest_header_view_notify_observers(self, NULL, NULL);
1329 g_mutex_unlock (priv->observers_lock);
1331 /* Notify the observers that the update is over */
1332 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1338 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1339 GtkTreeViewColumn *column, gpointer userdata)
1341 ModestHeaderView *self = NULL;
1342 ModestHeaderViewPrivate *priv = NULL;
1344 GtkTreeModel *model = NULL;
1345 TnyHeader *header = NULL;
1346 TnyHeaderFlags flags;
1348 self = MODEST_HEADER_VIEW (treeview);
1349 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1351 model = gtk_tree_view_get_model (treeview);
1352 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1355 /* get the first selected item */
1356 gtk_tree_model_get (model, &iter,
1357 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1358 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1361 /* Dont open DELETED messages */
1362 if (flags & TNY_HEADER_FLAG_DELETED) {
1365 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1366 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1367 modest_platform_information_banner (NULL, NULL, msg);
1373 g_signal_emit (G_OBJECT(self),
1374 signals[HEADER_ACTIVATED_SIGNAL],
1380 g_object_unref (G_OBJECT (header));
1385 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1387 GtkTreeModel *model;
1388 TnyHeader *header = NULL;
1389 GtkTreePath *path = NULL;
1391 ModestHeaderView *self;
1392 ModestHeaderViewPrivate *priv;
1393 GList *selected = NULL;
1395 g_return_if_fail (sel);
1396 g_return_if_fail (user_data);
1398 self = MODEST_HEADER_VIEW (user_data);
1399 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1401 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1402 if (selected != NULL)
1403 path = (GtkTreePath *) selected->data;
1404 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1405 return; /* msg was _un_selected */
1407 gtk_tree_model_get (model, &iter,
1408 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1412 g_signal_emit (G_OBJECT(self),
1413 signals[HEADER_SELECTED_SIGNAL],
1416 g_object_unref (G_OBJECT (header));
1418 /* free all items in 'selected' */
1419 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1420 g_list_free (selected);
1424 /* PROTECTED method. It's useful when we want to force a given
1425 selection to reload a msg. For example if we have selected a header
1426 in offline mode, when Modest become online, we want to reload the
1427 message automatically without an user click over the header */
1429 _modest_header_view_change_selection (GtkTreeSelection *selection,
1432 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1433 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1435 on_selection_changed (selection, user_data);
1439 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1446 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1450 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1454 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1462 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1469 /* static int counter = 0; */
1471 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1472 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1473 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1477 case TNY_HEADER_FLAG_ATTACHMENTS:
1479 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1480 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1481 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1482 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1484 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1485 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1487 return cmp ? cmp : t1 - t2;
1489 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1490 TnyHeader *header1 = NULL, *header2 = NULL;
1492 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1493 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1494 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1495 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1497 /* This is for making priority values respect the intuitive sort relationship
1498 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1500 if (header1 && header2) {
1501 cmp = compare_priorities (tny_header_get_priority (header1),
1502 tny_header_get_priority (header2));
1503 g_object_unref (header1);
1504 g_object_unref (header2);
1506 return cmp ? cmp : t1 - t2;
1512 return &iter1 - &iter2; /* oughhhh */
1517 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1523 /* static int counter = 0; */
1525 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1527 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1528 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1529 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1530 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1532 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1533 val2 + modest_text_utils_get_subject_prefix_len(val2),
1540 /* Drag and drop stuff */
1542 drag_data_get_cb (GtkWidget *widget,
1543 GdkDragContext *context,
1544 GtkSelectionData *selection_data,
1549 ModestHeaderView *self = NULL;
1550 ModestHeaderViewPrivate *priv = NULL;
1552 self = MODEST_HEADER_VIEW (widget);
1553 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1555 /* Set the data. Do not use the current selection because it
1556 could be different than the selection at the beginning of
1558 modest_dnd_selection_data_set_paths (selection_data,
1559 priv->drag_begin_cached_selected_rows);
1563 * We're caching the selected rows at the beginning because the
1564 * selection could change between drag-begin and drag-data-get, for
1565 * example if we have a set of rows already selected, and then we
1566 * click in one of them (without SHIFT key pressed) and begin a drag,
1567 * the selection at that moment contains all the selected lines, but
1568 * after dropping the selection, the release event provokes that only
1569 * the row used to begin the drag is selected, so at the end the
1570 * drag&drop affects only one rows instead of all the selected ones.
1574 drag_begin_cb (GtkWidget *widget,
1575 GdkDragContext *context,
1578 ModestHeaderView *self = NULL;
1579 ModestHeaderViewPrivate *priv = NULL;
1580 GtkTreeSelection *selection;
1582 self = MODEST_HEADER_VIEW (widget);
1583 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1585 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1586 priv->drag_begin_cached_selected_rows =
1587 gtk_tree_selection_get_selected_rows (selection, NULL);
1591 * We use the drag-end signal to clear the cached selection, we use
1592 * this because this allways happens, whether or not the d&d was a
1596 drag_end_cb (GtkWidget *widget,
1600 ModestHeaderView *self = NULL;
1601 ModestHeaderViewPrivate *priv = NULL;
1603 self = MODEST_HEADER_VIEW (widget);
1604 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1606 /* Free cached data */
1607 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1608 g_list_free (priv->drag_begin_cached_selected_rows);
1609 priv->drag_begin_cached_selected_rows = NULL;
1612 /* Header view drag types */
1613 const GtkTargetEntry header_view_drag_types[] = {
1614 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1618 enable_drag_and_drop (GtkWidget *self)
1620 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1621 header_view_drag_types,
1622 G_N_ELEMENTS (header_view_drag_types),
1623 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1627 disable_drag_and_drop (GtkWidget *self)
1629 gtk_drag_source_unset (self);
1633 setup_drag_and_drop (GtkWidget *self)
1635 enable_drag_and_drop(self);
1636 g_signal_connect(G_OBJECT (self), "drag_data_get",
1637 G_CALLBACK(drag_data_get_cb), NULL);
1639 g_signal_connect(G_OBJECT (self), "drag_begin",
1640 G_CALLBACK(drag_begin_cb), NULL);
1642 g_signal_connect(G_OBJECT (self), "drag_end",
1643 G_CALLBACK(drag_end_cb), NULL);
1646 static GtkTreePath *
1647 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1649 GtkTreePath *path = NULL;
1650 GtkTreeSelection *sel = NULL;
1653 sel = gtk_tree_view_get_selection(self);
1654 rows = gtk_tree_selection_get_selected_rows (sel, model);
1656 if ((rows == NULL) || (g_list_length(rows) != 1))
1659 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1664 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1671 * This function moves the tree view scroll to the current selected
1672 * row when the widget grabs the focus
1675 on_focus_in (GtkWidget *self,
1676 GdkEventFocus *event,
1679 GtkTreeSelection *selection;
1680 GtkTreeModel *model;
1681 GList *selected = NULL;
1682 GtkTreePath *selected_path = NULL;
1684 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1688 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1689 /* If none selected yet, pick the first one */
1690 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1694 /* Return if the model is empty */
1695 if (!gtk_tree_model_get_iter_first (model, &iter))
1698 path = gtk_tree_model_get_path (model, &iter);
1699 gtk_tree_selection_select_path (selection, path);
1700 gtk_tree_path_free (path);
1703 /* Need to get the all the rows because is selection multiple */
1704 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1705 if (selected == NULL) return FALSE;
1706 selected_path = (GtkTreePath *) selected->data;
1708 /* Check if we need to scroll */
1709 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1710 GtkTreePath *start_path = NULL;
1711 GtkTreePath *end_path = NULL;
1712 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1716 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1717 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1719 /* Scroll to first path */
1720 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1729 gtk_tree_path_free (start_path);
1731 gtk_tree_path_free (end_path);
1733 #endif /* GTK_CHECK_VERSION */
1736 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1737 g_list_free (selected);
1743 on_focus_out (GtkWidget *self,
1744 GdkEventFocus *event,
1748 if (!gtk_widget_is_focus (self)) {
1749 GtkTreeSelection *selection = NULL;
1750 GList *selected_rows = NULL;
1751 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1752 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1753 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1754 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1755 gtk_tree_selection_unselect_all (selection);
1756 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1757 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1758 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1759 g_list_free (selected_rows);
1766 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1768 enable_drag_and_drop(self);
1773 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1775 GtkTreeSelection *selection = NULL;
1776 GtkTreePath *path = NULL;
1777 gboolean already_selected = FALSE, already_opened = FALSE;
1778 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1780 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1782 GtkTreeModel *model;
1784 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1785 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1787 /* Get header from model */
1788 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1789 if (gtk_tree_model_get_iter (model, &iter, path)) {
1790 GValue value = {0,};
1793 gtk_tree_model_get_value (model, &iter,
1794 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1796 header = (TnyHeader *) g_value_get_object (&value);
1797 if (TNY_IS_HEADER (header)) {
1798 status = modest_tny_all_send_queues_get_msg_status (header);
1799 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1802 g_value_unset (&value);
1806 /* Enable drag and drop only if the user clicks on a row that
1807 it's already selected. If not, let him select items using
1808 the pointer. If the message is in an OUTBOX and in sending
1809 status disable drag and drop as well */
1810 if (!already_selected ||
1811 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1813 disable_drag_and_drop(self);
1816 gtk_tree_path_free(path);
1818 /* If it's already opened then do not let the button-press
1819 event go on because it'll perform a message open because
1820 we're clicking on to an already selected header */
1825 folder_monitor_update (TnyFolderObserver *self,
1826 TnyFolderChange *change)
1828 ModestHeaderViewPrivate *priv = NULL;
1829 TnyFolderChangeChanged changed;
1830 TnyFolder *folder = NULL;
1832 changed = tny_folder_change_get_changed (change);
1834 /* Do not notify the observers if the folder of the header
1835 view has changed before this call to the observer
1837 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1838 folder = tny_folder_change_get_folder (change);
1839 if (folder != priv->folder)
1842 MODEST_DEBUG_BLOCK (
1843 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1844 g_print ("ADDED %d/%d (r/t) \n",
1845 tny_folder_change_get_new_unread_count (change),
1846 tny_folder_change_get_new_all_count (change));
1847 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1848 g_print ("ALL COUNT %d\n",
1849 tny_folder_change_get_new_all_count (change));
1850 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1851 g_print ("UNREAD COUNT %d\n",
1852 tny_folder_change_get_new_unread_count (change));
1853 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1854 g_print ("EXPUNGED %d/%d (r/t) \n",
1855 tny_folder_change_get_new_unread_count (change),
1856 tny_folder_change_get_new_all_count (change));
1857 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1858 g_print ("FOLDER RENAME\n");
1859 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1860 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1861 tny_folder_change_get_new_unread_count (change),
1862 tny_folder_change_get_new_all_count (change));
1863 g_print ("---------------------------------------------------\n");
1866 /* Check folder count */
1867 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1868 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1870 g_mutex_lock (priv->observers_lock);
1872 /* Emit signal to evaluate how headers changes affects
1873 to the window view */
1874 g_signal_emit (G_OBJECT(self),
1875 signals[MSG_COUNT_CHANGED_SIGNAL],
1878 /* Added or removed headers, so data stored on cliboard are invalid */
1879 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1880 modest_email_clipboard_clear (priv->clipboard);
1882 g_mutex_unlock (priv->observers_lock);
1888 g_object_unref (folder);
1892 modest_header_view_is_empty (ModestHeaderView *self)
1894 ModestHeaderViewPrivate *priv;
1896 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1898 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1900 return priv->status == HEADER_VIEW_EMPTY;
1904 modest_header_view_clear (ModestHeaderView *self)
1906 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1908 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1912 modest_header_view_copy_selection (ModestHeaderView *header_view)
1914 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1916 /* Copy selection */
1917 _clipboard_set_selected_data (header_view, FALSE);
1921 modest_header_view_cut_selection (ModestHeaderView *header_view)
1923 ModestHeaderViewPrivate *priv = NULL;
1924 const gchar **hidding = NULL;
1925 guint i, n_selected;
1927 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1929 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1931 /* Copy selection */
1932 _clipboard_set_selected_data (header_view, TRUE);
1934 /* Get hidding ids */
1935 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1937 /* Clear hidding array created by previous cut operation */
1938 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1940 /* Copy hidding array */
1941 priv->n_selected = n_selected;
1942 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1943 for (i=0; i < n_selected; i++)
1944 priv->hidding_ids[i] = g_strdup(hidding[i]);
1946 /* Hide cut headers */
1947 modest_header_view_refilter (header_view);
1954 _clipboard_set_selected_data (ModestHeaderView *header_view,
1957 ModestHeaderViewPrivate *priv = NULL;
1958 TnyList *headers = NULL;
1960 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1961 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1963 /* Set selected data on clipboard */
1964 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1965 headers = modest_header_view_get_selected_headers (header_view);
1966 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1969 g_object_unref (headers);
1973 ModestHeaderView *self;
1978 notify_filter_change (gpointer data)
1980 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1982 g_signal_emit (info->self,
1983 signals[MSG_COUNT_CHANGED_SIGNAL],
1984 0, info->folder, NULL);
1990 notify_filter_change_destroy (gpointer data)
1992 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1993 ModestHeaderViewPrivate *priv;
1995 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
1996 priv->status_timeout = 0;
1998 g_object_unref (info->self);
1999 g_object_unref (info->folder);
2000 g_slice_free (NotifyFilterInfo, info);
2004 filter_row (GtkTreeModel *model,
2008 ModestHeaderViewPrivate *priv = NULL;
2009 TnyHeaderFlags flags;
2010 TnyHeader *header = NULL;
2013 gboolean visible = TRUE;
2014 gboolean found = FALSE;
2015 GValue value = {0,};
2016 HeaderViewStatus old_status;
2018 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2019 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2021 /* Get header from model */
2022 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2023 flags = (TnyHeaderFlags) g_value_get_int (&value);
2024 g_value_unset (&value);
2025 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2026 header = (TnyHeader *) g_value_get_object (&value);
2027 g_value_unset (&value);
2029 /* Hide deleted and mark as deleted heders */
2030 if (flags & TNY_HEADER_FLAG_DELETED ||
2031 flags & TNY_HEADER_FLAG_EXPUNGED) {
2036 /* If no data on clipboard, return always TRUE */
2037 if (modest_email_clipboard_cleared(priv->clipboard)) {
2042 /* Get message id from header (ensure is a valid id) */
2049 if (priv->hidding_ids != NULL) {
2050 id = tny_header_dup_message_id (header);
2051 for (i=0; i < priv->n_selected && !found; i++)
2052 if (priv->hidding_ids[i] != NULL && id != NULL)
2053 found = (!strcmp (priv->hidding_ids[i], id));
2060 old_status = priv->status;
2061 priv->status = ((gboolean) priv->status) && !visible;
2062 if ((priv->notify_status) && (priv->status != old_status)) {
2063 NotifyFilterInfo *info;
2065 if (priv->status_timeout)
2066 g_source_remove (priv->status_timeout);
2068 info = g_slice_new0 (NotifyFilterInfo);
2069 info->self = g_object_ref (G_OBJECT (user_data));
2070 info->folder = tny_header_get_folder (header);
2071 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2072 notify_filter_change,
2074 notify_filter_change_destroy);
2081 _clear_hidding_filter (ModestHeaderView *header_view)
2083 ModestHeaderViewPrivate *priv = NULL;
2086 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2087 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2089 if (priv->hidding_ids != NULL) {
2090 for (i=0; i < priv->n_selected; i++)
2091 g_free (priv->hidding_ids[i]);
2092 g_free(priv->hidding_ids);
2097 modest_header_view_refilter (ModestHeaderView *header_view)
2099 GtkTreeModel *model = NULL;
2100 ModestHeaderViewPrivate *priv = NULL;
2102 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2103 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2105 /* Hide cut headers */
2106 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2107 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2108 priv->status = HEADER_VIEW_INIT;
2109 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2114 * Called when an account is removed. If I'm showing a folder of the
2115 * account that has been removed then clear the view
2118 on_account_removed (TnyAccountStore *self,
2119 TnyAccount *account,
2122 ModestHeaderViewPrivate *priv = NULL;
2124 /* Ignore changes in transport accounts */
2125 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2128 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2131 TnyAccount *my_account;
2133 my_account = tny_folder_get_account (priv->folder);
2134 if (my_account == account)
2135 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2136 g_object_unref (my_account);
2141 modest_header_view_add_observer(ModestHeaderView *header_view,
2142 ModestHeaderViewObserver *observer)
2144 ModestHeaderViewPrivate *priv;
2146 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2147 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2149 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2151 g_mutex_lock(priv->observer_list_lock);
2152 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2153 g_mutex_unlock(priv->observer_list_lock);
2157 modest_header_view_remove_observer(ModestHeaderView *header_view,
2158 ModestHeaderViewObserver *observer)
2160 ModestHeaderViewPrivate *priv;
2162 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2163 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2165 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2167 g_mutex_lock(priv->observer_list_lock);
2168 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2169 g_mutex_unlock(priv->observer_list_lock);
2173 modest_header_view_notify_observers(ModestHeaderView *header_view,
2174 GtkTreeModel *model,
2175 const gchar *tny_folder_id)
2177 ModestHeaderViewPrivate *priv = NULL;
2179 ModestHeaderViewObserver *observer;
2182 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2184 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2186 g_mutex_lock(priv->observer_list_lock);
2187 iter = priv->observer_list;
2188 while(iter != NULL){
2189 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2190 modest_header_view_observer_update(observer, model,
2192 iter = g_slist_next(iter);
2194 g_mutex_unlock(priv->observer_list_lock);