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 static gboolean on_focus_in (GtkWidget *sef,
101 static gboolean on_focus_out (GtkWidget *self,
102 GdkEventFocus *event,
105 static void folder_monitor_update (TnyFolderObserver *self,
106 TnyFolderChange *change);
108 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
110 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
112 static void _clear_hidding_filter (ModestHeaderView *header_view);
114 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
116 const gchar *tny_folder_id);
118 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
119 GdkEventExpose *event,
123 HEADER_VIEW_NON_EMPTY,
128 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
129 struct _ModestHeaderViewPrivate {
131 ModestHeaderViewStyle style;
133 TnyFolderMonitor *monitor;
134 GMutex *observers_lock;
136 /*header-view-observer observer*/
137 GMutex *observer_list_lock;
138 GSList *observer_list;
140 /* not unref this object, its a singlenton */
141 ModestEmailClipboard *clipboard;
143 /* Filter tree model */
146 GtkTreeRowReference *autoselect_reference;
148 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
149 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
151 gulong selection_changed_handler;
152 gulong acc_removed_handler;
154 GList *drag_begin_cached_selected_rows;
156 HeaderViewStatus status;
157 guint status_timeout;
158 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
160 ModestDatetimeFormatter *datetime_formatter;
163 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
164 struct _HeadersCountChangedHelper {
165 ModestHeaderView *self;
166 TnyFolderChange *change;
170 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
171 MODEST_TYPE_HEADER_VIEW, \
172 ModestHeaderViewPrivate))
176 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
179 HEADER_SELECTED_SIGNAL,
180 HEADER_ACTIVATED_SIGNAL,
181 ITEM_NOT_FOUND_SIGNAL,
182 MSG_COUNT_CHANGED_SIGNAL,
183 UPDATING_MSG_LIST_SIGNAL,
188 static GObjectClass *parent_class = NULL;
190 /* uncomment the following if you have defined any signals */
191 static guint signals[LAST_SIGNAL] = {0};
194 modest_header_view_get_type (void)
196 static GType my_type = 0;
198 static const GTypeInfo my_info = {
199 sizeof(ModestHeaderViewClass),
200 NULL, /* base init */
201 NULL, /* base finalize */
202 (GClassInitFunc) modest_header_view_class_init,
203 NULL, /* class finalize */
204 NULL, /* class data */
205 sizeof(ModestHeaderView),
207 (GInstanceInitFunc) modest_header_view_init,
211 static const GInterfaceInfo tny_folder_observer_info =
213 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
214 NULL, /* interface_finalize */
215 NULL /* interface_data */
217 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
221 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
222 &tny_folder_observer_info);
230 modest_header_view_class_init (ModestHeaderViewClass *klass)
232 GObjectClass *gobject_class;
233 gobject_class = (GObjectClass*) klass;
235 parent_class = g_type_class_peek_parent (klass);
236 gobject_class->finalize = modest_header_view_finalize;
237 gobject_class->dispose = modest_header_view_dispose;
239 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
241 signals[HEADER_SELECTED_SIGNAL] =
242 g_signal_new ("header_selected",
243 G_TYPE_FROM_CLASS (gobject_class),
245 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
247 g_cclosure_marshal_VOID__POINTER,
248 G_TYPE_NONE, 1, G_TYPE_POINTER);
250 signals[HEADER_ACTIVATED_SIGNAL] =
251 g_signal_new ("header_activated",
252 G_TYPE_FROM_CLASS (gobject_class),
254 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
256 gtk_marshal_VOID__POINTER_POINTER,
257 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
260 signals[ITEM_NOT_FOUND_SIGNAL] =
261 g_signal_new ("item_not_found",
262 G_TYPE_FROM_CLASS (gobject_class),
264 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
266 g_cclosure_marshal_VOID__INT,
267 G_TYPE_NONE, 1, G_TYPE_INT);
269 signals[MSG_COUNT_CHANGED_SIGNAL] =
270 g_signal_new ("msg_count_changed",
271 G_TYPE_FROM_CLASS (gobject_class),
273 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
275 modest_marshal_VOID__POINTER_POINTER,
276 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
278 signals[UPDATING_MSG_LIST_SIGNAL] =
279 g_signal_new ("updating-msg-list",
280 G_TYPE_FROM_CLASS (gobject_class),
282 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
284 g_cclosure_marshal_VOID__BOOLEAN,
285 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
287 #ifdef MODEST_TOOLKIT_HILDON2
288 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
294 tny_folder_observer_init (TnyFolderObserverIface *klass)
296 klass->update = folder_monitor_update;
299 static GtkTreeViewColumn*
300 get_new_column (const gchar *name, GtkCellRenderer *renderer,
301 gboolean resizable, gint sort_col_id, gboolean show_as_text,
302 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
304 GtkTreeViewColumn *column;
306 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
307 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
309 gtk_tree_view_column_set_resizable (column, resizable);
311 gtk_tree_view_column_set_expand (column, TRUE);
314 gtk_tree_view_column_add_attribute (column, renderer, "text",
316 if (sort_col_id >= 0)
317 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
319 gtk_tree_view_column_set_sort_indicator (column, FALSE);
320 gtk_tree_view_column_set_reorderable (column, TRUE);
323 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
330 remove_all_columns (ModestHeaderView *obj)
332 GList *columns, *cursor;
334 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
336 for (cursor = columns; cursor; cursor = cursor->next)
337 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
338 GTK_TREE_VIEW_COLUMN(cursor->data));
339 g_list_free (columns);
343 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
345 GtkTreeModel *tree_filter, *sortable;
346 GtkTreeViewColumn *column=NULL;
347 GtkTreeSelection *selection = NULL;
348 GtkCellRenderer *renderer_header,
349 *renderer_attach, *renderer_compact_date_or_status;
350 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
351 *renderer_subject, *renderer_subject_box, *renderer_recpt,
353 ModestHeaderViewPrivate *priv;
354 GtkTreeViewColumn *compact_column = NULL;
357 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
358 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
360 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
362 /* TODO: check whether these renderers need to be freed */
363 renderer_attach = gtk_cell_renderer_pixbuf_new ();
364 renderer_priority = gtk_cell_renderer_pixbuf_new ();
365 renderer_header = gtk_cell_renderer_text_new ();
367 renderer_compact_header = modest_vbox_cell_renderer_new ();
368 renderer_recpt_box = modest_hbox_cell_renderer_new ();
369 renderer_subject_box = modest_hbox_cell_renderer_new ();
370 renderer_recpt = gtk_cell_renderer_text_new ();
371 renderer_subject = gtk_cell_renderer_text_new ();
372 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
374 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
375 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
376 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
377 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
378 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
379 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
380 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
381 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
382 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
383 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
384 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
385 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
386 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
387 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
389 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
390 #ifndef MODEST_TOOLKIT_GTK
391 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
392 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
394 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
395 g_object_set(G_OBJECT(renderer_header),
396 "ellipsize", PANGO_ELLIPSIZE_END,
398 g_object_set (G_OBJECT (renderer_subject),
399 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
401 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
402 g_object_set (G_OBJECT (renderer_recpt),
403 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
405 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
406 g_object_set(G_OBJECT(renderer_compact_date_or_status),
407 "xalign", 1.0, "yalign", 0.0,
409 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
410 g_object_set (G_OBJECT (renderer_priority),
411 "yalign", 1.0, NULL);
412 g_object_set (G_OBJECT (renderer_attach),
413 "yalign", 0.0, NULL);
415 #ifndef MODEST_TOOLKIT_GTK
416 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
417 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
418 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
420 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
421 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
422 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
425 remove_all_columns (self);
427 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
428 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
429 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
430 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
432 /* Add new columns */
433 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
434 ModestHeaderViewColumn col =
435 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
437 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
438 g_printerr ("modest: invalid column %d in column list\n", col);
444 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
445 column = get_new_column (_("A"), renderer_attach, FALSE,
446 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
448 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
450 gtk_tree_view_column_set_fixed_width (column, 45);
454 case MODEST_HEADER_VIEW_COLUMN_FROM:
455 column = get_new_column (_("From"), renderer_header, TRUE,
456 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
458 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
459 GINT_TO_POINTER(TRUE));
462 case MODEST_HEADER_VIEW_COLUMN_TO:
463 column = get_new_column (_("To"), renderer_header, TRUE,
464 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
466 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
467 GINT_TO_POINTER(FALSE));
470 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
471 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
472 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
474 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
475 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
476 compact_column = column;
479 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
480 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
481 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
483 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
484 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
485 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
486 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
487 compact_column = column;
491 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
492 column = get_new_column (_("Subject"), renderer_header, TRUE,
493 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
495 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
499 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
500 column = get_new_column (_("Received"), renderer_header, TRUE,
501 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
503 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
504 GINT_TO_POINTER(TRUE));
507 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
508 column = get_new_column (_("Sent"), renderer_header, TRUE,
509 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
511 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
512 GINT_TO_POINTER(FALSE));
515 case MODEST_HEADER_VIEW_COLUMN_SIZE:
516 column = get_new_column (_("Size"), renderer_header, TRUE,
517 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
519 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
522 case MODEST_HEADER_VIEW_COLUMN_STATUS:
523 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
524 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
526 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
531 g_return_val_if_reached(FALSE);
534 /* we keep the column id around */
535 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
536 GINT_TO_POINTER(col));
538 /* we need this ptr when sorting the rows */
539 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
541 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
545 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
546 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
547 (GtkTreeIterCompareFunc) cmp_rows,
548 compact_column, NULL);
549 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
550 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
551 (GtkTreeIterCompareFunc) cmp_subject_rows,
552 compact_column, NULL);
559 datetime_format_changed (ModestDatetimeFormatter *formatter,
560 ModestHeaderView *self)
562 gtk_widget_queue_draw (GTK_WIDGET (self));
566 modest_header_view_init (ModestHeaderView *obj)
568 ModestHeaderViewPrivate *priv;
571 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
575 priv->monitor = NULL;
576 priv->observers_lock = g_mutex_new ();
577 priv->autoselect_reference = NULL;
579 priv->status = HEADER_VIEW_INIT;
580 priv->status_timeout = 0;
581 priv->notify_status = TRUE;
583 priv->observer_list_lock = g_mutex_new();
584 priv->observer_list = NULL;
586 priv->clipboard = modest_runtime_get_email_clipboard ();
587 priv->hidding_ids = NULL;
588 priv->n_selected = 0;
589 priv->selection_changed_handler = 0;
590 priv->acc_removed_handler = 0;
592 /* Sort parameters */
593 for (j=0; j < 2; j++) {
594 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
595 priv->sort_colid[j][i] = -1;
596 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
600 priv->datetime_formatter = modest_datetime_formatter_new ();
601 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
602 G_CALLBACK (datetime_format_changed), (gpointer) obj);
604 setup_drag_and_drop (GTK_WIDGET(obj));
608 modest_header_view_dispose (GObject *obj)
610 ModestHeaderView *self;
611 ModestHeaderViewPrivate *priv;
612 GtkTreeSelection *sel;
614 self = MODEST_HEADER_VIEW(obj);
615 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
617 if (priv->datetime_formatter) {
618 g_object_unref (priv->datetime_formatter);
619 priv->datetime_formatter = NULL;
622 /* Free in the dispose to avoid unref cycles */
624 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
625 g_object_unref (G_OBJECT (priv->folder));
629 /* We need to do this here in the dispose because the
630 selection won't exist when finalizing */
631 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
632 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
633 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
634 priv->selection_changed_handler = 0;
637 G_OBJECT_CLASS(parent_class)->dispose (obj);
641 modest_header_view_finalize (GObject *obj)
643 ModestHeaderView *self;
644 ModestHeaderViewPrivate *priv;
646 self = MODEST_HEADER_VIEW(obj);
647 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
649 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
650 priv->acc_removed_handler)) {
651 g_signal_handler_disconnect (modest_runtime_get_account_store (),
652 priv->acc_removed_handler);
655 /* There is no need to lock because there should not be any
656 * reference to self now. */
657 g_mutex_free(priv->observer_list_lock);
658 g_slist_free(priv->observer_list);
660 g_mutex_lock (priv->observers_lock);
662 tny_folder_monitor_stop (priv->monitor);
663 g_object_unref (G_OBJECT (priv->monitor));
665 g_mutex_unlock (priv->observers_lock);
666 g_mutex_free (priv->observers_lock);
668 /* Clear hidding array created by cut operation */
669 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
671 if (priv->autoselect_reference != NULL) {
672 gtk_tree_row_reference_free (priv->autoselect_reference);
673 priv->autoselect_reference = NULL;
676 G_OBJECT_CLASS(parent_class)->finalize (obj);
681 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
684 GtkTreeSelection *sel;
685 ModestHeaderView *self;
686 ModestHeaderViewPrivate *priv;
688 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
691 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
692 self = MODEST_HEADER_VIEW(obj);
693 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
695 modest_header_view_set_style (self, style);
697 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
698 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
699 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
701 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
702 TRUE); /* alternating row colors */
704 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
705 priv->selection_changed_handler =
706 g_signal_connect_after (sel, "changed",
707 G_CALLBACK(on_selection_changed), self);
709 g_signal_connect (self, "row-activated",
710 G_CALLBACK (on_header_row_activated), NULL);
712 g_signal_connect (self, "focus-in-event",
713 G_CALLBACK(on_focus_in), NULL);
714 g_signal_connect (self, "focus-out-event",
715 G_CALLBACK(on_focus_out), NULL);
717 g_signal_connect (self, "button-press-event",
718 G_CALLBACK(on_button_press_event), NULL);
719 g_signal_connect (self, "button-release-event",
720 G_CALLBACK(on_button_release_event), NULL);
722 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
724 G_CALLBACK (on_account_removed),
727 g_signal_connect (self, "expose-event",
728 G_CALLBACK(modest_header_view_on_expose_event),
731 return GTK_WIDGET(self);
736 modest_header_view_count_selected_headers (ModestHeaderView *self)
738 GtkTreeSelection *sel;
741 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
743 /* Get selection object and check selected rows count */
744 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
745 selected_rows = gtk_tree_selection_count_selected_rows (sel);
747 return selected_rows;
751 modest_header_view_has_selected_headers (ModestHeaderView *self)
753 GtkTreeSelection *sel;
756 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
758 /* Get selection object and check selected rows count */
759 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
760 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
767 modest_header_view_get_selected_headers (ModestHeaderView *self)
769 GtkTreeSelection *sel;
770 ModestHeaderViewPrivate *priv;
771 TnyList *header_list = NULL;
773 GList *list, *tmp = NULL;
774 GtkTreeModel *tree_model = NULL;
777 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
779 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
781 /* Get selected rows */
782 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
783 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
786 header_list = tny_simple_list_new();
788 list = g_list_reverse (list);
791 /* get header from selection */
792 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
793 gtk_tree_model_get (tree_model, &iter,
794 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
796 /* Prepend to list */
797 tny_list_prepend (header_list, G_OBJECT (header));
798 g_object_unref (G_OBJECT (header));
800 tmp = g_list_next (tmp);
803 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
810 /* scroll our list view so the selected item is visible */
812 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
814 #ifdef MODEST_TOOLKIT_GTK
816 GtkTreePath *selected_path;
817 GtkTreePath *start, *end;
821 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
822 selected_path = gtk_tree_model_get_path (model, iter);
824 start = gtk_tree_path_new ();
825 end = gtk_tree_path_new ();
827 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
829 if (gtk_tree_path_compare (selected_path, start) < 0 ||
830 gtk_tree_path_compare (end, selected_path) < 0)
831 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
832 selected_path, NULL, TRUE,
835 gtk_tree_path_free (selected_path);
836 gtk_tree_path_free (start);
837 gtk_tree_path_free (end);
839 #endif /* MODEST_TOOLKIT_GTK */
844 modest_header_view_select_next (ModestHeaderView *self)
846 GtkTreeSelection *sel;
851 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
853 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
854 path = get_selected_row (GTK_TREE_VIEW(self), &model);
855 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
856 /* Unselect previous path */
857 gtk_tree_selection_unselect_path (sel, path);
859 /* Move path down and selects new one */
860 if (gtk_tree_model_iter_next (model, &iter)) {
861 gtk_tree_selection_select_iter (sel, &iter);
862 scroll_to_selected (self, &iter, FALSE);
864 gtk_tree_path_free(path);
870 modest_header_view_select_prev (ModestHeaderView *self)
872 GtkTreeSelection *sel;
877 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
879 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
880 path = get_selected_row (GTK_TREE_VIEW(self), &model);
881 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
882 /* Unselect previous path */
883 gtk_tree_selection_unselect_path (sel, path);
886 if (gtk_tree_path_prev (path)) {
887 gtk_tree_model_get_iter (model, &iter, path);
889 /* Select the new one */
890 gtk_tree_selection_select_iter (sel, &iter);
891 scroll_to_selected (self, &iter, TRUE);
894 gtk_tree_path_free (path);
899 modest_header_view_get_columns (ModestHeaderView *self)
901 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
903 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
909 modest_header_view_set_style (ModestHeaderView *self,
910 ModestHeaderViewStyle style)
912 ModestHeaderViewPrivate *priv;
913 gboolean show_col_headers = FALSE;
914 ModestHeaderViewStyle old_style;
916 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
917 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
920 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
921 if (priv->style == style)
922 return TRUE; /* nothing to do */
925 case MODEST_HEADER_VIEW_STYLE_DETAILS:
926 show_col_headers = TRUE;
928 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
931 g_return_val_if_reached (FALSE);
933 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
934 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
936 old_style = priv->style;
943 ModestHeaderViewStyle
944 modest_header_view_get_style (ModestHeaderView *self)
946 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
948 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
951 /* This is used to automatically select the first header if the user
952 * has not selected any header yet.
955 modest_header_view_on_expose_event(GtkTreeView *header_view,
956 GdkEventExpose *event,
959 GtkTreeSelection *sel;
961 GtkTreeIter tree_iter;
962 ModestHeaderViewPrivate *priv;
964 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
965 model = gtk_tree_view_get_model(header_view);
970 #ifdef MODEST_TOOLKIT_HILDON2
971 HildonUIMode ui_mode;
972 g_object_get (G_OBJECT (header_view), "hildon-ui-mode", &ui_mode, NULL);
973 if (ui_mode == HILDON_UI_MODE_NORMAL)
974 /* As in hildon 2.2 normal mode there's no selection, we just simply return */
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 RefreshAsyncUserCallback callback,
1320 ModestHeaderViewPrivate *priv;
1321 ModestWindow *main_win;
1323 g_return_if_fail (self);
1325 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1327 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1328 FALSE); /* don't create */
1330 g_warning ("%s: BUG: no main window", __FUNCTION__);
1335 if (priv->status_timeout) {
1336 g_source_remove (priv->status_timeout);
1337 priv->status_timeout = 0;
1340 g_mutex_lock (priv->observers_lock);
1341 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1342 g_object_unref (priv->folder);
1343 priv->folder = NULL;
1344 g_mutex_unlock (priv->observers_lock);
1348 GtkTreeSelection *selection;
1349 SetFolderHelper *info;
1350 ModestMailOperation *mail_op = NULL;
1352 /* Set folder in the model */
1353 modest_header_view_set_folder_intern (self, folder);
1355 /* Pick my reference. Nothing to do with the mail operation */
1356 priv->folder = g_object_ref (folder);
1358 /* Do not notify about filterings until the refresh finishes */
1359 priv->notify_status = FALSE;
1361 /* Clear the selection if exists */
1362 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1363 gtk_tree_selection_unselect_all(selection);
1364 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1366 /* Notify the observers that the update begins */
1367 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1370 /* create the helper */
1371 info = g_malloc0 (sizeof (SetFolderHelper));
1372 info->header_view = g_object_ref (self);
1373 info->cb = callback;
1374 info->user_data = user_data;
1376 /* Create the mail operation (source will be the parent widget) */
1377 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1378 refresh_folder_error_handler,
1381 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1384 /* Refresh the folder asynchronously */
1385 modest_mail_operation_refresh_folder (mail_op,
1387 folder_refreshed_cb,
1390 folder_refreshed_cb (mail_op, folder, info);
1393 g_object_unref (mail_op);
1395 g_mutex_lock (priv->observers_lock);
1397 if (priv->monitor) {
1398 tny_folder_monitor_stop (priv->monitor);
1399 g_object_unref (G_OBJECT (priv->monitor));
1400 priv->monitor = NULL;
1403 if (priv->autoselect_reference) {
1404 gtk_tree_row_reference_free (priv->autoselect_reference);
1405 priv->autoselect_reference = NULL;
1408 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1410 modest_header_view_notify_observers(self, NULL, NULL);
1412 g_mutex_unlock (priv->observers_lock);
1414 /* Notify the observers that the update is over */
1415 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1421 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1422 GtkTreeViewColumn *column, gpointer userdata)
1424 ModestHeaderView *self = NULL;
1425 ModestHeaderViewPrivate *priv = NULL;
1427 GtkTreeModel *model = NULL;
1428 TnyHeader *header = NULL;
1429 TnyHeaderFlags flags;
1431 self = MODEST_HEADER_VIEW (treeview);
1432 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1434 model = gtk_tree_view_get_model (treeview);
1435 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1438 /* get the first selected item */
1439 gtk_tree_model_get (model, &iter,
1440 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1441 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1444 /* Dont open DELETED messages */
1445 if (flags & TNY_HEADER_FLAG_DELETED) {
1448 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1449 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1450 modest_platform_information_banner (NULL, NULL, msg);
1456 g_signal_emit (G_OBJECT(self),
1457 signals[HEADER_ACTIVATED_SIGNAL],
1463 g_object_unref (G_OBJECT (header));
1468 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1470 GtkTreeModel *model;
1471 TnyHeader *header = NULL;
1472 GtkTreePath *path = NULL;
1474 ModestHeaderView *self;
1475 ModestHeaderViewPrivate *priv;
1476 GList *selected = NULL;
1478 g_return_if_fail (sel);
1479 g_return_if_fail (user_data);
1481 self = MODEST_HEADER_VIEW (user_data);
1482 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1484 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1485 if (selected != NULL)
1486 path = (GtkTreePath *) selected->data;
1487 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1488 return; /* msg was _un_selected */
1490 gtk_tree_model_get (model, &iter,
1491 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1495 g_signal_emit (G_OBJECT(self),
1496 signals[HEADER_SELECTED_SIGNAL],
1499 g_object_unref (G_OBJECT (header));
1501 /* free all items in 'selected' */
1502 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1503 g_list_free (selected);
1507 /* PROTECTED method. It's useful when we want to force a given
1508 selection to reload a msg. For example if we have selected a header
1509 in offline mode, when Modest become online, we want to reload the
1510 message automatically without an user click over the header */
1512 _modest_header_view_change_selection (GtkTreeSelection *selection,
1515 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1516 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1518 on_selection_changed (selection, user_data);
1522 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1529 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1533 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1537 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1545 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1552 /* static int counter = 0; */
1554 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1555 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1556 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1560 case TNY_HEADER_FLAG_ATTACHMENTS:
1562 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1563 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1564 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1565 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1567 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1568 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1570 return cmp ? cmp : t1 - t2;
1572 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1573 TnyHeader *header1 = NULL, *header2 = NULL;
1575 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1576 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1577 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1578 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1580 /* This is for making priority values respect the intuitive sort relationship
1581 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1583 if (header1 && header2) {
1584 cmp = compare_priorities (tny_header_get_priority (header1),
1585 tny_header_get_priority (header2));
1586 g_object_unref (header1);
1587 g_object_unref (header2);
1589 return cmp ? cmp : t1 - t2;
1595 return &iter1 - &iter2; /* oughhhh */
1600 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1606 /* static int counter = 0; */
1608 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1610 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1611 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1612 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1613 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1615 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1616 val2 + modest_text_utils_get_subject_prefix_len(val2),
1623 /* Drag and drop stuff */
1625 drag_data_get_cb (GtkWidget *widget,
1626 GdkDragContext *context,
1627 GtkSelectionData *selection_data,
1632 ModestHeaderView *self = NULL;
1633 ModestHeaderViewPrivate *priv = NULL;
1635 self = MODEST_HEADER_VIEW (widget);
1636 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1638 /* Set the data. Do not use the current selection because it
1639 could be different than the selection at the beginning of
1641 modest_dnd_selection_data_set_paths (selection_data,
1642 priv->drag_begin_cached_selected_rows);
1646 * We're caching the selected rows at the beginning because the
1647 * selection could change between drag-begin and drag-data-get, for
1648 * example if we have a set of rows already selected, and then we
1649 * click in one of them (without SHIFT key pressed) and begin a drag,
1650 * the selection at that moment contains all the selected lines, but
1651 * after dropping the selection, the release event provokes that only
1652 * the row used to begin the drag is selected, so at the end the
1653 * drag&drop affects only one rows instead of all the selected ones.
1657 drag_begin_cb (GtkWidget *widget,
1658 GdkDragContext *context,
1661 ModestHeaderView *self = NULL;
1662 ModestHeaderViewPrivate *priv = NULL;
1663 GtkTreeSelection *selection;
1665 self = MODEST_HEADER_VIEW (widget);
1666 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1668 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1669 priv->drag_begin_cached_selected_rows =
1670 gtk_tree_selection_get_selected_rows (selection, NULL);
1674 * We use the drag-end signal to clear the cached selection, we use
1675 * this because this allways happens, whether or not the d&d was a
1679 drag_end_cb (GtkWidget *widget,
1683 ModestHeaderView *self = NULL;
1684 ModestHeaderViewPrivate *priv = NULL;
1686 self = MODEST_HEADER_VIEW (widget);
1687 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1689 /* Free cached data */
1690 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1691 g_list_free (priv->drag_begin_cached_selected_rows);
1692 priv->drag_begin_cached_selected_rows = NULL;
1695 /* Header view drag types */
1696 const GtkTargetEntry header_view_drag_types[] = {
1697 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1701 enable_drag_and_drop (GtkWidget *self)
1703 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1704 header_view_drag_types,
1705 G_N_ELEMENTS (header_view_drag_types),
1706 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1710 disable_drag_and_drop (GtkWidget *self)
1712 gtk_drag_source_unset (self);
1716 setup_drag_and_drop (GtkWidget *self)
1718 enable_drag_and_drop(self);
1719 g_signal_connect(G_OBJECT (self), "drag_data_get",
1720 G_CALLBACK(drag_data_get_cb), NULL);
1722 g_signal_connect(G_OBJECT (self), "drag_begin",
1723 G_CALLBACK(drag_begin_cb), NULL);
1725 g_signal_connect(G_OBJECT (self), "drag_end",
1726 G_CALLBACK(drag_end_cb), NULL);
1729 static GtkTreePath *
1730 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1732 GtkTreePath *path = NULL;
1733 GtkTreeSelection *sel = NULL;
1736 sel = gtk_tree_view_get_selection(self);
1737 rows = gtk_tree_selection_get_selected_rows (sel, model);
1739 if ((rows == NULL) || (g_list_length(rows) != 1))
1742 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1747 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1754 * This function moves the tree view scroll to the current selected
1755 * row when the widget grabs the focus
1758 on_focus_in (GtkWidget *self,
1759 GdkEventFocus *event,
1762 GtkTreeSelection *selection;
1763 GtkTreeModel *model;
1764 GList *selected = NULL;
1765 GtkTreePath *selected_path = NULL;
1767 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1771 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1772 /* If none selected yet, pick the first one */
1773 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1777 /* Return if the model is empty */
1778 if (!gtk_tree_model_get_iter_first (model, &iter))
1781 path = gtk_tree_model_get_path (model, &iter);
1782 gtk_tree_selection_select_path (selection, path);
1783 gtk_tree_path_free (path);
1786 /* Need to get the all the rows because is selection multiple */
1787 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1788 if (selected == NULL) return FALSE;
1789 selected_path = (GtkTreePath *) selected->data;
1792 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1793 g_list_free (selected);
1799 on_focus_out (GtkWidget *self,
1800 GdkEventFocus *event,
1804 if (!gtk_widget_is_focus (self)) {
1805 GtkTreeSelection *selection = NULL;
1806 GList *selected_rows = NULL;
1807 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1808 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1809 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1810 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1811 gtk_tree_selection_unselect_all (selection);
1812 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1813 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1814 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1815 g_list_free (selected_rows);
1822 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1824 enable_drag_and_drop(self);
1829 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1831 GtkTreeSelection *selection = NULL;
1832 GtkTreePath *path = NULL;
1833 gboolean already_selected = FALSE, already_opened = FALSE;
1834 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1836 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1838 GtkTreeModel *model;
1840 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1841 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1843 /* Get header from model */
1844 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1845 if (gtk_tree_model_get_iter (model, &iter, path)) {
1846 GValue value = {0,};
1849 gtk_tree_model_get_value (model, &iter,
1850 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1852 header = (TnyHeader *) g_value_get_object (&value);
1853 if (TNY_IS_HEADER (header)) {
1854 status = modest_tny_all_send_queues_get_msg_status (header);
1855 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1858 g_value_unset (&value);
1862 /* Enable drag and drop only if the user clicks on a row that
1863 it's already selected. If not, let him select items using
1864 the pointer. If the message is in an OUTBOX and in sending
1865 status disable drag and drop as well */
1866 if (!already_selected ||
1867 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1869 disable_drag_and_drop(self);
1872 gtk_tree_path_free(path);
1874 /* If it's already opened then do not let the button-press
1875 event go on because it'll perform a message open because
1876 we're clicking on to an already selected header */
1881 folder_monitor_update (TnyFolderObserver *self,
1882 TnyFolderChange *change)
1884 ModestHeaderViewPrivate *priv = NULL;
1885 TnyFolderChangeChanged changed;
1886 TnyFolder *folder = NULL;
1888 changed = tny_folder_change_get_changed (change);
1890 /* Do not notify the observers if the folder of the header
1891 view has changed before this call to the observer
1893 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1894 folder = tny_folder_change_get_folder (change);
1895 if (folder != priv->folder)
1898 MODEST_DEBUG_BLOCK (
1899 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1900 g_print ("ADDED %d/%d (r/t) \n",
1901 tny_folder_change_get_new_unread_count (change),
1902 tny_folder_change_get_new_all_count (change));
1903 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1904 g_print ("ALL COUNT %d\n",
1905 tny_folder_change_get_new_all_count (change));
1906 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1907 g_print ("UNREAD COUNT %d\n",
1908 tny_folder_change_get_new_unread_count (change));
1909 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1910 g_print ("EXPUNGED %d/%d (r/t) \n",
1911 tny_folder_change_get_new_unread_count (change),
1912 tny_folder_change_get_new_all_count (change));
1913 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1914 g_print ("FOLDER RENAME\n");
1915 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1916 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1917 tny_folder_change_get_new_unread_count (change),
1918 tny_folder_change_get_new_all_count (change));
1919 g_print ("---------------------------------------------------\n");
1922 /* Check folder count */
1923 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1924 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1926 g_mutex_lock (priv->observers_lock);
1928 /* Emit signal to evaluate how headers changes affects
1929 to the window view */
1930 g_signal_emit (G_OBJECT(self),
1931 signals[MSG_COUNT_CHANGED_SIGNAL],
1934 /* Added or removed headers, so data stored on cliboard are invalid */
1935 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1936 modest_email_clipboard_clear (priv->clipboard);
1938 g_mutex_unlock (priv->observers_lock);
1944 g_object_unref (folder);
1948 modest_header_view_is_empty (ModestHeaderView *self)
1950 ModestHeaderViewPrivate *priv;
1952 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1954 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1956 return priv->status == HEADER_VIEW_EMPTY;
1960 modest_header_view_clear (ModestHeaderView *self)
1962 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1964 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1968 modest_header_view_copy_selection (ModestHeaderView *header_view)
1970 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1972 /* Copy selection */
1973 _clipboard_set_selected_data (header_view, FALSE);
1977 modest_header_view_cut_selection (ModestHeaderView *header_view)
1979 ModestHeaderViewPrivate *priv = NULL;
1980 const gchar **hidding = NULL;
1981 guint i, n_selected;
1983 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1985 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1987 /* Copy selection */
1988 _clipboard_set_selected_data (header_view, TRUE);
1990 /* Get hidding ids */
1991 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1993 /* Clear hidding array created by previous cut operation */
1994 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1996 /* Copy hidding array */
1997 priv->n_selected = n_selected;
1998 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1999 for (i=0; i < n_selected; i++)
2000 priv->hidding_ids[i] = g_strdup(hidding[i]);
2002 /* Hide cut headers */
2003 modest_header_view_refilter (header_view);
2010 _clipboard_set_selected_data (ModestHeaderView *header_view,
2013 ModestHeaderViewPrivate *priv = NULL;
2014 TnyList *headers = NULL;
2016 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2017 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2019 /* Set selected data on clipboard */
2020 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2021 headers = modest_header_view_get_selected_headers (header_view);
2022 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2025 g_object_unref (headers);
2029 ModestHeaderView *self;
2034 notify_filter_change (gpointer data)
2036 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2038 g_signal_emit (info->self,
2039 signals[MSG_COUNT_CHANGED_SIGNAL],
2040 0, info->folder, NULL);
2046 notify_filter_change_destroy (gpointer data)
2048 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2049 ModestHeaderViewPrivate *priv;
2051 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2052 priv->status_timeout = 0;
2054 g_object_unref (info->self);
2055 g_object_unref (info->folder);
2056 g_slice_free (NotifyFilterInfo, info);
2060 filter_row (GtkTreeModel *model,
2064 ModestHeaderViewPrivate *priv = NULL;
2065 TnyHeaderFlags flags;
2066 TnyHeader *header = NULL;
2069 gboolean visible = TRUE;
2070 gboolean found = FALSE;
2071 GValue value = {0,};
2072 HeaderViewStatus old_status;
2074 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2075 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2077 /* Get header from model */
2078 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2079 flags = (TnyHeaderFlags) g_value_get_int (&value);
2080 g_value_unset (&value);
2081 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2082 header = (TnyHeader *) g_value_get_object (&value);
2083 g_value_unset (&value);
2085 /* Hide deleted and mark as deleted heders */
2086 if (flags & TNY_HEADER_FLAG_DELETED ||
2087 flags & TNY_HEADER_FLAG_EXPUNGED) {
2092 /* If no data on clipboard, return always TRUE */
2093 if (modest_email_clipboard_cleared(priv->clipboard)) {
2098 /* Get message id from header (ensure is a valid id) */
2105 if (priv->hidding_ids != NULL) {
2106 id = tny_header_dup_message_id (header);
2107 for (i=0; i < priv->n_selected && !found; i++)
2108 if (priv->hidding_ids[i] != NULL && id != NULL)
2109 found = (!strcmp (priv->hidding_ids[i], id));
2116 old_status = priv->status;
2117 priv->status = ((gboolean) priv->status) && !visible;
2118 if ((priv->notify_status) && (priv->status != old_status)) {
2119 NotifyFilterInfo *info;
2121 if (priv->status_timeout)
2122 g_source_remove (priv->status_timeout);
2124 info = g_slice_new0 (NotifyFilterInfo);
2125 info->self = g_object_ref (G_OBJECT (user_data));
2126 info->folder = tny_header_get_folder (header);
2127 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2128 notify_filter_change,
2130 notify_filter_change_destroy);
2137 _clear_hidding_filter (ModestHeaderView *header_view)
2139 ModestHeaderViewPrivate *priv = NULL;
2142 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2143 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2145 if (priv->hidding_ids != NULL) {
2146 for (i=0; i < priv->n_selected; i++)
2147 g_free (priv->hidding_ids[i]);
2148 g_free(priv->hidding_ids);
2153 modest_header_view_refilter (ModestHeaderView *header_view)
2155 GtkTreeModel *model = NULL;
2156 ModestHeaderViewPrivate *priv = NULL;
2158 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2159 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2161 /* Hide cut headers */
2162 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2163 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2164 priv->status = HEADER_VIEW_INIT;
2165 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2170 * Called when an account is removed. If I'm showing a folder of the
2171 * account that has been removed then clear the view
2174 on_account_removed (TnyAccountStore *self,
2175 TnyAccount *account,
2178 ModestHeaderViewPrivate *priv = NULL;
2180 /* Ignore changes in transport accounts */
2181 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2184 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2187 TnyAccount *my_account;
2189 my_account = tny_folder_get_account (priv->folder);
2190 if (my_account == account)
2191 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2192 g_object_unref (my_account);
2197 modest_header_view_add_observer(ModestHeaderView *header_view,
2198 ModestHeaderViewObserver *observer)
2200 ModestHeaderViewPrivate *priv;
2202 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2203 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2205 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2207 g_mutex_lock(priv->observer_list_lock);
2208 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2209 g_mutex_unlock(priv->observer_list_lock);
2213 modest_header_view_remove_observer(ModestHeaderView *header_view,
2214 ModestHeaderViewObserver *observer)
2216 ModestHeaderViewPrivate *priv;
2218 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2219 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2221 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2223 g_mutex_lock(priv->observer_list_lock);
2224 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2225 g_mutex_unlock(priv->observer_list_lock);
2229 modest_header_view_notify_observers(ModestHeaderView *header_view,
2230 GtkTreeModel *model,
2231 const gchar *tny_folder_id)
2233 ModestHeaderViewPrivate *priv = NULL;
2235 ModestHeaderViewObserver *observer;
2238 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2240 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2242 g_mutex_lock(priv->observer_list_lock);
2243 iter = priv->observer_list;
2244 while(iter != NULL){
2245 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2246 modest_header_view_observer_update(observer, model,
2248 iter = g_slist_next(iter);
2250 g_mutex_unlock(priv->observer_list_lock);
2254 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2256 ModestHeaderViewPrivate *priv = NULL;
2258 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2259 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);