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);
133 HEADER_VIEW_NON_EMPTY,
138 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
139 struct _ModestHeaderViewPrivate {
141 ModestHeaderViewStyle style;
144 TnyFolderMonitor *monitor;
145 GMutex *observers_lock;
147 /*header-view-observer observer*/
148 GMutex *observer_list_lock;
149 GSList *observer_list;
151 /* not unref this object, its a singlenton */
152 ModestEmailClipboard *clipboard;
154 /* Filter tree model */
157 GtkTreeRowReference *autoselect_reference;
158 ModestHeaderViewFilter filter;
159 #ifdef MODEST_TOOLKIT_HILDON2
160 GtkWidget *live_search;
161 guint live_search_timeout;
164 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
165 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
167 gulong selection_changed_handler;
168 gulong acc_removed_handler;
170 GList *drag_begin_cached_selected_rows;
172 HeaderViewStatus status;
173 guint status_timeout;
174 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
176 ModestDatetimeFormatter *datetime_formatter;
178 GtkCellRenderer *renderer_subject;
179 GtkCellRenderer *renderer_address;
180 GtkCellRenderer *renderer_date_status;
182 GdkColor active_color;
183 GdkColor secondary_color;
187 gchar *filter_string;
188 gchar **filter_string_splitted;
189 gboolean filter_date_range;
190 time_t date_range_start;
191 time_t date_range_end;
194 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
195 struct _HeadersCountChangedHelper {
196 ModestHeaderView *self;
197 TnyFolderChange *change;
201 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
202 MODEST_TYPE_HEADER_VIEW, \
203 ModestHeaderViewPrivate))
207 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
209 #define _HEADER_VIEW_SUBJECT_FOLD "_subject_modest_header_view"
210 #define _HEADER_VIEW_FROM_FOLD "_from_modest_header_view"
211 #define _HEADER_VIEW_TO_FOLD "_to_modest_header_view"
212 #define _HEADER_VIEW_CC_FOLD "_cc_modest_header_view"
213 #define _HEADER_VIEW_BCC_FOLD "_bcc_modest_header_view"
216 HEADER_SELECTED_SIGNAL,
217 HEADER_ACTIVATED_SIGNAL,
218 ITEM_NOT_FOUND_SIGNAL,
219 MSG_COUNT_CHANGED_SIGNAL,
220 UPDATING_MSG_LIST_SIGNAL,
225 static GObjectClass *parent_class = NULL;
227 /* uncomment the following if you have defined any signals */
228 static guint signals[LAST_SIGNAL] = {0};
231 modest_header_view_get_type (void)
233 static GType my_type = 0;
235 static const GTypeInfo my_info = {
236 sizeof(ModestHeaderViewClass),
237 NULL, /* base init */
238 NULL, /* base finalize */
239 (GClassInitFunc) modest_header_view_class_init,
240 NULL, /* class finalize */
241 NULL, /* class data */
242 sizeof(ModestHeaderView),
244 (GInstanceInitFunc) modest_header_view_init,
248 static const GInterfaceInfo tny_folder_observer_info =
250 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
251 NULL, /* interface_finalize */
252 NULL /* interface_data */
254 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
258 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
259 &tny_folder_observer_info);
267 modest_header_view_class_init (ModestHeaderViewClass *klass)
269 GObjectClass *gobject_class;
270 gobject_class = (GObjectClass*) klass;
272 parent_class = g_type_class_peek_parent (klass);
273 gobject_class->finalize = modest_header_view_finalize;
274 gobject_class->dispose = modest_header_view_dispose;
276 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
278 signals[HEADER_SELECTED_SIGNAL] =
279 g_signal_new ("header_selected",
280 G_TYPE_FROM_CLASS (gobject_class),
282 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
284 g_cclosure_marshal_VOID__POINTER,
285 G_TYPE_NONE, 1, G_TYPE_POINTER);
287 signals[HEADER_ACTIVATED_SIGNAL] =
288 g_signal_new ("header_activated",
289 G_TYPE_FROM_CLASS (gobject_class),
291 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
293 gtk_marshal_VOID__POINTER_POINTER,
294 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
297 signals[ITEM_NOT_FOUND_SIGNAL] =
298 g_signal_new ("item_not_found",
299 G_TYPE_FROM_CLASS (gobject_class),
301 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
303 g_cclosure_marshal_VOID__INT,
304 G_TYPE_NONE, 1, G_TYPE_INT);
306 signals[MSG_COUNT_CHANGED_SIGNAL] =
307 g_signal_new ("msg_count_changed",
308 G_TYPE_FROM_CLASS (gobject_class),
310 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
312 modest_marshal_VOID__POINTER_POINTER,
313 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
315 signals[UPDATING_MSG_LIST_SIGNAL] =
316 g_signal_new ("updating-msg-list",
317 G_TYPE_FROM_CLASS (gobject_class),
319 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
321 g_cclosure_marshal_VOID__BOOLEAN,
322 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
324 #ifdef MODEST_TOOLKIT_HILDON2
325 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
331 tny_folder_observer_init (TnyFolderObserverIface *klass)
333 klass->update = folder_monitor_update;
336 static GtkTreeViewColumn*
337 get_new_column (const gchar *name, GtkCellRenderer *renderer,
338 gboolean resizable, gint sort_col_id, gboolean show_as_text,
339 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
341 GtkTreeViewColumn *column;
343 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
344 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
346 gtk_tree_view_column_set_resizable (column, resizable);
348 gtk_tree_view_column_set_expand (column, TRUE);
351 gtk_tree_view_column_add_attribute (column, renderer, "text",
353 if (sort_col_id >= 0)
354 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
356 gtk_tree_view_column_set_sort_indicator (column, FALSE);
357 gtk_tree_view_column_set_reorderable (column, TRUE);
360 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
367 remove_all_columns (ModestHeaderView *obj)
369 GList *columns, *cursor;
371 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
373 for (cursor = columns; cursor; cursor = cursor->next)
374 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
375 GTK_TREE_VIEW_COLUMN(cursor->data));
376 g_list_free (columns);
380 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
382 GtkTreeModel *sortable, *filter_model;
383 GtkTreeViewColumn *column=NULL;
384 GtkTreeSelection *selection = NULL;
385 GtkCellRenderer *renderer_header,
386 *renderer_attach, *renderer_compact_date_or_status;
387 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
388 *renderer_subject_box, *renderer_recpt,
390 ModestHeaderViewPrivate *priv;
391 GtkTreeViewColumn *compact_column = NULL;
394 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
395 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
397 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
399 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
401 /* TODO: check whether these renderers need to be freed */
402 renderer_attach = gtk_cell_renderer_pixbuf_new ();
403 renderer_priority = gtk_cell_renderer_pixbuf_new ();
404 renderer_header = gtk_cell_renderer_text_new ();
406 renderer_compact_header = modest_vbox_cell_renderer_new ();
407 renderer_recpt_box = modest_hbox_cell_renderer_new ();
408 renderer_subject_box = modest_hbox_cell_renderer_new ();
409 renderer_recpt = gtk_cell_renderer_text_new ();
410 priv->renderer_address = renderer_recpt;
411 priv->renderer_subject = gtk_cell_renderer_text_new ();
412 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
413 priv->renderer_date_status = renderer_compact_date_or_status;
415 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
416 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
417 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
418 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
419 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
420 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
421 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
422 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
423 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
424 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
425 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
426 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
427 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
428 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
430 #ifdef MODEST_TOOLKIT_HILDON2
431 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
433 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
434 #ifndef MODEST_TOOLKIT_GTK
435 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
436 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
438 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
439 g_object_set(G_OBJECT(renderer_header),
440 "ellipsize", PANGO_ELLIPSIZE_END,
442 g_object_set (G_OBJECT (priv->renderer_subject),
443 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
445 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
446 g_object_set (G_OBJECT (renderer_recpt),
447 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
449 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
450 g_object_set(G_OBJECT(renderer_compact_date_or_status),
451 "xalign", 1.0, "yalign", 0.1,
453 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
454 #ifdef MODEST_TOOLKIT_HILDON2
455 g_object_set (G_OBJECT (renderer_priority),
457 "xalign", 0.0, NULL);
458 g_object_set (G_OBJECT (renderer_attach),
460 "xalign", 0.0, NULL);
462 g_object_set (G_OBJECT (renderer_priority),
463 "yalign", 0.5, NULL);
464 g_object_set (G_OBJECT (renderer_attach),
465 "yalign", 0.0, NULL);
468 #ifdef MODEST_TOOLKIT_HILDON1
469 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
470 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
471 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
472 #elif MODEST_TOOLKIT_HILDON2
473 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
474 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
475 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
477 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
478 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
479 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
482 remove_all_columns (self);
484 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
485 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
487 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
488 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
489 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
492 /* Add new columns */
493 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
494 ModestHeaderViewColumn col =
495 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
497 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
498 g_printerr ("modest: invalid column %d in column list\n", col);
504 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
505 column = get_new_column (_("A"), renderer_attach, FALSE,
506 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
508 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
510 gtk_tree_view_column_set_fixed_width (column, 45);
514 case MODEST_HEADER_VIEW_COLUMN_FROM:
515 column = get_new_column (_("From"), renderer_header, TRUE,
516 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
518 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
519 GINT_TO_POINTER(TRUE));
522 case MODEST_HEADER_VIEW_COLUMN_TO:
523 column = get_new_column (_("To"), renderer_header, TRUE,
524 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
526 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
527 GINT_TO_POINTER(FALSE));
530 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
531 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
532 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
534 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
535 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
536 compact_column = column;
539 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
540 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
541 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
543 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
544 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
545 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
546 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
547 compact_column = column;
551 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
552 column = get_new_column (_("Subject"), renderer_header, TRUE,
553 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
555 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
559 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
560 column = get_new_column (_("Received"), renderer_header, TRUE,
561 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
563 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
564 GINT_TO_POINTER(TRUE));
567 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
568 column = get_new_column (_("Sent"), renderer_header, TRUE,
569 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
571 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
572 GINT_TO_POINTER(FALSE));
575 case MODEST_HEADER_VIEW_COLUMN_SIZE:
576 column = get_new_column (_("Size"), renderer_header, TRUE,
577 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
579 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
582 case MODEST_HEADER_VIEW_COLUMN_STATUS:
583 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
584 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
586 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
591 g_return_val_if_reached(FALSE);
594 /* we keep the column id around */
595 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
596 GINT_TO_POINTER(col));
598 /* we need this ptr when sorting the rows */
599 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
601 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
605 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
606 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
607 (GtkTreeIterCompareFunc) cmp_rows,
608 compact_column, NULL);
609 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
610 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
611 (GtkTreeIterCompareFunc) cmp_subject_rows,
612 compact_column, NULL);
616 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
622 datetime_format_changed (ModestDatetimeFormatter *formatter,
623 ModestHeaderView *self)
625 gtk_widget_queue_draw (GTK_WIDGET (self));
629 modest_header_view_init (ModestHeaderView *obj)
631 ModestHeaderViewPrivate *priv;
634 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
636 priv->show_latest = 0;
639 priv->is_outbox = FALSE;
641 priv->monitor = NULL;
642 priv->observers_lock = g_mutex_new ();
643 priv->autoselect_reference = NULL;
645 priv->status = HEADER_VIEW_INIT;
646 priv->status_timeout = 0;
647 priv->notify_status = TRUE;
649 priv->observer_list_lock = g_mutex_new();
650 priv->observer_list = NULL;
652 priv->clipboard = modest_runtime_get_email_clipboard ();
653 priv->hidding_ids = NULL;
654 priv->n_selected = 0;
655 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
656 #ifdef MODEST_TOOLKIT_HILDON2
657 priv->live_search = NULL;
658 priv->live_search_timeout = 0;
660 priv->filter_string = NULL;
661 priv->filter_string_splitted = NULL;
662 priv->filter_date_range = FALSE;
663 priv->selection_changed_handler = 0;
664 priv->acc_removed_handler = 0;
666 /* Sort parameters */
667 for (j=0; j < 2; j++) {
668 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
669 priv->sort_colid[j][i] = -1;
670 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
674 priv->datetime_formatter = modest_datetime_formatter_new ();
675 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
676 G_CALLBACK (datetime_format_changed), (gpointer) obj);
678 setup_drag_and_drop (GTK_WIDGET(obj));
682 modest_header_view_dispose (GObject *obj)
684 ModestHeaderView *self;
685 ModestHeaderViewPrivate *priv;
686 GtkTreeSelection *sel;
688 self = MODEST_HEADER_VIEW(obj);
689 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
691 #ifdef MODEST_TOOLKIT_HILDON2
692 if (priv->live_search_timeout > 0) {
693 g_source_remove (priv->live_search_timeout);
694 priv->live_search_timeout = 0;
698 if (priv->datetime_formatter) {
699 g_object_unref (priv->datetime_formatter);
700 priv->datetime_formatter = NULL;
703 /* Free in the dispose to avoid unref cycles */
705 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
706 g_object_unref (G_OBJECT (priv->folder));
710 /* We need to do this here in the dispose because the
711 selection won't exist when finalizing */
712 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
713 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
714 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
715 priv->selection_changed_handler = 0;
718 G_OBJECT_CLASS(parent_class)->dispose (obj);
722 modest_header_view_finalize (GObject *obj)
724 ModestHeaderView *self;
725 ModestHeaderViewPrivate *priv;
727 self = MODEST_HEADER_VIEW(obj);
728 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
730 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
731 priv->acc_removed_handler)) {
732 g_signal_handler_disconnect (modest_runtime_get_account_store (),
733 priv->acc_removed_handler);
736 /* There is no need to lock because there should not be any
737 * reference to self now. */
738 g_mutex_free(priv->observer_list_lock);
739 g_slist_free(priv->observer_list);
741 g_mutex_lock (priv->observers_lock);
743 tny_folder_monitor_stop (priv->monitor);
744 g_object_unref (G_OBJECT (priv->monitor));
746 g_mutex_unlock (priv->observers_lock);
747 g_mutex_free (priv->observers_lock);
749 /* Clear hidding array created by cut operation */
750 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
752 if (priv->autoselect_reference != NULL) {
753 gtk_tree_row_reference_free (priv->autoselect_reference);
754 priv->autoselect_reference = NULL;
757 if (priv->filter_string) {
758 g_free (priv->filter_string);
761 if (priv->filter_string_splitted) {
762 g_strfreev (priv->filter_string_splitted);
765 G_OBJECT_CLASS(parent_class)->finalize (obj);
770 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
773 GtkTreeSelection *sel;
774 ModestHeaderView *self;
775 ModestHeaderViewPrivate *priv;
777 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
780 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
781 self = MODEST_HEADER_VIEW(obj);
782 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
784 modest_header_view_set_style (self, style);
786 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
787 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
788 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
790 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
791 TRUE); /* alternating row colors */
793 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
794 priv->selection_changed_handler =
795 g_signal_connect_after (sel, "changed",
796 G_CALLBACK(on_selection_changed), self);
798 g_signal_connect (self, "row-activated",
799 G_CALLBACK (on_header_row_activated), NULL);
801 #ifndef MODEST_TOOLKIT_HILDON2
802 g_signal_connect (self, "focus-in-event",
803 G_CALLBACK(on_focus_in), NULL);
804 g_signal_connect (self, "focus-out-event",
805 G_CALLBACK(on_focus_out), NULL);
808 g_signal_connect (self, "button-press-event",
809 G_CALLBACK(on_button_press_event), NULL);
810 g_signal_connect (self, "button-release-event",
811 G_CALLBACK(on_button_release_event), NULL);
813 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
815 G_CALLBACK (on_account_removed),
818 g_signal_connect (self, "expose-event",
819 G_CALLBACK(modest_header_view_on_expose_event),
822 return GTK_WIDGET(self);
827 modest_header_view_count_selected_headers (ModestHeaderView *self)
829 GtkTreeSelection *sel;
832 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
834 /* Get selection object and check selected rows count */
835 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
836 selected_rows = gtk_tree_selection_count_selected_rows (sel);
838 return selected_rows;
842 modest_header_view_has_selected_headers (ModestHeaderView *self)
844 GtkTreeSelection *sel;
847 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
849 /* Get selection object and check selected rows count */
850 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
851 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
858 modest_header_view_get_selected_headers (ModestHeaderView *self)
860 GtkTreeSelection *sel;
861 TnyList *header_list = NULL;
863 GList *list, *tmp = NULL;
864 GtkTreeModel *tree_model = NULL;
867 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
870 /* Get selected rows */
871 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
872 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
875 header_list = tny_simple_list_new();
877 list = g_list_reverse (list);
880 /* get header from selection */
881 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
882 gtk_tree_model_get (tree_model, &iter,
883 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
885 /* Prepend to list */
886 tny_list_prepend (header_list, G_OBJECT (header));
887 g_object_unref (G_OBJECT (header));
889 tmp = g_list_next (tmp);
892 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
899 /* scroll our list view so the selected item is visible */
901 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
903 #ifdef MODEST_TOOLKIT_GTK
905 GtkTreePath *selected_path;
906 GtkTreePath *start, *end;
910 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
911 selected_path = gtk_tree_model_get_path (model, iter);
913 start = gtk_tree_path_new ();
914 end = gtk_tree_path_new ();
916 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
918 if (gtk_tree_path_compare (selected_path, start) < 0 ||
919 gtk_tree_path_compare (end, selected_path) < 0)
920 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
921 selected_path, NULL, TRUE,
924 gtk_tree_path_free (selected_path);
925 gtk_tree_path_free (start);
926 gtk_tree_path_free (end);
928 #endif /* MODEST_TOOLKIT_GTK */
933 modest_header_view_select_next (ModestHeaderView *self)
935 GtkTreeSelection *sel;
940 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
942 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
943 path = get_selected_row (GTK_TREE_VIEW(self), &model);
944 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
945 /* Unselect previous path */
946 gtk_tree_selection_unselect_path (sel, path);
948 /* Move path down and selects new one */
949 if (gtk_tree_model_iter_next (model, &iter)) {
950 gtk_tree_selection_select_iter (sel, &iter);
951 scroll_to_selected (self, &iter, FALSE);
953 gtk_tree_path_free(path);
959 modest_header_view_select_prev (ModestHeaderView *self)
961 GtkTreeSelection *sel;
966 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
968 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
969 path = get_selected_row (GTK_TREE_VIEW(self), &model);
970 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
971 /* Unselect previous path */
972 gtk_tree_selection_unselect_path (sel, path);
975 if (gtk_tree_path_prev (path)) {
976 gtk_tree_model_get_iter (model, &iter, path);
978 /* Select the new one */
979 gtk_tree_selection_select_iter (sel, &iter);
980 scroll_to_selected (self, &iter, TRUE);
983 gtk_tree_path_free (path);
988 modest_header_view_get_columns (ModestHeaderView *self)
990 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
992 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
998 modest_header_view_set_style (ModestHeaderView *self,
999 ModestHeaderViewStyle style)
1001 ModestHeaderViewPrivate *priv;
1002 gboolean show_col_headers = FALSE;
1003 ModestHeaderViewStyle old_style;
1005 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
1006 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
1009 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1010 if (priv->style == style)
1011 return TRUE; /* nothing to do */
1014 case MODEST_HEADER_VIEW_STYLE_DETAILS:
1015 show_col_headers = TRUE;
1017 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
1020 g_return_val_if_reached (FALSE);
1022 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
1023 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
1025 old_style = priv->style;
1026 priv->style = style;
1032 ModestHeaderViewStyle
1033 modest_header_view_get_style (ModestHeaderView *self)
1035 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
1037 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
1040 /* This is used to automatically select the first header if the user
1041 * has not selected any header yet.
1044 modest_header_view_on_expose_event(GtkTreeView *header_view,
1045 GdkEventExpose *event,
1048 GtkTreeSelection *sel;
1049 GtkTreeModel *model;
1050 GtkTreeIter tree_iter;
1051 ModestHeaderViewPrivate *priv;
1053 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1054 model = gtk_tree_view_get_model(header_view);
1059 #ifdef MODEST_TOOLKIT_HILDON2
1062 sel = gtk_tree_view_get_selection(header_view);
1063 if(!gtk_tree_selection_count_selected_rows(sel)) {
1064 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1065 GtkTreePath *tree_iter_path;
1066 /* Prevent the widget from getting the focus
1067 when selecting the first item */
1068 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1069 g_object_set(header_view, "can-focus", FALSE, NULL);
1070 gtk_tree_selection_select_iter(sel, &tree_iter);
1071 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1072 g_object_set(header_view, "can-focus", TRUE, NULL);
1073 if (priv->autoselect_reference) {
1074 gtk_tree_row_reference_free (priv->autoselect_reference);
1076 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1077 gtk_tree_path_free (tree_iter_path);
1080 if (priv->autoselect_reference != NULL && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
1081 gboolean moved_selection = FALSE;
1082 GtkTreePath * last_path;
1083 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1084 moved_selection = TRUE;
1088 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1089 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1090 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1091 moved_selection = TRUE;
1092 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1094 gtk_tree_path_free (last_path);
1096 if (moved_selection) {
1097 gtk_tree_row_reference_free (priv->autoselect_reference);
1098 priv->autoselect_reference = NULL;
1101 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1102 GtkTreePath *current_path;
1103 current_path = gtk_tree_model_get_path (model, &tree_iter);
1104 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1105 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1106 g_object_set(header_view, "can-focus", FALSE, NULL);
1107 gtk_tree_selection_unselect_all (sel);
1108 gtk_tree_selection_select_iter(sel, &tree_iter);
1109 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1110 g_object_set(header_view, "can-focus", TRUE, NULL);
1111 gtk_tree_row_reference_free (priv->autoselect_reference);
1112 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1114 gtk_tree_path_free (current_path);
1115 gtk_tree_path_free (last_path);
1125 modest_header_view_get_folder (ModestHeaderView *self)
1127 ModestHeaderViewPrivate *priv;
1129 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1131 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1134 g_object_ref (priv->folder);
1136 return priv->folder;
1140 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1146 ModestHeaderView *self;
1147 ModestHeaderViewPrivate *priv;
1149 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1151 self = MODEST_HEADER_VIEW (user_data);
1152 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1154 if (cancelled || err)
1157 /* Add IDLE observer (monitor) and another folder observer for
1158 new messages (self) */
1159 g_mutex_lock (priv->observers_lock);
1160 if (priv->monitor) {
1161 tny_folder_monitor_stop (priv->monitor);
1162 g_object_unref (G_OBJECT (priv->monitor));
1164 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1165 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1166 tny_folder_monitor_start (priv->monitor);
1167 g_mutex_unlock (priv->observers_lock);
1171 modest_header_view_set_folder_intern (ModestHeaderView *self,
1177 ModestHeaderViewPrivate *priv;
1178 GList *cols, *cursor;
1179 GtkTreeModel *filter_model, *sortable;
1181 GtkSortType sort_type;
1183 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1185 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1186 tny_gtk_header_list_model_set_update_in_batches (TNY_GTK_HEADER_LIST_MODEL (headers), 300);
1187 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (headers), priv->show_latest);
1189 /* Start the monitor in the callback of the
1190 tny_gtk_header_list_model_set_folder call. It's crucial to
1191 do it there and not just after the call because we want the
1192 monitor to observe only the headers returned by the
1193 tny_folder_get_headers_async call that it's inside the
1194 tny_gtk_header_list_model_set_folder call. This way the
1195 monitor infrastructure could successfully cope with
1196 duplicates. For example if a tny_folder_add_msg_async is
1197 happening while tny_gtk_header_list_model_set_folder is
1198 invoked, then the first call could add a header that will
1199 be added again by tny_gtk_header_list_model_set_folder, so
1200 we'd end up with duplicate headers. sergio */
1201 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1203 set_folder_intern_get_headers_async_cb,
1206 /* Init filter_row function to examine empty status */
1207 priv->status = HEADER_VIEW_INIT;
1209 /* Create sortable model */
1210 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (headers));
1211 g_object_unref (headers);
1213 /* Create a tree model filter to hide and show rows for cut operations */
1214 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (sortable), NULL);
1215 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1216 filter_row, self, NULL);
1217 g_object_unref (sortable);
1219 /* install our special sorting functions */
1220 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1222 /* Restore sort column id */
1224 type = modest_tny_folder_guess_folder_type (folder);
1225 if (type == TNY_FOLDER_TYPE_INVALID)
1226 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1228 sort_colid = modest_header_view_get_sort_column_id (self, type);
1229 sort_type = modest_header_view_get_sort_type (self, type);
1230 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1233 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1234 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1235 (GtkTreeIterCompareFunc) cmp_rows,
1237 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1238 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1239 (GtkTreeIterCompareFunc) cmp_subject_rows,
1244 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1245 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1246 g_object_unref (filter_model);
1253 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1255 GtkSortType sort_type)
1257 ModestHeaderViewPrivate *priv = NULL;
1258 GtkTreeModel *sortable = NULL, *filter_model = NULL;
1261 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1262 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1264 /* Get model and private data */
1265 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1266 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1267 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
1268 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1271 /* Sort tree model */
1272 type = modest_tny_folder_guess_folder_type (priv->folder);
1273 if (type == TNY_FOLDER_TYPE_INVALID)
1274 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1276 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1279 /* Store new sort parameters */
1280 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1285 modest_header_view_set_sort_params (ModestHeaderView *self,
1287 GtkSortType sort_type,
1290 ModestHeaderViewPrivate *priv;
1291 ModestHeaderViewStyle style;
1293 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1294 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1295 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1297 style = modest_header_view_get_style (self);
1298 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1300 priv->sort_colid[style][type] = sort_colid;
1301 priv->sort_type[style][type] = sort_type;
1305 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1308 ModestHeaderViewPrivate *priv;
1309 ModestHeaderViewStyle style;
1311 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1312 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1314 style = modest_header_view_get_style (self);
1315 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1317 return priv->sort_colid[style][type];
1321 modest_header_view_get_sort_type (ModestHeaderView *self,
1324 ModestHeaderViewPrivate *priv;
1325 ModestHeaderViewStyle style;
1327 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1328 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1330 style = modest_header_view_get_style (self);
1331 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1333 return priv->sort_type[style][type];
1337 ModestHeaderView *header_view;
1338 RefreshAsyncUserCallback cb;
1343 folder_refreshed_cb (ModestMailOperation *mail_op,
1347 ModestHeaderViewPrivate *priv;
1348 SetFolderHelper *info;
1350 info = (SetFolderHelper*) user_data;
1352 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1356 info->cb (mail_op, folder, info->user_data);
1358 /* Start the folder count changes observer. We do not need it
1359 before the refresh. Note that the monitor could still be
1360 called for this refresh but now we know that the callback
1361 was previously called */
1362 g_mutex_lock (priv->observers_lock);
1363 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1364 g_mutex_unlock (priv->observers_lock);
1366 /* Notify the observers that the update is over */
1367 g_signal_emit (G_OBJECT (info->header_view),
1368 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1370 /* Allow filtering notifications from now on if the current
1371 folder is still the same (if not then the user has selected
1372 another one to refresh, we should wait until that refresh
1374 if (priv->folder == folder)
1375 priv->notify_status = TRUE;
1378 g_object_unref (info->header_view);
1383 refresh_folder_error_handler (ModestMailOperation *mail_op,
1386 const GError *error = modest_mail_operation_get_error (mail_op);
1388 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1389 error->code == TNY_IO_ERROR_WRITE ||
1390 error->code == TNY_IO_ERROR_READ) {
1391 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1392 /* If the mail op has been cancelled then it's not an error: don't show any message */
1393 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1394 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1395 modest_platform_information_banner (NULL, NULL, msg);
1402 modest_header_view_set_folder (ModestHeaderView *self,
1405 ModestWindow *progress_window,
1406 RefreshAsyncUserCallback callback,
1409 ModestHeaderViewPrivate *priv;
1411 g_return_if_fail (self);
1413 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1416 if (priv->status_timeout) {
1417 g_source_remove (priv->status_timeout);
1418 priv->status_timeout = 0;
1421 g_mutex_lock (priv->observers_lock);
1422 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1423 g_object_unref (priv->folder);
1424 priv->folder = NULL;
1425 g_mutex_unlock (priv->observers_lock);
1429 GtkTreeSelection *selection;
1430 SetFolderHelper *info;
1431 ModestMailOperation *mail_op = NULL;
1433 /* Set folder in the model */
1434 modest_header_view_set_folder_intern (self, folder, refresh);
1436 /* Pick my reference. Nothing to do with the mail operation */
1437 priv->folder = g_object_ref (folder);
1439 /* Do not notify about filterings until the refresh finishes */
1440 priv->notify_status = FALSE;
1442 /* Clear the selection if exists */
1443 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1444 gtk_tree_selection_unselect_all(selection);
1445 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1447 /* Notify the observers that the update begins */
1448 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1451 /* create the helper */
1452 info = g_malloc0 (sizeof (SetFolderHelper));
1453 info->header_view = g_object_ref (self);
1454 info->cb = callback;
1455 info->user_data = user_data;
1457 /* Create the mail operation (source will be the parent widget) */
1458 if (progress_window)
1459 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1460 refresh_folder_error_handler,
1463 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1466 /* Refresh the folder asynchronously */
1467 modest_mail_operation_refresh_folder (mail_op,
1469 folder_refreshed_cb,
1472 folder_refreshed_cb (mail_op, folder, info);
1476 g_object_unref (mail_op);
1478 g_mutex_lock (priv->observers_lock);
1480 if (priv->monitor) {
1481 tny_folder_monitor_stop (priv->monitor);
1482 g_object_unref (G_OBJECT (priv->monitor));
1483 priv->monitor = NULL;
1486 if (priv->autoselect_reference) {
1487 gtk_tree_row_reference_free (priv->autoselect_reference);
1488 priv->autoselect_reference = NULL;
1491 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1493 modest_header_view_notify_observers(self, NULL, NULL);
1495 g_mutex_unlock (priv->observers_lock);
1497 /* Notify the observers that the update is over */
1498 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1504 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1505 GtkTreeViewColumn *column, gpointer userdata)
1507 ModestHeaderView *self = NULL;
1509 GtkTreeModel *model = NULL;
1510 TnyHeader *header = NULL;
1511 TnyHeaderFlags flags;
1513 self = MODEST_HEADER_VIEW (treeview);
1515 model = gtk_tree_view_get_model (treeview);
1516 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1519 /* get the first selected item */
1520 gtk_tree_model_get (model, &iter,
1521 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1522 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1525 /* Dont open DELETED messages */
1526 if (flags & TNY_HEADER_FLAG_DELETED) {
1529 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1530 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1531 modest_platform_information_banner (NULL, NULL, msg);
1537 g_signal_emit (G_OBJECT(self),
1538 signals[HEADER_ACTIVATED_SIGNAL],
1544 g_object_unref (G_OBJECT (header));
1549 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1551 GtkTreeModel *model;
1552 TnyHeader *header = NULL;
1553 GtkTreePath *path = NULL;
1555 ModestHeaderView *self;
1556 GList *selected = NULL;
1558 g_return_if_fail (sel);
1559 g_return_if_fail (user_data);
1561 self = MODEST_HEADER_VIEW (user_data);
1563 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1564 if (selected != NULL)
1565 path = (GtkTreePath *) selected->data;
1566 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1567 return; /* msg was _un_selected */
1569 gtk_tree_model_get (model, &iter,
1570 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1574 g_signal_emit (G_OBJECT(self),
1575 signals[HEADER_SELECTED_SIGNAL],
1578 g_object_unref (G_OBJECT (header));
1580 /* free all items in 'selected' */
1581 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1582 g_list_free (selected);
1586 /* PROTECTED method. It's useful when we want to force a given
1587 selection to reload a msg. For example if we have selected a header
1588 in offline mode, when Modest become online, we want to reload the
1589 message automatically without an user click over the header */
1591 _modest_header_view_change_selection (GtkTreeSelection *selection,
1594 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1595 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1597 on_selection_changed (selection, user_data);
1601 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1608 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1612 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1616 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1624 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1632 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1633 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1637 case TNY_HEADER_FLAG_ATTACHMENTS:
1639 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1640 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1641 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1642 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1644 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1645 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1647 return cmp ? cmp : t1 - t2;
1649 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1650 TnyHeader *header1 = NULL, *header2 = NULL;
1652 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1653 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1654 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1655 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1657 /* This is for making priority values respect the intuitive sort relationship
1658 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1660 if (header1 && header2) {
1661 cmp = compare_priorities (tny_header_get_priority (header1),
1662 tny_header_get_priority (header2));
1663 g_object_unref (header1);
1664 g_object_unref (header2);
1666 return cmp ? cmp : t1 - t2;
1672 return &iter1 - &iter2; /* oughhhh */
1677 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1684 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1686 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1687 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1688 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1689 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1691 /* Do not use the prefixes for sorting. Consume all the blank
1692 spaces for sorting */
1693 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1694 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1697 /* If they're equal based on subject without prefix then just
1698 sort them by length. This will show messages like this.
1705 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1712 /* Drag and drop stuff */
1714 drag_data_get_cb (GtkWidget *widget,
1715 GdkDragContext *context,
1716 GtkSelectionData *selection_data,
1721 ModestHeaderView *self = NULL;
1722 ModestHeaderViewPrivate *priv = NULL;
1724 self = MODEST_HEADER_VIEW (widget);
1725 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1727 /* Set the data. Do not use the current selection because it
1728 could be different than the selection at the beginning of
1730 modest_dnd_selection_data_set_paths (selection_data,
1731 priv->drag_begin_cached_selected_rows);
1735 * We're caching the selected rows at the beginning because the
1736 * selection could change between drag-begin and drag-data-get, for
1737 * example if we have a set of rows already selected, and then we
1738 * click in one of them (without SHIFT key pressed) and begin a drag,
1739 * the selection at that moment contains all the selected lines, but
1740 * after dropping the selection, the release event provokes that only
1741 * the row used to begin the drag is selected, so at the end the
1742 * drag&drop affects only one rows instead of all the selected ones.
1746 drag_begin_cb (GtkWidget *widget,
1747 GdkDragContext *context,
1750 ModestHeaderView *self = NULL;
1751 ModestHeaderViewPrivate *priv = NULL;
1752 GtkTreeSelection *selection;
1754 self = MODEST_HEADER_VIEW (widget);
1755 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1757 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1758 priv->drag_begin_cached_selected_rows =
1759 gtk_tree_selection_get_selected_rows (selection, NULL);
1763 * We use the drag-end signal to clear the cached selection, we use
1764 * this because this allways happens, whether or not the d&d was a
1768 drag_end_cb (GtkWidget *widget,
1772 ModestHeaderView *self = NULL;
1773 ModestHeaderViewPrivate *priv = NULL;
1775 self = MODEST_HEADER_VIEW (widget);
1776 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1778 /* Free cached data */
1779 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1780 g_list_free (priv->drag_begin_cached_selected_rows);
1781 priv->drag_begin_cached_selected_rows = NULL;
1784 /* Header view drag types */
1785 const GtkTargetEntry header_view_drag_types[] = {
1786 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1790 enable_drag_and_drop (GtkWidget *self)
1792 #ifdef MODEST_TOOLKIT_HILDON2
1795 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1796 header_view_drag_types,
1797 G_N_ELEMENTS (header_view_drag_types),
1798 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1802 disable_drag_and_drop (GtkWidget *self)
1804 #ifdef MODEST_TOOLKIT_HILDON2
1807 gtk_drag_source_unset (self);
1811 setup_drag_and_drop (GtkWidget *self)
1813 #ifdef MODEST_TOOLKIT_HILDON2
1816 enable_drag_and_drop(self);
1817 g_signal_connect(G_OBJECT (self), "drag_data_get",
1818 G_CALLBACK(drag_data_get_cb), NULL);
1820 g_signal_connect(G_OBJECT (self), "drag_begin",
1821 G_CALLBACK(drag_begin_cb), NULL);
1823 g_signal_connect(G_OBJECT (self), "drag_end",
1824 G_CALLBACK(drag_end_cb), NULL);
1827 static GtkTreePath *
1828 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1830 GtkTreePath *path = NULL;
1831 GtkTreeSelection *sel = NULL;
1834 sel = gtk_tree_view_get_selection(self);
1835 rows = gtk_tree_selection_get_selected_rows (sel, model);
1837 if ((rows == NULL) || (g_list_length(rows) != 1))
1840 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1845 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1851 #ifndef MODEST_TOOLKIT_HILDON2
1853 * This function moves the tree view scroll to the current selected
1854 * row when the widget grabs the focus
1857 on_focus_in (GtkWidget *self,
1858 GdkEventFocus *event,
1861 GtkTreeSelection *selection;
1862 GtkTreeModel *model;
1863 GList *selected = NULL;
1864 GtkTreePath *selected_path = NULL;
1866 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1870 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1871 /* If none selected yet, pick the first one */
1872 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1876 /* Return if the model is empty */
1877 if (!gtk_tree_model_get_iter_first (model, &iter))
1880 path = gtk_tree_model_get_path (model, &iter);
1881 gtk_tree_selection_select_path (selection, path);
1882 gtk_tree_path_free (path);
1885 /* Need to get the all the rows because is selection multiple */
1886 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1887 if (selected == NULL) return FALSE;
1888 selected_path = (GtkTreePath *) selected->data;
1891 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1892 g_list_free (selected);
1898 on_focus_out (GtkWidget *self,
1899 GdkEventFocus *event,
1903 if (!gtk_widget_is_focus (self)) {
1904 GtkTreeSelection *selection = NULL;
1905 GList *selected_rows = NULL;
1906 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1907 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1908 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1909 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1910 gtk_tree_selection_unselect_all (selection);
1911 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1912 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1913 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1914 g_list_free (selected_rows);
1922 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1924 enable_drag_and_drop(self);
1929 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1931 GtkTreeSelection *selection = NULL;
1932 GtkTreePath *path = NULL;
1933 gboolean already_selected = FALSE, already_opened = FALSE;
1934 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1936 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1938 GtkTreeModel *model;
1940 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1941 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1943 /* Get header from model */
1944 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1945 if (gtk_tree_model_get_iter (model, &iter, path)) {
1946 GValue value = {0,};
1949 gtk_tree_model_get_value (model, &iter,
1950 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1952 header = (TnyHeader *) g_value_get_object (&value);
1953 if (TNY_IS_HEADER (header)) {
1954 status = modest_tny_all_send_queues_get_msg_status (header);
1955 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1958 g_value_unset (&value);
1962 /* Enable drag and drop only if the user clicks on a row that
1963 it's already selected. If not, let him select items using
1964 the pointer. If the message is in an OUTBOX and in sending
1965 status disable drag and drop as well */
1966 if (!already_selected ||
1967 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1969 disable_drag_and_drop(self);
1972 gtk_tree_path_free(path);
1974 /* If it's already opened then do not let the button-press
1975 event go on because it'll perform a message open because
1976 we're clicking on to an already selected header */
1981 folder_monitor_update (TnyFolderObserver *self,
1982 TnyFolderChange *change)
1984 ModestHeaderViewPrivate *priv = NULL;
1985 TnyFolderChangeChanged changed;
1986 TnyFolder *folder = NULL;
1988 changed = tny_folder_change_get_changed (change);
1990 /* Do not notify the observers if the folder of the header
1991 view has changed before this call to the observer
1993 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1994 folder = tny_folder_change_get_folder (change);
1995 if (folder != priv->folder)
1998 MODEST_DEBUG_BLOCK (
1999 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
2000 g_print ("ADDED %d/%d (r/t) \n",
2001 tny_folder_change_get_new_unread_count (change),
2002 tny_folder_change_get_new_all_count (change));
2003 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
2004 g_print ("ALL COUNT %d\n",
2005 tny_folder_change_get_new_all_count (change));
2006 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
2007 g_print ("UNREAD COUNT %d\n",
2008 tny_folder_change_get_new_unread_count (change));
2009 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
2010 g_print ("EXPUNGED %d/%d (r/t) \n",
2011 tny_folder_change_get_new_unread_count (change),
2012 tny_folder_change_get_new_all_count (change));
2013 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
2014 g_print ("FOLDER RENAME\n");
2015 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
2016 g_print ("MSG RECEIVED %d/%d (r/t) \n",
2017 tny_folder_change_get_new_unread_count (change),
2018 tny_folder_change_get_new_all_count (change));
2019 g_print ("---------------------------------------------------\n");
2022 /* Check folder count */
2023 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
2024 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
2026 g_mutex_lock (priv->observers_lock);
2028 /* Emit signal to evaluate how headers changes affects
2029 to the window view */
2030 g_signal_emit (G_OBJECT(self),
2031 signals[MSG_COUNT_CHANGED_SIGNAL],
2034 /* Added or removed headers, so data stored on cliboard are invalid */
2035 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
2036 modest_email_clipboard_clear (priv->clipboard);
2038 g_mutex_unlock (priv->observers_lock);
2044 g_object_unref (folder);
2048 modest_header_view_is_empty (ModestHeaderView *self)
2050 ModestHeaderViewPrivate *priv;
2052 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
2054 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2056 return priv->status == HEADER_VIEW_EMPTY;
2060 modest_header_view_clear (ModestHeaderView *self)
2062 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2064 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2068 modest_header_view_copy_selection (ModestHeaderView *header_view)
2070 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2072 /* Copy selection */
2073 _clipboard_set_selected_data (header_view, FALSE);
2077 modest_header_view_cut_selection (ModestHeaderView *header_view)
2079 ModestHeaderViewPrivate *priv = NULL;
2080 const gchar **hidding = NULL;
2081 guint i, n_selected;
2083 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2085 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2087 /* Copy selection */
2088 _clipboard_set_selected_data (header_view, TRUE);
2090 /* Get hidding ids */
2091 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2093 /* Clear hidding array created by previous cut operation */
2094 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2096 /* Copy hidding array */
2097 priv->n_selected = n_selected;
2098 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2099 for (i=0; i < n_selected; i++)
2100 priv->hidding_ids[i] = g_strdup(hidding[i]);
2102 /* Hide cut headers */
2103 modest_header_view_refilter (header_view);
2110 _clipboard_set_selected_data (ModestHeaderView *header_view,
2113 ModestHeaderViewPrivate *priv = NULL;
2114 TnyList *headers = NULL;
2116 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2117 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2119 /* Set selected data on clipboard */
2120 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2121 headers = modest_header_view_get_selected_headers (header_view);
2122 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2125 g_object_unref (headers);
2129 ModestHeaderView *self;
2134 notify_filter_change (gpointer data)
2136 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2138 g_signal_emit (info->self,
2139 signals[MSG_COUNT_CHANGED_SIGNAL],
2140 0, info->folder, NULL);
2146 notify_filter_change_destroy (gpointer data)
2148 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2149 ModestHeaderViewPrivate *priv;
2151 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2152 priv->status_timeout = 0;
2154 g_object_unref (info->self);
2155 g_object_unref (info->folder);
2156 g_slice_free (NotifyFilterInfo, info);
2160 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2162 /* For the moment we only need to filter outbox */
2163 return priv->is_outbox;
2167 header_match_string (TnyHeader *header, gchar **words)
2169 gchar *subject_fold;
2175 gchar **current_word;
2178 subject_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD);
2179 if (subject_fold == NULL) {
2181 subject = tny_header_dup_subject (header);
2182 if (subject != NULL) {
2183 subject_fold = subject?g_utf8_casefold (subject, -1):NULL;
2184 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD,
2185 subject_fold, (GDestroyNotify) g_free);
2190 from_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD);
2191 if (from_fold == NULL) {
2193 from = tny_header_dup_from (header);
2195 from_fold = from?g_utf8_casefold (from, -1):NULL;
2196 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD,
2197 from_fold, (GDestroyNotify) g_free);
2202 to_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_TO_FOLD);
2203 if (to_fold == NULL) {
2205 to = tny_header_dup_to (header);
2207 to_fold = to?g_utf8_casefold (to, -1):NULL;
2208 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_TO_FOLD,
2209 to_fold, (GDestroyNotify) g_free);
2214 cc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_CC_FOLD);
2215 if (cc_fold == NULL) {
2217 cc = tny_header_dup_cc (header);
2219 cc_fold = cc?g_utf8_casefold (cc, -1):NULL;
2220 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_CC_FOLD,
2221 cc_fold, (GDestroyNotify) g_free);
2226 bcc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD);
2227 if (bcc_fold == NULL) {
2229 bcc = tny_header_dup_bcc (header);
2231 bcc_fold = bcc?g_utf8_casefold (bcc, -1):NULL;
2232 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD,
2233 bcc_fold, (GDestroyNotify) g_free);
2240 for (current_word = words; *current_word != NULL; current_word++) {
2242 if ((subject_fold && g_strstr_len (subject_fold, -1, *current_word))
2243 || (cc_fold && g_strstr_len (cc_fold, -1, *current_word))
2244 || (bcc_fold && g_strstr_len (bcc_fold, -1, *current_word))
2245 || (to_fold && g_strstr_len (to_fold, -1, *current_word))
2246 || (from_fold && g_strstr_len (from_fold, -1, *current_word))) {
2258 filter_row (GtkTreeModel *model,
2262 ModestHeaderViewPrivate *priv = NULL;
2263 TnyHeaderFlags flags;
2264 TnyHeader *header = NULL;
2267 gboolean visible = TRUE;
2268 gboolean found = FALSE;
2269 GValue value = {0,};
2270 HeaderViewStatus old_status;
2272 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2273 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2275 /* Get header from model */
2276 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2277 header = (TnyHeader *) g_value_get_object (&value);
2278 g_value_unset (&value);
2279 flags = tny_header_get_flags (header);
2281 /* Get message id from header (ensure is a valid id) */
2287 /* Hide deleted and mark as deleted heders */
2288 if (flags & TNY_HEADER_FLAG_DELETED ||
2289 flags & TNY_HEADER_FLAG_EXPUNGED) {
2294 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2295 if (current_folder_needs_filtering (priv) &&
2296 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2302 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2303 if (current_folder_needs_filtering (priv) &&
2304 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2310 if (visible && priv->filter_string) {
2311 if (!header_match_string (header, priv->filter_string_splitted)) {
2315 if (priv->filter_date_range) {
2316 if ((tny_header_get_date_sent (TNY_HEADER (header)) < priv->date_range_start) ||
2317 ((priv->date_range_end != -1) && (tny_header_get_date_sent (TNY_HEADER (header)) > priv->date_range_end))) {
2324 /* If no data on clipboard, return always TRUE */
2325 if (modest_email_clipboard_cleared(priv->clipboard)) {
2331 if (priv->hidding_ids != NULL) {
2332 id = tny_header_dup_message_id (header);
2333 for (i=0; i < priv->n_selected && !found; i++)
2334 if (priv->hidding_ids[i] != NULL && id != NULL)
2335 found = (!strcmp (priv->hidding_ids[i], id));
2342 old_status = priv->status;
2343 priv->status = ((gboolean) priv->status) && !visible;
2344 if ((priv->notify_status) && (priv->status != old_status)) {
2345 if (priv->status_timeout)
2346 g_source_remove (priv->status_timeout);
2349 NotifyFilterInfo *info;
2351 info = g_slice_new0 (NotifyFilterInfo);
2352 info->self = g_object_ref (G_OBJECT (user_data));
2354 info->folder = tny_header_get_folder (header);
2355 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2356 notify_filter_change,
2358 notify_filter_change_destroy);
2366 _clear_hidding_filter (ModestHeaderView *header_view)
2368 ModestHeaderViewPrivate *priv = NULL;
2371 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2372 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2374 if (priv->hidding_ids != NULL) {
2375 for (i=0; i < priv->n_selected; i++)
2376 g_free (priv->hidding_ids[i]);
2377 g_free(priv->hidding_ids);
2382 modest_header_view_refilter (ModestHeaderView *header_view)
2384 GtkTreeModel *filter_model = NULL;
2385 ModestHeaderViewPrivate *priv = NULL;
2387 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2388 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2390 /* Hide cut headers */
2391 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2392 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
2393 priv->status = HEADER_VIEW_INIT;
2394 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2399 * Called when an account is removed. If I'm showing a folder of the
2400 * account that has been removed then clear the view
2403 on_account_removed (TnyAccountStore *self,
2404 TnyAccount *account,
2407 ModestHeaderViewPrivate *priv = NULL;
2409 /* Ignore changes in transport accounts */
2410 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2413 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2416 TnyAccount *my_account;
2418 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2419 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2420 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2421 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2423 my_account = tny_folder_get_account (priv->folder);
2427 if (my_account == account)
2428 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2429 g_object_unref (my_account);
2435 modest_header_view_add_observer(ModestHeaderView *header_view,
2436 ModestHeaderViewObserver *observer)
2438 ModestHeaderViewPrivate *priv;
2440 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2441 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2443 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2445 g_mutex_lock(priv->observer_list_lock);
2446 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2447 g_mutex_unlock(priv->observer_list_lock);
2451 modest_header_view_remove_observer(ModestHeaderView *header_view,
2452 ModestHeaderViewObserver *observer)
2454 ModestHeaderViewPrivate *priv;
2456 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2457 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2459 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2461 g_mutex_lock(priv->observer_list_lock);
2462 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2463 g_mutex_unlock(priv->observer_list_lock);
2467 modest_header_view_notify_observers(ModestHeaderView *header_view,
2468 GtkTreeModel *model,
2469 const gchar *tny_folder_id)
2471 ModestHeaderViewPrivate *priv = NULL;
2473 ModestHeaderViewObserver *observer;
2476 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2478 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2480 g_mutex_lock(priv->observer_list_lock);
2481 iter = priv->observer_list;
2482 while(iter != NULL){
2483 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2484 modest_header_view_observer_update(observer, model,
2486 iter = g_slist_next(iter);
2488 g_mutex_unlock(priv->observer_list_lock);
2492 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2494 ModestHeaderViewPrivate *priv = NULL;
2496 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2497 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2501 modest_header_view_set_filter (ModestHeaderView *self,
2502 ModestHeaderViewFilter filter)
2504 ModestHeaderViewPrivate *priv;
2506 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2507 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2509 priv->filter |= filter;
2511 if (current_folder_needs_filtering (priv))
2512 modest_header_view_refilter (self);
2516 modest_header_view_unset_filter (ModestHeaderView *self,
2517 ModestHeaderViewFilter filter)
2519 ModestHeaderViewPrivate *priv;
2521 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2522 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2524 priv->filter &= ~filter;
2526 if (current_folder_needs_filtering (priv))
2527 modest_header_view_refilter (self);
2531 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2533 if (strcmp ("style", spec->name) == 0) {
2534 update_style (MODEST_HEADER_VIEW (obj));
2535 gtk_widget_queue_draw (GTK_WIDGET (obj));
2540 update_style (ModestHeaderView *self)
2542 ModestHeaderViewPrivate *priv;
2543 GdkColor style_color;
2544 GdkColor style_active_color;
2545 PangoAttrList *attr_list;
2547 PangoAttribute *attr;
2549 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2550 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2554 attr_list = pango_attr_list_new ();
2555 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2556 gdk_color_parse ("grey", &style_color);
2558 priv->secondary_color = style_color;
2559 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2560 pango_attr_list_insert (attr_list, attr);
2563 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2565 "SmallSystemFont", NULL,
2568 attr = pango_attr_font_desc_new (pango_font_description_copy
2569 (style->font_desc));
2570 pango_attr_list_insert (attr_list, attr);
2572 g_object_set (G_OBJECT (priv->renderer_address),
2573 "foreground-gdk", &(priv->secondary_color),
2574 "foreground-set", TRUE,
2575 "attributes", attr_list,
2577 g_object_set (G_OBJECT (priv->renderer_date_status),
2578 "foreground-gdk", &(priv->secondary_color),
2579 "foreground-set", TRUE,
2580 "attributes", attr_list,
2582 pango_attr_list_unref (attr_list);
2584 g_object_set (G_OBJECT (priv->renderer_address),
2585 "foreground-gdk", &(priv->secondary_color),
2586 "foreground-set", TRUE,
2587 "scale", PANGO_SCALE_SMALL,
2590 g_object_set (G_OBJECT (priv->renderer_date_status),
2591 "foreground-gdk", &(priv->secondary_color),
2592 "foreground-set", TRUE,
2593 "scale", PANGO_SCALE_SMALL,
2598 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2599 priv->active_color = style_active_color;
2600 #ifdef MODEST_TOOLKIT_HILDON2
2601 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2602 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2605 #ifdef MODEST_TOOLKIT_HILDON2
2606 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2612 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2617 GtkTreeModel *tree_model;
2622 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2630 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2631 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2635 gtk_tree_model_get (tree_model, &iter,
2636 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2643 parse_date_side (const gchar *string, time_t *date_side)
2649 gboolean result = FALSE;
2651 if (string && string[0] == '\0') {
2656 casefold = g_utf8_casefold (string, -1);
2657 today = g_utf8_casefold (dgettext ("gtk20", "Today"), -1);
2658 yesterday = g_utf8_casefold (dgettext ("gtk20", "Yesterday"), -1);
2659 date = g_date_new ();
2661 if (g_utf8_collate (casefold, today) == 0) {
2662 *date_side = time (NULL);
2667 if (g_utf8_collate (casefold, yesterday) == 0) {
2668 *date_side = time (NULL) - 24*60*60;
2673 g_date_set_parse (date, string);
2674 if (g_date_valid (date)) {
2676 g_date_to_struct_tm (date, &tm);
2677 *date_side = mktime (&tm);
2692 parse_date_range (const gchar *string, time_t *date_range_start, time_t *date_range_end)
2697 parts = g_strsplit (string, "..", 2);
2700 if (g_strv_length (parts) != 2) {
2707 if (!parse_date_side (parts[0], date_range_start)) {
2712 if (parse_date_side (parts[1], date_range_end)) {
2713 if (*date_range_end == 0) {
2714 *date_range_end = (time_t) -1;
2716 *date_range_end += (24*60*60 - 1);
2729 modest_header_view_set_show_latest (ModestHeaderView *header_view,
2732 ModestHeaderViewPrivate *priv;
2733 GtkTreeModel *sortable, *filter, *model;
2735 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2736 priv->show_latest = show_latest;
2738 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2739 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2740 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2741 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2742 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2744 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (model), priv->show_latest);
2751 modest_header_view_get_show_latest (ModestHeaderView *header_view)
2753 ModestHeaderViewPrivate *priv;
2754 GtkTreeModel *sortable, *filter, *model;
2757 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2759 result = priv->show_latest;
2760 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2761 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2762 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2763 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2764 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2766 result = tny_gtk_header_list_model_get_show_latest (TNY_GTK_HEADER_LIST_MODEL (model));
2775 modest_header_view_get_not_latest (ModestHeaderView *header_view)
2777 ModestHeaderViewPrivate *priv;
2778 gint not_latest = 0;
2779 GtkTreeModel *sortable, *filter, *model;
2781 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2783 if (priv->show_latest == 0)
2786 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2787 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2788 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2789 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2790 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2792 not_latest = MAX (0, tny_list_get_length (TNY_LIST (model)) - priv->show_latest);
2801 modest_header_view_set_filter_string (ModestHeaderView *self,
2802 const gchar *filter_string)
2804 ModestHeaderViewPrivate *priv;
2806 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2807 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2809 if (priv->filter_string)
2810 g_free (priv->filter_string);
2812 priv->filter_string = g_strdup (filter_string);
2813 priv->filter_date_range = FALSE;
2815 if (priv->filter_string_splitted) {
2816 g_strfreev (priv->filter_string_splitted);
2817 priv->filter_string_splitted = NULL;
2820 if (priv->filter_string) {
2821 gchar **split, **current, **current_target;
2823 split = g_strsplit (priv->filter_string, " ", 0);
2825 priv->filter_string_splitted = g_malloc0 (sizeof (gchar *)*(g_strv_length (split) + 1));
2826 current_target = priv->filter_string_splitted;
2827 for (current = split; *current != 0; current ++) {
2828 gboolean has_date_range = FALSE;;
2829 if (g_strstr_len (*current, -1, "..") && strcmp(*current, "..")) {
2830 time_t range_start, range_end;
2831 /* It contains .. but it's not ".." so it may be a date range */
2832 if (parse_date_range (*current, &range_start, &range_end)) {
2833 priv->filter_date_range = TRUE;
2834 has_date_range = TRUE;
2835 priv->date_range_start = range_start;
2836 priv->date_range_end = range_end;
2839 if (!has_date_range) {
2840 *current_target = g_utf8_casefold (*current, -1);
2844 *current_target = '\0';
2847 modest_header_view_refilter (MODEST_HEADER_VIEW (self));
2850 #ifdef MODEST_TOOLKIT_HILDON2
2853 on_live_search_timeout (ModestHeaderView *self)
2855 const gchar *needle;
2856 ModestHeaderViewPrivate *priv;
2858 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2860 needle = hildon_live_search_get_text (HILDON_LIVE_SEARCH (priv->live_search));
2861 if (needle && needle[0] != '\0') {
2862 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), needle);
2863 if (priv->show_latest > 0)
2864 modest_header_view_set_show_latest (MODEST_HEADER_VIEW (self), 0);
2866 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), NULL);
2869 priv->live_search_timeout = 0;
2875 on_live_search_refilter (HildonLiveSearch *livesearch,
2876 ModestHeaderView *self)
2878 ModestHeaderViewPrivate *priv;
2879 GtkTreeModel *model, *sortable, *filter;
2881 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2883 if (priv->live_search_timeout > 0) {
2884 g_source_remove (priv->live_search_timeout);
2885 priv->live_search_timeout = 0;
2889 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2890 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2891 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2892 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2893 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2897 if (model && tny_list_get_length (TNY_LIST (model)) > 250) {
2898 priv->live_search_timeout = g_timeout_add (1000, (GSourceFunc) on_live_search_timeout, self);
2900 on_live_search_timeout (self);
2907 modest_header_view_setup_live_search (ModestHeaderView *self)
2909 ModestHeaderViewPrivate *priv;
2911 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (self), NULL);
2912 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2913 priv->live_search = hildon_live_search_new ();
2915 g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), self);
2917 return priv->live_search;