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>
36 #include <tny-merge-folder.h>
39 #include <modest-header-view.h>
40 #include <modest-header-view-priv.h>
41 #include <modest-dnd.h>
42 #include <modest-tny-folder.h>
43 #include <modest-debug.h>
44 #include <modest-main-window.h>
45 #include <modest-ui-actions.h>
46 #include <modest-marshal.h>
47 #include <modest-text-utils.h>
48 #include <modest-icon-names.h>
49 #include <modest-runtime.h>
50 #include "modest-platform.h"
51 #include <modest-hbox-cell-renderer.h>
52 #include <modest-vbox-cell-renderer.h>
53 #include <modest-datetime-formatter.h>
54 #include <modest-ui-constants.h>
55 #ifdef MODEST_TOOLKIT_HILDON2
56 #include <hildon/hildon.h>
59 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
60 static void modest_header_view_init (ModestHeaderView *obj);
61 static void modest_header_view_finalize (GObject *obj);
62 static void modest_header_view_dispose (GObject *obj);
64 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
65 GtkTreeViewColumn *column, gpointer userdata);
67 static gint cmp_rows (GtkTreeModel *tree_model,
72 static gint cmp_subject_rows (GtkTreeModel *tree_model,
77 static gboolean filter_row (GtkTreeModel *model,
81 static void on_account_removed (TnyAccountStore *self,
85 static void on_selection_changed (GtkTreeSelection *sel,
88 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
91 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
94 static void setup_drag_and_drop (GtkWidget *self);
96 static void enable_drag_and_drop (GtkWidget *self);
98 static void disable_drag_and_drop (GtkWidget *self);
100 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
102 #ifndef MODEST_TOOLKIT_HILDON2
103 static gboolean on_focus_in (GtkWidget *sef,
104 GdkEventFocus *event,
107 static gboolean on_focus_out (GtkWidget *self,
108 GdkEventFocus *event,
112 static void folder_monitor_update (TnyFolderObserver *self,
113 TnyFolderChange *change);
115 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
117 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
119 static void _clear_hidding_filter (ModestHeaderView *header_view);
121 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
123 const gchar *tny_folder_id);
125 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
126 GdkEventExpose *event,
129 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
130 static void update_style (ModestHeaderView *self);
131 static void modest_header_view_refilter_by_chunks (ModestHeaderView *self);
134 HEADER_VIEW_NON_EMPTY,
139 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
140 struct _ModestHeaderViewPrivate {
142 ModestHeaderViewStyle style;
145 TnyFolderMonitor *monitor;
146 GMutex *observers_lock;
148 /*header-view-observer observer*/
149 GMutex *observer_list_lock;
150 GSList *observer_list;
152 /* not unref this object, its a singlenton */
153 ModestEmailClipboard *clipboard;
155 /* Filter tree model */
158 GtkTreeRowReference *autoselect_reference;
159 ModestHeaderViewFilter filter;
160 #ifdef MODEST_TOOLKIT_HILDON2
161 GtkWidget *live_search;
162 guint live_search_timeout;
165 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
166 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
168 gulong selection_changed_handler;
169 gulong acc_removed_handler;
171 GList *drag_begin_cached_selected_rows;
173 HeaderViewStatus status;
174 guint status_timeout;
175 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
177 ModestDatetimeFormatter *datetime_formatter;
179 GtkCellRenderer *renderer_subject;
180 GtkCellRenderer *renderer_address;
181 GtkCellRenderer *renderer_date_status;
183 GdkColor active_color;
184 GdkColor secondary_color;
188 gchar *filter_string;
189 gchar **filter_string_splitted;
190 gboolean filter_date_range;
191 time_t date_range_start;
192 time_t date_range_end;
194 guint refilter_handler_id;
195 GtkTreeModel *filtered_model;
196 GtkTreeIter refilter_iter;
199 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
200 struct _HeadersCountChangedHelper {
201 ModestHeaderView *self;
202 TnyFolderChange *change;
206 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
207 MODEST_TYPE_HEADER_VIEW, \
208 ModestHeaderViewPrivate))
212 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
214 #define _HEADER_VIEW_SUBJECT_FOLD "_subject_modest_header_view"
215 #define _HEADER_VIEW_FROM_FOLD "_from_modest_header_view"
216 #define _HEADER_VIEW_TO_FOLD "_to_modest_header_view"
217 #define _HEADER_VIEW_CC_FOLD "_cc_modest_header_view"
218 #define _HEADER_VIEW_BCC_FOLD "_bcc_modest_header_view"
221 HEADER_SELECTED_SIGNAL,
222 HEADER_ACTIVATED_SIGNAL,
223 ITEM_NOT_FOUND_SIGNAL,
224 MSG_COUNT_CHANGED_SIGNAL,
225 UPDATING_MSG_LIST_SIGNAL,
230 static GObjectClass *parent_class = NULL;
232 /* uncomment the following if you have defined any signals */
233 static guint signals[LAST_SIGNAL] = {0};
236 modest_header_view_get_type (void)
238 static GType my_type = 0;
240 static const GTypeInfo my_info = {
241 sizeof(ModestHeaderViewClass),
242 NULL, /* base init */
243 NULL, /* base finalize */
244 (GClassInitFunc) modest_header_view_class_init,
245 NULL, /* class finalize */
246 NULL, /* class data */
247 sizeof(ModestHeaderView),
249 (GInstanceInitFunc) modest_header_view_init,
253 static const GInterfaceInfo tny_folder_observer_info =
255 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
256 NULL, /* interface_finalize */
257 NULL /* interface_data */
259 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
263 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
264 &tny_folder_observer_info);
272 modest_header_view_class_init (ModestHeaderViewClass *klass)
274 GObjectClass *gobject_class;
275 gobject_class = (GObjectClass*) klass;
277 parent_class = g_type_class_peek_parent (klass);
278 gobject_class->finalize = modest_header_view_finalize;
279 gobject_class->dispose = modest_header_view_dispose;
281 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
283 signals[HEADER_SELECTED_SIGNAL] =
284 g_signal_new ("header_selected",
285 G_TYPE_FROM_CLASS (gobject_class),
287 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
289 g_cclosure_marshal_VOID__POINTER,
290 G_TYPE_NONE, 1, G_TYPE_POINTER);
292 signals[HEADER_ACTIVATED_SIGNAL] =
293 g_signal_new ("header_activated",
294 G_TYPE_FROM_CLASS (gobject_class),
296 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
298 gtk_marshal_VOID__POINTER_POINTER,
299 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
302 signals[ITEM_NOT_FOUND_SIGNAL] =
303 g_signal_new ("item_not_found",
304 G_TYPE_FROM_CLASS (gobject_class),
306 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
308 g_cclosure_marshal_VOID__INT,
309 G_TYPE_NONE, 1, G_TYPE_INT);
311 signals[MSG_COUNT_CHANGED_SIGNAL] =
312 g_signal_new ("msg_count_changed",
313 G_TYPE_FROM_CLASS (gobject_class),
315 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
317 modest_marshal_VOID__POINTER_POINTER,
318 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
320 signals[UPDATING_MSG_LIST_SIGNAL] =
321 g_signal_new ("updating-msg-list",
322 G_TYPE_FROM_CLASS (gobject_class),
324 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
326 g_cclosure_marshal_VOID__BOOLEAN,
327 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
329 #ifdef MODEST_TOOLKIT_HILDON2
330 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
336 tny_folder_observer_init (TnyFolderObserverIface *klass)
338 klass->update = folder_monitor_update;
341 static GtkTreeViewColumn*
342 get_new_column (const gchar *name, GtkCellRenderer *renderer,
343 gboolean resizable, gint sort_col_id, gboolean show_as_text,
344 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
346 GtkTreeViewColumn *column;
348 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
349 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
351 gtk_tree_view_column_set_resizable (column, resizable);
353 gtk_tree_view_column_set_expand (column, TRUE);
356 gtk_tree_view_column_add_attribute (column, renderer, "text",
358 if (sort_col_id >= 0)
359 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
361 gtk_tree_view_column_set_sort_indicator (column, FALSE);
362 gtk_tree_view_column_set_reorderable (column, TRUE);
365 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
372 remove_all_columns (ModestHeaderView *obj)
374 GList *columns, *cursor;
376 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
378 for (cursor = columns; cursor; cursor = cursor->next)
379 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
380 GTK_TREE_VIEW_COLUMN(cursor->data));
381 g_list_free (columns);
385 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
387 GtkTreeModel *sortable, *filter_model;
388 GtkTreeViewColumn *column=NULL;
389 GtkTreeSelection *selection = NULL;
390 GtkCellRenderer *renderer_header,
391 *renderer_attach, *renderer_compact_date_or_status;
392 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
393 *renderer_subject_box, *renderer_recpt,
395 ModestHeaderViewPrivate *priv;
396 GtkTreeViewColumn *compact_column = NULL;
399 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
400 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
402 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
404 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
406 /* TODO: check whether these renderers need to be freed */
407 renderer_attach = gtk_cell_renderer_pixbuf_new ();
408 renderer_priority = gtk_cell_renderer_pixbuf_new ();
409 renderer_header = gtk_cell_renderer_text_new ();
411 renderer_compact_header = modest_vbox_cell_renderer_new ();
412 renderer_recpt_box = modest_hbox_cell_renderer_new ();
413 renderer_subject_box = modest_hbox_cell_renderer_new ();
414 renderer_recpt = gtk_cell_renderer_text_new ();
415 priv->renderer_address = renderer_recpt;
416 priv->renderer_subject = gtk_cell_renderer_text_new ();
417 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
418 priv->renderer_date_status = renderer_compact_date_or_status;
420 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
421 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
422 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
423 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
424 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
425 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
426 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
427 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
428 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
429 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
430 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
431 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
432 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
433 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
435 #ifdef MODEST_TOOLKIT_HILDON2
436 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
438 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
439 #ifndef MODEST_TOOLKIT_GTK
440 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
441 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
443 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
444 g_object_set(G_OBJECT(renderer_header),
445 "ellipsize", PANGO_ELLIPSIZE_END,
447 g_object_set (G_OBJECT (priv->renderer_subject),
448 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
450 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
451 g_object_set (G_OBJECT (renderer_recpt),
452 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
454 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
455 g_object_set(G_OBJECT(renderer_compact_date_or_status),
456 "xalign", 1.0, "yalign", 0.1,
458 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
459 #ifdef MODEST_TOOLKIT_HILDON2
460 g_object_set (G_OBJECT (renderer_priority),
462 "xalign", 0.0, NULL);
463 g_object_set (G_OBJECT (renderer_attach),
465 "xalign", 0.0, NULL);
467 g_object_set (G_OBJECT (renderer_priority),
468 "yalign", 0.5, NULL);
469 g_object_set (G_OBJECT (renderer_attach),
470 "yalign", 0.0, NULL);
473 #ifdef MODEST_TOOLKIT_HILDON1
474 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
475 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
476 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
477 #elif MODEST_TOOLKIT_HILDON2
478 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
479 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
480 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
482 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
483 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
484 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
487 remove_all_columns (self);
489 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
490 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
492 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
493 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
494 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
497 /* Add new columns */
498 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
499 ModestHeaderViewColumn col =
500 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
502 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
503 g_printerr ("modest: invalid column %d in column list\n", col);
509 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
510 column = get_new_column (_("A"), renderer_attach, FALSE,
511 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
513 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
515 gtk_tree_view_column_set_fixed_width (column, 45);
519 case MODEST_HEADER_VIEW_COLUMN_FROM:
520 column = get_new_column (_("From"), renderer_header, TRUE,
521 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
523 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
524 GINT_TO_POINTER(TRUE));
527 case MODEST_HEADER_VIEW_COLUMN_TO:
528 column = get_new_column (_("To"), renderer_header, TRUE,
529 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
531 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
532 GINT_TO_POINTER(FALSE));
535 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
536 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
537 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
539 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
540 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
541 compact_column = column;
544 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
545 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
546 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
548 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
549 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
550 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
551 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
552 compact_column = column;
556 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
557 column = get_new_column (_("Subject"), renderer_header, TRUE,
558 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
560 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
564 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
565 column = get_new_column (_("Received"), renderer_header, TRUE,
566 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
568 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
569 GINT_TO_POINTER(TRUE));
572 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
573 column = get_new_column (_("Sent"), renderer_header, TRUE,
574 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
576 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
577 GINT_TO_POINTER(FALSE));
580 case MODEST_HEADER_VIEW_COLUMN_SIZE:
581 column = get_new_column (_("Size"), renderer_header, TRUE,
582 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
584 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
587 case MODEST_HEADER_VIEW_COLUMN_STATUS:
588 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
589 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
591 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
596 g_return_val_if_reached(FALSE);
599 /* we keep the column id around */
600 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
601 GINT_TO_POINTER(col));
603 /* we need this ptr when sorting the rows */
604 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
606 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
610 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
611 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
612 (GtkTreeIterCompareFunc) cmp_rows,
613 compact_column, NULL);
614 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
615 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
616 (GtkTreeIterCompareFunc) cmp_subject_rows,
617 compact_column, NULL);
621 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
627 datetime_format_changed (ModestDatetimeFormatter *formatter,
628 ModestHeaderView *self)
630 gtk_widget_queue_draw (GTK_WIDGET (self));
634 modest_header_view_init (ModestHeaderView *obj)
636 ModestHeaderViewPrivate *priv;
639 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
641 priv->show_latest = 0;
644 priv->is_outbox = FALSE;
646 priv->monitor = NULL;
647 priv->observers_lock = g_mutex_new ();
648 priv->autoselect_reference = NULL;
650 priv->status = HEADER_VIEW_INIT;
651 priv->status_timeout = 0;
652 priv->notify_status = TRUE;
654 priv->observer_list_lock = g_mutex_new();
655 priv->observer_list = NULL;
657 priv->clipboard = modest_runtime_get_email_clipboard ();
658 priv->hidding_ids = NULL;
659 priv->n_selected = 0;
660 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
661 #ifdef MODEST_TOOLKIT_HILDON2
662 priv->live_search = NULL;
663 priv->live_search_timeout = 0;
665 priv->filter_string = NULL;
666 priv->filter_string_splitted = NULL;
667 priv->filter_date_range = FALSE;
668 priv->selection_changed_handler = 0;
669 priv->acc_removed_handler = 0;
671 priv->filtered_model = NULL;
672 priv->refilter_handler_id = 0;
674 /* Sort parameters */
675 for (j=0; j < 2; j++) {
676 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
677 priv->sort_colid[j][i] = -1;
678 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
682 priv->datetime_formatter = modest_datetime_formatter_new ();
683 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
684 G_CALLBACK (datetime_format_changed), (gpointer) obj);
686 setup_drag_and_drop (GTK_WIDGET(obj));
690 modest_header_view_dispose (GObject *obj)
692 ModestHeaderView *self;
693 ModestHeaderViewPrivate *priv;
694 GtkTreeSelection *sel;
696 self = MODEST_HEADER_VIEW(obj);
697 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
699 if (priv->refilter_handler_id > 0) {
700 g_source_remove (priv->refilter_handler_id);
701 priv->refilter_handler_id = 0;
702 priv->filtered_model = NULL;
705 #ifdef MODEST_TOOLKIT_HILDON2
706 if (priv->live_search_timeout > 0) {
707 g_source_remove (priv->live_search_timeout);
708 priv->live_search_timeout = 0;
712 if (priv->datetime_formatter) {
713 g_object_unref (priv->datetime_formatter);
714 priv->datetime_formatter = NULL;
717 /* Free in the dispose to avoid unref cycles */
719 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
720 g_object_unref (G_OBJECT (priv->folder));
724 /* We need to do this here in the dispose because the
725 selection won't exist when finalizing */
726 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
727 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
728 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
729 priv->selection_changed_handler = 0;
732 G_OBJECT_CLASS(parent_class)->dispose (obj);
736 modest_header_view_finalize (GObject *obj)
738 ModestHeaderView *self;
739 ModestHeaderViewPrivate *priv;
741 self = MODEST_HEADER_VIEW(obj);
742 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
744 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
745 priv->acc_removed_handler)) {
746 g_signal_handler_disconnect (modest_runtime_get_account_store (),
747 priv->acc_removed_handler);
750 /* There is no need to lock because there should not be any
751 * reference to self now. */
752 g_mutex_free(priv->observer_list_lock);
753 g_slist_free(priv->observer_list);
755 g_mutex_lock (priv->observers_lock);
757 tny_folder_monitor_stop (priv->monitor);
758 g_object_unref (G_OBJECT (priv->monitor));
760 g_mutex_unlock (priv->observers_lock);
761 g_mutex_free (priv->observers_lock);
763 /* Clear hidding array created by cut operation */
764 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
766 if (priv->autoselect_reference != NULL) {
767 gtk_tree_row_reference_free (priv->autoselect_reference);
768 priv->autoselect_reference = NULL;
771 if (priv->filter_string) {
772 g_free (priv->filter_string);
775 if (priv->filter_string_splitted) {
776 g_strfreev (priv->filter_string_splitted);
779 G_OBJECT_CLASS(parent_class)->finalize (obj);
784 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
787 GtkTreeSelection *sel;
788 ModestHeaderView *self;
789 ModestHeaderViewPrivate *priv;
791 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
794 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
795 self = MODEST_HEADER_VIEW(obj);
796 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
798 modest_header_view_set_style (self, style);
800 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
801 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
802 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
804 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
805 TRUE); /* alternating row colors */
807 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
808 priv->selection_changed_handler =
809 g_signal_connect_after (sel, "changed",
810 G_CALLBACK(on_selection_changed), self);
812 g_signal_connect (self, "row-activated",
813 G_CALLBACK (on_header_row_activated), NULL);
815 #ifndef MODEST_TOOLKIT_HILDON2
816 g_signal_connect (self, "focus-in-event",
817 G_CALLBACK(on_focus_in), NULL);
818 g_signal_connect (self, "focus-out-event",
819 G_CALLBACK(on_focus_out), NULL);
822 g_signal_connect (self, "button-press-event",
823 G_CALLBACK(on_button_press_event), NULL);
824 g_signal_connect (self, "button-release-event",
825 G_CALLBACK(on_button_release_event), NULL);
827 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
829 G_CALLBACK (on_account_removed),
832 g_signal_connect (self, "expose-event",
833 G_CALLBACK(modest_header_view_on_expose_event),
836 return GTK_WIDGET(self);
841 modest_header_view_count_selected_headers (ModestHeaderView *self)
843 GtkTreeSelection *sel;
846 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
848 /* Get selection object and check selected rows count */
849 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
850 selected_rows = gtk_tree_selection_count_selected_rows (sel);
852 return selected_rows;
856 modest_header_view_has_selected_headers (ModestHeaderView *self)
858 GtkTreeSelection *sel;
861 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
863 /* Get selection object and check selected rows count */
864 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
865 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
872 modest_header_view_get_selected_headers (ModestHeaderView *self)
874 GtkTreeSelection *sel;
875 TnyList *header_list = NULL;
877 GList *list, *tmp = NULL;
878 GtkTreeModel *tree_model = NULL;
881 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
884 /* Get selected rows */
885 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
886 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
889 header_list = tny_simple_list_new();
891 list = g_list_reverse (list);
894 /* get header from selection */
895 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
896 gtk_tree_model_get (tree_model, &iter,
897 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
899 /* Prepend to list */
900 tny_list_prepend (header_list, G_OBJECT (header));
901 g_object_unref (G_OBJECT (header));
903 tmp = g_list_next (tmp);
906 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
913 /* scroll our list view so the selected item is visible */
915 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
917 #ifdef MODEST_TOOLKIT_GTK
919 GtkTreePath *selected_path;
920 GtkTreePath *start, *end;
924 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
925 selected_path = gtk_tree_model_get_path (model, iter);
927 start = gtk_tree_path_new ();
928 end = gtk_tree_path_new ();
930 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
932 if (gtk_tree_path_compare (selected_path, start) < 0 ||
933 gtk_tree_path_compare (end, selected_path) < 0)
934 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
935 selected_path, NULL, TRUE,
938 gtk_tree_path_free (selected_path);
939 gtk_tree_path_free (start);
940 gtk_tree_path_free (end);
942 #endif /* MODEST_TOOLKIT_GTK */
947 modest_header_view_select_next (ModestHeaderView *self)
949 GtkTreeSelection *sel;
954 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
956 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
957 path = get_selected_row (GTK_TREE_VIEW(self), &model);
958 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
959 /* Unselect previous path */
960 gtk_tree_selection_unselect_path (sel, path);
962 /* Move path down and selects new one */
963 if (gtk_tree_model_iter_next (model, &iter)) {
964 gtk_tree_selection_select_iter (sel, &iter);
965 scroll_to_selected (self, &iter, FALSE);
967 gtk_tree_path_free(path);
973 modest_header_view_select_prev (ModestHeaderView *self)
975 GtkTreeSelection *sel;
980 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
982 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
983 path = get_selected_row (GTK_TREE_VIEW(self), &model);
984 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
985 /* Unselect previous path */
986 gtk_tree_selection_unselect_path (sel, path);
989 if (gtk_tree_path_prev (path)) {
990 gtk_tree_model_get_iter (model, &iter, path);
992 /* Select the new one */
993 gtk_tree_selection_select_iter (sel, &iter);
994 scroll_to_selected (self, &iter, TRUE);
997 gtk_tree_path_free (path);
1002 modest_header_view_get_columns (ModestHeaderView *self)
1004 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1006 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1012 modest_header_view_set_style (ModestHeaderView *self,
1013 ModestHeaderViewStyle style)
1015 ModestHeaderViewPrivate *priv;
1016 gboolean show_col_headers = FALSE;
1017 ModestHeaderViewStyle old_style;
1019 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
1020 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
1023 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1024 if (priv->style == style)
1025 return TRUE; /* nothing to do */
1028 case MODEST_HEADER_VIEW_STYLE_DETAILS:
1029 show_col_headers = TRUE;
1031 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
1034 g_return_val_if_reached (FALSE);
1036 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
1037 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
1039 old_style = priv->style;
1040 priv->style = style;
1046 ModestHeaderViewStyle
1047 modest_header_view_get_style (ModestHeaderView *self)
1049 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
1051 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
1054 /* This is used to automatically select the first header if the user
1055 * has not selected any header yet.
1058 modest_header_view_on_expose_event(GtkTreeView *header_view,
1059 GdkEventExpose *event,
1062 GtkTreeSelection *sel;
1063 GtkTreeModel *model;
1064 GtkTreeIter tree_iter;
1065 ModestHeaderViewPrivate *priv;
1067 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1068 model = gtk_tree_view_get_model(header_view);
1073 #ifdef MODEST_TOOLKIT_HILDON2
1076 sel = gtk_tree_view_get_selection(header_view);
1077 if(!gtk_tree_selection_count_selected_rows(sel)) {
1078 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1079 GtkTreePath *tree_iter_path;
1080 /* Prevent the widget from getting the focus
1081 when selecting the first item */
1082 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1083 g_object_set(header_view, "can-focus", FALSE, NULL);
1084 gtk_tree_selection_select_iter(sel, &tree_iter);
1085 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1086 g_object_set(header_view, "can-focus", TRUE, NULL);
1087 if (priv->autoselect_reference) {
1088 gtk_tree_row_reference_free (priv->autoselect_reference);
1090 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1091 gtk_tree_path_free (tree_iter_path);
1094 if (priv->autoselect_reference != NULL && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
1095 gboolean moved_selection = FALSE;
1096 GtkTreePath * last_path;
1097 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1098 moved_selection = TRUE;
1102 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1103 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1104 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1105 moved_selection = TRUE;
1106 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1108 gtk_tree_path_free (last_path);
1110 if (moved_selection) {
1111 gtk_tree_row_reference_free (priv->autoselect_reference);
1112 priv->autoselect_reference = NULL;
1115 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1116 GtkTreePath *current_path;
1117 current_path = gtk_tree_model_get_path (model, &tree_iter);
1118 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1119 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1120 g_object_set(header_view, "can-focus", FALSE, NULL);
1121 gtk_tree_selection_unselect_all (sel);
1122 gtk_tree_selection_select_iter(sel, &tree_iter);
1123 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1124 g_object_set(header_view, "can-focus", TRUE, NULL);
1125 gtk_tree_row_reference_free (priv->autoselect_reference);
1126 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1128 gtk_tree_path_free (current_path);
1129 gtk_tree_path_free (last_path);
1139 modest_header_view_get_folder (ModestHeaderView *self)
1141 ModestHeaderViewPrivate *priv;
1143 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1145 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1148 g_object_ref (priv->folder);
1150 return priv->folder;
1154 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1160 ModestHeaderView *self;
1161 ModestHeaderViewPrivate *priv;
1163 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1165 self = MODEST_HEADER_VIEW (user_data);
1166 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1168 if (cancelled || err)
1171 /* Add IDLE observer (monitor) and another folder observer for
1172 new messages (self) */
1173 g_mutex_lock (priv->observers_lock);
1174 if (priv->monitor) {
1175 tny_folder_monitor_stop (priv->monitor);
1176 g_object_unref (G_OBJECT (priv->monitor));
1178 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1179 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1180 tny_folder_monitor_start (priv->monitor);
1181 g_mutex_unlock (priv->observers_lock);
1185 modest_header_view_set_folder_intern (ModestHeaderView *self,
1191 ModestHeaderViewPrivate *priv;
1192 GList *cols, *cursor;
1193 GtkTreeModel *filter_model, *sortable;
1195 GtkSortType sort_type;
1197 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1199 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1200 tny_gtk_header_list_model_set_update_in_batches (TNY_GTK_HEADER_LIST_MODEL (headers), 300);
1201 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (headers), priv->show_latest);
1203 /* Start the monitor in the callback of the
1204 tny_gtk_header_list_model_set_folder call. It's crucial to
1205 do it there and not just after the call because we want the
1206 monitor to observe only the headers returned by the
1207 tny_folder_get_headers_async call that it's inside the
1208 tny_gtk_header_list_model_set_folder call. This way the
1209 monitor infrastructure could successfully cope with
1210 duplicates. For example if a tny_folder_add_msg_async is
1211 happening while tny_gtk_header_list_model_set_folder is
1212 invoked, then the first call could add a header that will
1213 be added again by tny_gtk_header_list_model_set_folder, so
1214 we'd end up with duplicate headers. sergio */
1215 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1217 set_folder_intern_get_headers_async_cb,
1220 /* Init filter_row function to examine empty status */
1221 priv->status = HEADER_VIEW_INIT;
1223 /* Create sortable model */
1224 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (headers));
1225 g_object_unref (headers);
1227 /* Create a tree model filter to hide and show rows for cut operations */
1228 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (sortable), NULL);
1229 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1230 filter_row, self, NULL);
1231 g_object_unref (sortable);
1233 /* install our special sorting functions */
1234 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1236 /* Restore sort column id */
1238 type = modest_tny_folder_guess_folder_type (folder);
1239 if (type == TNY_FOLDER_TYPE_INVALID)
1240 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1242 sort_colid = modest_header_view_get_sort_column_id (self, type);
1243 sort_type = modest_header_view_get_sort_type (self, type);
1244 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1247 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1248 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1249 (GtkTreeIterCompareFunc) cmp_rows,
1251 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1252 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1253 (GtkTreeIterCompareFunc) cmp_subject_rows,
1258 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1259 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1260 g_object_unref (filter_model);
1267 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1269 GtkSortType sort_type)
1271 ModestHeaderViewPrivate *priv = NULL;
1272 GtkTreeModel *sortable = NULL, *filter_model = NULL;
1275 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1276 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1278 /* Get model and private data */
1279 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1280 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1281 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
1282 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1285 /* Sort tree model */
1286 type = modest_tny_folder_guess_folder_type (priv->folder);
1287 if (type == TNY_FOLDER_TYPE_INVALID)
1288 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1290 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1293 /* Store new sort parameters */
1294 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1299 modest_header_view_set_sort_params (ModestHeaderView *self,
1301 GtkSortType sort_type,
1304 ModestHeaderViewPrivate *priv;
1305 ModestHeaderViewStyle style;
1307 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1308 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1309 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1311 style = modest_header_view_get_style (self);
1312 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1314 priv->sort_colid[style][type] = sort_colid;
1315 priv->sort_type[style][type] = sort_type;
1319 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1322 ModestHeaderViewPrivate *priv;
1323 ModestHeaderViewStyle style;
1325 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1326 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1328 style = modest_header_view_get_style (self);
1329 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1331 return priv->sort_colid[style][type];
1335 modest_header_view_get_sort_type (ModestHeaderView *self,
1338 ModestHeaderViewPrivate *priv;
1339 ModestHeaderViewStyle style;
1341 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1342 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1344 style = modest_header_view_get_style (self);
1345 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1347 return priv->sort_type[style][type];
1351 ModestHeaderView *header_view;
1352 RefreshAsyncUserCallback cb;
1357 folder_refreshed_cb (ModestMailOperation *mail_op,
1361 ModestHeaderViewPrivate *priv;
1362 SetFolderHelper *info;
1364 info = (SetFolderHelper*) user_data;
1366 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1370 info->cb (mail_op, folder, info->user_data);
1372 /* Start the folder count changes observer. We do not need it
1373 before the refresh. Note that the monitor could still be
1374 called for this refresh but now we know that the callback
1375 was previously called */
1376 g_mutex_lock (priv->observers_lock);
1377 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1378 g_mutex_unlock (priv->observers_lock);
1380 /* Notify the observers that the update is over */
1381 g_signal_emit (G_OBJECT (info->header_view),
1382 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1384 /* Allow filtering notifications from now on if the current
1385 folder is still the same (if not then the user has selected
1386 another one to refresh, we should wait until that refresh
1388 if (priv->folder == folder)
1389 priv->notify_status = TRUE;
1392 g_object_unref (info->header_view);
1397 refresh_folder_error_handler (ModestMailOperation *mail_op,
1400 const GError *error = modest_mail_operation_get_error (mail_op);
1402 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1403 error->code == TNY_IO_ERROR_WRITE ||
1404 error->code == TNY_IO_ERROR_READ) {
1405 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1406 /* If the mail op has been cancelled then it's not an error: don't show any message */
1407 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1408 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1409 modest_platform_information_banner (NULL, NULL, msg);
1416 modest_header_view_set_folder (ModestHeaderView *self,
1419 ModestWindow *progress_window,
1420 RefreshAsyncUserCallback callback,
1423 ModestHeaderViewPrivate *priv;
1425 g_return_if_fail (self);
1427 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1430 if (priv->status_timeout) {
1431 g_source_remove (priv->status_timeout);
1432 priv->status_timeout = 0;
1435 g_mutex_lock (priv->observers_lock);
1436 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1437 g_object_unref (priv->folder);
1438 priv->folder = NULL;
1439 g_mutex_unlock (priv->observers_lock);
1443 GtkTreeSelection *selection;
1444 SetFolderHelper *info;
1445 ModestMailOperation *mail_op = NULL;
1447 /* Set folder in the model */
1448 modest_header_view_set_folder_intern (self, folder, refresh);
1450 /* Pick my reference. Nothing to do with the mail operation */
1451 priv->folder = g_object_ref (folder);
1453 /* Do not notify about filterings until the refresh finishes */
1454 priv->notify_status = FALSE;
1456 /* Clear the selection if exists */
1457 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1458 gtk_tree_selection_unselect_all(selection);
1459 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1461 /* Notify the observers that the update begins */
1462 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1465 /* create the helper */
1466 info = g_malloc0 (sizeof (SetFolderHelper));
1467 info->header_view = g_object_ref (self);
1468 info->cb = callback;
1469 info->user_data = user_data;
1471 /* Create the mail operation (source will be the parent widget) */
1472 if (progress_window)
1473 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1474 refresh_folder_error_handler,
1477 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1480 /* Refresh the folder asynchronously */
1481 modest_mail_operation_refresh_folder (mail_op,
1483 folder_refreshed_cb,
1486 folder_refreshed_cb (mail_op, folder, info);
1490 g_object_unref (mail_op);
1492 g_mutex_lock (priv->observers_lock);
1494 if (priv->monitor) {
1495 tny_folder_monitor_stop (priv->monitor);
1496 g_object_unref (G_OBJECT (priv->monitor));
1497 priv->monitor = NULL;
1500 if (priv->autoselect_reference) {
1501 gtk_tree_row_reference_free (priv->autoselect_reference);
1502 priv->autoselect_reference = NULL;
1505 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1507 modest_header_view_notify_observers(self, NULL, NULL);
1509 g_mutex_unlock (priv->observers_lock);
1511 /* Notify the observers that the update is over */
1512 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1518 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1519 GtkTreeViewColumn *column, gpointer userdata)
1521 ModestHeaderView *self = NULL;
1523 GtkTreeModel *model = NULL;
1524 TnyHeader *header = NULL;
1525 TnyHeaderFlags flags;
1527 self = MODEST_HEADER_VIEW (treeview);
1529 model = gtk_tree_view_get_model (treeview);
1530 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1533 /* get the first selected item */
1534 gtk_tree_model_get (model, &iter,
1535 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1536 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1539 /* Dont open DELETED messages */
1540 if (flags & TNY_HEADER_FLAG_DELETED) {
1543 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1544 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1545 modest_platform_information_banner (NULL, NULL, msg);
1551 g_signal_emit (G_OBJECT(self),
1552 signals[HEADER_ACTIVATED_SIGNAL],
1558 g_object_unref (G_OBJECT (header));
1563 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1565 GtkTreeModel *model;
1566 TnyHeader *header = NULL;
1567 GtkTreePath *path = NULL;
1569 ModestHeaderView *self;
1570 GList *selected = NULL;
1572 g_return_if_fail (sel);
1573 g_return_if_fail (user_data);
1575 self = MODEST_HEADER_VIEW (user_data);
1577 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1578 if (selected != NULL)
1579 path = (GtkTreePath *) selected->data;
1580 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1581 return; /* msg was _un_selected */
1583 gtk_tree_model_get (model, &iter,
1584 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1588 g_signal_emit (G_OBJECT(self),
1589 signals[HEADER_SELECTED_SIGNAL],
1592 g_object_unref (G_OBJECT (header));
1594 /* free all items in 'selected' */
1595 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1596 g_list_free (selected);
1600 /* PROTECTED method. It's useful when we want to force a given
1601 selection to reload a msg. For example if we have selected a header
1602 in offline mode, when Modest become online, we want to reload the
1603 message automatically without an user click over the header */
1605 _modest_header_view_change_selection (GtkTreeSelection *selection,
1608 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1609 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1611 on_selection_changed (selection, user_data);
1615 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1622 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1626 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1630 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1638 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1646 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1647 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1651 case TNY_HEADER_FLAG_ATTACHMENTS:
1653 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1654 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1655 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1656 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1658 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1659 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1661 return cmp ? cmp : t1 - t2;
1663 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1664 TnyHeader *header1 = NULL, *header2 = NULL;
1666 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1667 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1668 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1669 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1671 /* This is for making priority values respect the intuitive sort relationship
1672 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1674 if (header1 && header2) {
1675 cmp = compare_priorities (tny_header_get_priority (header1),
1676 tny_header_get_priority (header2));
1677 g_object_unref (header1);
1678 g_object_unref (header2);
1680 return cmp ? cmp : t1 - t2;
1686 return &iter1 - &iter2; /* oughhhh */
1691 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1698 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1700 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1701 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1702 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1703 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1705 /* Do not use the prefixes for sorting. Consume all the blank
1706 spaces for sorting */
1707 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1708 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1711 /* If they're equal based on subject without prefix then just
1712 sort them by length. This will show messages like this.
1719 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1726 /* Drag and drop stuff */
1728 drag_data_get_cb (GtkWidget *widget,
1729 GdkDragContext *context,
1730 GtkSelectionData *selection_data,
1735 ModestHeaderView *self = NULL;
1736 ModestHeaderViewPrivate *priv = NULL;
1738 self = MODEST_HEADER_VIEW (widget);
1739 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1741 /* Set the data. Do not use the current selection because it
1742 could be different than the selection at the beginning of
1744 modest_dnd_selection_data_set_paths (selection_data,
1745 priv->drag_begin_cached_selected_rows);
1749 * We're caching the selected rows at the beginning because the
1750 * selection could change between drag-begin and drag-data-get, for
1751 * example if we have a set of rows already selected, and then we
1752 * click in one of them (without SHIFT key pressed) and begin a drag,
1753 * the selection at that moment contains all the selected lines, but
1754 * after dropping the selection, the release event provokes that only
1755 * the row used to begin the drag is selected, so at the end the
1756 * drag&drop affects only one rows instead of all the selected ones.
1760 drag_begin_cb (GtkWidget *widget,
1761 GdkDragContext *context,
1764 ModestHeaderView *self = NULL;
1765 ModestHeaderViewPrivate *priv = NULL;
1766 GtkTreeSelection *selection;
1768 self = MODEST_HEADER_VIEW (widget);
1769 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1771 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1772 priv->drag_begin_cached_selected_rows =
1773 gtk_tree_selection_get_selected_rows (selection, NULL);
1777 * We use the drag-end signal to clear the cached selection, we use
1778 * this because this allways happens, whether or not the d&d was a
1782 drag_end_cb (GtkWidget *widget,
1786 ModestHeaderView *self = NULL;
1787 ModestHeaderViewPrivate *priv = NULL;
1789 self = MODEST_HEADER_VIEW (widget);
1790 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1792 /* Free cached data */
1793 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1794 g_list_free (priv->drag_begin_cached_selected_rows);
1795 priv->drag_begin_cached_selected_rows = NULL;
1798 /* Header view drag types */
1799 const GtkTargetEntry header_view_drag_types[] = {
1800 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1804 enable_drag_and_drop (GtkWidget *self)
1806 #ifdef MODEST_TOOLKIT_HILDON2
1809 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1810 header_view_drag_types,
1811 G_N_ELEMENTS (header_view_drag_types),
1812 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1816 disable_drag_and_drop (GtkWidget *self)
1818 #ifdef MODEST_TOOLKIT_HILDON2
1821 gtk_drag_source_unset (self);
1825 setup_drag_and_drop (GtkWidget *self)
1827 #ifdef MODEST_TOOLKIT_HILDON2
1830 enable_drag_and_drop(self);
1831 g_signal_connect(G_OBJECT (self), "drag_data_get",
1832 G_CALLBACK(drag_data_get_cb), NULL);
1834 g_signal_connect(G_OBJECT (self), "drag_begin",
1835 G_CALLBACK(drag_begin_cb), NULL);
1837 g_signal_connect(G_OBJECT (self), "drag_end",
1838 G_CALLBACK(drag_end_cb), NULL);
1841 static GtkTreePath *
1842 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1844 GtkTreePath *path = NULL;
1845 GtkTreeSelection *sel = NULL;
1848 sel = gtk_tree_view_get_selection(self);
1849 rows = gtk_tree_selection_get_selected_rows (sel, model);
1851 if ((rows == NULL) || (g_list_length(rows) != 1))
1854 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1859 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1865 #ifndef MODEST_TOOLKIT_HILDON2
1867 * This function moves the tree view scroll to the current selected
1868 * row when the widget grabs the focus
1871 on_focus_in (GtkWidget *self,
1872 GdkEventFocus *event,
1875 GtkTreeSelection *selection;
1876 GtkTreeModel *model;
1877 GList *selected = NULL;
1878 GtkTreePath *selected_path = NULL;
1880 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1884 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1885 /* If none selected yet, pick the first one */
1886 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1890 /* Return if the model is empty */
1891 if (!gtk_tree_model_get_iter_first (model, &iter))
1894 path = gtk_tree_model_get_path (model, &iter);
1895 gtk_tree_selection_select_path (selection, path);
1896 gtk_tree_path_free (path);
1899 /* Need to get the all the rows because is selection multiple */
1900 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1901 if (selected == NULL) return FALSE;
1902 selected_path = (GtkTreePath *) selected->data;
1905 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1906 g_list_free (selected);
1912 on_focus_out (GtkWidget *self,
1913 GdkEventFocus *event,
1917 if (!gtk_widget_is_focus (self)) {
1918 GtkTreeSelection *selection = NULL;
1919 GList *selected_rows = NULL;
1920 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1921 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1922 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1923 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1924 gtk_tree_selection_unselect_all (selection);
1925 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1926 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1927 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1928 g_list_free (selected_rows);
1936 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1938 enable_drag_and_drop(self);
1943 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1945 GtkTreeSelection *selection = NULL;
1946 GtkTreePath *path = NULL;
1947 gboolean already_selected = FALSE, already_opened = FALSE;
1948 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1950 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1952 GtkTreeModel *model;
1954 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1955 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1957 /* Get header from model */
1958 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1959 if (gtk_tree_model_get_iter (model, &iter, path)) {
1960 GValue value = {0,};
1963 gtk_tree_model_get_value (model, &iter,
1964 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1966 header = (TnyHeader *) g_value_get_object (&value);
1967 if (TNY_IS_HEADER (header)) {
1968 status = modest_tny_all_send_queues_get_msg_status (header);
1969 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1972 g_value_unset (&value);
1976 /* Enable drag and drop only if the user clicks on a row that
1977 it's already selected. If not, let him select items using
1978 the pointer. If the message is in an OUTBOX and in sending
1979 status disable drag and drop as well */
1980 if (!already_selected ||
1981 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1983 disable_drag_and_drop(self);
1986 gtk_tree_path_free(path);
1988 /* If it's already opened then do not let the button-press
1989 event go on because it'll perform a message open because
1990 we're clicking on to an already selected header */
1995 folder_monitor_update (TnyFolderObserver *self,
1996 TnyFolderChange *change)
1998 ModestHeaderViewPrivate *priv = NULL;
1999 TnyFolderChangeChanged changed;
2000 TnyFolder *folder = NULL;
2002 changed = tny_folder_change_get_changed (change);
2004 /* Do not notify the observers if the folder of the header
2005 view has changed before this call to the observer
2007 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2008 folder = tny_folder_change_get_folder (change);
2009 if (folder != priv->folder)
2012 MODEST_DEBUG_BLOCK (
2013 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
2014 g_print ("ADDED %d/%d (r/t) \n",
2015 tny_folder_change_get_new_unread_count (change),
2016 tny_folder_change_get_new_all_count (change));
2017 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
2018 g_print ("ALL COUNT %d\n",
2019 tny_folder_change_get_new_all_count (change));
2020 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
2021 g_print ("UNREAD COUNT %d\n",
2022 tny_folder_change_get_new_unread_count (change));
2023 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
2024 g_print ("EXPUNGED %d/%d (r/t) \n",
2025 tny_folder_change_get_new_unread_count (change),
2026 tny_folder_change_get_new_all_count (change));
2027 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
2028 g_print ("FOLDER RENAME\n");
2029 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
2030 g_print ("MSG RECEIVED %d/%d (r/t) \n",
2031 tny_folder_change_get_new_unread_count (change),
2032 tny_folder_change_get_new_all_count (change));
2033 g_print ("---------------------------------------------------\n");
2036 /* Check folder count */
2037 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
2038 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
2040 g_mutex_lock (priv->observers_lock);
2042 /* Emit signal to evaluate how headers changes affects
2043 to the window view */
2044 g_signal_emit (G_OBJECT(self),
2045 signals[MSG_COUNT_CHANGED_SIGNAL],
2048 /* Added or removed headers, so data stored on cliboard are invalid */
2049 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
2050 modest_email_clipboard_clear (priv->clipboard);
2052 g_mutex_unlock (priv->observers_lock);
2058 g_object_unref (folder);
2062 modest_header_view_is_empty (ModestHeaderView *self)
2064 ModestHeaderViewPrivate *priv;
2066 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
2068 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2070 return priv->status == HEADER_VIEW_EMPTY;
2074 modest_header_view_clear (ModestHeaderView *self)
2076 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2078 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2082 modest_header_view_copy_selection (ModestHeaderView *header_view)
2084 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2086 /* Copy selection */
2087 _clipboard_set_selected_data (header_view, FALSE);
2091 modest_header_view_cut_selection (ModestHeaderView *header_view)
2093 ModestHeaderViewPrivate *priv = NULL;
2094 const gchar **hidding = NULL;
2095 guint i, n_selected;
2097 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2099 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2101 /* Copy selection */
2102 _clipboard_set_selected_data (header_view, TRUE);
2104 /* Get hidding ids */
2105 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2107 /* Clear hidding array created by previous cut operation */
2108 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2110 /* Copy hidding array */
2111 priv->n_selected = n_selected;
2112 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2113 for (i=0; i < n_selected; i++)
2114 priv->hidding_ids[i] = g_strdup(hidding[i]);
2116 /* Hide cut headers */
2117 modest_header_view_refilter (header_view);
2124 _clipboard_set_selected_data (ModestHeaderView *header_view,
2127 ModestHeaderViewPrivate *priv = NULL;
2128 TnyList *headers = NULL;
2130 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2131 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2133 /* Set selected data on clipboard */
2134 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2135 headers = modest_header_view_get_selected_headers (header_view);
2136 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2139 g_object_unref (headers);
2143 ModestHeaderView *self;
2148 notify_filter_change (gpointer data)
2150 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2152 g_signal_emit (info->self,
2153 signals[MSG_COUNT_CHANGED_SIGNAL],
2154 0, info->folder, NULL);
2160 notify_filter_change_destroy (gpointer data)
2162 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2163 ModestHeaderViewPrivate *priv;
2165 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2166 priv->status_timeout = 0;
2168 g_object_unref (info->self);
2169 g_object_unref (info->folder);
2170 g_slice_free (NotifyFilterInfo, info);
2174 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2176 /* For the moment we only need to filter outbox */
2177 return priv->is_outbox;
2181 header_match_string (TnyHeader *header, gchar **words)
2183 gchar *subject_fold;
2189 gchar **current_word;
2192 subject_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD);
2193 if (subject_fold == NULL) {
2195 subject = tny_header_dup_subject (header);
2196 if (subject != NULL) {
2197 subject_fold = subject?g_utf8_casefold (subject, -1):NULL;
2198 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD,
2199 subject_fold, (GDestroyNotify) g_free);
2204 from_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD);
2205 if (from_fold == NULL) {
2207 from = tny_header_dup_from (header);
2209 from_fold = from?g_utf8_casefold (from, -1):NULL;
2210 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD,
2211 from_fold, (GDestroyNotify) g_free);
2216 to_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_TO_FOLD);
2217 if (to_fold == NULL) {
2219 to = tny_header_dup_to (header);
2221 to_fold = to?g_utf8_casefold (to, -1):NULL;
2222 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_TO_FOLD,
2223 to_fold, (GDestroyNotify) g_free);
2228 cc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_CC_FOLD);
2229 if (cc_fold == NULL) {
2231 cc = tny_header_dup_cc (header);
2233 cc_fold = cc?g_utf8_casefold (cc, -1):NULL;
2234 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_CC_FOLD,
2235 cc_fold, (GDestroyNotify) g_free);
2240 bcc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD);
2241 if (bcc_fold == NULL) {
2243 bcc = tny_header_dup_bcc (header);
2245 bcc_fold = bcc?g_utf8_casefold (bcc, -1):NULL;
2246 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD,
2247 bcc_fold, (GDestroyNotify) g_free);
2254 for (current_word = words; *current_word != NULL; current_word++) {
2256 if ((subject_fold && g_strstr_len (subject_fold, -1, *current_word))
2257 || (cc_fold && g_strstr_len (cc_fold, -1, *current_word))
2258 || (bcc_fold && g_strstr_len (bcc_fold, -1, *current_word))
2259 || (to_fold && g_strstr_len (to_fold, -1, *current_word))
2260 || (from_fold && g_strstr_len (from_fold, -1, *current_word))) {
2272 filter_row (GtkTreeModel *model,
2276 ModestHeaderViewPrivate *priv = NULL;
2277 TnyHeaderFlags flags;
2278 TnyHeader *header = NULL;
2281 gboolean visible = TRUE;
2282 gboolean found = FALSE;
2283 GValue value = {0,};
2284 HeaderViewStatus old_status;
2286 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2287 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2289 /* Get header from model */
2290 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2291 header = (TnyHeader *) g_value_get_object (&value);
2292 g_value_unset (&value);
2293 flags = tny_header_get_flags (header);
2295 /* Get message id from header (ensure is a valid id) */
2301 /* Hide deleted and mark as deleted heders */
2302 if (flags & TNY_HEADER_FLAG_DELETED ||
2303 flags & TNY_HEADER_FLAG_EXPUNGED) {
2308 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2309 if (current_folder_needs_filtering (priv) &&
2310 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2316 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2317 if (current_folder_needs_filtering (priv) &&
2318 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2324 if (visible && priv->filter_string) {
2325 if (!header_match_string (header, priv->filter_string_splitted)) {
2329 if (priv->filter_date_range) {
2330 if ((tny_header_get_date_sent (TNY_HEADER (header)) < priv->date_range_start) ||
2331 ((priv->date_range_end != -1) && (tny_header_get_date_sent (TNY_HEADER (header)) > priv->date_range_end))) {
2338 /* If no data on clipboard, return always TRUE */
2339 if (modest_email_clipboard_cleared(priv->clipboard)) {
2345 if (priv->hidding_ids != NULL) {
2346 id = tny_header_dup_message_id (header);
2347 for (i=0; i < priv->n_selected && !found; i++)
2348 if (priv->hidding_ids[i] != NULL && id != NULL)
2349 found = (!strcmp (priv->hidding_ids[i], id));
2356 old_status = priv->status;
2357 priv->status = ((gboolean) priv->status) && !visible;
2358 if ((priv->notify_status) && (priv->status != old_status)) {
2359 if (priv->status_timeout)
2360 g_source_remove (priv->status_timeout);
2363 NotifyFilterInfo *info;
2365 info = g_slice_new0 (NotifyFilterInfo);
2366 info->self = g_object_ref (G_OBJECT (user_data));
2368 info->folder = tny_header_get_folder (header);
2369 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2370 notify_filter_change,
2372 notify_filter_change_destroy);
2380 _clear_hidding_filter (ModestHeaderView *header_view)
2382 ModestHeaderViewPrivate *priv = NULL;
2385 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2386 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2388 if (priv->hidding_ids != NULL) {
2389 for (i=0; i < priv->n_selected; i++)
2390 g_free (priv->hidding_ids[i]);
2391 g_free(priv->hidding_ids);
2396 modest_header_view_refilter (ModestHeaderView *header_view)
2398 GtkTreeModel *filter_model = NULL;
2399 ModestHeaderViewPrivate *priv = NULL;
2401 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2402 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2404 /* Hide cut headers */
2405 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2406 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
2407 priv->status = HEADER_VIEW_INIT;
2408 modest_header_view_refilter_by_chunks (header_view);
2413 * Called when an account is removed. If I'm showing a folder of the
2414 * account that has been removed then clear the view
2417 on_account_removed (TnyAccountStore *self,
2418 TnyAccount *account,
2421 ModestHeaderViewPrivate *priv = NULL;
2423 /* Ignore changes in transport accounts */
2424 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2427 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2430 TnyAccount *my_account;
2432 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2433 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2434 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2435 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2437 my_account = tny_folder_get_account (priv->folder);
2441 if (my_account == account)
2442 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2443 g_object_unref (my_account);
2449 modest_header_view_add_observer(ModestHeaderView *header_view,
2450 ModestHeaderViewObserver *observer)
2452 ModestHeaderViewPrivate *priv;
2454 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2455 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2457 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2459 g_mutex_lock(priv->observer_list_lock);
2460 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2461 g_mutex_unlock(priv->observer_list_lock);
2465 modest_header_view_remove_observer(ModestHeaderView *header_view,
2466 ModestHeaderViewObserver *observer)
2468 ModestHeaderViewPrivate *priv;
2470 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2471 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2473 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2475 g_mutex_lock(priv->observer_list_lock);
2476 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2477 g_mutex_unlock(priv->observer_list_lock);
2481 modest_header_view_notify_observers(ModestHeaderView *header_view,
2482 GtkTreeModel *model,
2483 const gchar *tny_folder_id)
2485 ModestHeaderViewPrivate *priv = NULL;
2487 ModestHeaderViewObserver *observer;
2490 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2492 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2494 g_mutex_lock(priv->observer_list_lock);
2495 iter = priv->observer_list;
2496 while(iter != NULL){
2497 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2498 modest_header_view_observer_update(observer, model,
2500 iter = g_slist_next(iter);
2502 g_mutex_unlock(priv->observer_list_lock);
2506 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2508 ModestHeaderViewPrivate *priv = NULL;
2510 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2511 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2515 modest_header_view_set_filter (ModestHeaderView *self,
2516 ModestHeaderViewFilter filter)
2518 ModestHeaderViewPrivate *priv;
2520 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2521 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2523 priv->filter |= filter;
2525 if (current_folder_needs_filtering (priv))
2526 modest_header_view_refilter (self);
2530 modest_header_view_unset_filter (ModestHeaderView *self,
2531 ModestHeaderViewFilter filter)
2533 ModestHeaderViewPrivate *priv;
2535 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2536 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2538 priv->filter &= ~filter;
2540 if (current_folder_needs_filtering (priv))
2541 modest_header_view_refilter (self);
2545 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2547 if (strcmp ("style", spec->name) == 0) {
2548 update_style (MODEST_HEADER_VIEW (obj));
2549 gtk_widget_queue_draw (GTK_WIDGET (obj));
2554 update_style (ModestHeaderView *self)
2556 ModestHeaderViewPrivate *priv;
2557 GdkColor style_color;
2558 GdkColor style_active_color;
2559 PangoAttrList *attr_list;
2561 PangoAttribute *attr;
2563 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2564 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2568 attr_list = pango_attr_list_new ();
2569 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2570 gdk_color_parse ("grey", &style_color);
2572 priv->secondary_color = style_color;
2573 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2574 pango_attr_list_insert (attr_list, attr);
2577 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2579 "SmallSystemFont", NULL,
2582 attr = pango_attr_font_desc_new (pango_font_description_copy
2583 (style->font_desc));
2584 pango_attr_list_insert (attr_list, attr);
2586 g_object_set (G_OBJECT (priv->renderer_address),
2587 "foreground-gdk", &(priv->secondary_color),
2588 "foreground-set", TRUE,
2589 "attributes", attr_list,
2591 g_object_set (G_OBJECT (priv->renderer_date_status),
2592 "foreground-gdk", &(priv->secondary_color),
2593 "foreground-set", TRUE,
2594 "attributes", attr_list,
2596 pango_attr_list_unref (attr_list);
2598 g_object_set (G_OBJECT (priv->renderer_address),
2599 "foreground-gdk", &(priv->secondary_color),
2600 "foreground-set", TRUE,
2601 "scale", PANGO_SCALE_SMALL,
2604 g_object_set (G_OBJECT (priv->renderer_date_status),
2605 "foreground-gdk", &(priv->secondary_color),
2606 "foreground-set", TRUE,
2607 "scale", PANGO_SCALE_SMALL,
2612 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2613 priv->active_color = style_active_color;
2614 #ifdef MODEST_TOOLKIT_HILDON2
2615 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2616 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2619 #ifdef MODEST_TOOLKIT_HILDON2
2620 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2626 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2631 GtkTreeModel *tree_model;
2636 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2644 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2645 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2649 gtk_tree_model_get (tree_model, &iter,
2650 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2657 parse_date_side (const gchar *string, time_t *date_side)
2663 gboolean result = FALSE;
2665 if (string && string[0] == '\0') {
2670 casefold = g_utf8_casefold (string, -1);
2671 today = g_utf8_casefold (dgettext ("gtk20", "Today"), -1);
2672 yesterday = g_utf8_casefold (dgettext ("gtk20", "Yesterday"), -1);
2673 date = g_date_new ();
2675 if (g_utf8_collate (casefold, today) == 0) {
2676 *date_side = time (NULL);
2681 if (g_utf8_collate (casefold, yesterday) == 0) {
2682 *date_side = time (NULL) - 24*60*60;
2687 g_date_set_parse (date, string);
2688 if (g_date_valid (date)) {
2690 g_date_to_struct_tm (date, &tm);
2691 *date_side = mktime (&tm);
2706 parse_date_range (const gchar *string, time_t *date_range_start, time_t *date_range_end)
2711 parts = g_strsplit (string, "..", 2);
2714 if (g_strv_length (parts) != 2) {
2721 if (!parse_date_side (parts[0], date_range_start)) {
2726 if (parse_date_side (parts[1], date_range_end)) {
2727 if (*date_range_end == 0) {
2728 *date_range_end = (time_t) -1;
2730 *date_range_end += (24*60*60 - 1);
2743 modest_header_view_set_show_latest (ModestHeaderView *header_view,
2746 ModestHeaderViewPrivate *priv;
2747 GtkTreeModel *sortable, *filter, *model;
2749 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2750 priv->show_latest = show_latest;
2752 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2753 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2754 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2755 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2756 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2758 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (model), priv->show_latest);
2765 modest_header_view_get_show_latest (ModestHeaderView *header_view)
2767 ModestHeaderViewPrivate *priv;
2768 GtkTreeModel *sortable, *filter, *model;
2771 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2773 result = priv->show_latest;
2774 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2775 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2776 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2777 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2778 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2780 result = tny_gtk_header_list_model_get_show_latest (TNY_GTK_HEADER_LIST_MODEL (model));
2789 modest_header_view_get_not_latest (ModestHeaderView *header_view)
2791 ModestHeaderViewPrivate *priv;
2792 gint not_latest = 0;
2793 GtkTreeModel *sortable, *filter, *model;
2795 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2797 if (priv->show_latest == 0)
2800 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2801 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2802 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2803 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2804 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2806 not_latest = MAX (0, tny_list_get_length (TNY_LIST (model)) - priv->show_latest);
2815 modest_header_view_set_filter_string (ModestHeaderView *self,
2816 const gchar *filter_string)
2818 ModestHeaderViewPrivate *priv;
2820 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2821 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2823 if (priv->filter_string)
2824 g_free (priv->filter_string);
2826 priv->filter_string = g_strdup (filter_string);
2827 priv->filter_date_range = FALSE;
2829 if (priv->filter_string_splitted) {
2830 g_strfreev (priv->filter_string_splitted);
2831 priv->filter_string_splitted = NULL;
2834 if (priv->filter_string) {
2835 gchar **split, **current, **current_target;
2837 split = g_strsplit (priv->filter_string, " ", 0);
2839 priv->filter_string_splitted = g_malloc0 (sizeof (gchar *)*(g_strv_length (split) + 1));
2840 current_target = priv->filter_string_splitted;
2841 for (current = split; *current != 0; current ++) {
2842 gboolean has_date_range = FALSE;;
2843 if (g_strstr_len (*current, -1, "..") && strcmp(*current, "..")) {
2844 time_t range_start, range_end;
2845 /* It contains .. but it's not ".." so it may be a date range */
2846 if (parse_date_range (*current, &range_start, &range_end)) {
2847 priv->filter_date_range = TRUE;
2848 has_date_range = TRUE;
2849 priv->date_range_start = range_start;
2850 priv->date_range_end = range_end;
2853 if (!has_date_range) {
2854 *current_target = g_utf8_casefold (*current, -1);
2858 *current_target = '\0';
2861 modest_header_view_refilter (MODEST_HEADER_VIEW (self));
2864 #ifdef MODEST_TOOLKIT_HILDON2
2867 on_live_search_timeout (ModestHeaderView *self)
2869 const gchar *needle;
2870 ModestHeaderViewPrivate *priv;
2872 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2874 needle = hildon_live_search_get_text (HILDON_LIVE_SEARCH (priv->live_search));
2875 if (needle && needle[0] != '\0') {
2876 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), needle);
2877 if (priv->show_latest > 0)
2878 modest_header_view_set_show_latest (MODEST_HEADER_VIEW (self), 0);
2880 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), NULL);
2883 priv->live_search_timeout = 0;
2889 on_live_search_refilter (HildonLiveSearch *livesearch,
2890 ModestHeaderView *self)
2892 ModestHeaderViewPrivate *priv;
2893 GtkTreeModel *model, *sortable, *filter;
2895 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2897 if (priv->live_search_timeout > 0) {
2898 g_source_remove (priv->live_search_timeout);
2899 priv->live_search_timeout = 0;
2903 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2904 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2905 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2906 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2907 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2911 if (model && tny_list_get_length (TNY_LIST (model)) > 250) {
2912 priv->live_search_timeout = g_timeout_add (1000, (GSourceFunc) on_live_search_timeout, self);
2914 on_live_search_timeout (self);
2921 modest_header_view_setup_live_search (ModestHeaderView *self)
2923 ModestHeaderViewPrivate *priv;
2925 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (self), NULL);
2926 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2927 priv->live_search = hildon_live_search_new ();
2929 g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), self);
2931 return priv->live_search;
2936 refilter_idle_handler (gpointer userdata)
2938 ModestHeaderView *self = MODEST_HEADER_VIEW (userdata);
2939 ModestHeaderViewPrivate *priv;
2940 GtkTreeModel *filter_model;
2941 GtkTreeModel *filtered_model;
2945 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2946 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2947 filtered_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
2949 if (filtered_model != priv->filtered_model) {
2950 priv->refilter_handler_id = 0;
2951 priv->filtered_model = NULL;
2955 if (!gtk_tree_model_sort_iter_is_valid (GTK_TREE_MODEL_SORT (filtered_model), &(priv->refilter_iter))) {
2956 priv->refilter_handler_id = 0;
2957 priv->filtered_model = NULL;
2958 modest_header_view_refilter_by_chunks (self);
2965 path = gtk_tree_model_get_path (priv->filtered_model, &(priv->refilter_iter));
2966 gtk_tree_model_row_changed (priv->filtered_model, path, &(priv->refilter_iter));
2967 gtk_tree_path_free (path);
2970 has_more = gtk_tree_model_iter_next (priv->filtered_model, &(priv->refilter_iter));
2971 } while (i < 100 && has_more);
2976 priv->filtered_model = NULL;
2977 priv->refilter_handler_id = 0;
2983 modest_header_view_refilter_by_chunks (ModestHeaderView *self)
2985 ModestHeaderViewPrivate *priv;
2986 GtkTreeModel *filter_model;
2988 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2989 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2991 if (priv->refilter_handler_id > 0) {
2992 g_source_remove (priv->refilter_handler_id);
2993 priv->refilter_handler_id = 0;
2996 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2997 priv->filtered_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
2999 if (gtk_tree_model_get_iter_first (priv->filtered_model, &(priv->refilter_iter))) {
3000 priv->refilter_handler_id = g_idle_add (refilter_idle_handler, self);