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 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
384 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
385 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
386 g_object_set(G_OBJECT(renderer_header),
387 "ellipsize", PANGO_ELLIPSIZE_END,
389 g_object_set (G_OBJECT (renderer_subject),
390 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
392 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
393 g_object_set (G_OBJECT (renderer_recpt),
394 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
396 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
397 g_object_set(G_OBJECT(renderer_compact_date_or_status),
398 "xalign", 1.0, "yalign", 0.0,
400 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
401 g_object_set (G_OBJECT (renderer_priority),
402 "yalign", 1.0, NULL);
403 g_object_set (G_OBJECT (renderer_attach),
404 "yalign", 0.0, NULL);
406 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
407 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
408 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
410 remove_all_columns (self);
412 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
413 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
414 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
415 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
417 /* Add new columns */
418 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
419 ModestHeaderViewColumn col =
420 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
422 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
423 g_printerr ("modest: invalid column %d in column list\n", col);
429 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
430 column = get_new_column (_("M"), renderer_msgtype, FALSE,
431 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
433 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
435 gtk_tree_view_column_set_fixed_width (column, 45);
438 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
439 column = get_new_column (_("A"), renderer_attach, FALSE,
440 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
442 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
444 gtk_tree_view_column_set_fixed_width (column, 45);
448 case MODEST_HEADER_VIEW_COLUMN_FROM:
449 column = get_new_column (_("From"), renderer_header, TRUE,
450 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
452 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
453 GINT_TO_POINTER(TRUE));
456 case MODEST_HEADER_VIEW_COLUMN_TO:
457 column = get_new_column (_("To"), renderer_header, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
461 GINT_TO_POINTER(FALSE));
464 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
465 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
466 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
468 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
469 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
470 compact_column = column;
473 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
474 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
475 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
477 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
478 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
479 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
480 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
481 compact_column = column;
485 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
486 column = get_new_column (_("Subject"), renderer_header, TRUE,
487 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
489 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
493 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
494 column = get_new_column (_("Received"), renderer_header, TRUE,
495 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
497 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
498 GINT_TO_POINTER(TRUE));
501 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
502 column = get_new_column (_("Sent"), renderer_header, TRUE,
503 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
505 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
506 GINT_TO_POINTER(FALSE));
509 case MODEST_HEADER_VIEW_COLUMN_SIZE:
510 column = get_new_column (_("Size"), renderer_header, TRUE,
511 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
513 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
516 case MODEST_HEADER_VIEW_COLUMN_STATUS:
517 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
518 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
520 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
525 g_return_val_if_reached(FALSE);
528 /* we keep the column id around */
529 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
530 GINT_TO_POINTER(col));
532 /* we need this ptr when sorting the rows */
533 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
535 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
539 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
540 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
541 (GtkTreeIterCompareFunc) cmp_rows,
542 compact_column, NULL);
543 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
544 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
545 (GtkTreeIterCompareFunc) cmp_subject_rows,
546 compact_column, NULL);
554 modest_header_view_init (ModestHeaderView *obj)
556 ModestHeaderViewPrivate *priv;
559 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
563 priv->monitor = NULL;
564 priv->observers_lock = g_mutex_new ();
566 priv->status = HEADER_VIEW_INIT;
567 priv->status_timeout = 0;
568 priv->notify_status = TRUE;
570 priv->observer_list_lock = g_mutex_new();
571 priv->observer_list = NULL;
573 priv->clipboard = modest_runtime_get_email_clipboard ();
574 priv->hidding_ids = NULL;
575 priv->n_selected = 0;
576 priv->selection_changed_handler = 0;
577 priv->acc_removed_handler = 0;
579 /* Sort parameters */
580 for (j=0; j < 2; j++) {
581 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
582 priv->sort_colid[j][i] = -1;
583 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
587 setup_drag_and_drop (GTK_WIDGET(obj));
591 modest_header_view_dispose (GObject *obj)
593 ModestHeaderView *self;
594 ModestHeaderViewPrivate *priv;
595 GtkTreeSelection *sel;
597 self = MODEST_HEADER_VIEW(obj);
598 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
600 /* Free in the dispose to avoid unref cycles */
602 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
603 g_object_unref (G_OBJECT (priv->folder));
607 /* We need to do this here in the dispose because the
608 selection won't exist when finalizing */
609 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
610 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
611 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
612 priv->selection_changed_handler = 0;
615 G_OBJECT_CLASS(parent_class)->dispose (obj);
619 modest_header_view_finalize (GObject *obj)
621 ModestHeaderView *self;
622 ModestHeaderViewPrivate *priv;
624 self = MODEST_HEADER_VIEW(obj);
625 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
627 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
628 priv->acc_removed_handler)) {
629 g_signal_handler_disconnect (modest_runtime_get_account_store (),
630 priv->acc_removed_handler);
633 /* There is no need to lock because there should not be any
634 * reference to self now. */
635 g_mutex_free(priv->observer_list_lock);
636 g_slist_free(priv->observer_list);
638 g_mutex_lock (priv->observers_lock);
640 tny_folder_monitor_stop (priv->monitor);
641 g_object_unref (G_OBJECT (priv->monitor));
643 g_mutex_unlock (priv->observers_lock);
644 g_mutex_free (priv->observers_lock);
646 /* Clear hidding array created by cut operation */
647 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
649 G_OBJECT_CLASS(parent_class)->finalize (obj);
654 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
657 GtkTreeSelection *sel;
658 ModestHeaderView *self;
659 ModestHeaderViewPrivate *priv;
661 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
664 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
665 self = MODEST_HEADER_VIEW(obj);
666 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
668 modest_header_view_set_style (self, style);
670 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
671 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
672 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
674 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
675 TRUE); /* alternating row colors */
677 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
678 priv->selection_changed_handler =
679 g_signal_connect_after (sel, "changed",
680 G_CALLBACK(on_selection_changed), self);
682 g_signal_connect (self, "row-activated",
683 G_CALLBACK (on_header_row_activated), NULL);
685 g_signal_connect (self, "focus-in-event",
686 G_CALLBACK(on_focus_in), NULL);
687 g_signal_connect (self, "focus-out-event",
688 G_CALLBACK(on_focus_out), NULL);
690 g_signal_connect (self, "button-press-event",
691 G_CALLBACK(on_button_press_event), NULL);
692 g_signal_connect (self, "button-release-event",
693 G_CALLBACK(on_button_release_event), NULL);
695 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
697 G_CALLBACK (on_account_removed),
700 g_signal_connect (self, "expose-event",
701 G_CALLBACK(modest_header_view_on_expose_event),
704 return GTK_WIDGET(self);
709 modest_header_view_count_selected_headers (ModestHeaderView *self)
711 GtkTreeSelection *sel;
714 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
716 /* Get selection object and check selected rows count */
717 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
718 selected_rows = gtk_tree_selection_count_selected_rows (sel);
720 return selected_rows;
724 modest_header_view_has_selected_headers (ModestHeaderView *self)
726 GtkTreeSelection *sel;
729 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
731 /* Get selection object and check selected rows count */
732 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
733 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
740 modest_header_view_get_selected_headers (ModestHeaderView *self)
742 GtkTreeSelection *sel;
743 ModestHeaderViewPrivate *priv;
744 TnyList *header_list = NULL;
746 GList *list, *tmp = NULL;
747 GtkTreeModel *tree_model = NULL;
750 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
752 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
754 /* Get selected rows */
755 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
756 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
759 header_list = tny_simple_list_new();
761 list = g_list_reverse (list);
764 /* get header from selection */
765 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
766 gtk_tree_model_get (tree_model, &iter,
767 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
769 /* Prepend to list */
770 tny_list_prepend (header_list, G_OBJECT (header));
771 g_object_unref (G_OBJECT (header));
773 tmp = g_list_next (tmp);
776 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
783 /* scroll our list view so the selected item is visible */
785 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
787 #ifdef MODEST_PLATFORM_GNOME
789 GtkTreePath *selected_path;
790 GtkTreePath *start, *end;
794 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
795 selected_path = gtk_tree_model_get_path (model, iter);
797 start = gtk_tree_path_new ();
798 end = gtk_tree_path_new ();
800 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
802 if (gtk_tree_path_compare (selected_path, start) < 0 ||
803 gtk_tree_path_compare (end, selected_path) < 0)
804 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
805 selected_path, NULL, TRUE,
808 gtk_tree_path_free (selected_path);
809 gtk_tree_path_free (start);
810 gtk_tree_path_free (end);
812 #endif /* MODEST_PLATFORM_GNOME */
817 modest_header_view_select_next (ModestHeaderView *self)
819 GtkTreeSelection *sel;
824 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
826 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
827 path = get_selected_row (GTK_TREE_VIEW(self), &model);
828 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
829 /* Unselect previous path */
830 gtk_tree_selection_unselect_path (sel, path);
832 /* Move path down and selects new one */
833 if (gtk_tree_model_iter_next (model, &iter)) {
834 gtk_tree_selection_select_iter (sel, &iter);
835 scroll_to_selected (self, &iter, FALSE);
837 gtk_tree_path_free(path);
843 modest_header_view_select_prev (ModestHeaderView *self)
845 GtkTreeSelection *sel;
850 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
852 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
853 path = get_selected_row (GTK_TREE_VIEW(self), &model);
854 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
855 /* Unselect previous path */
856 gtk_tree_selection_unselect_path (sel, path);
859 if (gtk_tree_path_prev (path)) {
860 gtk_tree_model_get_iter (model, &iter, path);
862 /* Select the new one */
863 gtk_tree_selection_select_iter (sel, &iter);
864 scroll_to_selected (self, &iter, TRUE);
867 gtk_tree_path_free (path);
872 modest_header_view_get_columns (ModestHeaderView *self)
874 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
876 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
882 modest_header_view_set_style (ModestHeaderView *self,
883 ModestHeaderViewStyle style)
885 ModestHeaderViewPrivate *priv;
886 gboolean show_col_headers = FALSE;
887 ModestHeaderViewStyle old_style;
889 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
890 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
893 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
894 if (priv->style == style)
895 return TRUE; /* nothing to do */
898 case MODEST_HEADER_VIEW_STYLE_DETAILS:
899 show_col_headers = TRUE;
901 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
904 g_return_val_if_reached (FALSE);
906 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
907 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
909 old_style = priv->style;
916 ModestHeaderViewStyle
917 modest_header_view_get_style (ModestHeaderView *self)
919 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
921 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
924 /* This is used to automatically select the first header if the user
925 * has not selected any header yet.
928 modest_header_view_on_expose_event(GtkTreeView *header_view,
929 GdkEventExpose *event,
932 GtkTreeSelection *sel;
934 GtkTreeIter tree_iter;
936 model = gtk_tree_view_get_model(header_view);
941 sel = gtk_tree_view_get_selection(header_view);
942 if(!gtk_tree_selection_count_selected_rows(sel))
943 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
944 /* Prevent the widget from getting the focus
945 when selecting the first item */
946 g_object_set(header_view, "can-focus", FALSE, NULL);
947 gtk_tree_selection_select_iter(sel, &tree_iter);
948 g_object_set(header_view, "can-focus", TRUE, NULL);
955 modest_header_view_get_folder (ModestHeaderView *self)
957 ModestHeaderViewPrivate *priv;
959 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
961 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
964 g_object_ref (priv->folder);
970 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
976 ModestHeaderView *self;
977 ModestHeaderViewPrivate *priv;
979 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
981 self = MODEST_HEADER_VIEW (user_data);
982 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
984 /* Add IDLE observer (monitor) and another folder observer for
985 new messages (self) */
986 g_mutex_lock (priv->observers_lock);
988 tny_folder_monitor_stop (priv->monitor);
989 g_object_unref (G_OBJECT (priv->monitor));
991 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
992 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
993 tny_folder_monitor_start (priv->monitor);
994 g_mutex_unlock (priv->observers_lock);
998 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1002 ModestHeaderViewPrivate *priv;
1003 GList *cols, *cursor;
1004 GtkTreeModel *filter_model, *sortable;
1006 GtkSortType sort_type;
1008 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1010 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1012 /* Start the monitor in the callback of the
1013 tny_gtk_header_list_model_set_folder call. It's crucial to
1014 do it there and not just after the call because we want the
1015 monitor to observe only the headers returned by the
1016 tny_folder_get_headers_async call that it's inside the
1017 tny_gtk_header_list_model_set_folder call. This way the
1018 monitor infrastructure could successfully cope with
1019 duplicates. For example if a tny_folder_add_msg_async is
1020 happening while tny_gtk_header_list_model_set_folder is
1021 invoked, then the first call could add a header that will
1022 be added again by tny_gtk_header_list_model_set_folder, so
1023 we'd end up with duplicate headers. sergio */
1024 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1026 set_folder_intern_get_headers_async_cb,
1029 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1030 g_object_unref (G_OBJECT (headers));
1032 /* Init filter_row function to examine empty status */
1033 priv->status = HEADER_VIEW_INIT;
1035 /* Create a tree model filter to hide and show rows for cut operations */
1036 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1037 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1041 g_object_unref (G_OBJECT (sortable));
1043 /* install our special sorting functions */
1044 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1046 /* Restore sort column id */
1048 type = modest_tny_folder_guess_folder_type (folder);
1049 if (type == TNY_FOLDER_TYPE_INVALID)
1050 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1052 sort_colid = modest_header_view_get_sort_column_id (self, type);
1053 sort_type = modest_header_view_get_sort_type (self, type);
1054 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1057 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1058 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1059 (GtkTreeIterCompareFunc) cmp_rows,
1061 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1062 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1063 (GtkTreeIterCompareFunc) cmp_subject_rows,
1068 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1069 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1070 tny_folder_get_id(folder));
1071 g_object_unref (G_OBJECT (filter_model));
1078 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1080 GtkSortType sort_type)
1082 ModestHeaderViewPrivate *priv = NULL;
1083 GtkTreeModel *tree_filter, *sortable = NULL;
1086 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1087 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1089 /* Get model and private data */
1090 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1091 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1092 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1093 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1095 /* Sort tree model */
1096 type = modest_tny_folder_guess_folder_type (priv->folder);
1097 if (type == TNY_FOLDER_TYPE_INVALID)
1098 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1100 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1103 /* Store new sort parameters */
1104 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1109 modest_header_view_set_sort_params (ModestHeaderView *self,
1111 GtkSortType sort_type,
1114 ModestHeaderViewPrivate *priv;
1115 ModestHeaderViewStyle style;
1117 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1118 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1119 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1121 style = modest_header_view_get_style (self);
1122 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1124 priv->sort_colid[style][type] = sort_colid;
1125 priv->sort_type[style][type] = sort_type;
1129 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1132 ModestHeaderViewPrivate *priv;
1133 ModestHeaderViewStyle style;
1135 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1136 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1138 style = modest_header_view_get_style (self);
1139 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1141 return priv->sort_colid[style][type];
1145 modest_header_view_get_sort_type (ModestHeaderView *self,
1148 ModestHeaderViewPrivate *priv;
1149 ModestHeaderViewStyle style;
1151 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1152 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1154 style = modest_header_view_get_style (self);
1155 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1157 return priv->sort_type[style][type];
1161 ModestHeaderView *header_view;
1162 RefreshAsyncUserCallback cb;
1167 folder_refreshed_cb (ModestMailOperation *mail_op,
1171 ModestHeaderViewPrivate *priv;
1172 SetFolderHelper *info;
1174 info = (SetFolderHelper*) user_data;
1176 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1180 info->cb (mail_op, folder, info->user_data);
1182 /* Start the folder count changes observer. We do not need it
1183 before the refresh. Note that the monitor could still be
1184 called for this refresh but now we know that the callback
1185 was previously called */
1186 g_mutex_lock (priv->observers_lock);
1187 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1188 g_mutex_unlock (priv->observers_lock);
1190 /* Notify the observers that the update is over */
1191 g_signal_emit (G_OBJECT (info->header_view),
1192 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1194 /* Allow filtering notifications from now on if the current
1195 folder is still the same (if not then the user has selected
1196 another one to refresh, we should wait until that refresh
1198 if (priv->folder == folder)
1199 priv->notify_status = TRUE;
1202 g_object_unref (info->header_view);
1207 refresh_folder_error_handler (ModestMailOperation *mail_op,
1210 const GError *error = modest_mail_operation_get_error (mail_op);
1212 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1213 error->code == TNY_IO_ERROR_WRITE ||
1214 error->code == TNY_IO_ERROR_READ) {
1215 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1216 /* If the mail op has been cancelled then it's not an error: don't show any message */
1217 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1218 modest_platform_information_banner (NULL, NULL,
1220 "cerm_device_memory_full"));
1226 modest_header_view_set_folder (ModestHeaderView *self,
1228 RefreshAsyncUserCallback callback,
1231 ModestHeaderViewPrivate *priv;
1232 SetFolderHelper *info;
1233 ModestWindow *main_win;
1235 g_return_if_fail (self);
1237 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1239 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1240 FALSE); /* don't create */
1242 g_warning ("%s: BUG: no main window", __FUNCTION__);
1247 if (priv->status_timeout) {
1248 g_source_remove (priv->status_timeout);
1249 priv->status_timeout = 0;
1252 g_mutex_lock (priv->observers_lock);
1253 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1254 g_object_unref (priv->folder);
1255 priv->folder = NULL;
1256 g_mutex_unlock (priv->observers_lock);
1260 ModestMailOperation *mail_op = NULL;
1261 GtkTreeSelection *selection;
1263 /* Set folder in the model */
1264 modest_header_view_set_folder_intern (self, folder);
1266 /* Pick my reference. Nothing to do with the mail operation */
1267 priv->folder = g_object_ref (folder);
1269 /* Do not notify about filterings until the refresh finishes */
1270 priv->notify_status = FALSE;
1272 /* Clear the selection if exists */
1273 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1274 gtk_tree_selection_unselect_all(selection);
1275 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1277 /* Notify the observers that the update begins */
1278 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1281 /* create the helper */
1282 info = g_malloc0 (sizeof(SetFolderHelper));
1283 info->header_view = g_object_ref (self);
1284 info->cb = callback;
1285 info->user_data = user_data;
1287 /* Create the mail operation (source will be the parent widget) */
1288 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1289 refresh_folder_error_handler,
1291 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1294 /* Refresh the folder asynchronously */
1295 modest_mail_operation_refresh_folder (mail_op,
1297 folder_refreshed_cb,
1301 g_object_unref (mail_op);
1303 g_mutex_lock (priv->observers_lock);
1305 if (priv->monitor) {
1306 tny_folder_monitor_stop (priv->monitor);
1307 g_object_unref (G_OBJECT (priv->monitor));
1308 priv->monitor = NULL;
1310 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1312 modest_header_view_notify_observers(self, NULL, NULL);
1314 g_mutex_unlock (priv->observers_lock);
1316 /* Notify the observers that the update is over */
1317 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1323 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1324 GtkTreeViewColumn *column, gpointer userdata)
1326 ModestHeaderView *self = NULL;
1327 ModestHeaderViewPrivate *priv = NULL;
1329 GtkTreeModel *model = NULL;
1330 TnyHeader *header = NULL;
1331 TnyHeaderFlags flags;
1333 self = MODEST_HEADER_VIEW (treeview);
1334 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1336 model = gtk_tree_view_get_model (treeview);
1337 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1340 /* get the first selected item */
1341 gtk_tree_model_get (model, &iter,
1342 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1343 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1346 /* Dont open DELETED messages */
1347 if (flags & TNY_HEADER_FLAG_DELETED) {
1350 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1351 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1352 modest_platform_information_banner (NULL, NULL, msg);
1358 g_signal_emit (G_OBJECT(self),
1359 signals[HEADER_ACTIVATED_SIGNAL],
1365 g_object_unref (G_OBJECT (header));
1370 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1372 GtkTreeModel *model;
1373 TnyHeader *header = NULL;
1374 GtkTreePath *path = NULL;
1376 ModestHeaderView *self;
1377 ModestHeaderViewPrivate *priv;
1378 GList *selected = NULL;
1380 g_return_if_fail (sel);
1381 g_return_if_fail (user_data);
1383 self = MODEST_HEADER_VIEW (user_data);
1384 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1386 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1387 if (selected != NULL)
1388 path = (GtkTreePath *) selected->data;
1389 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1390 return; /* msg was _un_selected */
1392 gtk_tree_model_get (model, &iter,
1393 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1397 g_signal_emit (G_OBJECT(self),
1398 signals[HEADER_SELECTED_SIGNAL],
1401 g_object_unref (G_OBJECT (header));
1403 /* free all items in 'selected' */
1404 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1405 g_list_free (selected);
1409 /* PROTECTED method. It's useful when we want to force a given
1410 selection to reload a msg. For example if we have selected a header
1411 in offline mode, when Modest become online, we want to reload the
1412 message automatically without an user click over the header */
1414 _modest_header_view_change_selection (GtkTreeSelection *selection,
1417 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1418 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1420 on_selection_changed (selection, user_data);
1424 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1431 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1435 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1439 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1447 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1454 /* static int counter = 0; */
1456 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1457 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1458 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1462 case TNY_HEADER_FLAG_ATTACHMENTS:
1464 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1465 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1466 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1467 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1469 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1470 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1472 return cmp ? cmp : t1 - t2;
1474 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1475 TnyHeader *header1 = NULL, *header2 = NULL;
1477 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1478 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1479 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1480 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1482 /* This is for making priority values respect the intuitive sort relationship
1483 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1485 if (header1 && header2) {
1486 cmp = compare_priorities (tny_header_get_priority (header1),
1487 tny_header_get_priority (header2));
1488 g_object_unref (header1);
1489 g_object_unref (header2);
1491 return cmp ? cmp : t1 - t2;
1497 return &iter1 - &iter2; /* oughhhh */
1502 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1508 /* static int counter = 0; */
1510 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1512 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1513 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1514 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1515 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1517 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1518 val2 + modest_text_utils_get_subject_prefix_len(val2),
1525 /* Drag and drop stuff */
1527 drag_data_get_cb (GtkWidget *widget,
1528 GdkDragContext *context,
1529 GtkSelectionData *selection_data,
1534 ModestHeaderView *self = NULL;
1535 ModestHeaderViewPrivate *priv = NULL;
1537 self = MODEST_HEADER_VIEW (widget);
1538 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1540 /* Set the data. Do not use the current selection because it
1541 could be different than the selection at the beginning of
1543 modest_dnd_selection_data_set_paths (selection_data,
1544 priv->drag_begin_cached_selected_rows);
1548 * We're caching the selected rows at the beginning because the
1549 * selection could change between drag-begin and drag-data-get, for
1550 * example if we have a set of rows already selected, and then we
1551 * click in one of them (without SHIFT key pressed) and begin a drag,
1552 * the selection at that moment contains all the selected lines, but
1553 * after dropping the selection, the release event provokes that only
1554 * the row used to begin the drag is selected, so at the end the
1555 * drag&drop affects only one rows instead of all the selected ones.
1559 drag_begin_cb (GtkWidget *widget,
1560 GdkDragContext *context,
1563 ModestHeaderView *self = NULL;
1564 ModestHeaderViewPrivate *priv = NULL;
1565 GtkTreeSelection *selection;
1567 self = MODEST_HEADER_VIEW (widget);
1568 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1570 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1571 priv->drag_begin_cached_selected_rows =
1572 gtk_tree_selection_get_selected_rows (selection, NULL);
1576 * We use the drag-end signal to clear the cached selection, we use
1577 * this because this allways happens, whether or not the d&d was a
1581 drag_end_cb (GtkWidget *widget,
1585 ModestHeaderView *self = NULL;
1586 ModestHeaderViewPrivate *priv = NULL;
1588 self = MODEST_HEADER_VIEW (widget);
1589 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1591 /* Free cached data */
1592 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1593 g_list_free (priv->drag_begin_cached_selected_rows);
1594 priv->drag_begin_cached_selected_rows = NULL;
1597 /* Header view drag types */
1598 const GtkTargetEntry header_view_drag_types[] = {
1599 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1603 enable_drag_and_drop (GtkWidget *self)
1605 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1606 header_view_drag_types,
1607 G_N_ELEMENTS (header_view_drag_types),
1608 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1612 disable_drag_and_drop (GtkWidget *self)
1614 gtk_drag_source_unset (self);
1618 setup_drag_and_drop (GtkWidget *self)
1620 enable_drag_and_drop(self);
1621 g_signal_connect(G_OBJECT (self), "drag_data_get",
1622 G_CALLBACK(drag_data_get_cb), NULL);
1624 g_signal_connect(G_OBJECT (self), "drag_begin",
1625 G_CALLBACK(drag_begin_cb), NULL);
1627 g_signal_connect(G_OBJECT (self), "drag_end",
1628 G_CALLBACK(drag_end_cb), NULL);
1631 static GtkTreePath *
1632 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1634 GtkTreePath *path = NULL;
1635 GtkTreeSelection *sel = NULL;
1638 sel = gtk_tree_view_get_selection(self);
1639 rows = gtk_tree_selection_get_selected_rows (sel, model);
1641 if ((rows == NULL) || (g_list_length(rows) != 1))
1644 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1649 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1656 * This function moves the tree view scroll to the current selected
1657 * row when the widget grabs the focus
1660 on_focus_in (GtkWidget *self,
1661 GdkEventFocus *event,
1664 GtkTreeSelection *selection;
1665 GtkTreeModel *model;
1666 GList *selected = NULL;
1667 GtkTreePath *selected_path = NULL;
1669 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1673 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1674 /* If none selected yet, pick the first one */
1675 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1679 /* Return if the model is empty */
1680 if (!gtk_tree_model_get_iter_first (model, &iter))
1683 path = gtk_tree_model_get_path (model, &iter);
1684 gtk_tree_selection_select_path (selection, path);
1685 gtk_tree_path_free (path);
1688 /* Need to get the all the rows because is selection multiple */
1689 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1690 if (selected == NULL) return FALSE;
1691 selected_path = (GtkTreePath *) selected->data;
1693 /* Check if we need to scroll */
1694 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1695 GtkTreePath *start_path = NULL;
1696 GtkTreePath *end_path = NULL;
1697 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1701 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1702 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1704 /* Scroll to first path */
1705 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1714 gtk_tree_path_free (start_path);
1716 gtk_tree_path_free (end_path);
1718 #endif /* GTK_CHECK_VERSION */
1721 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1722 g_list_free (selected);
1728 on_focus_out (GtkWidget *self,
1729 GdkEventFocus *event,
1733 if (!gtk_widget_is_focus (self)) {
1734 GtkTreeSelection *selection = NULL;
1735 GList *selected_rows = NULL;
1736 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1737 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1738 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1739 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1740 gtk_tree_selection_unselect_all (selection);
1741 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1742 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1743 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1744 g_list_free (selected_rows);
1751 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1753 enable_drag_and_drop(self);
1758 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1760 GtkTreeSelection *selection = NULL;
1761 GtkTreePath *path = NULL;
1762 gboolean already_selected = FALSE, already_opened = FALSE;
1763 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1765 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1767 GtkTreeModel *model;
1769 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1770 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1772 /* Get header from model */
1773 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1774 if (gtk_tree_model_get_iter (model, &iter, path)) {
1775 GValue value = {0,};
1778 gtk_tree_model_get_value (model, &iter,
1779 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1781 header = (TnyHeader *) g_value_get_object (&value);
1782 if (TNY_IS_HEADER (header)) {
1783 status = modest_tny_all_send_queues_get_msg_status (header);
1784 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1787 g_value_unset (&value);
1791 /* Enable drag and drop only if the user clicks on a row that
1792 it's already selected. If not, let him select items using
1793 the pointer. If the message is in an OUTBOX and in sending
1794 status disable drag and drop as well */
1795 if (!already_selected ||
1796 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1798 disable_drag_and_drop(self);
1801 gtk_tree_path_free(path);
1803 /* If it's already opened then do not let the button-press
1804 event go on because it'll perform a message open because
1805 we're clicking on to an already selected header */
1810 folder_monitor_update (TnyFolderObserver *self,
1811 TnyFolderChange *change)
1813 ModestHeaderViewPrivate *priv = NULL;
1814 TnyFolderChangeChanged changed;
1815 TnyFolder *folder = NULL;
1817 changed = tny_folder_change_get_changed (change);
1819 /* Do not notify the observers if the folder of the header
1820 view has changed before this call to the observer
1822 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1823 folder = tny_folder_change_get_folder (change);
1824 if (folder != priv->folder)
1827 MODEST_DEBUG_BLOCK (
1828 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1829 g_print ("ADDED %d/%d (r/t) \n",
1830 tny_folder_change_get_new_unread_count (change),
1831 tny_folder_change_get_new_all_count (change));
1832 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1833 g_print ("ALL COUNT %d\n",
1834 tny_folder_change_get_new_all_count (change));
1835 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1836 g_print ("UNREAD COUNT %d\n",
1837 tny_folder_change_get_new_unread_count (change));
1838 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1839 g_print ("EXPUNGED %d/%d (r/t) \n",
1840 tny_folder_change_get_new_unread_count (change),
1841 tny_folder_change_get_new_all_count (change));
1842 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1843 g_print ("FOLDER RENAME\n");
1844 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1845 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1846 tny_folder_change_get_new_unread_count (change),
1847 tny_folder_change_get_new_all_count (change));
1848 g_print ("---------------------------------------------------\n");
1851 /* Check folder count */
1852 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1853 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1855 g_mutex_lock (priv->observers_lock);
1857 /* Emit signal to evaluate how headers changes affects
1858 to the window view */
1859 g_signal_emit (G_OBJECT(self),
1860 signals[MSG_COUNT_CHANGED_SIGNAL],
1863 /* Added or removed headers, so data stored on cliboard are invalid */
1864 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1865 modest_email_clipboard_clear (priv->clipboard);
1867 g_mutex_unlock (priv->observers_lock);
1873 g_object_unref (folder);
1877 modest_header_view_is_empty (ModestHeaderView *self)
1879 ModestHeaderViewPrivate *priv;
1881 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1883 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1885 return priv->status == HEADER_VIEW_EMPTY;
1889 modest_header_view_clear (ModestHeaderView *self)
1891 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1893 modest_header_view_set_folder (self, NULL, NULL, NULL);
1897 modest_header_view_copy_selection (ModestHeaderView *header_view)
1899 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1901 /* Copy selection */
1902 _clipboard_set_selected_data (header_view, FALSE);
1906 modest_header_view_cut_selection (ModestHeaderView *header_view)
1908 ModestHeaderViewPrivate *priv = NULL;
1909 const gchar **hidding = NULL;
1910 guint i, n_selected;
1912 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1914 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1916 /* Copy selection */
1917 _clipboard_set_selected_data (header_view, TRUE);
1919 /* Get hidding ids */
1920 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1922 /* Clear hidding array created by previous cut operation */
1923 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1925 /* Copy hidding array */
1926 priv->n_selected = n_selected;
1927 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1928 for (i=0; i < n_selected; i++)
1929 priv->hidding_ids[i] = g_strdup(hidding[i]);
1931 /* Hide cut headers */
1932 modest_header_view_refilter (header_view);
1939 _clipboard_set_selected_data (ModestHeaderView *header_view,
1942 ModestHeaderViewPrivate *priv = NULL;
1943 TnyList *headers = NULL;
1945 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1946 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1948 /* Set selected data on clipboard */
1949 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1950 headers = modest_header_view_get_selected_headers (header_view);
1951 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1954 g_object_unref (headers);
1958 ModestHeaderView *self;
1963 notify_filter_change (gpointer data)
1965 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1967 g_signal_emit (info->self,
1968 signals[MSG_COUNT_CHANGED_SIGNAL],
1969 0, info->folder, NULL);
1975 notify_filter_change_destroy (gpointer data)
1977 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1978 ModestHeaderViewPrivate *priv;
1980 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
1981 priv->status_timeout = 0;
1983 g_object_unref (info->self);
1984 g_object_unref (info->folder);
1985 g_slice_free (NotifyFilterInfo, info);
1989 filter_row (GtkTreeModel *model,
1993 ModestHeaderViewPrivate *priv = NULL;
1994 TnyHeaderFlags flags;
1995 TnyHeader *header = NULL;
1998 gboolean visible = TRUE;
1999 gboolean found = FALSE;
2000 GValue value = {0,};
2001 HeaderViewStatus old_status;
2003 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2004 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2006 /* Get header from model */
2007 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2008 flags = (TnyHeaderFlags) g_value_get_int (&value);
2009 g_value_unset (&value);
2010 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2011 header = (TnyHeader *) g_value_get_object (&value);
2012 g_value_unset (&value);
2014 /* Hide deleted and mark as deleted heders */
2015 if (flags & TNY_HEADER_FLAG_DELETED ||
2016 flags & TNY_HEADER_FLAG_EXPUNGED) {
2021 /* If no data on clipboard, return always TRUE */
2022 if (modest_email_clipboard_cleared(priv->clipboard)) {
2027 /* Get message id from header (ensure is a valid id) */
2034 if (priv->hidding_ids != NULL) {
2035 id = tny_header_dup_message_id (header);
2036 for (i=0; i < priv->n_selected && !found; i++)
2037 if (priv->hidding_ids[i] != NULL && id != NULL)
2038 found = (!strcmp (priv->hidding_ids[i], id));
2045 old_status = priv->status;
2046 priv->status = ((gboolean) priv->status) && !visible;
2047 if ((priv->notify_status) && (priv->status != old_status)) {
2048 NotifyFilterInfo *info;
2050 if (priv->status_timeout)
2051 g_source_remove (priv->status_timeout);
2053 info = g_slice_new0 (NotifyFilterInfo);
2054 info->self = g_object_ref (G_OBJECT (user_data));
2055 info->folder = tny_header_get_folder (header);
2056 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2057 notify_filter_change,
2059 notify_filter_change_destroy);
2066 _clear_hidding_filter (ModestHeaderView *header_view)
2068 ModestHeaderViewPrivate *priv = NULL;
2071 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2072 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2074 if (priv->hidding_ids != NULL) {
2075 for (i=0; i < priv->n_selected; i++)
2076 g_free (priv->hidding_ids[i]);
2077 g_free(priv->hidding_ids);
2082 modest_header_view_refilter (ModestHeaderView *header_view)
2084 GtkTreeModel *model = NULL;
2085 ModestHeaderViewPrivate *priv = NULL;
2087 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2088 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2090 /* Hide cut headers */
2091 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2092 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2093 priv->status = HEADER_VIEW_INIT;
2094 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2099 * Called when an account is removed. If I'm showing a folder of the
2100 * account that has been removed then clear the view
2103 on_account_removed (TnyAccountStore *self,
2104 TnyAccount *account,
2107 ModestHeaderViewPrivate *priv = NULL;
2109 /* Ignore changes in transport accounts */
2110 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2113 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2116 TnyAccount *my_account;
2118 my_account = tny_folder_get_account (priv->folder);
2119 if (my_account == account)
2120 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2121 g_object_unref (my_account);
2126 modest_header_view_add_observer(ModestHeaderView *header_view,
2127 ModestHeaderViewObserver *observer)
2129 ModestHeaderViewPrivate *priv;
2131 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2132 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2134 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2136 g_mutex_lock(priv->observer_list_lock);
2137 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2138 g_mutex_unlock(priv->observer_list_lock);
2142 modest_header_view_remove_observer(ModestHeaderView *header_view,
2143 ModestHeaderViewObserver *observer)
2145 ModestHeaderViewPrivate *priv;
2147 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2148 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2150 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2152 g_mutex_lock(priv->observer_list_lock);
2153 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2154 g_mutex_unlock(priv->observer_list_lock);
2158 modest_header_view_notify_observers(ModestHeaderView *header_view,
2159 GtkTreeModel *model,
2160 const gchar *tny_folder_id)
2162 ModestHeaderViewPrivate *priv = NULL;
2164 ModestHeaderViewObserver *observer;
2167 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2169 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2171 g_mutex_lock(priv->observer_list_lock);
2172 iter = priv->observer_list;
2173 while(iter != NULL){
2174 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2175 modest_header_view_observer_update(observer, model,
2177 iter = g_slist_next(iter);
2179 g_mutex_unlock(priv->observer_list_lock);