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_FLAGS_COLUMN, &value);
2276 flags = (TnyHeaderFlags) g_value_get_int (&value);
2277 g_value_unset (&value);
2278 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2279 header = (TnyHeader *) g_value_get_object (&value);
2280 g_value_unset (&value);
2282 /* Get message id from header (ensure is a valid id) */
2288 /* Hide deleted and mark as deleted heders */
2289 if (flags & TNY_HEADER_FLAG_DELETED ||
2290 flags & TNY_HEADER_FLAG_EXPUNGED) {
2295 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2296 if (current_folder_needs_filtering (priv) &&
2297 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2303 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2304 if (current_folder_needs_filtering (priv) &&
2305 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2311 if (visible && priv->filter_string) {
2312 if (!header_match_string (header, priv->filter_string_splitted)) {
2316 if (priv->filter_date_range) {
2317 if ((tny_header_get_date_sent (TNY_HEADER (header)) < priv->date_range_start) ||
2318 ((priv->date_range_end != -1) && (tny_header_get_date_sent (TNY_HEADER (header)) > priv->date_range_end))) {
2325 /* If no data on clipboard, return always TRUE */
2326 if (modest_email_clipboard_cleared(priv->clipboard)) {
2332 if (priv->hidding_ids != NULL) {
2333 id = tny_header_dup_message_id (header);
2334 for (i=0; i < priv->n_selected && !found; i++)
2335 if (priv->hidding_ids[i] != NULL && id != NULL)
2336 found = (!strcmp (priv->hidding_ids[i], id));
2343 old_status = priv->status;
2344 priv->status = ((gboolean) priv->status) && !visible;
2345 if ((priv->notify_status) && (priv->status != old_status)) {
2346 if (priv->status_timeout)
2347 g_source_remove (priv->status_timeout);
2350 NotifyFilterInfo *info;
2352 info = g_slice_new0 (NotifyFilterInfo);
2353 info->self = g_object_ref (G_OBJECT (user_data));
2355 info->folder = tny_header_get_folder (header);
2356 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2357 notify_filter_change,
2359 notify_filter_change_destroy);
2367 _clear_hidding_filter (ModestHeaderView *header_view)
2369 ModestHeaderViewPrivate *priv = NULL;
2372 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2373 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2375 if (priv->hidding_ids != NULL) {
2376 for (i=0; i < priv->n_selected; i++)
2377 g_free (priv->hidding_ids[i]);
2378 g_free(priv->hidding_ids);
2383 modest_header_view_refilter (ModestHeaderView *header_view)
2385 GtkTreeModel *filter_model = NULL;
2386 ModestHeaderViewPrivate *priv = NULL;
2388 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2389 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2391 /* Hide cut headers */
2392 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2393 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
2394 priv->status = HEADER_VIEW_INIT;
2395 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2400 * Called when an account is removed. If I'm showing a folder of the
2401 * account that has been removed then clear the view
2404 on_account_removed (TnyAccountStore *self,
2405 TnyAccount *account,
2408 ModestHeaderViewPrivate *priv = NULL;
2410 /* Ignore changes in transport accounts */
2411 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2414 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2417 TnyAccount *my_account;
2419 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2420 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2421 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2422 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2424 my_account = tny_folder_get_account (priv->folder);
2428 if (my_account == account)
2429 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2430 g_object_unref (my_account);
2436 modest_header_view_add_observer(ModestHeaderView *header_view,
2437 ModestHeaderViewObserver *observer)
2439 ModestHeaderViewPrivate *priv;
2441 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2442 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2444 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2446 g_mutex_lock(priv->observer_list_lock);
2447 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2448 g_mutex_unlock(priv->observer_list_lock);
2452 modest_header_view_remove_observer(ModestHeaderView *header_view,
2453 ModestHeaderViewObserver *observer)
2455 ModestHeaderViewPrivate *priv;
2457 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2458 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2460 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2462 g_mutex_lock(priv->observer_list_lock);
2463 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2464 g_mutex_unlock(priv->observer_list_lock);
2468 modest_header_view_notify_observers(ModestHeaderView *header_view,
2469 GtkTreeModel *model,
2470 const gchar *tny_folder_id)
2472 ModestHeaderViewPrivate *priv = NULL;
2474 ModestHeaderViewObserver *observer;
2477 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2479 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2481 g_mutex_lock(priv->observer_list_lock);
2482 iter = priv->observer_list;
2483 while(iter != NULL){
2484 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2485 modest_header_view_observer_update(observer, model,
2487 iter = g_slist_next(iter);
2489 g_mutex_unlock(priv->observer_list_lock);
2493 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2495 ModestHeaderViewPrivate *priv = NULL;
2497 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2498 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2502 modest_header_view_set_filter (ModestHeaderView *self,
2503 ModestHeaderViewFilter filter)
2505 ModestHeaderViewPrivate *priv;
2507 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2508 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2510 priv->filter |= filter;
2512 if (current_folder_needs_filtering (priv))
2513 modest_header_view_refilter (self);
2517 modest_header_view_unset_filter (ModestHeaderView *self,
2518 ModestHeaderViewFilter filter)
2520 ModestHeaderViewPrivate *priv;
2522 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2523 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2525 priv->filter &= ~filter;
2527 if (current_folder_needs_filtering (priv))
2528 modest_header_view_refilter (self);
2532 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2534 if (strcmp ("style", spec->name) == 0) {
2535 update_style (MODEST_HEADER_VIEW (obj));
2536 gtk_widget_queue_draw (GTK_WIDGET (obj));
2541 update_style (ModestHeaderView *self)
2543 ModestHeaderViewPrivate *priv;
2544 GdkColor style_color;
2545 GdkColor style_active_color;
2546 PangoAttrList *attr_list;
2548 PangoAttribute *attr;
2550 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2551 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2555 attr_list = pango_attr_list_new ();
2556 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2557 gdk_color_parse ("grey", &style_color);
2559 priv->secondary_color = style_color;
2560 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2561 pango_attr_list_insert (attr_list, attr);
2564 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2566 "SmallSystemFont", NULL,
2569 attr = pango_attr_font_desc_new (pango_font_description_copy
2570 (style->font_desc));
2571 pango_attr_list_insert (attr_list, attr);
2573 g_object_set (G_OBJECT (priv->renderer_address),
2574 "foreground-gdk", &(priv->secondary_color),
2575 "foreground-set", TRUE,
2576 "attributes", attr_list,
2578 g_object_set (G_OBJECT (priv->renderer_date_status),
2579 "foreground-gdk", &(priv->secondary_color),
2580 "foreground-set", TRUE,
2581 "attributes", attr_list,
2583 pango_attr_list_unref (attr_list);
2585 g_object_set (G_OBJECT (priv->renderer_address),
2586 "foreground-gdk", &(priv->secondary_color),
2587 "foreground-set", TRUE,
2588 "scale", PANGO_SCALE_SMALL,
2591 g_object_set (G_OBJECT (priv->renderer_date_status),
2592 "foreground-gdk", &(priv->secondary_color),
2593 "foreground-set", TRUE,
2594 "scale", PANGO_SCALE_SMALL,
2599 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2600 priv->active_color = style_active_color;
2601 #ifdef MODEST_TOOLKIT_HILDON2
2602 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2603 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2606 #ifdef MODEST_TOOLKIT_HILDON2
2607 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2613 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2618 GtkTreeModel *tree_model;
2623 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2631 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2632 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2636 gtk_tree_model_get (tree_model, &iter,
2637 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2644 parse_date_side (const gchar *string, time_t *date_side)
2650 gboolean result = FALSE;
2652 if (string && string[0] == '\0') {
2657 casefold = g_utf8_casefold (string, -1);
2658 today = g_utf8_casefold (dgettext ("gtk20", "Today"), -1);
2659 yesterday = g_utf8_casefold (dgettext ("gtk20", "Yesterday"), -1);
2660 date = g_date_new ();
2662 if (g_utf8_collate (casefold, today) == 0) {
2663 *date_side = time (NULL);
2668 if (g_utf8_collate (casefold, yesterday) == 0) {
2669 *date_side = time (NULL) - 24*60*60;
2674 g_date_set_parse (date, string);
2675 if (g_date_valid (date)) {
2677 g_date_to_struct_tm (date, &tm);
2678 *date_side = mktime (&tm);
2693 parse_date_range (const gchar *string, time_t *date_range_start, time_t *date_range_end)
2698 parts = g_strsplit (string, "..", 2);
2701 if (g_strv_length (parts) != 2) {
2708 if (!parse_date_side (parts[0], date_range_start)) {
2713 if (parse_date_side (parts[1], date_range_end)) {
2714 if (*date_range_end == 0) {
2715 *date_range_end = (time_t) -1;
2717 *date_range_end += (24*60*60 - 1);
2730 modest_header_view_set_show_latest (ModestHeaderView *header_view,
2733 ModestHeaderViewPrivate *priv;
2734 GtkTreeModel *sortable, *filter, *model;
2736 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2737 priv->show_latest = show_latest;
2739 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2740 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2741 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2742 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2743 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2745 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (model), priv->show_latest);
2752 modest_header_view_get_show_latest (ModestHeaderView *header_view)
2754 ModestHeaderViewPrivate *priv;
2755 GtkTreeModel *sortable, *filter, *model;
2758 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2760 result = priv->show_latest;
2761 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2762 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2763 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2764 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2765 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2767 result = tny_gtk_header_list_model_get_show_latest (TNY_GTK_HEADER_LIST_MODEL (model));
2776 modest_header_view_get_not_latest (ModestHeaderView *header_view)
2778 ModestHeaderViewPrivate *priv;
2779 gint not_latest = 0;
2780 GtkTreeModel *sortable, *filter, *model;
2782 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2784 if (priv->show_latest == 0)
2787 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2788 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2789 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2790 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2791 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2793 not_latest = MAX (0, tny_list_get_length (TNY_LIST (model)) - priv->show_latest);
2802 modest_header_view_set_filter_string (ModestHeaderView *self,
2803 const gchar *filter_string)
2805 ModestHeaderViewPrivate *priv;
2807 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2808 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2810 if (priv->filter_string)
2811 g_free (priv->filter_string);
2813 priv->filter_string = g_strdup (filter_string);
2814 priv->filter_date_range = FALSE;
2816 if (priv->filter_string_splitted) {
2817 g_strfreev (priv->filter_string_splitted);
2818 priv->filter_string_splitted = NULL;
2821 if (priv->filter_string) {
2822 gchar **split, **current, **current_target;
2824 split = g_strsplit (priv->filter_string, " ", 0);
2826 priv->filter_string_splitted = g_malloc0 (sizeof (gchar *)*(g_strv_length (split) + 1));
2827 current_target = priv->filter_string_splitted;
2828 for (current = split; *current != 0; current ++) {
2829 gboolean has_date_range = FALSE;;
2830 if (g_strstr_len (*current, -1, "..") && strcmp(*current, "..")) {
2831 time_t range_start, range_end;
2832 /* It contains .. but it's not ".." so it may be a date range */
2833 if (parse_date_range (*current, &range_start, &range_end)) {
2834 priv->filter_date_range = TRUE;
2835 has_date_range = TRUE;
2836 priv->date_range_start = range_start;
2837 priv->date_range_end = range_end;
2840 if (!has_date_range) {
2841 *current_target = g_utf8_casefold (*current, -1);
2845 *current_target = '\0';
2848 modest_header_view_refilter (MODEST_HEADER_VIEW (self));
2851 #ifdef MODEST_TOOLKIT_HILDON2
2854 on_live_search_timeout (ModestHeaderView *self)
2856 const gchar *needle;
2857 ModestHeaderViewPrivate *priv;
2859 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2861 needle = hildon_live_search_get_text (HILDON_LIVE_SEARCH (priv->live_search));
2862 if (needle && needle[0] != '\0') {
2863 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), needle);
2864 if (priv->show_latest > 0)
2865 modest_header_view_set_show_latest (MODEST_HEADER_VIEW (self), 0);
2867 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), NULL);
2870 priv->live_search_timeout = 0;
2876 on_live_search_refilter (HildonLiveSearch *livesearch,
2877 ModestHeaderView *self)
2879 ModestHeaderViewPrivate *priv;
2880 GtkTreeModel *model, *sortable, *filter;
2882 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2884 if (priv->live_search_timeout > 0) {
2885 g_source_remove (priv->live_search_timeout);
2886 priv->live_search_timeout = 0;
2890 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2891 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2892 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2893 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2894 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2898 if (model && tny_list_get_length (TNY_LIST (model)) > 250) {
2899 priv->live_search_timeout = g_timeout_add (1000, (GSourceFunc) on_live_search_timeout, self);
2901 on_live_search_timeout (self);
2908 modest_header_view_setup_live_search (ModestHeaderView *self)
2910 ModestHeaderViewPrivate *priv;
2912 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (self), NULL);
2913 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2914 priv->live_search = hildon_live_search_new ();
2916 g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), self);
2918 return priv->live_search;