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_show_latest (TNY_GTK_HEADER_LIST_MODEL (headers), priv->show_latest);
1188 /* Start the monitor in the callback of the
1189 tny_gtk_header_list_model_set_folder call. It's crucial to
1190 do it there and not just after the call because we want the
1191 monitor to observe only the headers returned by the
1192 tny_folder_get_headers_async call that it's inside the
1193 tny_gtk_header_list_model_set_folder call. This way the
1194 monitor infrastructure could successfully cope with
1195 duplicates. For example if a tny_folder_add_msg_async is
1196 happening while tny_gtk_header_list_model_set_folder is
1197 invoked, then the first call could add a header that will
1198 be added again by tny_gtk_header_list_model_set_folder, so
1199 we'd end up with duplicate headers. sergio */
1200 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1202 set_folder_intern_get_headers_async_cb,
1205 /* Init filter_row function to examine empty status */
1206 priv->status = HEADER_VIEW_INIT;
1208 /* Create sortable model */
1209 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (headers));
1210 g_object_unref (headers);
1212 /* Create a tree model filter to hide and show rows for cut operations */
1213 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (sortable), NULL);
1214 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1215 filter_row, self, NULL);
1216 g_object_unref (sortable);
1218 /* install our special sorting functions */
1219 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1221 /* Restore sort column id */
1223 type = modest_tny_folder_guess_folder_type (folder);
1224 if (type == TNY_FOLDER_TYPE_INVALID)
1225 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1227 sort_colid = modest_header_view_get_sort_column_id (self, type);
1228 sort_type = modest_header_view_get_sort_type (self, type);
1229 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1232 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1233 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1234 (GtkTreeIterCompareFunc) cmp_rows,
1236 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1237 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1238 (GtkTreeIterCompareFunc) cmp_subject_rows,
1243 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1244 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1245 g_object_unref (filter_model);
1252 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1254 GtkSortType sort_type)
1256 ModestHeaderViewPrivate *priv = NULL;
1257 GtkTreeModel *sortable = NULL, *filter_model = NULL;
1260 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1261 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1263 /* Get model and private data */
1264 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1265 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1266 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
1267 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1270 /* Sort tree model */
1271 type = modest_tny_folder_guess_folder_type (priv->folder);
1272 if (type == TNY_FOLDER_TYPE_INVALID)
1273 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1275 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1278 /* Store new sort parameters */
1279 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1284 modest_header_view_set_sort_params (ModestHeaderView *self,
1286 GtkSortType sort_type,
1289 ModestHeaderViewPrivate *priv;
1290 ModestHeaderViewStyle style;
1292 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1293 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1294 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1296 style = modest_header_view_get_style (self);
1297 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1299 priv->sort_colid[style][type] = sort_colid;
1300 priv->sort_type[style][type] = sort_type;
1304 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1307 ModestHeaderViewPrivate *priv;
1308 ModestHeaderViewStyle style;
1310 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1311 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1313 style = modest_header_view_get_style (self);
1314 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1316 return priv->sort_colid[style][type];
1320 modest_header_view_get_sort_type (ModestHeaderView *self,
1323 ModestHeaderViewPrivate *priv;
1324 ModestHeaderViewStyle style;
1326 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1327 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1329 style = modest_header_view_get_style (self);
1330 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1332 return priv->sort_type[style][type];
1336 ModestHeaderView *header_view;
1337 RefreshAsyncUserCallback cb;
1342 folder_refreshed_cb (ModestMailOperation *mail_op,
1346 ModestHeaderViewPrivate *priv;
1347 SetFolderHelper *info;
1349 info = (SetFolderHelper*) user_data;
1351 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1355 info->cb (mail_op, folder, info->user_data);
1357 /* Start the folder count changes observer. We do not need it
1358 before the refresh. Note that the monitor could still be
1359 called for this refresh but now we know that the callback
1360 was previously called */
1361 g_mutex_lock (priv->observers_lock);
1362 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1363 g_mutex_unlock (priv->observers_lock);
1365 /* Notify the observers that the update is over */
1366 g_signal_emit (G_OBJECT (info->header_view),
1367 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1369 /* Allow filtering notifications from now on if the current
1370 folder is still the same (if not then the user has selected
1371 another one to refresh, we should wait until that refresh
1373 if (priv->folder == folder)
1374 priv->notify_status = TRUE;
1377 g_object_unref (info->header_view);
1382 refresh_folder_error_handler (ModestMailOperation *mail_op,
1385 const GError *error = modest_mail_operation_get_error (mail_op);
1387 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1388 error->code == TNY_IO_ERROR_WRITE ||
1389 error->code == TNY_IO_ERROR_READ) {
1390 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1391 /* If the mail op has been cancelled then it's not an error: don't show any message */
1392 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1393 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1394 modest_platform_information_banner (NULL, NULL, msg);
1401 modest_header_view_set_folder (ModestHeaderView *self,
1404 ModestWindow *progress_window,
1405 RefreshAsyncUserCallback callback,
1408 ModestHeaderViewPrivate *priv;
1410 g_return_if_fail (self);
1412 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1415 if (priv->status_timeout) {
1416 g_source_remove (priv->status_timeout);
1417 priv->status_timeout = 0;
1420 g_mutex_lock (priv->observers_lock);
1421 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1422 g_object_unref (priv->folder);
1423 priv->folder = NULL;
1424 g_mutex_unlock (priv->observers_lock);
1428 GtkTreeSelection *selection;
1429 SetFolderHelper *info;
1430 ModestMailOperation *mail_op = NULL;
1432 /* Set folder in the model */
1433 modest_header_view_set_folder_intern (self, folder, refresh);
1435 /* Pick my reference. Nothing to do with the mail operation */
1436 priv->folder = g_object_ref (folder);
1438 /* Do not notify about filterings until the refresh finishes */
1439 priv->notify_status = FALSE;
1441 /* Clear the selection if exists */
1442 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1443 gtk_tree_selection_unselect_all(selection);
1444 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1446 /* Notify the observers that the update begins */
1447 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1450 /* create the helper */
1451 info = g_malloc0 (sizeof (SetFolderHelper));
1452 info->header_view = g_object_ref (self);
1453 info->cb = callback;
1454 info->user_data = user_data;
1456 /* Create the mail operation (source will be the parent widget) */
1457 if (progress_window)
1458 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1459 refresh_folder_error_handler,
1462 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1465 /* Refresh the folder asynchronously */
1466 modest_mail_operation_refresh_folder (mail_op,
1468 folder_refreshed_cb,
1471 folder_refreshed_cb (mail_op, folder, info);
1475 g_object_unref (mail_op);
1477 g_mutex_lock (priv->observers_lock);
1479 if (priv->monitor) {
1480 tny_folder_monitor_stop (priv->monitor);
1481 g_object_unref (G_OBJECT (priv->monitor));
1482 priv->monitor = NULL;
1485 if (priv->autoselect_reference) {
1486 gtk_tree_row_reference_free (priv->autoselect_reference);
1487 priv->autoselect_reference = NULL;
1490 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1492 modest_header_view_notify_observers(self, NULL, NULL);
1494 g_mutex_unlock (priv->observers_lock);
1496 /* Notify the observers that the update is over */
1497 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1503 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1504 GtkTreeViewColumn *column, gpointer userdata)
1506 ModestHeaderView *self = NULL;
1508 GtkTreeModel *model = NULL;
1509 TnyHeader *header = NULL;
1510 TnyHeaderFlags flags;
1512 self = MODEST_HEADER_VIEW (treeview);
1514 model = gtk_tree_view_get_model (treeview);
1515 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1518 /* get the first selected item */
1519 gtk_tree_model_get (model, &iter,
1520 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1521 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1524 /* Dont open DELETED messages */
1525 if (flags & TNY_HEADER_FLAG_DELETED) {
1528 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1529 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1530 modest_platform_information_banner (NULL, NULL, msg);
1536 g_signal_emit (G_OBJECT(self),
1537 signals[HEADER_ACTIVATED_SIGNAL],
1543 g_object_unref (G_OBJECT (header));
1548 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1550 GtkTreeModel *model;
1551 TnyHeader *header = NULL;
1552 GtkTreePath *path = NULL;
1554 ModestHeaderView *self;
1555 GList *selected = NULL;
1557 g_return_if_fail (sel);
1558 g_return_if_fail (user_data);
1560 self = MODEST_HEADER_VIEW (user_data);
1562 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1563 if (selected != NULL)
1564 path = (GtkTreePath *) selected->data;
1565 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1566 return; /* msg was _un_selected */
1568 gtk_tree_model_get (model, &iter,
1569 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1573 g_signal_emit (G_OBJECT(self),
1574 signals[HEADER_SELECTED_SIGNAL],
1577 g_object_unref (G_OBJECT (header));
1579 /* free all items in 'selected' */
1580 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1581 g_list_free (selected);
1585 /* PROTECTED method. It's useful when we want to force a given
1586 selection to reload a msg. For example if we have selected a header
1587 in offline mode, when Modest become online, we want to reload the
1588 message automatically without an user click over the header */
1590 _modest_header_view_change_selection (GtkTreeSelection *selection,
1593 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1594 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1596 on_selection_changed (selection, user_data);
1600 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1607 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1611 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1615 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1623 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1631 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1632 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1636 case TNY_HEADER_FLAG_ATTACHMENTS:
1638 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1639 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1640 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1641 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1643 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1644 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1646 return cmp ? cmp : t1 - t2;
1648 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1649 TnyHeader *header1 = NULL, *header2 = NULL;
1651 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1652 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1653 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1654 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1656 /* This is for making priority values respect the intuitive sort relationship
1657 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1659 if (header1 && header2) {
1660 cmp = compare_priorities (tny_header_get_priority (header1),
1661 tny_header_get_priority (header2));
1662 g_object_unref (header1);
1663 g_object_unref (header2);
1665 return cmp ? cmp : t1 - t2;
1671 return &iter1 - &iter2; /* oughhhh */
1676 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1683 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1685 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1686 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1687 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1688 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1690 /* Do not use the prefixes for sorting. Consume all the blank
1691 spaces for sorting */
1692 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1693 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1696 /* If they're equal based on subject without prefix then just
1697 sort them by length. This will show messages like this.
1704 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1711 /* Drag and drop stuff */
1713 drag_data_get_cb (GtkWidget *widget,
1714 GdkDragContext *context,
1715 GtkSelectionData *selection_data,
1720 ModestHeaderView *self = NULL;
1721 ModestHeaderViewPrivate *priv = NULL;
1723 self = MODEST_HEADER_VIEW (widget);
1724 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1726 /* Set the data. Do not use the current selection because it
1727 could be different than the selection at the beginning of
1729 modest_dnd_selection_data_set_paths (selection_data,
1730 priv->drag_begin_cached_selected_rows);
1734 * We're caching the selected rows at the beginning because the
1735 * selection could change between drag-begin and drag-data-get, for
1736 * example if we have a set of rows already selected, and then we
1737 * click in one of them (without SHIFT key pressed) and begin a drag,
1738 * the selection at that moment contains all the selected lines, but
1739 * after dropping the selection, the release event provokes that only
1740 * the row used to begin the drag is selected, so at the end the
1741 * drag&drop affects only one rows instead of all the selected ones.
1745 drag_begin_cb (GtkWidget *widget,
1746 GdkDragContext *context,
1749 ModestHeaderView *self = NULL;
1750 ModestHeaderViewPrivate *priv = NULL;
1751 GtkTreeSelection *selection;
1753 self = MODEST_HEADER_VIEW (widget);
1754 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1756 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1757 priv->drag_begin_cached_selected_rows =
1758 gtk_tree_selection_get_selected_rows (selection, NULL);
1762 * We use the drag-end signal to clear the cached selection, we use
1763 * this because this allways happens, whether or not the d&d was a
1767 drag_end_cb (GtkWidget *widget,
1771 ModestHeaderView *self = NULL;
1772 ModestHeaderViewPrivate *priv = NULL;
1774 self = MODEST_HEADER_VIEW (widget);
1775 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1777 /* Free cached data */
1778 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1779 g_list_free (priv->drag_begin_cached_selected_rows);
1780 priv->drag_begin_cached_selected_rows = NULL;
1783 /* Header view drag types */
1784 const GtkTargetEntry header_view_drag_types[] = {
1785 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1789 enable_drag_and_drop (GtkWidget *self)
1791 #ifdef MODEST_TOOLKIT_HILDON2
1794 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1795 header_view_drag_types,
1796 G_N_ELEMENTS (header_view_drag_types),
1797 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1801 disable_drag_and_drop (GtkWidget *self)
1803 #ifdef MODEST_TOOLKIT_HILDON2
1806 gtk_drag_source_unset (self);
1810 setup_drag_and_drop (GtkWidget *self)
1812 #ifdef MODEST_TOOLKIT_HILDON2
1815 enable_drag_and_drop(self);
1816 g_signal_connect(G_OBJECT (self), "drag_data_get",
1817 G_CALLBACK(drag_data_get_cb), NULL);
1819 g_signal_connect(G_OBJECT (self), "drag_begin",
1820 G_CALLBACK(drag_begin_cb), NULL);
1822 g_signal_connect(G_OBJECT (self), "drag_end",
1823 G_CALLBACK(drag_end_cb), NULL);
1826 static GtkTreePath *
1827 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1829 GtkTreePath *path = NULL;
1830 GtkTreeSelection *sel = NULL;
1833 sel = gtk_tree_view_get_selection(self);
1834 rows = gtk_tree_selection_get_selected_rows (sel, model);
1836 if ((rows == NULL) || (g_list_length(rows) != 1))
1839 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1844 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1850 #ifndef MODEST_TOOLKIT_HILDON2
1852 * This function moves the tree view scroll to the current selected
1853 * row when the widget grabs the focus
1856 on_focus_in (GtkWidget *self,
1857 GdkEventFocus *event,
1860 GtkTreeSelection *selection;
1861 GtkTreeModel *model;
1862 GList *selected = NULL;
1863 GtkTreePath *selected_path = NULL;
1865 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1869 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1870 /* If none selected yet, pick the first one */
1871 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1875 /* Return if the model is empty */
1876 if (!gtk_tree_model_get_iter_first (model, &iter))
1879 path = gtk_tree_model_get_path (model, &iter);
1880 gtk_tree_selection_select_path (selection, path);
1881 gtk_tree_path_free (path);
1884 /* Need to get the all the rows because is selection multiple */
1885 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1886 if (selected == NULL) return FALSE;
1887 selected_path = (GtkTreePath *) selected->data;
1890 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1891 g_list_free (selected);
1897 on_focus_out (GtkWidget *self,
1898 GdkEventFocus *event,
1902 if (!gtk_widget_is_focus (self)) {
1903 GtkTreeSelection *selection = NULL;
1904 GList *selected_rows = NULL;
1905 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1906 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1907 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1908 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1909 gtk_tree_selection_unselect_all (selection);
1910 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1911 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1912 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1913 g_list_free (selected_rows);
1921 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1923 enable_drag_and_drop(self);
1928 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1930 GtkTreeSelection *selection = NULL;
1931 GtkTreePath *path = NULL;
1932 gboolean already_selected = FALSE, already_opened = FALSE;
1933 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1935 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1937 GtkTreeModel *model;
1939 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1940 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1942 /* Get header from model */
1943 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1944 if (gtk_tree_model_get_iter (model, &iter, path)) {
1945 GValue value = {0,};
1948 gtk_tree_model_get_value (model, &iter,
1949 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1951 header = (TnyHeader *) g_value_get_object (&value);
1952 if (TNY_IS_HEADER (header)) {
1953 status = modest_tny_all_send_queues_get_msg_status (header);
1954 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1957 g_value_unset (&value);
1961 /* Enable drag and drop only if the user clicks on a row that
1962 it's already selected. If not, let him select items using
1963 the pointer. If the message is in an OUTBOX and in sending
1964 status disable drag and drop as well */
1965 if (!already_selected ||
1966 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1968 disable_drag_and_drop(self);
1971 gtk_tree_path_free(path);
1973 /* If it's already opened then do not let the button-press
1974 event go on because it'll perform a message open because
1975 we're clicking on to an already selected header */
1980 folder_monitor_update (TnyFolderObserver *self,
1981 TnyFolderChange *change)
1983 ModestHeaderViewPrivate *priv = NULL;
1984 TnyFolderChangeChanged changed;
1985 TnyFolder *folder = NULL;
1987 changed = tny_folder_change_get_changed (change);
1989 /* Do not notify the observers if the folder of the header
1990 view has changed before this call to the observer
1992 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1993 folder = tny_folder_change_get_folder (change);
1994 if (folder != priv->folder)
1997 MODEST_DEBUG_BLOCK (
1998 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1999 g_print ("ADDED %d/%d (r/t) \n",
2000 tny_folder_change_get_new_unread_count (change),
2001 tny_folder_change_get_new_all_count (change));
2002 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
2003 g_print ("ALL COUNT %d\n",
2004 tny_folder_change_get_new_all_count (change));
2005 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
2006 g_print ("UNREAD COUNT %d\n",
2007 tny_folder_change_get_new_unread_count (change));
2008 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
2009 g_print ("EXPUNGED %d/%d (r/t) \n",
2010 tny_folder_change_get_new_unread_count (change),
2011 tny_folder_change_get_new_all_count (change));
2012 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
2013 g_print ("FOLDER RENAME\n");
2014 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
2015 g_print ("MSG RECEIVED %d/%d (r/t) \n",
2016 tny_folder_change_get_new_unread_count (change),
2017 tny_folder_change_get_new_all_count (change));
2018 g_print ("---------------------------------------------------\n");
2021 /* Check folder count */
2022 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
2023 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
2025 g_mutex_lock (priv->observers_lock);
2027 /* Emit signal to evaluate how headers changes affects
2028 to the window view */
2029 g_signal_emit (G_OBJECT(self),
2030 signals[MSG_COUNT_CHANGED_SIGNAL],
2033 /* Added or removed headers, so data stored on cliboard are invalid */
2034 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
2035 modest_email_clipboard_clear (priv->clipboard);
2037 g_mutex_unlock (priv->observers_lock);
2043 g_object_unref (folder);
2047 modest_header_view_is_empty (ModestHeaderView *self)
2049 ModestHeaderViewPrivate *priv;
2051 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
2053 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2055 return priv->status == HEADER_VIEW_EMPTY;
2059 modest_header_view_clear (ModestHeaderView *self)
2061 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2063 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2067 modest_header_view_copy_selection (ModestHeaderView *header_view)
2069 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2071 /* Copy selection */
2072 _clipboard_set_selected_data (header_view, FALSE);
2076 modest_header_view_cut_selection (ModestHeaderView *header_view)
2078 ModestHeaderViewPrivate *priv = NULL;
2079 const gchar **hidding = NULL;
2080 guint i, n_selected;
2082 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2084 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2086 /* Copy selection */
2087 _clipboard_set_selected_data (header_view, TRUE);
2089 /* Get hidding ids */
2090 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2092 /* Clear hidding array created by previous cut operation */
2093 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2095 /* Copy hidding array */
2096 priv->n_selected = n_selected;
2097 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2098 for (i=0; i < n_selected; i++)
2099 priv->hidding_ids[i] = g_strdup(hidding[i]);
2101 /* Hide cut headers */
2102 modest_header_view_refilter (header_view);
2109 _clipboard_set_selected_data (ModestHeaderView *header_view,
2112 ModestHeaderViewPrivate *priv = NULL;
2113 TnyList *headers = NULL;
2115 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2116 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2118 /* Set selected data on clipboard */
2119 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2120 headers = modest_header_view_get_selected_headers (header_view);
2121 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2124 g_object_unref (headers);
2128 ModestHeaderView *self;
2133 notify_filter_change (gpointer data)
2135 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2137 g_signal_emit (info->self,
2138 signals[MSG_COUNT_CHANGED_SIGNAL],
2139 0, info->folder, NULL);
2145 notify_filter_change_destroy (gpointer data)
2147 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2148 ModestHeaderViewPrivate *priv;
2150 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2151 priv->status_timeout = 0;
2153 g_object_unref (info->self);
2154 g_object_unref (info->folder);
2155 g_slice_free (NotifyFilterInfo, info);
2159 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2161 /* For the moment we only need to filter outbox */
2162 return priv->is_outbox;
2166 header_match_string (TnyHeader *header, gchar **words)
2168 gchar *subject_fold;
2174 gchar **current_word;
2177 subject_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD);
2178 if (subject_fold == NULL) {
2180 subject = tny_header_dup_subject (header);
2181 if (subject != NULL) {
2182 subject_fold = subject?g_utf8_casefold (subject, -1):NULL;
2183 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD,
2184 subject_fold, (GDestroyNotify) g_free);
2189 from_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD);
2190 if (from_fold == NULL) {
2192 from = tny_header_dup_from (header);
2194 from_fold = from?g_utf8_casefold (from, -1):NULL;
2195 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD,
2196 from_fold, (GDestroyNotify) g_free);
2201 to_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_TO_FOLD);
2202 if (to_fold == NULL) {
2204 to = tny_header_dup_to (header);
2206 to_fold = to?g_utf8_casefold (to, -1):NULL;
2207 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_TO_FOLD,
2208 to_fold, (GDestroyNotify) g_free);
2213 cc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_CC_FOLD);
2214 if (cc_fold == NULL) {
2216 cc = tny_header_dup_cc (header);
2218 cc_fold = cc?g_utf8_casefold (cc, -1):NULL;
2219 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_CC_FOLD,
2220 cc_fold, (GDestroyNotify) g_free);
2225 bcc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD);
2226 if (bcc_fold == NULL) {
2228 bcc = tny_header_dup_bcc (header);
2230 bcc_fold = bcc?g_utf8_casefold (bcc, -1):NULL;
2231 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD,
2232 bcc_fold, (GDestroyNotify) g_free);
2239 for (current_word = words; *current_word != NULL; current_word++) {
2241 if ((subject_fold && g_strstr_len (subject_fold, -1, *current_word))
2242 || (cc_fold && g_strstr_len (cc_fold, -1, *current_word))
2243 || (bcc_fold && g_strstr_len (bcc_fold, -1, *current_word))
2244 || (to_fold && g_strstr_len (to_fold, -1, *current_word))
2245 || (from_fold && g_strstr_len (from_fold, -1, *current_word))) {
2257 filter_row (GtkTreeModel *model,
2261 ModestHeaderViewPrivate *priv = NULL;
2262 TnyHeaderFlags flags;
2263 TnyHeader *header = NULL;
2266 gboolean visible = TRUE;
2267 gboolean found = FALSE;
2268 GValue value = {0,};
2269 HeaderViewStatus old_status;
2271 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2272 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2274 /* Get header from model */
2275 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2276 header = (TnyHeader *) g_value_get_object (&value);
2277 g_value_unset (&value);
2278 flags = tny_header_get_flags (header);
2280 /* Get message id from header (ensure is a valid id) */
2286 /* Hide deleted and mark as deleted heders */
2287 if (flags & TNY_HEADER_FLAG_DELETED ||
2288 flags & TNY_HEADER_FLAG_EXPUNGED) {
2293 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2294 if (current_folder_needs_filtering (priv) &&
2295 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2301 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2302 if (current_folder_needs_filtering (priv) &&
2303 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2309 if (visible && priv->filter_string) {
2310 if (!header_match_string (header, priv->filter_string_splitted)) {
2314 if (priv->filter_date_range) {
2315 if ((tny_header_get_date_sent (TNY_HEADER (header)) < priv->date_range_start) ||
2316 ((priv->date_range_end != -1) && (tny_header_get_date_sent (TNY_HEADER (header)) > priv->date_range_end))) {
2323 /* If no data on clipboard, return always TRUE */
2324 if (modest_email_clipboard_cleared(priv->clipboard)) {
2330 if (priv->hidding_ids != NULL) {
2331 id = tny_header_dup_message_id (header);
2332 for (i=0; i < priv->n_selected && !found; i++)
2333 if (priv->hidding_ids[i] != NULL && id != NULL)
2334 found = (!strcmp (priv->hidding_ids[i], id));
2341 old_status = priv->status;
2342 priv->status = ((gboolean) priv->status) && !visible;
2343 if ((priv->notify_status) && (priv->status != old_status)) {
2344 if (priv->status_timeout)
2345 g_source_remove (priv->status_timeout);
2348 NotifyFilterInfo *info;
2350 info = g_slice_new0 (NotifyFilterInfo);
2351 info->self = g_object_ref (G_OBJECT (user_data));
2353 info->folder = tny_header_get_folder (header);
2354 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2355 notify_filter_change,
2357 notify_filter_change_destroy);
2365 _clear_hidding_filter (ModestHeaderView *header_view)
2367 ModestHeaderViewPrivate *priv = NULL;
2370 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2371 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2373 if (priv->hidding_ids != NULL) {
2374 for (i=0; i < priv->n_selected; i++)
2375 g_free (priv->hidding_ids[i]);
2376 g_free(priv->hidding_ids);
2381 modest_header_view_refilter (ModestHeaderView *header_view)
2383 GtkTreeModel *filter_model = NULL;
2384 ModestHeaderViewPrivate *priv = NULL;
2386 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2387 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2389 /* Hide cut headers */
2390 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2391 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
2392 priv->status = HEADER_VIEW_INIT;
2393 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2398 * Called when an account is removed. If I'm showing a folder of the
2399 * account that has been removed then clear the view
2402 on_account_removed (TnyAccountStore *self,
2403 TnyAccount *account,
2406 ModestHeaderViewPrivate *priv = NULL;
2408 /* Ignore changes in transport accounts */
2409 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2412 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2415 TnyAccount *my_account;
2417 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2418 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2419 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2420 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2422 my_account = tny_folder_get_account (priv->folder);
2426 if (my_account == account)
2427 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2428 g_object_unref (my_account);
2434 modest_header_view_add_observer(ModestHeaderView *header_view,
2435 ModestHeaderViewObserver *observer)
2437 ModestHeaderViewPrivate *priv;
2439 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2440 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2442 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2444 g_mutex_lock(priv->observer_list_lock);
2445 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2446 g_mutex_unlock(priv->observer_list_lock);
2450 modest_header_view_remove_observer(ModestHeaderView *header_view,
2451 ModestHeaderViewObserver *observer)
2453 ModestHeaderViewPrivate *priv;
2455 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2456 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2458 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2460 g_mutex_lock(priv->observer_list_lock);
2461 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2462 g_mutex_unlock(priv->observer_list_lock);
2466 modest_header_view_notify_observers(ModestHeaderView *header_view,
2467 GtkTreeModel *model,
2468 const gchar *tny_folder_id)
2470 ModestHeaderViewPrivate *priv = NULL;
2472 ModestHeaderViewObserver *observer;
2475 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2477 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2479 g_mutex_lock(priv->observer_list_lock);
2480 iter = priv->observer_list;
2481 while(iter != NULL){
2482 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2483 modest_header_view_observer_update(observer, model,
2485 iter = g_slist_next(iter);
2487 g_mutex_unlock(priv->observer_list_lock);
2491 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2493 ModestHeaderViewPrivate *priv = NULL;
2495 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2496 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2500 modest_header_view_set_filter (ModestHeaderView *self,
2501 ModestHeaderViewFilter filter)
2503 ModestHeaderViewPrivate *priv;
2505 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2506 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2508 priv->filter |= filter;
2510 if (current_folder_needs_filtering (priv))
2511 modest_header_view_refilter (self);
2515 modest_header_view_unset_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 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2532 if (strcmp ("style", spec->name) == 0) {
2533 update_style (MODEST_HEADER_VIEW (obj));
2534 gtk_widget_queue_draw (GTK_WIDGET (obj));
2539 update_style (ModestHeaderView *self)
2541 ModestHeaderViewPrivate *priv;
2542 GdkColor style_color;
2543 GdkColor style_active_color;
2544 PangoAttrList *attr_list;
2546 PangoAttribute *attr;
2548 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2549 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2553 attr_list = pango_attr_list_new ();
2554 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2555 gdk_color_parse ("grey", &style_color);
2557 priv->secondary_color = style_color;
2558 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2559 pango_attr_list_insert (attr_list, attr);
2562 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2564 "SmallSystemFont", NULL,
2567 attr = pango_attr_font_desc_new (pango_font_description_copy
2568 (style->font_desc));
2569 pango_attr_list_insert (attr_list, attr);
2571 g_object_set (G_OBJECT (priv->renderer_address),
2572 "foreground-gdk", &(priv->secondary_color),
2573 "foreground-set", TRUE,
2574 "attributes", attr_list,
2576 g_object_set (G_OBJECT (priv->renderer_date_status),
2577 "foreground-gdk", &(priv->secondary_color),
2578 "foreground-set", TRUE,
2579 "attributes", attr_list,
2581 pango_attr_list_unref (attr_list);
2583 g_object_set (G_OBJECT (priv->renderer_address),
2584 "foreground-gdk", &(priv->secondary_color),
2585 "foreground-set", TRUE,
2586 "scale", PANGO_SCALE_SMALL,
2589 g_object_set (G_OBJECT (priv->renderer_date_status),
2590 "foreground-gdk", &(priv->secondary_color),
2591 "foreground-set", TRUE,
2592 "scale", PANGO_SCALE_SMALL,
2597 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2598 priv->active_color = style_active_color;
2599 #ifdef MODEST_TOOLKIT_HILDON2
2600 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2601 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2604 #ifdef MODEST_TOOLKIT_HILDON2
2605 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2611 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2616 GtkTreeModel *tree_model;
2621 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2629 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2630 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2634 gtk_tree_model_get (tree_model, &iter,
2635 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2642 parse_date_side (const gchar *string, time_t *date_side)
2648 gboolean result = FALSE;
2650 if (string && string[0] == '\0') {
2655 casefold = g_utf8_casefold (string, -1);
2656 today = g_utf8_casefold (dgettext ("gtk20", "Today"), -1);
2657 yesterday = g_utf8_casefold (dgettext ("gtk20", "Yesterday"), -1);
2658 date = g_date_new ();
2660 if (g_utf8_collate (casefold, today) == 0) {
2661 *date_side = time (NULL);
2666 if (g_utf8_collate (casefold, yesterday) == 0) {
2667 *date_side = time (NULL) - 24*60*60;
2672 g_date_set_parse (date, string);
2673 if (g_date_valid (date)) {
2675 g_date_to_struct_tm (date, &tm);
2676 *date_side = mktime (&tm);
2691 parse_date_range (const gchar *string, time_t *date_range_start, time_t *date_range_end)
2696 parts = g_strsplit (string, "..", 2);
2699 if (g_strv_length (parts) != 2) {
2706 if (!parse_date_side (parts[0], date_range_start)) {
2711 if (parse_date_side (parts[1], date_range_end)) {
2712 if (*date_range_end == 0) {
2713 *date_range_end = (time_t) -1;
2715 *date_range_end += (24*60*60 - 1);
2728 modest_header_view_set_show_latest (ModestHeaderView *header_view,
2731 ModestHeaderViewPrivate *priv;
2732 GtkTreeModel *sortable, *filter, *model;
2734 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2735 priv->show_latest = show_latest;
2737 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2738 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2739 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2740 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2741 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2743 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (model), priv->show_latest);
2750 modest_header_view_get_show_latest (ModestHeaderView *header_view)
2752 ModestHeaderViewPrivate *priv;
2753 GtkTreeModel *sortable, *filter, *model;
2756 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2758 result = priv->show_latest;
2759 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2760 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2761 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2762 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2763 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2765 result = tny_gtk_header_list_model_get_show_latest (TNY_GTK_HEADER_LIST_MODEL (model));
2774 modest_header_view_get_not_latest (ModestHeaderView *header_view)
2776 ModestHeaderViewPrivate *priv;
2777 gint not_latest = 0;
2778 GtkTreeModel *sortable, *filter, *model;
2780 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2782 if (priv->show_latest == 0)
2785 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2786 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2787 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2788 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2789 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2791 not_latest = MAX (0, tny_list_get_length (TNY_LIST (model)) - priv->show_latest);
2800 modest_header_view_set_filter_string (ModestHeaderView *self,
2801 const gchar *filter_string)
2803 ModestHeaderViewPrivate *priv;
2805 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2806 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2808 if (priv->filter_string)
2809 g_free (priv->filter_string);
2811 priv->filter_string = g_strdup (filter_string);
2812 priv->filter_date_range = FALSE;
2814 if (priv->filter_string_splitted) {
2815 g_strfreev (priv->filter_string_splitted);
2816 priv->filter_string_splitted = NULL;
2819 if (priv->filter_string) {
2820 gchar **split, **current, **current_target;
2822 split = g_strsplit (priv->filter_string, " ", 0);
2824 priv->filter_string_splitted = g_malloc0 (sizeof (gchar *)*(g_strv_length (split) + 1));
2825 current_target = priv->filter_string_splitted;
2826 for (current = split; *current != 0; current ++) {
2827 gboolean has_date_range = FALSE;;
2828 if (g_strstr_len (*current, -1, "..") && strcmp(*current, "..")) {
2829 time_t range_start, range_end;
2830 /* It contains .. but it's not ".." so it may be a date range */
2831 if (parse_date_range (*current, &range_start, &range_end)) {
2832 priv->filter_date_range = TRUE;
2833 has_date_range = TRUE;
2834 priv->date_range_start = range_start;
2835 priv->date_range_end = range_end;
2838 if (!has_date_range) {
2839 *current_target = g_utf8_casefold (*current, -1);
2843 *current_target = '\0';
2846 modest_header_view_refilter (MODEST_HEADER_VIEW (self));
2849 #ifdef MODEST_TOOLKIT_HILDON2
2852 on_live_search_timeout (ModestHeaderView *self)
2854 const gchar *needle;
2855 ModestHeaderViewPrivate *priv;
2857 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2859 needle = hildon_live_search_get_text (HILDON_LIVE_SEARCH (priv->live_search));
2860 if (needle && needle[0] != '\0') {
2861 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), needle);
2862 if (priv->show_latest > 0)
2863 modest_header_view_set_show_latest (MODEST_HEADER_VIEW (self), 0);
2865 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), NULL);
2868 priv->live_search_timeout = 0;
2874 on_live_search_refilter (HildonLiveSearch *livesearch,
2875 ModestHeaderView *self)
2877 ModestHeaderViewPrivate *priv;
2878 GtkTreeModel *model, *sortable, *filter;
2880 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2882 if (priv->live_search_timeout > 0) {
2883 g_source_remove (priv->live_search_timeout);
2884 priv->live_search_timeout = 0;
2888 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2889 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2890 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2891 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2892 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2896 if (model && tny_list_get_length (TNY_LIST (model)) > 250) {
2897 priv->live_search_timeout = g_timeout_add (1000, (GSourceFunc) on_live_search_timeout, self);
2899 on_live_search_timeout (self);
2906 modest_header_view_setup_live_search (ModestHeaderView *self)
2908 ModestHeaderViewPrivate *priv;
2910 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (self), NULL);
2911 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2912 priv->live_search = hildon_live_search_new ();
2914 g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), self);
2916 return priv->live_search;