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 gtk_marshal_VOID__POINTER_POINTER,
254 G_TYPE_NONE, 2, G_TYPE_POINTER, 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);
284 #ifdef MODEST_TOOLKIT_HILDON2
285 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
291 tny_folder_observer_init (TnyFolderObserverIface *klass)
293 klass->update = folder_monitor_update;
296 static GtkTreeViewColumn*
297 get_new_column (const gchar *name, GtkCellRenderer *renderer,
298 gboolean resizable, gint sort_col_id, gboolean show_as_text,
299 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
301 GtkTreeViewColumn *column;
303 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
304 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
306 gtk_tree_view_column_set_resizable (column, resizable);
308 gtk_tree_view_column_set_expand (column, TRUE);
311 gtk_tree_view_column_add_attribute (column, renderer, "text",
313 if (sort_col_id >= 0)
314 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
316 gtk_tree_view_column_set_sort_indicator (column, FALSE);
317 gtk_tree_view_column_set_reorderable (column, TRUE);
320 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
327 remove_all_columns (ModestHeaderView *obj)
329 GList *columns, *cursor;
331 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
333 for (cursor = columns; cursor; cursor = cursor->next)
334 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
335 GTK_TREE_VIEW_COLUMN(cursor->data));
336 g_list_free (columns);
340 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
342 GtkTreeModel *tree_filter, *sortable;
343 GtkTreeViewColumn *column=NULL;
344 GtkTreeSelection *selection = NULL;
345 GtkCellRenderer *renderer_header,
346 *renderer_attach, *renderer_compact_date_or_status;
347 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
348 *renderer_subject, *renderer_subject_box, *renderer_recpt,
350 ModestHeaderViewPrivate *priv;
351 GtkTreeViewColumn *compact_column = NULL;
354 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
355 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
357 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
359 /* TODO: check whether these renderers need to be freed */
360 renderer_attach = gtk_cell_renderer_pixbuf_new ();
361 renderer_priority = gtk_cell_renderer_pixbuf_new ();
362 renderer_header = gtk_cell_renderer_text_new ();
364 renderer_compact_header = modest_vbox_cell_renderer_new ();
365 renderer_recpt_box = modest_hbox_cell_renderer_new ();
366 renderer_subject_box = modest_hbox_cell_renderer_new ();
367 renderer_recpt = gtk_cell_renderer_text_new ();
368 renderer_subject = gtk_cell_renderer_text_new ();
369 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
371 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
372 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
373 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
374 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
375 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
376 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
377 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
378 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
379 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
380 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
381 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
382 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
383 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
384 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
386 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
387 #ifndef MODEST_TOOLKIT_GTK
388 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
389 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
391 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
392 g_object_set(G_OBJECT(renderer_header),
393 "ellipsize", PANGO_ELLIPSIZE_END,
395 g_object_set (G_OBJECT (renderer_subject),
396 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
398 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
399 g_object_set (G_OBJECT (renderer_recpt),
400 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
402 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
403 g_object_set(G_OBJECT(renderer_compact_date_or_status),
404 "xalign", 1.0, "yalign", 0.0,
406 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
407 g_object_set (G_OBJECT (renderer_priority),
408 "yalign", 1.0, NULL);
409 g_object_set (G_OBJECT (renderer_attach),
410 "yalign", 0.0, NULL);
412 #ifndef MODEST_TOOLKIT_GTK
413 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
414 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
415 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
417 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
418 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
419 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
422 remove_all_columns (self);
424 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
425 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
426 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
427 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
429 /* Add new columns */
430 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
431 ModestHeaderViewColumn col =
432 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
434 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
435 g_printerr ("modest: invalid column %d in column list\n", col);
441 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
442 column = get_new_column (_("A"), renderer_attach, FALSE,
443 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
445 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
447 gtk_tree_view_column_set_fixed_width (column, 45);
451 case MODEST_HEADER_VIEW_COLUMN_FROM:
452 column = get_new_column (_("From"), renderer_header, TRUE,
453 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
455 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
456 GINT_TO_POINTER(TRUE));
459 case MODEST_HEADER_VIEW_COLUMN_TO:
460 column = get_new_column (_("To"), renderer_header, TRUE,
461 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
463 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
464 GINT_TO_POINTER(FALSE));
467 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
468 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
469 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
471 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
472 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
473 compact_column = column;
476 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
477 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
478 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
480 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
481 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
482 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
483 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
484 compact_column = column;
488 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
489 column = get_new_column (_("Subject"), renderer_header, TRUE,
490 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
492 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
496 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
497 column = get_new_column (_("Received"), renderer_header, TRUE,
498 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
500 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
501 GINT_TO_POINTER(TRUE));
504 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
505 column = get_new_column (_("Sent"), renderer_header, TRUE,
506 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
508 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
509 GINT_TO_POINTER(FALSE));
512 case MODEST_HEADER_VIEW_COLUMN_SIZE:
513 column = get_new_column (_("Size"), renderer_header, TRUE,
514 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
516 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
519 case MODEST_HEADER_VIEW_COLUMN_STATUS:
520 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
521 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
523 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
528 g_return_val_if_reached(FALSE);
531 /* we keep the column id around */
532 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
533 GINT_TO_POINTER(col));
535 /* we need this ptr when sorting the rows */
536 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
538 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
542 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
543 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
544 (GtkTreeIterCompareFunc) cmp_rows,
545 compact_column, NULL);
546 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
547 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
548 (GtkTreeIterCompareFunc) cmp_subject_rows,
549 compact_column, NULL);
556 modest_header_view_init (ModestHeaderView *obj)
558 ModestHeaderViewPrivate *priv;
561 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
565 priv->monitor = NULL;
566 priv->observers_lock = g_mutex_new ();
567 priv->autoselect_reference = NULL;
569 priv->status = HEADER_VIEW_INIT;
570 priv->status_timeout = 0;
571 priv->notify_status = TRUE;
573 priv->observer_list_lock = g_mutex_new();
574 priv->observer_list = NULL;
576 priv->clipboard = modest_runtime_get_email_clipboard ();
577 priv->hidding_ids = NULL;
578 priv->n_selected = 0;
579 priv->selection_changed_handler = 0;
580 priv->acc_removed_handler = 0;
582 /* Sort parameters */
583 for (j=0; j < 2; j++) {
584 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
585 priv->sort_colid[j][i] = -1;
586 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
590 setup_drag_and_drop (GTK_WIDGET(obj));
594 modest_header_view_dispose (GObject *obj)
596 ModestHeaderView *self;
597 ModestHeaderViewPrivate *priv;
598 GtkTreeSelection *sel;
600 self = MODEST_HEADER_VIEW(obj);
601 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
603 /* Free in the dispose to avoid unref cycles */
605 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
606 g_object_unref (G_OBJECT (priv->folder));
610 /* We need to do this here in the dispose because the
611 selection won't exist when finalizing */
612 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
613 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
614 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
615 priv->selection_changed_handler = 0;
618 G_OBJECT_CLASS(parent_class)->dispose (obj);
622 modest_header_view_finalize (GObject *obj)
624 ModestHeaderView *self;
625 ModestHeaderViewPrivate *priv;
627 self = MODEST_HEADER_VIEW(obj);
628 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
630 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
631 priv->acc_removed_handler)) {
632 g_signal_handler_disconnect (modest_runtime_get_account_store (),
633 priv->acc_removed_handler);
636 /* There is no need to lock because there should not be any
637 * reference to self now. */
638 g_mutex_free(priv->observer_list_lock);
639 g_slist_free(priv->observer_list);
641 g_mutex_lock (priv->observers_lock);
643 tny_folder_monitor_stop (priv->monitor);
644 g_object_unref (G_OBJECT (priv->monitor));
646 g_mutex_unlock (priv->observers_lock);
647 g_mutex_free (priv->observers_lock);
649 /* Clear hidding array created by cut operation */
650 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
652 if (priv->autoselect_reference != NULL) {
653 gtk_tree_row_reference_free (priv->autoselect_reference);
654 priv->autoselect_reference = NULL;
657 G_OBJECT_CLASS(parent_class)->finalize (obj);
662 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
665 GtkTreeSelection *sel;
666 ModestHeaderView *self;
667 ModestHeaderViewPrivate *priv;
669 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
672 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
673 self = MODEST_HEADER_VIEW(obj);
674 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
676 modest_header_view_set_style (self, style);
678 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
679 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
680 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
682 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
683 TRUE); /* alternating row colors */
685 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
686 priv->selection_changed_handler =
687 g_signal_connect_after (sel, "changed",
688 G_CALLBACK(on_selection_changed), self);
690 g_signal_connect (self, "row-activated",
691 G_CALLBACK (on_header_row_activated), NULL);
693 g_signal_connect (self, "focus-in-event",
694 G_CALLBACK(on_focus_in), NULL);
695 g_signal_connect (self, "focus-out-event",
696 G_CALLBACK(on_focus_out), NULL);
698 g_signal_connect (self, "button-press-event",
699 G_CALLBACK(on_button_press_event), NULL);
700 g_signal_connect (self, "button-release-event",
701 G_CALLBACK(on_button_release_event), NULL);
703 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
705 G_CALLBACK (on_account_removed),
708 g_signal_connect (self, "expose-event",
709 G_CALLBACK(modest_header_view_on_expose_event),
712 return GTK_WIDGET(self);
717 modest_header_view_count_selected_headers (ModestHeaderView *self)
719 GtkTreeSelection *sel;
722 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
724 /* Get selection object and check selected rows count */
725 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
726 selected_rows = gtk_tree_selection_count_selected_rows (sel);
728 return selected_rows;
732 modest_header_view_has_selected_headers (ModestHeaderView *self)
734 GtkTreeSelection *sel;
737 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
739 /* Get selection object and check selected rows count */
740 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
741 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
748 modest_header_view_get_selected_headers (ModestHeaderView *self)
750 GtkTreeSelection *sel;
751 ModestHeaderViewPrivate *priv;
752 TnyList *header_list = NULL;
754 GList *list, *tmp = NULL;
755 GtkTreeModel *tree_model = NULL;
758 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
760 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
762 /* Get selected rows */
763 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
764 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
767 header_list = tny_simple_list_new();
769 list = g_list_reverse (list);
772 /* get header from selection */
773 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
774 gtk_tree_model_get (tree_model, &iter,
775 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
777 /* Prepend to list */
778 tny_list_prepend (header_list, G_OBJECT (header));
779 g_object_unref (G_OBJECT (header));
781 tmp = g_list_next (tmp);
784 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
791 /* scroll our list view so the selected item is visible */
793 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
795 #ifdef MODEST_TOOLKIT_GTK
797 GtkTreePath *selected_path;
798 GtkTreePath *start, *end;
802 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
803 selected_path = gtk_tree_model_get_path (model, iter);
805 start = gtk_tree_path_new ();
806 end = gtk_tree_path_new ();
808 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
810 if (gtk_tree_path_compare (selected_path, start) < 0 ||
811 gtk_tree_path_compare (end, selected_path) < 0)
812 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
813 selected_path, NULL, TRUE,
816 gtk_tree_path_free (selected_path);
817 gtk_tree_path_free (start);
818 gtk_tree_path_free (end);
820 #endif /* MODEST_TOOLKIT_GTK */
825 modest_header_view_select_next (ModestHeaderView *self)
827 GtkTreeSelection *sel;
832 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
834 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
835 path = get_selected_row (GTK_TREE_VIEW(self), &model);
836 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
837 /* Unselect previous path */
838 gtk_tree_selection_unselect_path (sel, path);
840 /* Move path down and selects new one */
841 if (gtk_tree_model_iter_next (model, &iter)) {
842 gtk_tree_selection_select_iter (sel, &iter);
843 scroll_to_selected (self, &iter, FALSE);
845 gtk_tree_path_free(path);
851 modest_header_view_select_prev (ModestHeaderView *self)
853 GtkTreeSelection *sel;
858 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
860 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
861 path = get_selected_row (GTK_TREE_VIEW(self), &model);
862 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
863 /* Unselect previous path */
864 gtk_tree_selection_unselect_path (sel, path);
867 if (gtk_tree_path_prev (path)) {
868 gtk_tree_model_get_iter (model, &iter, path);
870 /* Select the new one */
871 gtk_tree_selection_select_iter (sel, &iter);
872 scroll_to_selected (self, &iter, TRUE);
875 gtk_tree_path_free (path);
880 modest_header_view_get_columns (ModestHeaderView *self)
882 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
884 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
890 modest_header_view_set_style (ModestHeaderView *self,
891 ModestHeaderViewStyle style)
893 ModestHeaderViewPrivate *priv;
894 gboolean show_col_headers = FALSE;
895 ModestHeaderViewStyle old_style;
897 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
898 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
901 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
902 if (priv->style == style)
903 return TRUE; /* nothing to do */
906 case MODEST_HEADER_VIEW_STYLE_DETAILS:
907 show_col_headers = TRUE;
909 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
912 g_return_val_if_reached (FALSE);
914 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
915 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
917 old_style = priv->style;
924 ModestHeaderViewStyle
925 modest_header_view_get_style (ModestHeaderView *self)
927 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
929 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
932 /* This is used to automatically select the first header if the user
933 * has not selected any header yet.
936 modest_header_view_on_expose_event(GtkTreeView *header_view,
937 GdkEventExpose *event,
940 GtkTreeSelection *sel;
942 GtkTreeIter tree_iter;
943 ModestHeaderViewPrivate *priv;
945 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
946 model = gtk_tree_view_get_model(header_view);
951 #ifdef MODEST_TOOLKIT_HILDON2
952 HildonUIMode ui_mode;
953 g_object_get (G_OBJECT (header_view), "hildon-ui-mode", &ui_mode, NULL);
954 if (ui_mode == HILDON_UI_MODE_NORMAL)
955 /* As in hildon 2.2 normal mode there's no selection, we just simply return */
958 sel = gtk_tree_view_get_selection(header_view);
959 if(!gtk_tree_selection_count_selected_rows(sel)) {
960 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
961 GtkTreePath *tree_iter_path;
962 /* Prevent the widget from getting the focus
963 when selecting the first item */
964 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
965 g_object_set(header_view, "can-focus", FALSE, NULL);
966 gtk_tree_selection_select_iter(sel, &tree_iter);
967 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
968 g_object_set(header_view, "can-focus", TRUE, NULL);
969 if (priv->autoselect_reference) {
970 gtk_tree_row_reference_free (priv->autoselect_reference);
972 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
973 gtk_tree_path_free (tree_iter_path);
976 if (priv->autoselect_reference != NULL) {
977 gboolean moved_selection = FALSE;
978 GtkTreePath * last_path;
979 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
980 moved_selection = TRUE;
984 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
985 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
986 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
987 moved_selection = TRUE;
988 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
990 gtk_tree_path_free (last_path);
992 if (moved_selection) {
993 gtk_tree_row_reference_free (priv->autoselect_reference);
994 priv->autoselect_reference = NULL;
997 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
998 GtkTreePath *current_path;
999 current_path = gtk_tree_model_get_path (model, &tree_iter);
1000 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1001 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1002 g_object_set(header_view, "can-focus", FALSE, NULL);
1003 gtk_tree_selection_unselect_all (sel);
1004 gtk_tree_selection_select_iter(sel, &tree_iter);
1005 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1006 g_object_set(header_view, "can-focus", TRUE, NULL);
1007 gtk_tree_row_reference_free (priv->autoselect_reference);
1008 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1010 gtk_tree_path_free (current_path);
1011 gtk_tree_path_free (last_path);
1021 modest_header_view_get_folder (ModestHeaderView *self)
1023 ModestHeaderViewPrivate *priv;
1025 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1027 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1030 g_object_ref (priv->folder);
1032 return priv->folder;
1036 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1042 ModestHeaderView *self;
1043 ModestHeaderViewPrivate *priv;
1045 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1047 self = MODEST_HEADER_VIEW (user_data);
1048 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1050 if (cancelled || err)
1053 /* Add IDLE observer (monitor) and another folder observer for
1054 new messages (self) */
1055 g_mutex_lock (priv->observers_lock);
1056 if (priv->monitor) {
1057 tny_folder_monitor_stop (priv->monitor);
1058 g_object_unref (G_OBJECT (priv->monitor));
1060 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1061 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1062 tny_folder_monitor_start (priv->monitor);
1063 g_mutex_unlock (priv->observers_lock);
1067 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1071 ModestHeaderViewPrivate *priv;
1072 GList *cols, *cursor;
1073 GtkTreeModel *filter_model, *sortable;
1075 GtkSortType sort_type;
1077 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1079 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1081 /* Start the monitor in the callback of the
1082 tny_gtk_header_list_model_set_folder call. It's crucial to
1083 do it there and not just after the call because we want the
1084 monitor to observe only the headers returned by the
1085 tny_folder_get_headers_async call that it's inside the
1086 tny_gtk_header_list_model_set_folder call. This way the
1087 monitor infrastructure could successfully cope with
1088 duplicates. For example if a tny_folder_add_msg_async is
1089 happening while tny_gtk_header_list_model_set_folder is
1090 invoked, then the first call could add a header that will
1091 be added again by tny_gtk_header_list_model_set_folder, so
1092 we'd end up with duplicate headers. sergio */
1093 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1095 set_folder_intern_get_headers_async_cb,
1098 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1099 g_object_unref (G_OBJECT (headers));
1101 /* Init filter_row function to examine empty status */
1102 priv->status = HEADER_VIEW_INIT;
1104 /* Create a tree model filter to hide and show rows for cut operations */
1105 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1106 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1110 g_object_unref (G_OBJECT (sortable));
1112 /* install our special sorting functions */
1113 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1115 /* Restore sort column id */
1117 type = modest_tny_folder_guess_folder_type (folder);
1118 if (type == TNY_FOLDER_TYPE_INVALID)
1119 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1121 sort_colid = modest_header_view_get_sort_column_id (self, type);
1122 sort_type = modest_header_view_get_sort_type (self, type);
1123 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1126 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1127 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1128 (GtkTreeIterCompareFunc) cmp_rows,
1130 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1131 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1132 (GtkTreeIterCompareFunc) cmp_subject_rows,
1137 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1138 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1139 tny_folder_get_id(folder));
1140 g_object_unref (G_OBJECT (filter_model));
1147 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1149 GtkSortType sort_type)
1151 ModestHeaderViewPrivate *priv = NULL;
1152 GtkTreeModel *tree_filter, *sortable = NULL;
1155 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1156 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1158 /* Get model and private data */
1159 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1160 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1161 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1162 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1164 /* Sort tree model */
1165 type = modest_tny_folder_guess_folder_type (priv->folder);
1166 if (type == TNY_FOLDER_TYPE_INVALID)
1167 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1169 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1172 /* Store new sort parameters */
1173 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1178 modest_header_view_set_sort_params (ModestHeaderView *self,
1180 GtkSortType sort_type,
1183 ModestHeaderViewPrivate *priv;
1184 ModestHeaderViewStyle style;
1186 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1187 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1188 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1190 style = modest_header_view_get_style (self);
1191 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1193 priv->sort_colid[style][type] = sort_colid;
1194 priv->sort_type[style][type] = sort_type;
1198 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1201 ModestHeaderViewPrivate *priv;
1202 ModestHeaderViewStyle style;
1204 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1205 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1207 style = modest_header_view_get_style (self);
1208 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1210 return priv->sort_colid[style][type];
1214 modest_header_view_get_sort_type (ModestHeaderView *self,
1217 ModestHeaderViewPrivate *priv;
1218 ModestHeaderViewStyle style;
1220 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1221 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1223 style = modest_header_view_get_style (self);
1224 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1226 return priv->sort_type[style][type];
1230 ModestHeaderView *header_view;
1231 RefreshAsyncUserCallback cb;
1236 folder_refreshed_cb (ModestMailOperation *mail_op,
1240 ModestHeaderViewPrivate *priv;
1241 SetFolderHelper *info;
1243 info = (SetFolderHelper*) user_data;
1245 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1249 info->cb (mail_op, folder, info->user_data);
1251 /* Start the folder count changes observer. We do not need it
1252 before the refresh. Note that the monitor could still be
1253 called for this refresh but now we know that the callback
1254 was previously called */
1255 g_mutex_lock (priv->observers_lock);
1256 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1257 g_mutex_unlock (priv->observers_lock);
1259 /* Notify the observers that the update is over */
1260 g_signal_emit (G_OBJECT (info->header_view),
1261 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1263 /* Allow filtering notifications from now on if the current
1264 folder is still the same (if not then the user has selected
1265 another one to refresh, we should wait until that refresh
1267 if (priv->folder == folder)
1268 priv->notify_status = TRUE;
1271 g_object_unref (info->header_view);
1276 refresh_folder_error_handler (ModestMailOperation *mail_op,
1279 const GError *error = modest_mail_operation_get_error (mail_op);
1281 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1282 error->code == TNY_IO_ERROR_WRITE ||
1283 error->code == TNY_IO_ERROR_READ) {
1284 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1285 /* If the mail op has been cancelled then it's not an error: don't show any message */
1286 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1287 modest_platform_information_banner (NULL, NULL,
1289 "cerm_device_memory_full"));
1295 modest_header_view_set_folder (ModestHeaderView *self,
1298 ModestWindow *progress_window,
1299 RefreshAsyncUserCallback callback,
1302 ModestHeaderViewPrivate *priv;
1304 g_return_if_fail (self);
1306 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1309 if (priv->status_timeout) {
1310 g_source_remove (priv->status_timeout);
1311 priv->status_timeout = 0;
1314 g_mutex_lock (priv->observers_lock);
1315 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1316 g_object_unref (priv->folder);
1317 priv->folder = NULL;
1318 g_mutex_unlock (priv->observers_lock);
1322 GtkTreeSelection *selection;
1323 SetFolderHelper *info;
1324 ModestMailOperation *mail_op = NULL;
1326 /* Set folder in the model */
1327 modest_header_view_set_folder_intern (self, folder);
1329 /* Pick my reference. Nothing to do with the mail operation */
1330 priv->folder = g_object_ref (folder);
1332 /* Do not notify about filterings until the refresh finishes */
1333 priv->notify_status = FALSE;
1335 /* Clear the selection if exists */
1336 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1337 gtk_tree_selection_unselect_all(selection);
1338 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1340 /* Notify the observers that the update begins */
1341 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1344 /* create the helper */
1345 info = g_malloc0 (sizeof (SetFolderHelper));
1346 info->header_view = g_object_ref (self);
1347 info->cb = callback;
1348 info->user_data = user_data;
1350 /* Create the mail operation (source will be the parent widget) */
1351 if (progress_window)
1352 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1353 refresh_folder_error_handler,
1356 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1359 /* Refresh the folder asynchronously */
1360 modest_mail_operation_refresh_folder (mail_op,
1362 folder_refreshed_cb,
1365 folder_refreshed_cb (mail_op, folder, info);
1368 g_object_unref (mail_op);
1370 g_mutex_lock (priv->observers_lock);
1372 if (priv->monitor) {
1373 tny_folder_monitor_stop (priv->monitor);
1374 g_object_unref (G_OBJECT (priv->monitor));
1375 priv->monitor = NULL;
1378 if (priv->autoselect_reference) {
1379 gtk_tree_row_reference_free (priv->autoselect_reference);
1380 priv->autoselect_reference = NULL;
1383 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1385 modest_header_view_notify_observers(self, NULL, NULL);
1387 g_mutex_unlock (priv->observers_lock);
1389 /* Notify the observers that the update is over */
1390 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1396 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1397 GtkTreeViewColumn *column, gpointer userdata)
1399 ModestHeaderView *self = NULL;
1400 ModestHeaderViewPrivate *priv = NULL;
1402 GtkTreeModel *model = NULL;
1403 TnyHeader *header = NULL;
1404 TnyHeaderFlags flags;
1406 self = MODEST_HEADER_VIEW (treeview);
1407 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1409 model = gtk_tree_view_get_model (treeview);
1410 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1413 /* get the first selected item */
1414 gtk_tree_model_get (model, &iter,
1415 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1416 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1419 /* Dont open DELETED messages */
1420 if (flags & TNY_HEADER_FLAG_DELETED) {
1423 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1424 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1425 modest_platform_information_banner (NULL, NULL, msg);
1431 g_signal_emit (G_OBJECT(self),
1432 signals[HEADER_ACTIVATED_SIGNAL],
1438 g_object_unref (G_OBJECT (header));
1443 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1445 GtkTreeModel *model;
1446 TnyHeader *header = NULL;
1447 GtkTreePath *path = NULL;
1449 ModestHeaderView *self;
1450 ModestHeaderViewPrivate *priv;
1451 GList *selected = NULL;
1453 g_return_if_fail (sel);
1454 g_return_if_fail (user_data);
1456 self = MODEST_HEADER_VIEW (user_data);
1457 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1459 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1460 if (selected != NULL)
1461 path = (GtkTreePath *) selected->data;
1462 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1463 return; /* msg was _un_selected */
1465 gtk_tree_model_get (model, &iter,
1466 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1470 g_signal_emit (G_OBJECT(self),
1471 signals[HEADER_SELECTED_SIGNAL],
1474 g_object_unref (G_OBJECT (header));
1476 /* free all items in 'selected' */
1477 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1478 g_list_free (selected);
1482 /* PROTECTED method. It's useful when we want to force a given
1483 selection to reload a msg. For example if we have selected a header
1484 in offline mode, when Modest become online, we want to reload the
1485 message automatically without an user click over the header */
1487 _modest_header_view_change_selection (GtkTreeSelection *selection,
1490 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1491 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1493 on_selection_changed (selection, user_data);
1497 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1504 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1508 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1512 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1520 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1527 /* static int counter = 0; */
1529 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1530 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1531 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1535 case TNY_HEADER_FLAG_ATTACHMENTS:
1537 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1538 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1539 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1540 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1542 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1543 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1545 return cmp ? cmp : t1 - t2;
1547 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1548 TnyHeader *header1 = NULL, *header2 = NULL;
1550 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1551 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1552 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1553 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1555 /* This is for making priority values respect the intuitive sort relationship
1556 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1558 if (header1 && header2) {
1559 cmp = compare_priorities (tny_header_get_priority (header1),
1560 tny_header_get_priority (header2));
1561 g_object_unref (header1);
1562 g_object_unref (header2);
1564 return cmp ? cmp : t1 - t2;
1570 return &iter1 - &iter2; /* oughhhh */
1575 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1581 /* static int counter = 0; */
1583 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1585 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1586 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1587 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1588 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1590 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1591 val2 + modest_text_utils_get_subject_prefix_len(val2),
1598 /* Drag and drop stuff */
1600 drag_data_get_cb (GtkWidget *widget,
1601 GdkDragContext *context,
1602 GtkSelectionData *selection_data,
1607 ModestHeaderView *self = NULL;
1608 ModestHeaderViewPrivate *priv = NULL;
1610 self = MODEST_HEADER_VIEW (widget);
1611 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1613 /* Set the data. Do not use the current selection because it
1614 could be different than the selection at the beginning of
1616 modest_dnd_selection_data_set_paths (selection_data,
1617 priv->drag_begin_cached_selected_rows);
1621 * We're caching the selected rows at the beginning because the
1622 * selection could change between drag-begin and drag-data-get, for
1623 * example if we have a set of rows already selected, and then we
1624 * click in one of them (without SHIFT key pressed) and begin a drag,
1625 * the selection at that moment contains all the selected lines, but
1626 * after dropping the selection, the release event provokes that only
1627 * the row used to begin the drag is selected, so at the end the
1628 * drag&drop affects only one rows instead of all the selected ones.
1632 drag_begin_cb (GtkWidget *widget,
1633 GdkDragContext *context,
1636 ModestHeaderView *self = NULL;
1637 ModestHeaderViewPrivate *priv = NULL;
1638 GtkTreeSelection *selection;
1640 self = MODEST_HEADER_VIEW (widget);
1641 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1643 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1644 priv->drag_begin_cached_selected_rows =
1645 gtk_tree_selection_get_selected_rows (selection, NULL);
1649 * We use the drag-end signal to clear the cached selection, we use
1650 * this because this allways happens, whether or not the d&d was a
1654 drag_end_cb (GtkWidget *widget,
1658 ModestHeaderView *self = NULL;
1659 ModestHeaderViewPrivate *priv = NULL;
1661 self = MODEST_HEADER_VIEW (widget);
1662 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1664 /* Free cached data */
1665 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1666 g_list_free (priv->drag_begin_cached_selected_rows);
1667 priv->drag_begin_cached_selected_rows = NULL;
1670 /* Header view drag types */
1671 const GtkTargetEntry header_view_drag_types[] = {
1672 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1676 enable_drag_and_drop (GtkWidget *self)
1678 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1679 header_view_drag_types,
1680 G_N_ELEMENTS (header_view_drag_types),
1681 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1685 disable_drag_and_drop (GtkWidget *self)
1687 gtk_drag_source_unset (self);
1691 setup_drag_and_drop (GtkWidget *self)
1693 enable_drag_and_drop(self);
1694 g_signal_connect(G_OBJECT (self), "drag_data_get",
1695 G_CALLBACK(drag_data_get_cb), NULL);
1697 g_signal_connect(G_OBJECT (self), "drag_begin",
1698 G_CALLBACK(drag_begin_cb), NULL);
1700 g_signal_connect(G_OBJECT (self), "drag_end",
1701 G_CALLBACK(drag_end_cb), NULL);
1704 static GtkTreePath *
1705 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1707 GtkTreePath *path = NULL;
1708 GtkTreeSelection *sel = NULL;
1711 sel = gtk_tree_view_get_selection(self);
1712 rows = gtk_tree_selection_get_selected_rows (sel, model);
1714 if ((rows == NULL) || (g_list_length(rows) != 1))
1717 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1722 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1729 * This function moves the tree view scroll to the current selected
1730 * row when the widget grabs the focus
1733 on_focus_in (GtkWidget *self,
1734 GdkEventFocus *event,
1737 GtkTreeSelection *selection;
1738 GtkTreeModel *model;
1739 GList *selected = NULL;
1740 GtkTreePath *selected_path = NULL;
1742 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1746 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1747 /* If none selected yet, pick the first one */
1748 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1752 /* Return if the model is empty */
1753 if (!gtk_tree_model_get_iter_first (model, &iter))
1756 path = gtk_tree_model_get_path (model, &iter);
1757 gtk_tree_selection_select_path (selection, path);
1758 gtk_tree_path_free (path);
1761 /* Need to get the all the rows because is selection multiple */
1762 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1763 if (selected == NULL) return FALSE;
1764 selected_path = (GtkTreePath *) selected->data;
1767 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1768 g_list_free (selected);
1774 on_focus_out (GtkWidget *self,
1775 GdkEventFocus *event,
1779 if (!gtk_widget_is_focus (self)) {
1780 GtkTreeSelection *selection = NULL;
1781 GList *selected_rows = NULL;
1782 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1783 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1784 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1785 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1786 gtk_tree_selection_unselect_all (selection);
1787 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1788 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1789 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1790 g_list_free (selected_rows);
1797 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1799 enable_drag_and_drop(self);
1804 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1806 GtkTreeSelection *selection = NULL;
1807 GtkTreePath *path = NULL;
1808 gboolean already_selected = FALSE, already_opened = FALSE;
1809 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1811 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1813 GtkTreeModel *model;
1815 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1816 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1818 /* Get header from model */
1819 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1820 if (gtk_tree_model_get_iter (model, &iter, path)) {
1821 GValue value = {0,};
1824 gtk_tree_model_get_value (model, &iter,
1825 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1827 header = (TnyHeader *) g_value_get_object (&value);
1828 if (TNY_IS_HEADER (header)) {
1829 status = modest_tny_all_send_queues_get_msg_status (header);
1830 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1833 g_value_unset (&value);
1837 /* Enable drag and drop only if the user clicks on a row that
1838 it's already selected. If not, let him select items using
1839 the pointer. If the message is in an OUTBOX and in sending
1840 status disable drag and drop as well */
1841 if (!already_selected ||
1842 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1844 disable_drag_and_drop(self);
1847 gtk_tree_path_free(path);
1849 /* If it's already opened then do not let the button-press
1850 event go on because it'll perform a message open because
1851 we're clicking on to an already selected header */
1856 folder_monitor_update (TnyFolderObserver *self,
1857 TnyFolderChange *change)
1859 ModestHeaderViewPrivate *priv = NULL;
1860 TnyFolderChangeChanged changed;
1861 TnyFolder *folder = NULL;
1863 changed = tny_folder_change_get_changed (change);
1865 /* Do not notify the observers if the folder of the header
1866 view has changed before this call to the observer
1868 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1869 folder = tny_folder_change_get_folder (change);
1870 if (folder != priv->folder)
1873 MODEST_DEBUG_BLOCK (
1874 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1875 g_print ("ADDED %d/%d (r/t) \n",
1876 tny_folder_change_get_new_unread_count (change),
1877 tny_folder_change_get_new_all_count (change));
1878 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1879 g_print ("ALL COUNT %d\n",
1880 tny_folder_change_get_new_all_count (change));
1881 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1882 g_print ("UNREAD COUNT %d\n",
1883 tny_folder_change_get_new_unread_count (change));
1884 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1885 g_print ("EXPUNGED %d/%d (r/t) \n",
1886 tny_folder_change_get_new_unread_count (change),
1887 tny_folder_change_get_new_all_count (change));
1888 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1889 g_print ("FOLDER RENAME\n");
1890 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1891 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1892 tny_folder_change_get_new_unread_count (change),
1893 tny_folder_change_get_new_all_count (change));
1894 g_print ("---------------------------------------------------\n");
1897 /* Check folder count */
1898 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1899 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1901 g_mutex_lock (priv->observers_lock);
1903 /* Emit signal to evaluate how headers changes affects
1904 to the window view */
1905 g_signal_emit (G_OBJECT(self),
1906 signals[MSG_COUNT_CHANGED_SIGNAL],
1909 /* Added or removed headers, so data stored on cliboard are invalid */
1910 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1911 modest_email_clipboard_clear (priv->clipboard);
1913 g_mutex_unlock (priv->observers_lock);
1919 g_object_unref (folder);
1923 modest_header_view_is_empty (ModestHeaderView *self)
1925 ModestHeaderViewPrivate *priv;
1927 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1929 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1931 return priv->status == HEADER_VIEW_EMPTY;
1935 modest_header_view_clear (ModestHeaderView *self)
1937 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1939 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
1943 modest_header_view_copy_selection (ModestHeaderView *header_view)
1945 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1947 /* Copy selection */
1948 _clipboard_set_selected_data (header_view, FALSE);
1952 modest_header_view_cut_selection (ModestHeaderView *header_view)
1954 ModestHeaderViewPrivate *priv = NULL;
1955 const gchar **hidding = NULL;
1956 guint i, n_selected;
1958 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1960 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1962 /* Copy selection */
1963 _clipboard_set_selected_data (header_view, TRUE);
1965 /* Get hidding ids */
1966 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1968 /* Clear hidding array created by previous cut operation */
1969 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1971 /* Copy hidding array */
1972 priv->n_selected = n_selected;
1973 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1974 for (i=0; i < n_selected; i++)
1975 priv->hidding_ids[i] = g_strdup(hidding[i]);
1977 /* Hide cut headers */
1978 modest_header_view_refilter (header_view);
1985 _clipboard_set_selected_data (ModestHeaderView *header_view,
1988 ModestHeaderViewPrivate *priv = NULL;
1989 TnyList *headers = NULL;
1991 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1992 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1994 /* Set selected data on clipboard */
1995 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1996 headers = modest_header_view_get_selected_headers (header_view);
1997 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2000 g_object_unref (headers);
2004 ModestHeaderView *self;
2009 notify_filter_change (gpointer data)
2011 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2013 g_signal_emit (info->self,
2014 signals[MSG_COUNT_CHANGED_SIGNAL],
2015 0, info->folder, NULL);
2021 notify_filter_change_destroy (gpointer data)
2023 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2024 ModestHeaderViewPrivate *priv;
2026 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2027 priv->status_timeout = 0;
2029 g_object_unref (info->self);
2030 g_object_unref (info->folder);
2031 g_slice_free (NotifyFilterInfo, info);
2035 filter_row (GtkTreeModel *model,
2039 ModestHeaderViewPrivate *priv = NULL;
2040 TnyHeaderFlags flags;
2041 TnyHeader *header = NULL;
2044 gboolean visible = TRUE;
2045 gboolean found = FALSE;
2046 GValue value = {0,};
2047 HeaderViewStatus old_status;
2049 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2050 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2052 /* Get header from model */
2053 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2054 flags = (TnyHeaderFlags) g_value_get_int (&value);
2055 g_value_unset (&value);
2056 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2057 header = (TnyHeader *) g_value_get_object (&value);
2058 g_value_unset (&value);
2060 /* Hide deleted and mark as deleted heders */
2061 if (flags & TNY_HEADER_FLAG_DELETED ||
2062 flags & TNY_HEADER_FLAG_EXPUNGED) {
2067 /* If no data on clipboard, return always TRUE */
2068 if (modest_email_clipboard_cleared(priv->clipboard)) {
2073 /* Get message id from header (ensure is a valid id) */
2080 if (priv->hidding_ids != NULL) {
2081 id = tny_header_dup_message_id (header);
2082 for (i=0; i < priv->n_selected && !found; i++)
2083 if (priv->hidding_ids[i] != NULL && id != NULL)
2084 found = (!strcmp (priv->hidding_ids[i], id));
2091 old_status = priv->status;
2092 priv->status = ((gboolean) priv->status) && !visible;
2093 if ((priv->notify_status) && (priv->status != old_status)) {
2094 NotifyFilterInfo *info;
2096 if (priv->status_timeout)
2097 g_source_remove (priv->status_timeout);
2099 info = g_slice_new0 (NotifyFilterInfo);
2100 info->self = g_object_ref (G_OBJECT (user_data));
2101 info->folder = tny_header_get_folder (header);
2102 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2103 notify_filter_change,
2105 notify_filter_change_destroy);
2112 _clear_hidding_filter (ModestHeaderView *header_view)
2114 ModestHeaderViewPrivate *priv = NULL;
2117 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2118 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2120 if (priv->hidding_ids != NULL) {
2121 for (i=0; i < priv->n_selected; i++)
2122 g_free (priv->hidding_ids[i]);
2123 g_free(priv->hidding_ids);
2128 modest_header_view_refilter (ModestHeaderView *header_view)
2130 GtkTreeModel *model = NULL;
2131 ModestHeaderViewPrivate *priv = NULL;
2133 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2134 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2136 /* Hide cut headers */
2137 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2138 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2139 priv->status = HEADER_VIEW_INIT;
2140 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2145 * Called when an account is removed. If I'm showing a folder of the
2146 * account that has been removed then clear the view
2149 on_account_removed (TnyAccountStore *self,
2150 TnyAccount *account,
2153 ModestHeaderViewPrivate *priv = NULL;
2155 /* Ignore changes in transport accounts */
2156 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2159 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2162 TnyAccount *my_account;
2164 my_account = tny_folder_get_account (priv->folder);
2165 if (my_account == account)
2166 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2167 g_object_unref (my_account);
2172 modest_header_view_add_observer(ModestHeaderView *header_view,
2173 ModestHeaderViewObserver *observer)
2175 ModestHeaderViewPrivate *priv;
2177 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2178 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2180 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2182 g_mutex_lock(priv->observer_list_lock);
2183 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2184 g_mutex_unlock(priv->observer_list_lock);
2188 modest_header_view_remove_observer(ModestHeaderView *header_view,
2189 ModestHeaderViewObserver *observer)
2191 ModestHeaderViewPrivate *priv;
2193 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2194 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2196 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2198 g_mutex_lock(priv->observer_list_lock);
2199 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2200 g_mutex_unlock(priv->observer_list_lock);
2204 modest_header_view_notify_observers(ModestHeaderView *header_view,
2205 GtkTreeModel *model,
2206 const gchar *tny_folder_id)
2208 ModestHeaderViewPrivate *priv = NULL;
2210 ModestHeaderViewObserver *observer;
2213 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2215 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2217 g_mutex_lock(priv->observer_list_lock);
2218 iter = priv->observer_list;
2219 while(iter != NULL){
2220 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2221 modest_header_view_observer_update(observer, model,
2223 iter = g_slist_next(iter);
2225 g_mutex_unlock(priv->observer_list_lock);