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>
52 #include <modest-datetime-formatter.h>
54 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
55 static void modest_header_view_init (ModestHeaderView *obj);
56 static void modest_header_view_finalize (GObject *obj);
57 static void modest_header_view_dispose (GObject *obj);
59 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
60 GtkTreeViewColumn *column, gpointer userdata);
62 static gint cmp_rows (GtkTreeModel *tree_model,
67 static gint cmp_subject_rows (GtkTreeModel *tree_model,
72 static gboolean filter_row (GtkTreeModel *model,
76 static void on_account_removed (TnyAccountStore *self,
80 static void on_selection_changed (GtkTreeSelection *sel,
83 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
86 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
89 static void setup_drag_and_drop (GtkWidget *self);
91 static void enable_drag_and_drop (GtkWidget *self);
93 static void disable_drag_and_drop (GtkWidget *self);
95 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
97 #ifndef MODEST_TOOLKIT_HILDON2
98 static gboolean on_focus_in (GtkWidget *sef,
102 static gboolean on_focus_out (GtkWidget *self,
103 GdkEventFocus *event,
107 static void folder_monitor_update (TnyFolderObserver *self,
108 TnyFolderChange *change);
110 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
112 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
114 static void _clear_hidding_filter (ModestHeaderView *header_view);
116 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
118 const gchar *tny_folder_id);
120 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
121 GdkEventExpose *event,
125 HEADER_VIEW_NON_EMPTY,
130 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
131 struct _ModestHeaderViewPrivate {
133 ModestHeaderViewStyle style;
135 TnyFolderMonitor *monitor;
136 GMutex *observers_lock;
138 /*header-view-observer observer*/
139 GMutex *observer_list_lock;
140 GSList *observer_list;
142 /* not unref this object, its a singlenton */
143 ModestEmailClipboard *clipboard;
145 /* Filter tree model */
148 GtkTreeRowReference *autoselect_reference;
150 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
151 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
153 gulong selection_changed_handler;
154 gulong acc_removed_handler;
156 GList *drag_begin_cached_selected_rows;
158 HeaderViewStatus status;
159 guint status_timeout;
160 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
162 ModestDatetimeFormatter *datetime_formatter;
165 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
166 struct _HeadersCountChangedHelper {
167 ModestHeaderView *self;
168 TnyFolderChange *change;
172 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
173 MODEST_TYPE_HEADER_VIEW, \
174 ModestHeaderViewPrivate))
178 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
181 HEADER_SELECTED_SIGNAL,
182 HEADER_ACTIVATED_SIGNAL,
183 ITEM_NOT_FOUND_SIGNAL,
184 MSG_COUNT_CHANGED_SIGNAL,
185 UPDATING_MSG_LIST_SIGNAL,
190 static GObjectClass *parent_class = NULL;
192 /* uncomment the following if you have defined any signals */
193 static guint signals[LAST_SIGNAL] = {0};
196 modest_header_view_get_type (void)
198 static GType my_type = 0;
200 static const GTypeInfo my_info = {
201 sizeof(ModestHeaderViewClass),
202 NULL, /* base init */
203 NULL, /* base finalize */
204 (GClassInitFunc) modest_header_view_class_init,
205 NULL, /* class finalize */
206 NULL, /* class data */
207 sizeof(ModestHeaderView),
209 (GInstanceInitFunc) modest_header_view_init,
213 static const GInterfaceInfo tny_folder_observer_info =
215 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
216 NULL, /* interface_finalize */
217 NULL /* interface_data */
219 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
223 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
224 &tny_folder_observer_info);
232 modest_header_view_class_init (ModestHeaderViewClass *klass)
234 GObjectClass *gobject_class;
235 gobject_class = (GObjectClass*) klass;
237 parent_class = g_type_class_peek_parent (klass);
238 gobject_class->finalize = modest_header_view_finalize;
239 gobject_class->dispose = modest_header_view_dispose;
241 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
243 signals[HEADER_SELECTED_SIGNAL] =
244 g_signal_new ("header_selected",
245 G_TYPE_FROM_CLASS (gobject_class),
247 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
249 g_cclosure_marshal_VOID__POINTER,
250 G_TYPE_NONE, 1, G_TYPE_POINTER);
252 signals[HEADER_ACTIVATED_SIGNAL] =
253 g_signal_new ("header_activated",
254 G_TYPE_FROM_CLASS (gobject_class),
256 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
258 gtk_marshal_VOID__POINTER_POINTER,
259 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
262 signals[ITEM_NOT_FOUND_SIGNAL] =
263 g_signal_new ("item_not_found",
264 G_TYPE_FROM_CLASS (gobject_class),
266 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
268 g_cclosure_marshal_VOID__INT,
269 G_TYPE_NONE, 1, G_TYPE_INT);
271 signals[MSG_COUNT_CHANGED_SIGNAL] =
272 g_signal_new ("msg_count_changed",
273 G_TYPE_FROM_CLASS (gobject_class),
275 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
277 modest_marshal_VOID__POINTER_POINTER,
278 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
280 signals[UPDATING_MSG_LIST_SIGNAL] =
281 g_signal_new ("updating-msg-list",
282 G_TYPE_FROM_CLASS (gobject_class),
284 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
286 g_cclosure_marshal_VOID__BOOLEAN,
287 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
289 #ifdef MODEST_TOOLKIT_HILDON2
290 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
296 tny_folder_observer_init (TnyFolderObserverIface *klass)
298 klass->update = folder_monitor_update;
301 static GtkTreeViewColumn*
302 get_new_column (const gchar *name, GtkCellRenderer *renderer,
303 gboolean resizable, gint sort_col_id, gboolean show_as_text,
304 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
306 GtkTreeViewColumn *column;
308 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
309 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
311 gtk_tree_view_column_set_resizable (column, resizable);
313 gtk_tree_view_column_set_expand (column, TRUE);
316 gtk_tree_view_column_add_attribute (column, renderer, "text",
318 if (sort_col_id >= 0)
319 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
321 gtk_tree_view_column_set_sort_indicator (column, FALSE);
322 gtk_tree_view_column_set_reorderable (column, TRUE);
325 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
332 remove_all_columns (ModestHeaderView *obj)
334 GList *columns, *cursor;
336 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
338 for (cursor = columns; cursor; cursor = cursor->next)
339 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
340 GTK_TREE_VIEW_COLUMN(cursor->data));
341 g_list_free (columns);
345 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
347 GtkTreeModel *tree_filter, *sortable;
348 GtkTreeViewColumn *column=NULL;
349 GtkTreeSelection *selection = NULL;
350 GtkCellRenderer *renderer_header,
351 *renderer_attach, *renderer_compact_date_or_status;
352 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
353 *renderer_subject, *renderer_subject_box, *renderer_recpt,
355 ModestHeaderViewPrivate *priv;
356 GtkTreeViewColumn *compact_column = NULL;
359 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
360 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
362 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
364 /* TODO: check whether these renderers need to be freed */
365 renderer_attach = gtk_cell_renderer_pixbuf_new ();
366 renderer_priority = gtk_cell_renderer_pixbuf_new ();
367 renderer_header = gtk_cell_renderer_text_new ();
369 renderer_compact_header = modest_vbox_cell_renderer_new ();
370 renderer_recpt_box = modest_hbox_cell_renderer_new ();
371 renderer_subject_box = modest_hbox_cell_renderer_new ();
372 renderer_recpt = gtk_cell_renderer_text_new ();
373 renderer_subject = gtk_cell_renderer_text_new ();
374 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
376 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
377 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
378 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
379 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
380 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
381 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
382 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
383 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
384 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
385 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
386 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
387 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
388 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
389 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
391 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
392 #ifndef MODEST_TOOLKIT_GTK
393 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
394 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
396 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
397 g_object_set(G_OBJECT(renderer_header),
398 "ellipsize", PANGO_ELLIPSIZE_END,
400 g_object_set (G_OBJECT (renderer_subject),
401 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
403 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
404 g_object_set (G_OBJECT (renderer_recpt),
405 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
407 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
408 g_object_set(G_OBJECT(renderer_compact_date_or_status),
409 "xalign", 1.0, "yalign", 0.0,
411 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
412 g_object_set (G_OBJECT (renderer_priority),
413 "yalign", 1.0, NULL);
414 g_object_set (G_OBJECT (renderer_attach),
415 "yalign", 0.0, NULL);
417 #ifndef MODEST_TOOLKIT_GTK
418 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
419 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
420 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
422 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
423 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
424 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
427 remove_all_columns (self);
429 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
430 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
431 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
432 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
434 /* Add new columns */
435 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
436 ModestHeaderViewColumn col =
437 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
439 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
440 g_printerr ("modest: invalid column %d in column list\n", col);
446 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
447 column = get_new_column (_("A"), renderer_attach, FALSE,
448 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
450 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
452 gtk_tree_view_column_set_fixed_width (column, 45);
456 case MODEST_HEADER_VIEW_COLUMN_FROM:
457 column = get_new_column (_("From"), renderer_header, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
461 GINT_TO_POINTER(TRUE));
464 case MODEST_HEADER_VIEW_COLUMN_TO:
465 column = get_new_column (_("To"), renderer_header, TRUE,
466 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
468 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
469 GINT_TO_POINTER(FALSE));
472 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
473 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
474 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
476 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
477 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
478 compact_column = column;
481 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
482 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
483 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
485 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
486 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
487 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
488 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
489 compact_column = column;
493 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
494 column = get_new_column (_("Subject"), renderer_header, TRUE,
495 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
497 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
501 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
502 column = get_new_column (_("Received"), renderer_header, TRUE,
503 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
505 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
506 GINT_TO_POINTER(TRUE));
509 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
510 column = get_new_column (_("Sent"), renderer_header, TRUE,
511 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
513 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
514 GINT_TO_POINTER(FALSE));
517 case MODEST_HEADER_VIEW_COLUMN_SIZE:
518 column = get_new_column (_("Size"), renderer_header, TRUE,
519 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
521 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
524 case MODEST_HEADER_VIEW_COLUMN_STATUS:
525 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
526 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
528 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
533 g_return_val_if_reached(FALSE);
536 /* we keep the column id around */
537 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
538 GINT_TO_POINTER(col));
540 /* we need this ptr when sorting the rows */
541 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
543 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
547 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
548 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
549 (GtkTreeIterCompareFunc) cmp_rows,
550 compact_column, NULL);
551 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
552 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
553 (GtkTreeIterCompareFunc) cmp_subject_rows,
554 compact_column, NULL);
561 datetime_format_changed (ModestDatetimeFormatter *formatter,
562 ModestHeaderView *self)
564 gtk_widget_queue_draw (GTK_WIDGET (self));
568 modest_header_view_init (ModestHeaderView *obj)
570 ModestHeaderViewPrivate *priv;
573 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
577 priv->monitor = NULL;
578 priv->observers_lock = g_mutex_new ();
579 priv->autoselect_reference = NULL;
581 priv->status = HEADER_VIEW_INIT;
582 priv->status_timeout = 0;
583 priv->notify_status = TRUE;
585 priv->observer_list_lock = g_mutex_new();
586 priv->observer_list = NULL;
588 priv->clipboard = modest_runtime_get_email_clipboard ();
589 priv->hidding_ids = NULL;
590 priv->n_selected = 0;
591 priv->selection_changed_handler = 0;
592 priv->acc_removed_handler = 0;
594 /* Sort parameters */
595 for (j=0; j < 2; j++) {
596 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
597 priv->sort_colid[j][i] = -1;
598 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
602 priv->datetime_formatter = modest_datetime_formatter_new ();
603 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
604 G_CALLBACK (datetime_format_changed), (gpointer) obj);
606 setup_drag_and_drop (GTK_WIDGET(obj));
610 modest_header_view_dispose (GObject *obj)
612 ModestHeaderView *self;
613 ModestHeaderViewPrivate *priv;
614 GtkTreeSelection *sel;
616 self = MODEST_HEADER_VIEW(obj);
617 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
619 if (priv->datetime_formatter) {
620 g_object_unref (priv->datetime_formatter);
621 priv->datetime_formatter = NULL;
624 /* Free in the dispose to avoid unref cycles */
626 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
627 g_object_unref (G_OBJECT (priv->folder));
631 /* We need to do this here in the dispose because the
632 selection won't exist when finalizing */
633 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
634 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
635 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
636 priv->selection_changed_handler = 0;
639 G_OBJECT_CLASS(parent_class)->dispose (obj);
643 modest_header_view_finalize (GObject *obj)
645 ModestHeaderView *self;
646 ModestHeaderViewPrivate *priv;
648 self = MODEST_HEADER_VIEW(obj);
649 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
651 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
652 priv->acc_removed_handler)) {
653 g_signal_handler_disconnect (modest_runtime_get_account_store (),
654 priv->acc_removed_handler);
657 /* There is no need to lock because there should not be any
658 * reference to self now. */
659 g_mutex_free(priv->observer_list_lock);
660 g_slist_free(priv->observer_list);
662 g_mutex_lock (priv->observers_lock);
664 tny_folder_monitor_stop (priv->monitor);
665 g_object_unref (G_OBJECT (priv->monitor));
667 g_mutex_unlock (priv->observers_lock);
668 g_mutex_free (priv->observers_lock);
670 /* Clear hidding array created by cut operation */
671 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
673 if (priv->autoselect_reference != NULL) {
674 gtk_tree_row_reference_free (priv->autoselect_reference);
675 priv->autoselect_reference = NULL;
678 G_OBJECT_CLASS(parent_class)->finalize (obj);
683 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
686 GtkTreeSelection *sel;
687 ModestHeaderView *self;
688 ModestHeaderViewPrivate *priv;
690 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
693 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
694 self = MODEST_HEADER_VIEW(obj);
695 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
697 modest_header_view_set_style (self, style);
699 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
700 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
701 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
703 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
704 TRUE); /* alternating row colors */
706 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
707 priv->selection_changed_handler =
708 g_signal_connect_after (sel, "changed",
709 G_CALLBACK(on_selection_changed), self);
711 g_signal_connect (self, "row-activated",
712 G_CALLBACK (on_header_row_activated), NULL);
714 #ifndef MODEST_TOOLKIT_HILDON2
715 g_signal_connect (self, "focus-in-event",
716 G_CALLBACK(on_focus_in), NULL);
717 g_signal_connect (self, "focus-out-event",
718 G_CALLBACK(on_focus_out), NULL);
721 g_signal_connect (self, "button-press-event",
722 G_CALLBACK(on_button_press_event), NULL);
723 g_signal_connect (self, "button-release-event",
724 G_CALLBACK(on_button_release_event), NULL);
726 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
728 G_CALLBACK (on_account_removed),
731 g_signal_connect (self, "expose-event",
732 G_CALLBACK(modest_header_view_on_expose_event),
735 return GTK_WIDGET(self);
740 modest_header_view_count_selected_headers (ModestHeaderView *self)
742 GtkTreeSelection *sel;
745 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
747 /* Get selection object and check selected rows count */
748 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
749 selected_rows = gtk_tree_selection_count_selected_rows (sel);
751 return selected_rows;
755 modest_header_view_has_selected_headers (ModestHeaderView *self)
757 GtkTreeSelection *sel;
760 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
762 /* Get selection object and check selected rows count */
763 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
764 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
771 modest_header_view_get_selected_headers (ModestHeaderView *self)
773 GtkTreeSelection *sel;
774 ModestHeaderViewPrivate *priv;
775 TnyList *header_list = NULL;
777 GList *list, *tmp = NULL;
778 GtkTreeModel *tree_model = NULL;
781 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
783 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
785 /* Get selected rows */
786 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
787 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
790 header_list = tny_simple_list_new();
792 list = g_list_reverse (list);
795 /* get header from selection */
796 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
797 gtk_tree_model_get (tree_model, &iter,
798 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
800 /* Prepend to list */
801 tny_list_prepend (header_list, G_OBJECT (header));
802 g_object_unref (G_OBJECT (header));
804 tmp = g_list_next (tmp);
807 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
814 /* scroll our list view so the selected item is visible */
816 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
818 #ifdef MODEST_TOOLKIT_GTK
820 GtkTreePath *selected_path;
821 GtkTreePath *start, *end;
825 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
826 selected_path = gtk_tree_model_get_path (model, iter);
828 start = gtk_tree_path_new ();
829 end = gtk_tree_path_new ();
831 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
833 if (gtk_tree_path_compare (selected_path, start) < 0 ||
834 gtk_tree_path_compare (end, selected_path) < 0)
835 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
836 selected_path, NULL, TRUE,
839 gtk_tree_path_free (selected_path);
840 gtk_tree_path_free (start);
841 gtk_tree_path_free (end);
843 #endif /* MODEST_TOOLKIT_GTK */
848 modest_header_view_select_next (ModestHeaderView *self)
850 GtkTreeSelection *sel;
855 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
857 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
858 path = get_selected_row (GTK_TREE_VIEW(self), &model);
859 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
860 /* Unselect previous path */
861 gtk_tree_selection_unselect_path (sel, path);
863 /* Move path down and selects new one */
864 if (gtk_tree_model_iter_next (model, &iter)) {
865 gtk_tree_selection_select_iter (sel, &iter);
866 scroll_to_selected (self, &iter, FALSE);
868 gtk_tree_path_free(path);
874 modest_header_view_select_prev (ModestHeaderView *self)
876 GtkTreeSelection *sel;
881 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
883 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
884 path = get_selected_row (GTK_TREE_VIEW(self), &model);
885 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
886 /* Unselect previous path */
887 gtk_tree_selection_unselect_path (sel, path);
890 if (gtk_tree_path_prev (path)) {
891 gtk_tree_model_get_iter (model, &iter, path);
893 /* Select the new one */
894 gtk_tree_selection_select_iter (sel, &iter);
895 scroll_to_selected (self, &iter, TRUE);
898 gtk_tree_path_free (path);
903 modest_header_view_get_columns (ModestHeaderView *self)
905 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
907 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
913 modest_header_view_set_style (ModestHeaderView *self,
914 ModestHeaderViewStyle style)
916 ModestHeaderViewPrivate *priv;
917 gboolean show_col_headers = FALSE;
918 ModestHeaderViewStyle old_style;
920 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
921 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
924 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
925 if (priv->style == style)
926 return TRUE; /* nothing to do */
929 case MODEST_HEADER_VIEW_STYLE_DETAILS:
930 show_col_headers = TRUE;
932 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
935 g_return_val_if_reached (FALSE);
937 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
938 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
940 old_style = priv->style;
947 ModestHeaderViewStyle
948 modest_header_view_get_style (ModestHeaderView *self)
950 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
952 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
955 /* This is used to automatically select the first header if the user
956 * has not selected any header yet.
959 modest_header_view_on_expose_event(GtkTreeView *header_view,
960 GdkEventExpose *event,
963 GtkTreeSelection *sel;
965 GtkTreeIter tree_iter;
966 ModestHeaderViewPrivate *priv;
968 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
969 model = gtk_tree_view_get_model(header_view);
974 #ifdef MODEST_TOOLKIT_HILDON2
977 sel = gtk_tree_view_get_selection(header_view);
978 if(!gtk_tree_selection_count_selected_rows(sel)) {
979 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
980 GtkTreePath *tree_iter_path;
981 /* Prevent the widget from getting the focus
982 when selecting the first item */
983 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
984 g_object_set(header_view, "can-focus", FALSE, NULL);
985 gtk_tree_selection_select_iter(sel, &tree_iter);
986 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
987 g_object_set(header_view, "can-focus", TRUE, NULL);
988 if (priv->autoselect_reference) {
989 gtk_tree_row_reference_free (priv->autoselect_reference);
991 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
992 gtk_tree_path_free (tree_iter_path);
995 if (priv->autoselect_reference != NULL) {
996 gboolean moved_selection = FALSE;
997 GtkTreePath * last_path;
998 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
999 moved_selection = TRUE;
1003 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1004 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1005 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1006 moved_selection = TRUE;
1007 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1009 gtk_tree_path_free (last_path);
1011 if (moved_selection) {
1012 gtk_tree_row_reference_free (priv->autoselect_reference);
1013 priv->autoselect_reference = NULL;
1016 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1017 GtkTreePath *current_path;
1018 current_path = gtk_tree_model_get_path (model, &tree_iter);
1019 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1020 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1021 g_object_set(header_view, "can-focus", FALSE, NULL);
1022 gtk_tree_selection_unselect_all (sel);
1023 gtk_tree_selection_select_iter(sel, &tree_iter);
1024 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1025 g_object_set(header_view, "can-focus", TRUE, NULL);
1026 gtk_tree_row_reference_free (priv->autoselect_reference);
1027 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1029 gtk_tree_path_free (current_path);
1030 gtk_tree_path_free (last_path);
1040 modest_header_view_get_folder (ModestHeaderView *self)
1042 ModestHeaderViewPrivate *priv;
1044 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1046 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1049 g_object_ref (priv->folder);
1051 return priv->folder;
1055 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1061 ModestHeaderView *self;
1062 ModestHeaderViewPrivate *priv;
1064 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1066 self = MODEST_HEADER_VIEW (user_data);
1067 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1069 if (cancelled || err)
1072 /* Add IDLE observer (monitor) and another folder observer for
1073 new messages (self) */
1074 g_mutex_lock (priv->observers_lock);
1075 if (priv->monitor) {
1076 tny_folder_monitor_stop (priv->monitor);
1077 g_object_unref (G_OBJECT (priv->monitor));
1079 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1080 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1081 tny_folder_monitor_start (priv->monitor);
1082 g_mutex_unlock (priv->observers_lock);
1086 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1090 ModestHeaderViewPrivate *priv;
1091 GList *cols, *cursor;
1092 GtkTreeModel *filter_model, *sortable;
1094 GtkSortType sort_type;
1096 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1098 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1100 /* Start the monitor in the callback of the
1101 tny_gtk_header_list_model_set_folder call. It's crucial to
1102 do it there and not just after the call because we want the
1103 monitor to observe only the headers returned by the
1104 tny_folder_get_headers_async call that it's inside the
1105 tny_gtk_header_list_model_set_folder call. This way the
1106 monitor infrastructure could successfully cope with
1107 duplicates. For example if a tny_folder_add_msg_async is
1108 happening while tny_gtk_header_list_model_set_folder is
1109 invoked, then the first call could add a header that will
1110 be added again by tny_gtk_header_list_model_set_folder, so
1111 we'd end up with duplicate headers. sergio */
1112 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1114 set_folder_intern_get_headers_async_cb,
1117 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1118 g_object_unref (G_OBJECT (headers));
1120 /* Init filter_row function to examine empty status */
1121 priv->status = HEADER_VIEW_INIT;
1123 /* Create a tree model filter to hide and show rows for cut operations */
1124 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1125 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1129 g_object_unref (G_OBJECT (sortable));
1131 /* install our special sorting functions */
1132 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1134 /* Restore sort column id */
1136 type = modest_tny_folder_guess_folder_type (folder);
1137 if (type == TNY_FOLDER_TYPE_INVALID)
1138 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1140 sort_colid = modest_header_view_get_sort_column_id (self, type);
1141 sort_type = modest_header_view_get_sort_type (self, type);
1142 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1145 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1146 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1147 (GtkTreeIterCompareFunc) cmp_rows,
1149 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1150 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1151 (GtkTreeIterCompareFunc) cmp_subject_rows,
1156 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1157 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1158 tny_folder_get_id(folder));
1159 g_object_unref (G_OBJECT (filter_model));
1166 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1168 GtkSortType sort_type)
1170 ModestHeaderViewPrivate *priv = NULL;
1171 GtkTreeModel *tree_filter, *sortable = NULL;
1174 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1175 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1177 /* Get model and private data */
1178 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1179 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1180 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1181 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1183 /* Sort tree model */
1184 type = modest_tny_folder_guess_folder_type (priv->folder);
1185 if (type == TNY_FOLDER_TYPE_INVALID)
1186 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1188 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1191 /* Store new sort parameters */
1192 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1197 modest_header_view_set_sort_params (ModestHeaderView *self,
1199 GtkSortType sort_type,
1202 ModestHeaderViewPrivate *priv;
1203 ModestHeaderViewStyle style;
1205 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1206 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1207 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1209 style = modest_header_view_get_style (self);
1210 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1212 priv->sort_colid[style][type] = sort_colid;
1213 priv->sort_type[style][type] = sort_type;
1217 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1220 ModestHeaderViewPrivate *priv;
1221 ModestHeaderViewStyle style;
1223 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1224 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1226 style = modest_header_view_get_style (self);
1227 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1229 return priv->sort_colid[style][type];
1233 modest_header_view_get_sort_type (ModestHeaderView *self,
1236 ModestHeaderViewPrivate *priv;
1237 ModestHeaderViewStyle style;
1239 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1240 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1242 style = modest_header_view_get_style (self);
1243 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1245 return priv->sort_type[style][type];
1249 ModestHeaderView *header_view;
1250 RefreshAsyncUserCallback cb;
1255 folder_refreshed_cb (ModestMailOperation *mail_op,
1259 ModestHeaderViewPrivate *priv;
1260 SetFolderHelper *info;
1262 info = (SetFolderHelper*) user_data;
1264 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1268 info->cb (mail_op, folder, info->user_data);
1270 /* Start the folder count changes observer. We do not need it
1271 before the refresh. Note that the monitor could still be
1272 called for this refresh but now we know that the callback
1273 was previously called */
1274 g_mutex_lock (priv->observers_lock);
1275 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1276 g_mutex_unlock (priv->observers_lock);
1278 /* Notify the observers that the update is over */
1279 g_signal_emit (G_OBJECT (info->header_view),
1280 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1282 /* Allow filtering notifications from now on if the current
1283 folder is still the same (if not then the user has selected
1284 another one to refresh, we should wait until that refresh
1286 if (priv->folder == folder)
1287 priv->notify_status = TRUE;
1290 g_object_unref (info->header_view);
1295 refresh_folder_error_handler (ModestMailOperation *mail_op,
1298 const GError *error = modest_mail_operation_get_error (mail_op);
1300 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1301 error->code == TNY_IO_ERROR_WRITE ||
1302 error->code == TNY_IO_ERROR_READ) {
1303 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1304 /* If the mail op has been cancelled then it's not an error: don't show any message */
1305 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1306 modest_platform_information_banner (NULL, NULL,
1308 "cerm_device_memory_full"));
1314 modest_header_view_set_folder (ModestHeaderView *self,
1317 ModestWindow *progress_window,
1318 RefreshAsyncUserCallback callback,
1321 ModestHeaderViewPrivate *priv;
1323 g_return_if_fail (self);
1325 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1328 if (priv->status_timeout) {
1329 g_source_remove (priv->status_timeout);
1330 priv->status_timeout = 0;
1333 g_mutex_lock (priv->observers_lock);
1334 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1335 g_object_unref (priv->folder);
1336 priv->folder = NULL;
1337 g_mutex_unlock (priv->observers_lock);
1341 GtkTreeSelection *selection;
1342 SetFolderHelper *info;
1343 ModestMailOperation *mail_op = NULL;
1345 /* Set folder in the model */
1346 modest_header_view_set_folder_intern (self, folder);
1348 /* Pick my reference. Nothing to do with the mail operation */
1349 priv->folder = g_object_ref (folder);
1351 /* Do not notify about filterings until the refresh finishes */
1352 priv->notify_status = FALSE;
1354 /* Clear the selection if exists */
1355 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1356 gtk_tree_selection_unselect_all(selection);
1357 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1359 /* Notify the observers that the update begins */
1360 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1363 /* create the helper */
1364 info = g_malloc0 (sizeof (SetFolderHelper));
1365 info->header_view = g_object_ref (self);
1366 info->cb = callback;
1367 info->user_data = user_data;
1369 /* Create the mail operation (source will be the parent widget) */
1370 if (progress_window)
1371 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1372 refresh_folder_error_handler,
1375 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1378 /* Refresh the folder asynchronously */
1379 modest_mail_operation_refresh_folder (mail_op,
1381 folder_refreshed_cb,
1384 folder_refreshed_cb (mail_op, folder, info);
1387 g_object_unref (mail_op);
1389 g_mutex_lock (priv->observers_lock);
1391 if (priv->monitor) {
1392 tny_folder_monitor_stop (priv->monitor);
1393 g_object_unref (G_OBJECT (priv->monitor));
1394 priv->monitor = NULL;
1397 if (priv->autoselect_reference) {
1398 gtk_tree_row_reference_free (priv->autoselect_reference);
1399 priv->autoselect_reference = NULL;
1402 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1404 modest_header_view_notify_observers(self, NULL, NULL);
1406 g_mutex_unlock (priv->observers_lock);
1408 /* Notify the observers that the update is over */
1409 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1415 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1416 GtkTreeViewColumn *column, gpointer userdata)
1418 ModestHeaderView *self = NULL;
1419 ModestHeaderViewPrivate *priv = NULL;
1421 GtkTreeModel *model = NULL;
1422 TnyHeader *header = NULL;
1423 TnyHeaderFlags flags;
1425 self = MODEST_HEADER_VIEW (treeview);
1426 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1428 model = gtk_tree_view_get_model (treeview);
1429 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1432 /* get the first selected item */
1433 gtk_tree_model_get (model, &iter,
1434 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1435 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1438 /* Dont open DELETED messages */
1439 if (flags & TNY_HEADER_FLAG_DELETED) {
1442 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1443 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1444 modest_platform_information_banner (NULL, NULL, msg);
1450 g_signal_emit (G_OBJECT(self),
1451 signals[HEADER_ACTIVATED_SIGNAL],
1457 g_object_unref (G_OBJECT (header));
1462 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1464 GtkTreeModel *model;
1465 TnyHeader *header = NULL;
1466 GtkTreePath *path = NULL;
1468 ModestHeaderView *self;
1469 ModestHeaderViewPrivate *priv;
1470 GList *selected = NULL;
1472 g_return_if_fail (sel);
1473 g_return_if_fail (user_data);
1475 self = MODEST_HEADER_VIEW (user_data);
1476 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1478 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1479 if (selected != NULL)
1480 path = (GtkTreePath *) selected->data;
1481 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1482 return; /* msg was _un_selected */
1484 gtk_tree_model_get (model, &iter,
1485 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1489 g_signal_emit (G_OBJECT(self),
1490 signals[HEADER_SELECTED_SIGNAL],
1493 g_object_unref (G_OBJECT (header));
1495 /* free all items in 'selected' */
1496 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1497 g_list_free (selected);
1501 /* PROTECTED method. It's useful when we want to force a given
1502 selection to reload a msg. For example if we have selected a header
1503 in offline mode, when Modest become online, we want to reload the
1504 message automatically without an user click over the header */
1506 _modest_header_view_change_selection (GtkTreeSelection *selection,
1509 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1510 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1512 on_selection_changed (selection, user_data);
1516 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1523 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1527 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1531 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1539 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1546 /* static int counter = 0; */
1548 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1549 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1550 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1554 case TNY_HEADER_FLAG_ATTACHMENTS:
1556 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
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_FLAGS_COLUMN, &val2,
1559 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1561 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1562 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1564 return cmp ? cmp : t1 - t2;
1566 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1567 TnyHeader *header1 = NULL, *header2 = NULL;
1569 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1570 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1571 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1572 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1574 /* This is for making priority values respect the intuitive sort relationship
1575 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1577 if (header1 && header2) {
1578 cmp = compare_priorities (tny_header_get_priority (header1),
1579 tny_header_get_priority (header2));
1580 g_object_unref (header1);
1581 g_object_unref (header2);
1583 return cmp ? cmp : t1 - t2;
1589 return &iter1 - &iter2; /* oughhhh */
1594 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1600 /* static int counter = 0; */
1602 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1604 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1605 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1606 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1607 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1609 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1610 val2 + modest_text_utils_get_subject_prefix_len(val2),
1617 /* Drag and drop stuff */
1619 drag_data_get_cb (GtkWidget *widget,
1620 GdkDragContext *context,
1621 GtkSelectionData *selection_data,
1626 ModestHeaderView *self = NULL;
1627 ModestHeaderViewPrivate *priv = NULL;
1629 self = MODEST_HEADER_VIEW (widget);
1630 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1632 /* Set the data. Do not use the current selection because it
1633 could be different than the selection at the beginning of
1635 modest_dnd_selection_data_set_paths (selection_data,
1636 priv->drag_begin_cached_selected_rows);
1640 * We're caching the selected rows at the beginning because the
1641 * selection could change between drag-begin and drag-data-get, for
1642 * example if we have a set of rows already selected, and then we
1643 * click in one of them (without SHIFT key pressed) and begin a drag,
1644 * the selection at that moment contains all the selected lines, but
1645 * after dropping the selection, the release event provokes that only
1646 * the row used to begin the drag is selected, so at the end the
1647 * drag&drop affects only one rows instead of all the selected ones.
1651 drag_begin_cb (GtkWidget *widget,
1652 GdkDragContext *context,
1655 ModestHeaderView *self = NULL;
1656 ModestHeaderViewPrivate *priv = NULL;
1657 GtkTreeSelection *selection;
1659 self = MODEST_HEADER_VIEW (widget);
1660 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1662 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1663 priv->drag_begin_cached_selected_rows =
1664 gtk_tree_selection_get_selected_rows (selection, NULL);
1668 * We use the drag-end signal to clear the cached selection, we use
1669 * this because this allways happens, whether or not the d&d was a
1673 drag_end_cb (GtkWidget *widget,
1677 ModestHeaderView *self = NULL;
1678 ModestHeaderViewPrivate *priv = NULL;
1680 self = MODEST_HEADER_VIEW (widget);
1681 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1683 /* Free cached data */
1684 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1685 g_list_free (priv->drag_begin_cached_selected_rows);
1686 priv->drag_begin_cached_selected_rows = NULL;
1689 /* Header view drag types */
1690 const GtkTargetEntry header_view_drag_types[] = {
1691 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1695 enable_drag_and_drop (GtkWidget *self)
1697 #ifdef MODEST_TOOLKIT_HILDON2
1700 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1701 header_view_drag_types,
1702 G_N_ELEMENTS (header_view_drag_types),
1703 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1707 disable_drag_and_drop (GtkWidget *self)
1709 #ifdef MODEST_TOOLKIT_HILDON2
1712 gtk_drag_source_unset (self);
1716 setup_drag_and_drop (GtkWidget *self)
1718 #ifdef MODEST_TOOLKIT_HILDON2
1721 enable_drag_and_drop(self);
1722 g_signal_connect(G_OBJECT (self), "drag_data_get",
1723 G_CALLBACK(drag_data_get_cb), NULL);
1725 g_signal_connect(G_OBJECT (self), "drag_begin",
1726 G_CALLBACK(drag_begin_cb), NULL);
1728 g_signal_connect(G_OBJECT (self), "drag_end",
1729 G_CALLBACK(drag_end_cb), NULL);
1732 static GtkTreePath *
1733 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1735 GtkTreePath *path = NULL;
1736 GtkTreeSelection *sel = NULL;
1739 sel = gtk_tree_view_get_selection(self);
1740 rows = gtk_tree_selection_get_selected_rows (sel, model);
1742 if ((rows == NULL) || (g_list_length(rows) != 1))
1745 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1750 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1756 #ifndef MODEST_TOOLKIT_HILDON2
1758 * This function moves the tree view scroll to the current selected
1759 * row when the widget grabs the focus
1762 on_focus_in (GtkWidget *self,
1763 GdkEventFocus *event,
1766 GtkTreeSelection *selection;
1767 GtkTreeModel *model;
1768 GList *selected = NULL;
1769 GtkTreePath *selected_path = NULL;
1771 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1775 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1776 /* If none selected yet, pick the first one */
1777 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1781 /* Return if the model is empty */
1782 if (!gtk_tree_model_get_iter_first (model, &iter))
1785 path = gtk_tree_model_get_path (model, &iter);
1786 gtk_tree_selection_select_path (selection, path);
1787 gtk_tree_path_free (path);
1790 /* Need to get the all the rows because is selection multiple */
1791 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1792 if (selected == NULL) return FALSE;
1793 selected_path = (GtkTreePath *) selected->data;
1796 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1797 g_list_free (selected);
1803 on_focus_out (GtkWidget *self,
1804 GdkEventFocus *event,
1808 if (!gtk_widget_is_focus (self)) {
1809 GtkTreeSelection *selection = NULL;
1810 GList *selected_rows = NULL;
1811 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1812 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1813 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1814 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1815 gtk_tree_selection_unselect_all (selection);
1816 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1817 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1818 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1819 g_list_free (selected_rows);
1827 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1829 enable_drag_and_drop(self);
1834 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1836 GtkTreeSelection *selection = NULL;
1837 GtkTreePath *path = NULL;
1838 gboolean already_selected = FALSE, already_opened = FALSE;
1839 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1841 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1843 GtkTreeModel *model;
1845 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1846 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1848 /* Get header from model */
1849 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1850 if (gtk_tree_model_get_iter (model, &iter, path)) {
1851 GValue value = {0,};
1854 gtk_tree_model_get_value (model, &iter,
1855 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1857 header = (TnyHeader *) g_value_get_object (&value);
1858 if (TNY_IS_HEADER (header)) {
1859 status = modest_tny_all_send_queues_get_msg_status (header);
1860 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1863 g_value_unset (&value);
1867 /* Enable drag and drop only if the user clicks on a row that
1868 it's already selected. If not, let him select items using
1869 the pointer. If the message is in an OUTBOX and in sending
1870 status disable drag and drop as well */
1871 if (!already_selected ||
1872 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1874 disable_drag_and_drop(self);
1877 gtk_tree_path_free(path);
1879 /* If it's already opened then do not let the button-press
1880 event go on because it'll perform a message open because
1881 we're clicking on to an already selected header */
1886 folder_monitor_update (TnyFolderObserver *self,
1887 TnyFolderChange *change)
1889 ModestHeaderViewPrivate *priv = NULL;
1890 TnyFolderChangeChanged changed;
1891 TnyFolder *folder = NULL;
1893 changed = tny_folder_change_get_changed (change);
1895 /* Do not notify the observers if the folder of the header
1896 view has changed before this call to the observer
1898 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1899 folder = tny_folder_change_get_folder (change);
1900 if (folder != priv->folder)
1903 MODEST_DEBUG_BLOCK (
1904 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1905 g_print ("ADDED %d/%d (r/t) \n",
1906 tny_folder_change_get_new_unread_count (change),
1907 tny_folder_change_get_new_all_count (change));
1908 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1909 g_print ("ALL COUNT %d\n",
1910 tny_folder_change_get_new_all_count (change));
1911 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1912 g_print ("UNREAD COUNT %d\n",
1913 tny_folder_change_get_new_unread_count (change));
1914 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1915 g_print ("EXPUNGED %d/%d (r/t) \n",
1916 tny_folder_change_get_new_unread_count (change),
1917 tny_folder_change_get_new_all_count (change));
1918 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1919 g_print ("FOLDER RENAME\n");
1920 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1921 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1922 tny_folder_change_get_new_unread_count (change),
1923 tny_folder_change_get_new_all_count (change));
1924 g_print ("---------------------------------------------------\n");
1927 /* Check folder count */
1928 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1929 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1931 g_mutex_lock (priv->observers_lock);
1933 /* Emit signal to evaluate how headers changes affects
1934 to the window view */
1935 g_signal_emit (G_OBJECT(self),
1936 signals[MSG_COUNT_CHANGED_SIGNAL],
1939 /* Added or removed headers, so data stored on cliboard are invalid */
1940 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1941 modest_email_clipboard_clear (priv->clipboard);
1943 g_mutex_unlock (priv->observers_lock);
1949 g_object_unref (folder);
1953 modest_header_view_is_empty (ModestHeaderView *self)
1955 ModestHeaderViewPrivate *priv;
1957 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1959 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1961 return priv->status == HEADER_VIEW_EMPTY;
1965 modest_header_view_clear (ModestHeaderView *self)
1967 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1969 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
1973 modest_header_view_copy_selection (ModestHeaderView *header_view)
1975 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1977 /* Copy selection */
1978 _clipboard_set_selected_data (header_view, FALSE);
1982 modest_header_view_cut_selection (ModestHeaderView *header_view)
1984 ModestHeaderViewPrivate *priv = NULL;
1985 const gchar **hidding = NULL;
1986 guint i, n_selected;
1988 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1990 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1992 /* Copy selection */
1993 _clipboard_set_selected_data (header_view, TRUE);
1995 /* Get hidding ids */
1996 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1998 /* Clear hidding array created by previous cut operation */
1999 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2001 /* Copy hidding array */
2002 priv->n_selected = n_selected;
2003 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2004 for (i=0; i < n_selected; i++)
2005 priv->hidding_ids[i] = g_strdup(hidding[i]);
2007 /* Hide cut headers */
2008 modest_header_view_refilter (header_view);
2015 _clipboard_set_selected_data (ModestHeaderView *header_view,
2018 ModestHeaderViewPrivate *priv = NULL;
2019 TnyList *headers = NULL;
2021 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2022 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2024 /* Set selected data on clipboard */
2025 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2026 headers = modest_header_view_get_selected_headers (header_view);
2027 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2030 g_object_unref (headers);
2034 ModestHeaderView *self;
2039 notify_filter_change (gpointer data)
2041 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2043 g_signal_emit (info->self,
2044 signals[MSG_COUNT_CHANGED_SIGNAL],
2045 0, info->folder, NULL);
2051 notify_filter_change_destroy (gpointer data)
2053 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2054 ModestHeaderViewPrivate *priv;
2056 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2057 priv->status_timeout = 0;
2059 g_object_unref (info->self);
2060 g_object_unref (info->folder);
2061 g_slice_free (NotifyFilterInfo, info);
2065 filter_row (GtkTreeModel *model,
2069 ModestHeaderViewPrivate *priv = NULL;
2070 TnyHeaderFlags flags;
2071 TnyHeader *header = NULL;
2074 gboolean visible = TRUE;
2075 gboolean found = FALSE;
2076 GValue value = {0,};
2077 HeaderViewStatus old_status;
2079 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2080 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2082 /* Get header from model */
2083 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2084 flags = (TnyHeaderFlags) g_value_get_int (&value);
2085 g_value_unset (&value);
2086 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2087 header = (TnyHeader *) g_value_get_object (&value);
2088 g_value_unset (&value);
2090 /* Hide deleted and mark as deleted heders */
2091 if (flags & TNY_HEADER_FLAG_DELETED ||
2092 flags & TNY_HEADER_FLAG_EXPUNGED) {
2097 /* If no data on clipboard, return always TRUE */
2098 if (modest_email_clipboard_cleared(priv->clipboard)) {
2103 /* Get message id from header (ensure is a valid id) */
2110 if (priv->hidding_ids != NULL) {
2111 id = tny_header_dup_message_id (header);
2112 for (i=0; i < priv->n_selected && !found; i++)
2113 if (priv->hidding_ids[i] != NULL && id != NULL)
2114 found = (!strcmp (priv->hidding_ids[i], id));
2121 old_status = priv->status;
2122 priv->status = ((gboolean) priv->status) && !visible;
2123 if ((priv->notify_status) && (priv->status != old_status)) {
2124 NotifyFilterInfo *info;
2126 if (priv->status_timeout)
2127 g_source_remove (priv->status_timeout);
2129 info = g_slice_new0 (NotifyFilterInfo);
2130 info->self = g_object_ref (G_OBJECT (user_data));
2131 info->folder = tny_header_get_folder (header);
2132 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2133 notify_filter_change,
2135 notify_filter_change_destroy);
2142 _clear_hidding_filter (ModestHeaderView *header_view)
2144 ModestHeaderViewPrivate *priv = NULL;
2147 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2148 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2150 if (priv->hidding_ids != NULL) {
2151 for (i=0; i < priv->n_selected; i++)
2152 g_free (priv->hidding_ids[i]);
2153 g_free(priv->hidding_ids);
2158 modest_header_view_refilter (ModestHeaderView *header_view)
2160 GtkTreeModel *model = NULL;
2161 ModestHeaderViewPrivate *priv = NULL;
2163 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2164 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2166 /* Hide cut headers */
2167 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2168 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2169 priv->status = HEADER_VIEW_INIT;
2170 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2175 * Called when an account is removed. If I'm showing a folder of the
2176 * account that has been removed then clear the view
2179 on_account_removed (TnyAccountStore *self,
2180 TnyAccount *account,
2183 ModestHeaderViewPrivate *priv = NULL;
2185 /* Ignore changes in transport accounts */
2186 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2189 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2192 TnyAccount *my_account;
2194 my_account = tny_folder_get_account (priv->folder);
2195 if (my_account == account)
2196 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2197 g_object_unref (my_account);
2202 modest_header_view_add_observer(ModestHeaderView *header_view,
2203 ModestHeaderViewObserver *observer)
2205 ModestHeaderViewPrivate *priv;
2207 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2208 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2210 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2212 g_mutex_lock(priv->observer_list_lock);
2213 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2214 g_mutex_unlock(priv->observer_list_lock);
2218 modest_header_view_remove_observer(ModestHeaderView *header_view,
2219 ModestHeaderViewObserver *observer)
2221 ModestHeaderViewPrivate *priv;
2223 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2224 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2226 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2228 g_mutex_lock(priv->observer_list_lock);
2229 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2230 g_mutex_unlock(priv->observer_list_lock);
2234 modest_header_view_notify_observers(ModestHeaderView *header_view,
2235 GtkTreeModel *model,
2236 const gchar *tny_folder_id)
2238 ModestHeaderViewPrivate *priv = NULL;
2240 ModestHeaderViewObserver *observer;
2243 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2245 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2247 g_mutex_lock(priv->observer_list_lock);
2248 iter = priv->observer_list;
2249 while(iter != NULL){
2250 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2251 modest_header_view_observer_update(observer, model,
2253 iter = g_slist_next(iter);
2255 g_mutex_unlock(priv->observer_list_lock);
2259 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2261 ModestHeaderViewPrivate *priv = NULL;
2263 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2264 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);