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(ModestHeaderView *header_view,
115 const gchar *tny_folder_id);
117 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
118 GdkEventExpose *event,
122 HEADER_VIEW_NON_EMPTY,
127 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
128 struct _ModestHeaderViewPrivate {
130 ModestHeaderViewStyle style;
132 TnyFolderMonitor *monitor;
133 GMutex *observers_lock;
135 /*header-view-observer observer*/
136 GMutex *observer_list_lock;
137 GSList *observer_list;
139 /* not unref this object, its a singlenton */
140 ModestEmailClipboard *clipboard;
142 /* Filter tree model */
145 GtkTreeRowReference *autoselect_reference;
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 MODEST_PLATFORM_MAEMO
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 MODEST_PLATFORM_MAEMO
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 ();
573 priv->autoselect_reference = NULL;
575 priv->status = HEADER_VIEW_INIT;
576 priv->status_timeout = 0;
577 priv->notify_status = TRUE;
579 priv->observer_list_lock = g_mutex_new();
580 priv->observer_list = NULL;
582 priv->clipboard = modest_runtime_get_email_clipboard ();
583 priv->hidding_ids = NULL;
584 priv->n_selected = 0;
585 priv->selection_changed_handler = 0;
586 priv->acc_removed_handler = 0;
588 /* Sort parameters */
589 for (j=0; j < 2; j++) {
590 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
591 priv->sort_colid[j][i] = -1;
592 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
596 setup_drag_and_drop (GTK_WIDGET(obj));
600 modest_header_view_dispose (GObject *obj)
602 ModestHeaderView *self;
603 ModestHeaderViewPrivate *priv;
604 GtkTreeSelection *sel;
606 self = MODEST_HEADER_VIEW(obj);
607 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
609 /* Free in the dispose to avoid unref cycles */
611 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
612 g_object_unref (G_OBJECT (priv->folder));
616 /* We need to do this here in the dispose because the
617 selection won't exist when finalizing */
618 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
619 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
620 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
621 priv->selection_changed_handler = 0;
624 G_OBJECT_CLASS(parent_class)->dispose (obj);
628 modest_header_view_finalize (GObject *obj)
630 ModestHeaderView *self;
631 ModestHeaderViewPrivate *priv;
633 self = MODEST_HEADER_VIEW(obj);
634 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
636 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
637 priv->acc_removed_handler)) {
638 g_signal_handler_disconnect (modest_runtime_get_account_store (),
639 priv->acc_removed_handler);
642 /* There is no need to lock because there should not be any
643 * reference to self now. */
644 g_mutex_free(priv->observer_list_lock);
645 g_slist_free(priv->observer_list);
647 g_mutex_lock (priv->observers_lock);
649 tny_folder_monitor_stop (priv->monitor);
650 g_object_unref (G_OBJECT (priv->monitor));
652 g_mutex_unlock (priv->observers_lock);
653 g_mutex_free (priv->observers_lock);
655 /* Clear hidding array created by cut operation */
656 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
658 if (priv->autoselect_reference != NULL) {
659 gtk_tree_row_reference_free (priv->autoselect_reference);
660 priv->autoselect_reference = NULL;
663 G_OBJECT_CLASS(parent_class)->finalize (obj);
668 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
671 GtkTreeSelection *sel;
672 ModestHeaderView *self;
673 ModestHeaderViewPrivate *priv;
675 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
678 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
679 self = MODEST_HEADER_VIEW(obj);
680 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
682 modest_header_view_set_style (self, style);
684 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
685 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
686 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
688 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
689 TRUE); /* alternating row colors */
691 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
692 priv->selection_changed_handler =
693 g_signal_connect_after (sel, "changed",
694 G_CALLBACK(on_selection_changed), self);
696 g_signal_connect (self, "row-activated",
697 G_CALLBACK (on_header_row_activated), NULL);
699 g_signal_connect (self, "focus-in-event",
700 G_CALLBACK(on_focus_in), NULL);
701 g_signal_connect (self, "focus-out-event",
702 G_CALLBACK(on_focus_out), NULL);
704 g_signal_connect (self, "button-press-event",
705 G_CALLBACK(on_button_press_event), NULL);
706 g_signal_connect (self, "button-release-event",
707 G_CALLBACK(on_button_release_event), NULL);
709 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
711 G_CALLBACK (on_account_removed),
714 g_signal_connect (self, "expose-event",
715 G_CALLBACK(modest_header_view_on_expose_event),
718 return GTK_WIDGET(self);
723 modest_header_view_count_selected_headers (ModestHeaderView *self)
725 GtkTreeSelection *sel;
728 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
730 /* Get selection object and check selected rows count */
731 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
732 selected_rows = gtk_tree_selection_count_selected_rows (sel);
734 return selected_rows;
738 modest_header_view_has_selected_headers (ModestHeaderView *self)
740 GtkTreeSelection *sel;
743 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
745 /* Get selection object and check selected rows count */
746 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
747 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
754 modest_header_view_get_selected_headers (ModestHeaderView *self)
756 GtkTreeSelection *sel;
757 ModestHeaderViewPrivate *priv;
758 TnyList *header_list = NULL;
760 GList *list, *tmp = NULL;
761 GtkTreeModel *tree_model = NULL;
764 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
766 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
768 /* Get selected rows */
769 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
770 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
773 header_list = tny_simple_list_new();
775 list = g_list_reverse (list);
778 /* get header from selection */
779 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
780 gtk_tree_model_get (tree_model, &iter,
781 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
783 /* Prepend to list */
784 tny_list_prepend (header_list, G_OBJECT (header));
785 g_object_unref (G_OBJECT (header));
787 tmp = g_list_next (tmp);
790 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
797 /* scroll our list view so the selected item is visible */
799 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
801 #ifdef MODEST_PLATFORM_GNOME
803 GtkTreePath *selected_path;
804 GtkTreePath *start, *end;
808 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
809 selected_path = gtk_tree_model_get_path (model, iter);
811 start = gtk_tree_path_new ();
812 end = gtk_tree_path_new ();
814 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
816 if (gtk_tree_path_compare (selected_path, start) < 0 ||
817 gtk_tree_path_compare (end, selected_path) < 0)
818 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
819 selected_path, NULL, TRUE,
822 gtk_tree_path_free (selected_path);
823 gtk_tree_path_free (start);
824 gtk_tree_path_free (end);
826 #endif /* MODEST_PLATFORM_GNOME */
831 modest_header_view_select_next (ModestHeaderView *self)
833 GtkTreeSelection *sel;
838 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
840 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
841 path = get_selected_row (GTK_TREE_VIEW(self), &model);
842 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
843 /* Unselect previous path */
844 gtk_tree_selection_unselect_path (sel, path);
846 /* Move path down and selects new one */
847 if (gtk_tree_model_iter_next (model, &iter)) {
848 gtk_tree_selection_select_iter (sel, &iter);
849 scroll_to_selected (self, &iter, FALSE);
851 gtk_tree_path_free(path);
857 modest_header_view_select_prev (ModestHeaderView *self)
859 GtkTreeSelection *sel;
864 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
866 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
867 path = get_selected_row (GTK_TREE_VIEW(self), &model);
868 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
869 /* Unselect previous path */
870 gtk_tree_selection_unselect_path (sel, path);
873 if (gtk_tree_path_prev (path)) {
874 gtk_tree_model_get_iter (model, &iter, path);
876 /* Select the new one */
877 gtk_tree_selection_select_iter (sel, &iter);
878 scroll_to_selected (self, &iter, TRUE);
881 gtk_tree_path_free (path);
886 modest_header_view_get_columns (ModestHeaderView *self)
888 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
890 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
896 modest_header_view_set_style (ModestHeaderView *self,
897 ModestHeaderViewStyle style)
899 ModestHeaderViewPrivate *priv;
900 gboolean show_col_headers = FALSE;
901 ModestHeaderViewStyle old_style;
903 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
904 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
907 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
908 if (priv->style == style)
909 return TRUE; /* nothing to do */
912 case MODEST_HEADER_VIEW_STYLE_DETAILS:
913 show_col_headers = TRUE;
915 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
918 g_return_val_if_reached (FALSE);
920 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
921 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
923 old_style = priv->style;
930 ModestHeaderViewStyle
931 modest_header_view_get_style (ModestHeaderView *self)
933 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
935 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
938 /* This is used to automatically select the first header if the user
939 * has not selected any header yet.
942 modest_header_view_on_expose_event(GtkTreeView *header_view,
943 GdkEventExpose *event,
946 GtkTreeSelection *sel;
948 GtkTreeIter tree_iter;
949 ModestHeaderViewPrivate *priv;
951 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
952 model = gtk_tree_view_get_model(header_view);
957 sel = gtk_tree_view_get_selection(header_view);
958 if(!gtk_tree_selection_count_selected_rows(sel)) {
959 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
960 GtkTreePath *tree_iter_path;
961 /* Prevent the widget from getting the focus
962 when selecting the first item */
963 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
964 g_object_set(header_view, "can-focus", FALSE, NULL);
965 gtk_tree_selection_select_iter(sel, &tree_iter);
966 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
967 g_object_set(header_view, "can-focus", TRUE, NULL);
968 if (priv->autoselect_reference) {
969 gtk_tree_row_reference_free (priv->autoselect_reference);
971 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
972 gtk_tree_path_free (tree_iter_path);
975 if (priv->autoselect_reference != NULL) {
976 gboolean moved_selection = FALSE;
977 GtkTreePath * last_path;
978 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
979 moved_selection = TRUE;
983 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
984 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
985 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
986 moved_selection = TRUE;
987 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
990 if (moved_selection) {
991 gtk_tree_row_reference_free (priv->autoselect_reference);
992 priv->autoselect_reference = NULL;
995 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
996 GtkTreePath *current_path;
997 current_path = gtk_tree_model_get_path (model, &tree_iter);
998 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
999 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1000 g_object_set(header_view, "can-focus", FALSE, NULL);
1001 gtk_tree_selection_unselect_all (sel);
1002 gtk_tree_selection_select_iter(sel, &tree_iter);
1003 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1004 g_object_set(header_view, "can-focus", TRUE, NULL);
1005 gtk_tree_row_reference_free (priv->autoselect_reference);
1006 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1008 gtk_tree_path_free (current_path);
1009 gtk_tree_path_free (last_path);
1019 modest_header_view_get_folder (ModestHeaderView *self)
1021 ModestHeaderViewPrivate *priv;
1023 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1025 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1028 g_object_ref (priv->folder);
1030 return priv->folder;
1034 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1040 ModestHeaderView *self;
1041 ModestHeaderViewPrivate *priv;
1043 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1045 self = MODEST_HEADER_VIEW (user_data);
1046 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1048 if (cancelled || err)
1051 /* Add IDLE observer (monitor) and another folder observer for
1052 new messages (self) */
1053 g_mutex_lock (priv->observers_lock);
1054 if (priv->monitor) {
1055 tny_folder_monitor_stop (priv->monitor);
1056 g_object_unref (G_OBJECT (priv->monitor));
1058 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1059 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1060 tny_folder_monitor_start (priv->monitor);
1061 g_mutex_unlock (priv->observers_lock);
1065 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1069 ModestHeaderViewPrivate *priv;
1070 GList *cols, *cursor;
1071 GtkTreeModel *filter_model, *sortable;
1073 GtkSortType sort_type;
1075 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1077 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1079 /* Start the monitor in the callback of the
1080 tny_gtk_header_list_model_set_folder call. It's crucial to
1081 do it there and not just after the call because we want the
1082 monitor to observe only the headers returned by the
1083 tny_folder_get_headers_async call that it's inside the
1084 tny_gtk_header_list_model_set_folder call. This way the
1085 monitor infrastructure could successfully cope with
1086 duplicates. For example if a tny_folder_add_msg_async is
1087 happening while tny_gtk_header_list_model_set_folder is
1088 invoked, then the first call could add a header that will
1089 be added again by tny_gtk_header_list_model_set_folder, so
1090 we'd end up with duplicate headers. sergio */
1091 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1093 set_folder_intern_get_headers_async_cb,
1096 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1097 g_object_unref (G_OBJECT (headers));
1099 /* Init filter_row function to examine empty status */
1100 priv->status = HEADER_VIEW_INIT;
1102 /* Create a tree model filter to hide and show rows for cut operations */
1103 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1104 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1108 g_object_unref (G_OBJECT (sortable));
1110 /* install our special sorting functions */
1111 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1113 /* Restore sort column id */
1115 type = modest_tny_folder_guess_folder_type (folder);
1116 if (type == TNY_FOLDER_TYPE_INVALID)
1117 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1119 sort_colid = modest_header_view_get_sort_column_id (self, type);
1120 sort_type = modest_header_view_get_sort_type (self, type);
1121 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1124 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1125 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1126 (GtkTreeIterCompareFunc) cmp_rows,
1128 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1129 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1130 (GtkTreeIterCompareFunc) cmp_subject_rows,
1135 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1136 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1137 tny_folder_get_id(folder));
1138 g_object_unref (G_OBJECT (filter_model));
1145 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1147 GtkSortType sort_type)
1149 ModestHeaderViewPrivate *priv = NULL;
1150 GtkTreeModel *tree_filter, *sortable = NULL;
1153 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1154 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1156 /* Get model and private data */
1157 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1158 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1159 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1160 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1162 /* Sort tree model */
1163 type = modest_tny_folder_guess_folder_type (priv->folder);
1164 if (type == TNY_FOLDER_TYPE_INVALID)
1165 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1167 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1170 /* Store new sort parameters */
1171 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1176 modest_header_view_set_sort_params (ModestHeaderView *self,
1178 GtkSortType sort_type,
1181 ModestHeaderViewPrivate *priv;
1182 ModestHeaderViewStyle style;
1184 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1185 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1186 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1188 style = modest_header_view_get_style (self);
1189 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1191 priv->sort_colid[style][type] = sort_colid;
1192 priv->sort_type[style][type] = sort_type;
1196 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1199 ModestHeaderViewPrivate *priv;
1200 ModestHeaderViewStyle style;
1202 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1203 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1205 style = modest_header_view_get_style (self);
1206 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1208 return priv->sort_colid[style][type];
1212 modest_header_view_get_sort_type (ModestHeaderView *self,
1215 ModestHeaderViewPrivate *priv;
1216 ModestHeaderViewStyle style;
1218 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1219 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1221 style = modest_header_view_get_style (self);
1222 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1224 return priv->sort_type[style][type];
1228 ModestHeaderView *header_view;
1229 RefreshAsyncUserCallback cb;
1234 folder_refreshed_cb (ModestMailOperation *mail_op,
1238 ModestHeaderViewPrivate *priv;
1239 SetFolderHelper *info;
1241 info = (SetFolderHelper*) user_data;
1243 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1247 info->cb (mail_op, folder, info->user_data);
1249 /* Start the folder count changes observer. We do not need it
1250 before the refresh. Note that the monitor could still be
1251 called for this refresh but now we know that the callback
1252 was previously called */
1253 g_mutex_lock (priv->observers_lock);
1254 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1255 g_mutex_unlock (priv->observers_lock);
1257 /* Notify the observers that the update is over */
1258 g_signal_emit (G_OBJECT (info->header_view),
1259 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1261 /* Allow filtering notifications from now on if the current
1262 folder is still the same (if not then the user has selected
1263 another one to refresh, we should wait until that refresh
1265 if (priv->folder == folder)
1266 priv->notify_status = TRUE;
1269 g_object_unref (info->header_view);
1274 refresh_folder_error_handler (ModestMailOperation *mail_op,
1277 const GError *error = modest_mail_operation_get_error (mail_op);
1279 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1280 error->code == TNY_IO_ERROR_WRITE ||
1281 error->code == TNY_IO_ERROR_READ) {
1282 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1283 /* If the mail op has been cancelled then it's not an error: don't show any message */
1284 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1285 modest_platform_information_banner (NULL, NULL,
1287 "cerm_device_memory_full"));
1293 modest_header_view_set_folder (ModestHeaderView *self,
1296 RefreshAsyncUserCallback callback,
1299 ModestHeaderViewPrivate *priv;
1300 ModestWindow *main_win;
1302 g_return_if_fail (self);
1304 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1306 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1307 FALSE); /* don't create */
1309 g_warning ("%s: BUG: no main window", __FUNCTION__);
1314 if (priv->status_timeout) {
1315 g_source_remove (priv->status_timeout);
1316 priv->status_timeout = 0;
1319 g_mutex_lock (priv->observers_lock);
1320 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1321 g_object_unref (priv->folder);
1322 priv->folder = NULL;
1323 g_mutex_unlock (priv->observers_lock);
1327 GtkTreeSelection *selection;
1328 SetFolderHelper *info;
1329 ModestMailOperation *mail_op = NULL;
1331 /* Set folder in the model */
1332 modest_header_view_set_folder_intern (self, folder);
1334 /* Pick my reference. Nothing to do with the mail operation */
1335 priv->folder = g_object_ref (folder);
1337 /* Do not notify about filterings until the refresh finishes */
1338 priv->notify_status = FALSE;
1340 /* Clear the selection if exists */
1341 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1342 gtk_tree_selection_unselect_all(selection);
1343 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1345 /* Notify the observers that the update begins */
1346 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1349 /* create the helper */
1350 info = g_malloc0 (sizeof (SetFolderHelper));
1351 info->header_view = g_object_ref (self);
1352 info->cb = callback;
1353 info->user_data = user_data;
1355 /* Create the mail operation (source will be the parent widget) */
1356 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1357 refresh_folder_error_handler,
1360 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1363 /* Refresh the folder asynchronously */
1364 modest_mail_operation_refresh_folder (mail_op,
1366 folder_refreshed_cb,
1369 folder_refreshed_cb (mail_op, folder, info);
1372 g_object_unref (mail_op);
1374 g_mutex_lock (priv->observers_lock);
1376 if (priv->monitor) {
1377 tny_folder_monitor_stop (priv->monitor);
1378 g_object_unref (G_OBJECT (priv->monitor));
1379 priv->monitor = NULL;
1382 if (priv->autoselect_reference) {
1383 gtk_tree_row_reference_free (priv->autoselect_reference);
1384 priv->autoselect_reference = NULL;
1387 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1389 modest_header_view_notify_observers(self, NULL, NULL);
1391 g_mutex_unlock (priv->observers_lock);
1393 /* Notify the observers that the update is over */
1394 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1400 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1401 GtkTreeViewColumn *column, gpointer userdata)
1403 ModestHeaderView *self = NULL;
1404 ModestHeaderViewPrivate *priv = NULL;
1406 GtkTreeModel *model = NULL;
1407 TnyHeader *header = NULL;
1408 TnyHeaderFlags flags;
1410 self = MODEST_HEADER_VIEW (treeview);
1411 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1413 model = gtk_tree_view_get_model (treeview);
1414 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1417 /* get the first selected item */
1418 gtk_tree_model_get (model, &iter,
1419 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1420 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1423 /* Dont open DELETED messages */
1424 if (flags & TNY_HEADER_FLAG_DELETED) {
1427 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1428 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1429 modest_platform_information_banner (NULL, NULL, msg);
1435 g_signal_emit (G_OBJECT(self),
1436 signals[HEADER_ACTIVATED_SIGNAL],
1442 g_object_unref (G_OBJECT (header));
1447 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1449 GtkTreeModel *model;
1450 TnyHeader *header = NULL;
1451 GtkTreePath *path = NULL;
1453 ModestHeaderView *self;
1454 ModestHeaderViewPrivate *priv;
1455 GList *selected = NULL;
1457 g_return_if_fail (sel);
1458 g_return_if_fail (user_data);
1460 self = MODEST_HEADER_VIEW (user_data);
1461 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1463 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1464 if (selected != NULL)
1465 path = (GtkTreePath *) selected->data;
1466 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1467 return; /* msg was _un_selected */
1469 gtk_tree_model_get (model, &iter,
1470 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1474 g_signal_emit (G_OBJECT(self),
1475 signals[HEADER_SELECTED_SIGNAL],
1478 g_object_unref (G_OBJECT (header));
1480 /* free all items in 'selected' */
1481 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1482 g_list_free (selected);
1486 /* PROTECTED method. It's useful when we want to force a given
1487 selection to reload a msg. For example if we have selected a header
1488 in offline mode, when Modest become online, we want to reload the
1489 message automatically without an user click over the header */
1491 _modest_header_view_change_selection (GtkTreeSelection *selection,
1494 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1495 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1497 on_selection_changed (selection, user_data);
1501 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1508 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1512 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1516 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1524 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1531 /* static int counter = 0; */
1533 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1534 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1535 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1539 case TNY_HEADER_FLAG_ATTACHMENTS:
1541 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1542 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1543 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1544 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1546 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1547 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1549 return cmp ? cmp : t1 - t2;
1551 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1552 TnyHeader *header1 = NULL, *header2 = NULL;
1554 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1555 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1556 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1557 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1559 /* This is for making priority values respect the intuitive sort relationship
1560 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1562 if (header1 && header2) {
1563 cmp = compare_priorities (tny_header_get_priority (header1),
1564 tny_header_get_priority (header2));
1565 g_object_unref (header1);
1566 g_object_unref (header2);
1568 return cmp ? cmp : t1 - t2;
1574 return &iter1 - &iter2; /* oughhhh */
1579 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1585 /* static int counter = 0; */
1587 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1589 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1590 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1591 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1592 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1594 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1595 val2 + modest_text_utils_get_subject_prefix_len(val2),
1602 /* Drag and drop stuff */
1604 drag_data_get_cb (GtkWidget *widget,
1605 GdkDragContext *context,
1606 GtkSelectionData *selection_data,
1611 ModestHeaderView *self = NULL;
1612 ModestHeaderViewPrivate *priv = NULL;
1614 self = MODEST_HEADER_VIEW (widget);
1615 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1617 /* Set the data. Do not use the current selection because it
1618 could be different than the selection at the beginning of
1620 modest_dnd_selection_data_set_paths (selection_data,
1621 priv->drag_begin_cached_selected_rows);
1625 * We're caching the selected rows at the beginning because the
1626 * selection could change between drag-begin and drag-data-get, for
1627 * example if we have a set of rows already selected, and then we
1628 * click in one of them (without SHIFT key pressed) and begin a drag,
1629 * the selection at that moment contains all the selected lines, but
1630 * after dropping the selection, the release event provokes that only
1631 * the row used to begin the drag is selected, so at the end the
1632 * drag&drop affects only one rows instead of all the selected ones.
1636 drag_begin_cb (GtkWidget *widget,
1637 GdkDragContext *context,
1640 ModestHeaderView *self = NULL;
1641 ModestHeaderViewPrivate *priv = NULL;
1642 GtkTreeSelection *selection;
1644 self = MODEST_HEADER_VIEW (widget);
1645 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1647 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1648 priv->drag_begin_cached_selected_rows =
1649 gtk_tree_selection_get_selected_rows (selection, NULL);
1653 * We use the drag-end signal to clear the cached selection, we use
1654 * this because this allways happens, whether or not the d&d was a
1658 drag_end_cb (GtkWidget *widget,
1662 ModestHeaderView *self = NULL;
1663 ModestHeaderViewPrivate *priv = NULL;
1665 self = MODEST_HEADER_VIEW (widget);
1666 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1668 /* Free cached data */
1669 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1670 g_list_free (priv->drag_begin_cached_selected_rows);
1671 priv->drag_begin_cached_selected_rows = NULL;
1674 /* Header view drag types */
1675 const GtkTargetEntry header_view_drag_types[] = {
1676 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1680 enable_drag_and_drop (GtkWidget *self)
1682 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1683 header_view_drag_types,
1684 G_N_ELEMENTS (header_view_drag_types),
1685 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1689 disable_drag_and_drop (GtkWidget *self)
1691 gtk_drag_source_unset (self);
1695 setup_drag_and_drop (GtkWidget *self)
1697 enable_drag_and_drop(self);
1698 g_signal_connect(G_OBJECT (self), "drag_data_get",
1699 G_CALLBACK(drag_data_get_cb), NULL);
1701 g_signal_connect(G_OBJECT (self), "drag_begin",
1702 G_CALLBACK(drag_begin_cb), NULL);
1704 g_signal_connect(G_OBJECT (self), "drag_end",
1705 G_CALLBACK(drag_end_cb), NULL);
1708 static GtkTreePath *
1709 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1711 GtkTreePath *path = NULL;
1712 GtkTreeSelection *sel = NULL;
1715 sel = gtk_tree_view_get_selection(self);
1716 rows = gtk_tree_selection_get_selected_rows (sel, model);
1718 if ((rows == NULL) || (g_list_length(rows) != 1))
1721 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1726 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1733 * This function moves the tree view scroll to the current selected
1734 * row when the widget grabs the focus
1737 on_focus_in (GtkWidget *self,
1738 GdkEventFocus *event,
1741 GtkTreeSelection *selection;
1742 GtkTreeModel *model;
1743 GList *selected = NULL;
1744 GtkTreePath *selected_path = NULL;
1746 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1750 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1751 /* If none selected yet, pick the first one */
1752 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1756 /* Return if the model is empty */
1757 if (!gtk_tree_model_get_iter_first (model, &iter))
1760 path = gtk_tree_model_get_path (model, &iter);
1761 gtk_tree_selection_select_path (selection, path);
1762 gtk_tree_path_free (path);
1765 /* Need to get the all the rows because is selection multiple */
1766 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1767 if (selected == NULL) return FALSE;
1768 selected_path = (GtkTreePath *) selected->data;
1771 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1772 g_list_free (selected);
1778 on_focus_out (GtkWidget *self,
1779 GdkEventFocus *event,
1783 if (!gtk_widget_is_focus (self)) {
1784 GtkTreeSelection *selection = NULL;
1785 GList *selected_rows = NULL;
1786 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1787 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1788 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1789 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1790 gtk_tree_selection_unselect_all (selection);
1791 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1792 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1793 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1794 g_list_free (selected_rows);
1801 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1803 enable_drag_and_drop(self);
1808 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1810 GtkTreeSelection *selection = NULL;
1811 GtkTreePath *path = NULL;
1812 gboolean already_selected = FALSE, already_opened = FALSE;
1813 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1815 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1817 GtkTreeModel *model;
1819 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1820 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1822 /* Get header from model */
1823 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1824 if (gtk_tree_model_get_iter (model, &iter, path)) {
1825 GValue value = {0,};
1828 gtk_tree_model_get_value (model, &iter,
1829 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1831 header = (TnyHeader *) g_value_get_object (&value);
1832 if (TNY_IS_HEADER (header)) {
1833 status = modest_tny_all_send_queues_get_msg_status (header);
1834 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1837 g_value_unset (&value);
1841 /* Enable drag and drop only if the user clicks on a row that
1842 it's already selected. If not, let him select items using
1843 the pointer. If the message is in an OUTBOX and in sending
1844 status disable drag and drop as well */
1845 if (!already_selected ||
1846 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1848 disable_drag_and_drop(self);
1851 gtk_tree_path_free(path);
1853 /* If it's already opened then do not let the button-press
1854 event go on because it'll perform a message open because
1855 we're clicking on to an already selected header */
1860 folder_monitor_update (TnyFolderObserver *self,
1861 TnyFolderChange *change)
1863 ModestHeaderViewPrivate *priv = NULL;
1864 TnyFolderChangeChanged changed;
1865 TnyFolder *folder = NULL;
1867 changed = tny_folder_change_get_changed (change);
1869 /* Do not notify the observers if the folder of the header
1870 view has changed before this call to the observer
1872 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1873 folder = tny_folder_change_get_folder (change);
1874 if (folder != priv->folder)
1877 MODEST_DEBUG_BLOCK (
1878 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1879 g_print ("ADDED %d/%d (r/t) \n",
1880 tny_folder_change_get_new_unread_count (change),
1881 tny_folder_change_get_new_all_count (change));
1882 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1883 g_print ("ALL COUNT %d\n",
1884 tny_folder_change_get_new_all_count (change));
1885 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1886 g_print ("UNREAD COUNT %d\n",
1887 tny_folder_change_get_new_unread_count (change));
1888 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1889 g_print ("EXPUNGED %d/%d (r/t) \n",
1890 tny_folder_change_get_new_unread_count (change),
1891 tny_folder_change_get_new_all_count (change));
1892 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1893 g_print ("FOLDER RENAME\n");
1894 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1895 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1896 tny_folder_change_get_new_unread_count (change),
1897 tny_folder_change_get_new_all_count (change));
1898 g_print ("---------------------------------------------------\n");
1901 /* Check folder count */
1902 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1903 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1905 g_mutex_lock (priv->observers_lock);
1907 /* Emit signal to evaluate how headers changes affects
1908 to the window view */
1909 g_signal_emit (G_OBJECT(self),
1910 signals[MSG_COUNT_CHANGED_SIGNAL],
1913 /* Added or removed headers, so data stored on cliboard are invalid */
1914 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1915 modest_email_clipboard_clear (priv->clipboard);
1917 g_mutex_unlock (priv->observers_lock);
1923 g_object_unref (folder);
1927 modest_header_view_is_empty (ModestHeaderView *self)
1929 ModestHeaderViewPrivate *priv;
1931 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1933 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1935 return priv->status == HEADER_VIEW_EMPTY;
1939 modest_header_view_clear (ModestHeaderView *self)
1941 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1943 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1947 modest_header_view_copy_selection (ModestHeaderView *header_view)
1949 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1951 /* Copy selection */
1952 _clipboard_set_selected_data (header_view, FALSE);
1956 modest_header_view_cut_selection (ModestHeaderView *header_view)
1958 ModestHeaderViewPrivate *priv = NULL;
1959 const gchar **hidding = NULL;
1960 guint i, n_selected;
1962 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1964 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1966 /* Copy selection */
1967 _clipboard_set_selected_data (header_view, TRUE);
1969 /* Get hidding ids */
1970 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1972 /* Clear hidding array created by previous cut operation */
1973 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1975 /* Copy hidding array */
1976 priv->n_selected = n_selected;
1977 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1978 for (i=0; i < n_selected; i++)
1979 priv->hidding_ids[i] = g_strdup(hidding[i]);
1981 /* Hide cut headers */
1982 modest_header_view_refilter (header_view);
1989 _clipboard_set_selected_data (ModestHeaderView *header_view,
1992 ModestHeaderViewPrivate *priv = NULL;
1993 TnyList *headers = NULL;
1995 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1996 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1998 /* Set selected data on clipboard */
1999 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2000 headers = modest_header_view_get_selected_headers (header_view);
2001 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2004 g_object_unref (headers);
2008 ModestHeaderView *self;
2013 notify_filter_change (gpointer data)
2015 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2017 g_signal_emit (info->self,
2018 signals[MSG_COUNT_CHANGED_SIGNAL],
2019 0, info->folder, NULL);
2025 notify_filter_change_destroy (gpointer data)
2027 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2028 ModestHeaderViewPrivate *priv;
2030 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2031 priv->status_timeout = 0;
2033 g_object_unref (info->self);
2034 g_object_unref (info->folder);
2035 g_slice_free (NotifyFilterInfo, info);
2039 filter_row (GtkTreeModel *model,
2043 ModestHeaderViewPrivate *priv = NULL;
2044 TnyHeaderFlags flags;
2045 TnyHeader *header = NULL;
2048 gboolean visible = TRUE;
2049 gboolean found = FALSE;
2050 GValue value = {0,};
2051 HeaderViewStatus old_status;
2053 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2054 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2056 /* Get header from model */
2057 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2058 flags = (TnyHeaderFlags) g_value_get_int (&value);
2059 g_value_unset (&value);
2060 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2061 header = (TnyHeader *) g_value_get_object (&value);
2062 g_value_unset (&value);
2064 /* Hide deleted and mark as deleted heders */
2065 if (flags & TNY_HEADER_FLAG_DELETED ||
2066 flags & TNY_HEADER_FLAG_EXPUNGED) {
2071 /* If no data on clipboard, return always TRUE */
2072 if (modest_email_clipboard_cleared(priv->clipboard)) {
2077 /* Get message id from header (ensure is a valid id) */
2084 if (priv->hidding_ids != NULL) {
2085 id = tny_header_dup_message_id (header);
2086 for (i=0; i < priv->n_selected && !found; i++)
2087 if (priv->hidding_ids[i] != NULL && id != NULL)
2088 found = (!strcmp (priv->hidding_ids[i], id));
2095 old_status = priv->status;
2096 priv->status = ((gboolean) priv->status) && !visible;
2097 if ((priv->notify_status) && (priv->status != old_status)) {
2098 NotifyFilterInfo *info;
2100 if (priv->status_timeout)
2101 g_source_remove (priv->status_timeout);
2103 info = g_slice_new0 (NotifyFilterInfo);
2104 info->self = g_object_ref (G_OBJECT (user_data));
2105 info->folder = tny_header_get_folder (header);
2106 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2107 notify_filter_change,
2109 notify_filter_change_destroy);
2116 _clear_hidding_filter (ModestHeaderView *header_view)
2118 ModestHeaderViewPrivate *priv = NULL;
2121 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2122 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2124 if (priv->hidding_ids != NULL) {
2125 for (i=0; i < priv->n_selected; i++)
2126 g_free (priv->hidding_ids[i]);
2127 g_free(priv->hidding_ids);
2132 modest_header_view_refilter (ModestHeaderView *header_view)
2134 GtkTreeModel *model = NULL;
2135 ModestHeaderViewPrivate *priv = NULL;
2137 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2138 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2140 /* Hide cut headers */
2141 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2142 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2143 priv->status = HEADER_VIEW_INIT;
2144 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2149 * Called when an account is removed. If I'm showing a folder of the
2150 * account that has been removed then clear the view
2153 on_account_removed (TnyAccountStore *self,
2154 TnyAccount *account,
2157 ModestHeaderViewPrivate *priv = NULL;
2159 /* Ignore changes in transport accounts */
2160 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2163 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2166 TnyAccount *my_account;
2168 my_account = tny_folder_get_account (priv->folder);
2169 if (my_account == account)
2170 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2171 g_object_unref (my_account);
2176 modest_header_view_add_observer(ModestHeaderView *header_view,
2177 ModestHeaderViewObserver *observer)
2179 ModestHeaderViewPrivate *priv;
2181 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2182 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2184 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2186 g_mutex_lock(priv->observer_list_lock);
2187 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2188 g_mutex_unlock(priv->observer_list_lock);
2192 modest_header_view_remove_observer(ModestHeaderView *header_view,
2193 ModestHeaderViewObserver *observer)
2195 ModestHeaderViewPrivate *priv;
2197 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2198 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2200 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2202 g_mutex_lock(priv->observer_list_lock);
2203 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2204 g_mutex_unlock(priv->observer_list_lock);
2208 modest_header_view_notify_observers(ModestHeaderView *header_view,
2209 GtkTreeModel *model,
2210 const gchar *tny_folder_id)
2212 ModestHeaderViewPrivate *priv = NULL;
2214 ModestHeaderViewObserver *observer;
2217 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2219 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2221 g_mutex_lock(priv->observer_list_lock);
2222 iter = priv->observer_list;
2223 while(iter != NULL){
2224 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2225 modest_header_view_observer_update(observer, model,
2227 iter = g_slist_next(iter);
2229 g_mutex_unlock(priv->observer_list_lock);