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 RefreshAsyncUserCallback callback,
1301 ModestHeaderViewPrivate *priv;
1302 ModestWindow *main_win;
1304 g_return_if_fail (self);
1306 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1308 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1309 FALSE); /* don't create */
1311 g_warning ("%s: BUG: no main window", __FUNCTION__);
1316 if (priv->status_timeout) {
1317 g_source_remove (priv->status_timeout);
1318 priv->status_timeout = 0;
1321 g_mutex_lock (priv->observers_lock);
1322 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1323 g_object_unref (priv->folder);
1324 priv->folder = NULL;
1325 g_mutex_unlock (priv->observers_lock);
1329 GtkTreeSelection *selection;
1330 SetFolderHelper *info;
1331 ModestMailOperation *mail_op = NULL;
1333 /* Set folder in the model */
1334 modest_header_view_set_folder_intern (self, folder);
1336 /* Pick my reference. Nothing to do with the mail operation */
1337 priv->folder = g_object_ref (folder);
1339 /* Do not notify about filterings until the refresh finishes */
1340 priv->notify_status = FALSE;
1342 /* Clear the selection if exists */
1343 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1344 gtk_tree_selection_unselect_all(selection);
1345 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1347 /* Notify the observers that the update begins */
1348 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1351 /* create the helper */
1352 info = g_malloc0 (sizeof (SetFolderHelper));
1353 info->header_view = g_object_ref (self);
1354 info->cb = callback;
1355 info->user_data = user_data;
1357 /* Create the mail operation (source will be the parent widget) */
1358 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1359 refresh_folder_error_handler,
1362 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1365 /* Refresh the folder asynchronously */
1366 modest_mail_operation_refresh_folder (mail_op,
1368 folder_refreshed_cb,
1371 folder_refreshed_cb (mail_op, folder, info);
1374 g_object_unref (mail_op);
1376 g_mutex_lock (priv->observers_lock);
1378 if (priv->monitor) {
1379 tny_folder_monitor_stop (priv->monitor);
1380 g_object_unref (G_OBJECT (priv->monitor));
1381 priv->monitor = NULL;
1384 if (priv->autoselect_reference) {
1385 gtk_tree_row_reference_free (priv->autoselect_reference);
1386 priv->autoselect_reference = NULL;
1389 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1391 modest_header_view_notify_observers(self, NULL, NULL);
1393 g_mutex_unlock (priv->observers_lock);
1395 /* Notify the observers that the update is over */
1396 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1402 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1403 GtkTreeViewColumn *column, gpointer userdata)
1405 ModestHeaderView *self = NULL;
1406 ModestHeaderViewPrivate *priv = NULL;
1408 GtkTreeModel *model = NULL;
1409 TnyHeader *header = NULL;
1410 TnyHeaderFlags flags;
1412 self = MODEST_HEADER_VIEW (treeview);
1413 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1415 model = gtk_tree_view_get_model (treeview);
1416 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1419 /* get the first selected item */
1420 gtk_tree_model_get (model, &iter,
1421 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1422 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1425 /* Dont open DELETED messages */
1426 if (flags & TNY_HEADER_FLAG_DELETED) {
1429 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1430 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1431 modest_platform_information_banner (NULL, NULL, msg);
1437 g_signal_emit (G_OBJECT(self),
1438 signals[HEADER_ACTIVATED_SIGNAL],
1444 g_object_unref (G_OBJECT (header));
1449 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1451 GtkTreeModel *model;
1452 TnyHeader *header = NULL;
1453 GtkTreePath *path = NULL;
1455 ModestHeaderView *self;
1456 ModestHeaderViewPrivate *priv;
1457 GList *selected = NULL;
1459 g_return_if_fail (sel);
1460 g_return_if_fail (user_data);
1462 self = MODEST_HEADER_VIEW (user_data);
1463 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1465 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1466 if (selected != NULL)
1467 path = (GtkTreePath *) selected->data;
1468 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1469 return; /* msg was _un_selected */
1471 gtk_tree_model_get (model, &iter,
1472 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1476 g_signal_emit (G_OBJECT(self),
1477 signals[HEADER_SELECTED_SIGNAL],
1480 g_object_unref (G_OBJECT (header));
1482 /* free all items in 'selected' */
1483 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1484 g_list_free (selected);
1488 /* PROTECTED method. It's useful when we want to force a given
1489 selection to reload a msg. For example if we have selected a header
1490 in offline mode, when Modest become online, we want to reload the
1491 message automatically without an user click over the header */
1493 _modest_header_view_change_selection (GtkTreeSelection *selection,
1496 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1497 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1499 on_selection_changed (selection, user_data);
1503 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1510 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1514 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1518 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1526 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1533 /* static int counter = 0; */
1535 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1536 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1537 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1541 case TNY_HEADER_FLAG_ATTACHMENTS:
1543 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1544 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1545 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1546 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1548 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1549 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1551 return cmp ? cmp : t1 - t2;
1553 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1554 TnyHeader *header1 = NULL, *header2 = NULL;
1556 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1557 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1558 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1559 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1561 /* This is for making priority values respect the intuitive sort relationship
1562 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1564 if (header1 && header2) {
1565 cmp = compare_priorities (tny_header_get_priority (header1),
1566 tny_header_get_priority (header2));
1567 g_object_unref (header1);
1568 g_object_unref (header2);
1570 return cmp ? cmp : t1 - t2;
1576 return &iter1 - &iter2; /* oughhhh */
1581 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1587 /* static int counter = 0; */
1589 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1591 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1592 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1593 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1594 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1596 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1597 val2 + modest_text_utils_get_subject_prefix_len(val2),
1604 /* Drag and drop stuff */
1606 drag_data_get_cb (GtkWidget *widget,
1607 GdkDragContext *context,
1608 GtkSelectionData *selection_data,
1613 ModestHeaderView *self = NULL;
1614 ModestHeaderViewPrivate *priv = NULL;
1616 self = MODEST_HEADER_VIEW (widget);
1617 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1619 /* Set the data. Do not use the current selection because it
1620 could be different than the selection at the beginning of
1622 modest_dnd_selection_data_set_paths (selection_data,
1623 priv->drag_begin_cached_selected_rows);
1627 * We're caching the selected rows at the beginning because the
1628 * selection could change between drag-begin and drag-data-get, for
1629 * example if we have a set of rows already selected, and then we
1630 * click in one of them (without SHIFT key pressed) and begin a drag,
1631 * the selection at that moment contains all the selected lines, but
1632 * after dropping the selection, the release event provokes that only
1633 * the row used to begin the drag is selected, so at the end the
1634 * drag&drop affects only one rows instead of all the selected ones.
1638 drag_begin_cb (GtkWidget *widget,
1639 GdkDragContext *context,
1642 ModestHeaderView *self = NULL;
1643 ModestHeaderViewPrivate *priv = NULL;
1644 GtkTreeSelection *selection;
1646 self = MODEST_HEADER_VIEW (widget);
1647 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1649 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1650 priv->drag_begin_cached_selected_rows =
1651 gtk_tree_selection_get_selected_rows (selection, NULL);
1655 * We use the drag-end signal to clear the cached selection, we use
1656 * this because this allways happens, whether or not the d&d was a
1660 drag_end_cb (GtkWidget *widget,
1664 ModestHeaderView *self = NULL;
1665 ModestHeaderViewPrivate *priv = NULL;
1667 self = MODEST_HEADER_VIEW (widget);
1668 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1670 /* Free cached data */
1671 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1672 g_list_free (priv->drag_begin_cached_selected_rows);
1673 priv->drag_begin_cached_selected_rows = NULL;
1676 /* Header view drag types */
1677 const GtkTargetEntry header_view_drag_types[] = {
1678 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1682 enable_drag_and_drop (GtkWidget *self)
1684 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1685 header_view_drag_types,
1686 G_N_ELEMENTS (header_view_drag_types),
1687 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1691 disable_drag_and_drop (GtkWidget *self)
1693 gtk_drag_source_unset (self);
1697 setup_drag_and_drop (GtkWidget *self)
1699 enable_drag_and_drop(self);
1700 g_signal_connect(G_OBJECT (self), "drag_data_get",
1701 G_CALLBACK(drag_data_get_cb), NULL);
1703 g_signal_connect(G_OBJECT (self), "drag_begin",
1704 G_CALLBACK(drag_begin_cb), NULL);
1706 g_signal_connect(G_OBJECT (self), "drag_end",
1707 G_CALLBACK(drag_end_cb), NULL);
1710 static GtkTreePath *
1711 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1713 GtkTreePath *path = NULL;
1714 GtkTreeSelection *sel = NULL;
1717 sel = gtk_tree_view_get_selection(self);
1718 rows = gtk_tree_selection_get_selected_rows (sel, model);
1720 if ((rows == NULL) || (g_list_length(rows) != 1))
1723 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1728 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1735 * This function moves the tree view scroll to the current selected
1736 * row when the widget grabs the focus
1739 on_focus_in (GtkWidget *self,
1740 GdkEventFocus *event,
1743 GtkTreeSelection *selection;
1744 GtkTreeModel *model;
1745 GList *selected = NULL;
1746 GtkTreePath *selected_path = NULL;
1748 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1752 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1753 /* If none selected yet, pick the first one */
1754 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1758 /* Return if the model is empty */
1759 if (!gtk_tree_model_get_iter_first (model, &iter))
1762 path = gtk_tree_model_get_path (model, &iter);
1763 gtk_tree_selection_select_path (selection, path);
1764 gtk_tree_path_free (path);
1767 /* Need to get the all the rows because is selection multiple */
1768 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1769 if (selected == NULL) return FALSE;
1770 selected_path = (GtkTreePath *) selected->data;
1773 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1774 g_list_free (selected);
1780 on_focus_out (GtkWidget *self,
1781 GdkEventFocus *event,
1785 if (!gtk_widget_is_focus (self)) {
1786 GtkTreeSelection *selection = NULL;
1787 GList *selected_rows = NULL;
1788 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1789 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1790 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1791 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1792 gtk_tree_selection_unselect_all (selection);
1793 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1794 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1795 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1796 g_list_free (selected_rows);
1803 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1805 enable_drag_and_drop(self);
1810 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1812 GtkTreeSelection *selection = NULL;
1813 GtkTreePath *path = NULL;
1814 gboolean already_selected = FALSE, already_opened = FALSE;
1815 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1817 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1819 GtkTreeModel *model;
1821 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1822 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1824 /* Get header from model */
1825 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1826 if (gtk_tree_model_get_iter (model, &iter, path)) {
1827 GValue value = {0,};
1830 gtk_tree_model_get_value (model, &iter,
1831 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1833 header = (TnyHeader *) g_value_get_object (&value);
1834 if (TNY_IS_HEADER (header)) {
1835 status = modest_tny_all_send_queues_get_msg_status (header);
1836 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1839 g_value_unset (&value);
1843 /* Enable drag and drop only if the user clicks on a row that
1844 it's already selected. If not, let him select items using
1845 the pointer. If the message is in an OUTBOX and in sending
1846 status disable drag and drop as well */
1847 if (!already_selected ||
1848 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1850 disable_drag_and_drop(self);
1853 gtk_tree_path_free(path);
1855 /* If it's already opened then do not let the button-press
1856 event go on because it'll perform a message open because
1857 we're clicking on to an already selected header */
1862 folder_monitor_update (TnyFolderObserver *self,
1863 TnyFolderChange *change)
1865 ModestHeaderViewPrivate *priv = NULL;
1866 TnyFolderChangeChanged changed;
1867 TnyFolder *folder = NULL;
1869 changed = tny_folder_change_get_changed (change);
1871 /* Do not notify the observers if the folder of the header
1872 view has changed before this call to the observer
1874 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1875 folder = tny_folder_change_get_folder (change);
1876 if (folder != priv->folder)
1879 MODEST_DEBUG_BLOCK (
1880 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1881 g_print ("ADDED %d/%d (r/t) \n",
1882 tny_folder_change_get_new_unread_count (change),
1883 tny_folder_change_get_new_all_count (change));
1884 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1885 g_print ("ALL COUNT %d\n",
1886 tny_folder_change_get_new_all_count (change));
1887 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1888 g_print ("UNREAD COUNT %d\n",
1889 tny_folder_change_get_new_unread_count (change));
1890 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1891 g_print ("EXPUNGED %d/%d (r/t) \n",
1892 tny_folder_change_get_new_unread_count (change),
1893 tny_folder_change_get_new_all_count (change));
1894 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1895 g_print ("FOLDER RENAME\n");
1896 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1897 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1898 tny_folder_change_get_new_unread_count (change),
1899 tny_folder_change_get_new_all_count (change));
1900 g_print ("---------------------------------------------------\n");
1903 /* Check folder count */
1904 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1905 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1907 g_mutex_lock (priv->observers_lock);
1909 /* Emit signal to evaluate how headers changes affects
1910 to the window view */
1911 g_signal_emit (G_OBJECT(self),
1912 signals[MSG_COUNT_CHANGED_SIGNAL],
1915 /* Added or removed headers, so data stored on cliboard are invalid */
1916 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1917 modest_email_clipboard_clear (priv->clipboard);
1919 g_mutex_unlock (priv->observers_lock);
1925 g_object_unref (folder);
1929 modest_header_view_is_empty (ModestHeaderView *self)
1931 ModestHeaderViewPrivate *priv;
1933 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1935 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1937 return priv->status == HEADER_VIEW_EMPTY;
1941 modest_header_view_clear (ModestHeaderView *self)
1943 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1945 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1949 modest_header_view_copy_selection (ModestHeaderView *header_view)
1951 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1953 /* Copy selection */
1954 _clipboard_set_selected_data (header_view, FALSE);
1958 modest_header_view_cut_selection (ModestHeaderView *header_view)
1960 ModestHeaderViewPrivate *priv = NULL;
1961 const gchar **hidding = NULL;
1962 guint i, n_selected;
1964 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1966 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1968 /* Copy selection */
1969 _clipboard_set_selected_data (header_view, TRUE);
1971 /* Get hidding ids */
1972 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1974 /* Clear hidding array created by previous cut operation */
1975 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1977 /* Copy hidding array */
1978 priv->n_selected = n_selected;
1979 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1980 for (i=0; i < n_selected; i++)
1981 priv->hidding_ids[i] = g_strdup(hidding[i]);
1983 /* Hide cut headers */
1984 modest_header_view_refilter (header_view);
1991 _clipboard_set_selected_data (ModestHeaderView *header_view,
1994 ModestHeaderViewPrivate *priv = NULL;
1995 TnyList *headers = NULL;
1997 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1998 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2000 /* Set selected data on clipboard */
2001 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2002 headers = modest_header_view_get_selected_headers (header_view);
2003 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2006 g_object_unref (headers);
2010 ModestHeaderView *self;
2015 notify_filter_change (gpointer data)
2017 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2019 g_signal_emit (info->self,
2020 signals[MSG_COUNT_CHANGED_SIGNAL],
2021 0, info->folder, NULL);
2027 notify_filter_change_destroy (gpointer data)
2029 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2030 ModestHeaderViewPrivate *priv;
2032 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2033 priv->status_timeout = 0;
2035 g_object_unref (info->self);
2036 g_object_unref (info->folder);
2037 g_slice_free (NotifyFilterInfo, info);
2041 filter_row (GtkTreeModel *model,
2045 ModestHeaderViewPrivate *priv = NULL;
2046 TnyHeaderFlags flags;
2047 TnyHeader *header = NULL;
2050 gboolean visible = TRUE;
2051 gboolean found = FALSE;
2052 GValue value = {0,};
2053 HeaderViewStatus old_status;
2055 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2056 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2058 /* Get header from model */
2059 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2060 flags = (TnyHeaderFlags) g_value_get_int (&value);
2061 g_value_unset (&value);
2062 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2063 header = (TnyHeader *) g_value_get_object (&value);
2064 g_value_unset (&value);
2066 /* Hide deleted and mark as deleted heders */
2067 if (flags & TNY_HEADER_FLAG_DELETED ||
2068 flags & TNY_HEADER_FLAG_EXPUNGED) {
2073 /* If no data on clipboard, return always TRUE */
2074 if (modest_email_clipboard_cleared(priv->clipboard)) {
2079 /* Get message id from header (ensure is a valid id) */
2086 if (priv->hidding_ids != NULL) {
2087 id = tny_header_dup_message_id (header);
2088 for (i=0; i < priv->n_selected && !found; i++)
2089 if (priv->hidding_ids[i] != NULL && id != NULL)
2090 found = (!strcmp (priv->hidding_ids[i], id));
2097 old_status = priv->status;
2098 priv->status = ((gboolean) priv->status) && !visible;
2099 if ((priv->notify_status) && (priv->status != old_status)) {
2100 NotifyFilterInfo *info;
2102 if (priv->status_timeout)
2103 g_source_remove (priv->status_timeout);
2105 info = g_slice_new0 (NotifyFilterInfo);
2106 info->self = g_object_ref (G_OBJECT (user_data));
2107 info->folder = tny_header_get_folder (header);
2108 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2109 notify_filter_change,
2111 notify_filter_change_destroy);
2118 _clear_hidding_filter (ModestHeaderView *header_view)
2120 ModestHeaderViewPrivate *priv = NULL;
2123 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2124 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2126 if (priv->hidding_ids != NULL) {
2127 for (i=0; i < priv->n_selected; i++)
2128 g_free (priv->hidding_ids[i]);
2129 g_free(priv->hidding_ids);
2134 modest_header_view_refilter (ModestHeaderView *header_view)
2136 GtkTreeModel *model = NULL;
2137 ModestHeaderViewPrivate *priv = NULL;
2139 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2140 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2142 /* Hide cut headers */
2143 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2144 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2145 priv->status = HEADER_VIEW_INIT;
2146 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2151 * Called when an account is removed. If I'm showing a folder of the
2152 * account that has been removed then clear the view
2155 on_account_removed (TnyAccountStore *self,
2156 TnyAccount *account,
2159 ModestHeaderViewPrivate *priv = NULL;
2161 /* Ignore changes in transport accounts */
2162 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2165 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2168 TnyAccount *my_account;
2170 my_account = tny_folder_get_account (priv->folder);
2171 if (my_account == account)
2172 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2173 g_object_unref (my_account);
2178 modest_header_view_add_observer(ModestHeaderView *header_view,
2179 ModestHeaderViewObserver *observer)
2181 ModestHeaderViewPrivate *priv;
2183 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2184 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2186 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2188 g_mutex_lock(priv->observer_list_lock);
2189 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2190 g_mutex_unlock(priv->observer_list_lock);
2194 modest_header_view_remove_observer(ModestHeaderView *header_view,
2195 ModestHeaderViewObserver *observer)
2197 ModestHeaderViewPrivate *priv;
2199 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2200 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2202 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2204 g_mutex_lock(priv->observer_list_lock);
2205 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2206 g_mutex_unlock(priv->observer_list_lock);
2210 modest_header_view_notify_observers(ModestHeaderView *header_view,
2211 GtkTreeModel *model,
2212 const gchar *tny_folder_id)
2214 ModestHeaderViewPrivate *priv = NULL;
2216 ModestHeaderViewObserver *observer;
2219 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2221 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2223 g_mutex_lock(priv->observer_list_lock);
2224 iter = priv->observer_list;
2225 while(iter != NULL){
2226 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2227 modest_header_view_observer_update(observer, model,
2229 iter = g_slist_next(iter);
2231 g_mutex_unlock(priv->observer_list_lock);