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-ui-actions.h>
45 #include <modest-marshal.h>
46 #include <modest-text-utils.h>
47 #include <modest-icon-names.h>
48 #include <modest-runtime.h>
49 #include "modest-platform.h"
50 #include <modest-hbox-cell-renderer.h>
51 #include <modest-vbox-cell-renderer.h>
52 #include <modest-datetime-formatter.h>
53 #include <modest-ui-constants.h>
54 #ifdef MODEST_TOOLKIT_HILDON2
55 #include <hildon/hildon.h>
58 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
59 static void modest_header_view_init (ModestHeaderView *obj);
60 static void modest_header_view_finalize (GObject *obj);
61 static void modest_header_view_dispose (GObject *obj);
63 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
64 GtkTreeViewColumn *column, gpointer userdata);
66 static gint cmp_rows (GtkTreeModel *tree_model,
71 static gint cmp_subject_rows (GtkTreeModel *tree_model,
76 static gboolean filter_row (GtkTreeModel *model,
80 static void on_account_removed (TnyAccountStore *self,
84 static void on_selection_changed (GtkTreeSelection *sel,
87 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
90 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
93 static void setup_drag_and_drop (GtkWidget *self);
95 static void enable_drag_and_drop (GtkWidget *self);
97 static void disable_drag_and_drop (GtkWidget *self);
99 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
101 #ifndef MODEST_TOOLKIT_HILDON2
102 static gboolean on_focus_in (GtkWidget *sef,
103 GdkEventFocus *event,
106 static gboolean on_focus_out (GtkWidget *self,
107 GdkEventFocus *event,
111 static void folder_monitor_update (TnyFolderObserver *self,
112 TnyFolderChange *change);
114 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
116 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
118 static void _clear_hidding_filter (ModestHeaderView *header_view);
120 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
122 const gchar *tny_folder_id);
124 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
125 GdkEventExpose *event,
128 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
129 static void update_style (ModestHeaderView *self);
130 static void modest_header_view_refilter_by_chunks (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;
185 gchar *filter_string;
186 gchar **filter_string_splitted;
187 gboolean filter_date_range;
188 time_t date_range_start;
189 time_t date_range_end;
191 guint refilter_handler_id;
192 GtkTreeModel *filtered_model;
193 GtkTreeIter refilter_iter;
196 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
197 struct _HeadersCountChangedHelper {
198 ModestHeaderView *self;
199 TnyFolderChange *change;
203 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
204 MODEST_TYPE_HEADER_VIEW, \
205 ModestHeaderViewPrivate))
209 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
211 #define _HEADER_VIEW_SUBJECT_FOLD "_subject_modest_header_view"
212 #define _HEADER_VIEW_FROM_FOLD "_from_modest_header_view"
213 #define _HEADER_VIEW_TO_FOLD "_to_modest_header_view"
214 #define _HEADER_VIEW_CC_FOLD "_cc_modest_header_view"
215 #define _HEADER_VIEW_BCC_FOLD "_bcc_modest_header_view"
218 HEADER_SELECTED_SIGNAL,
219 HEADER_ACTIVATED_SIGNAL,
220 ITEM_NOT_FOUND_SIGNAL,
221 MSG_COUNT_CHANGED_SIGNAL,
222 UPDATING_MSG_LIST_SIGNAL,
227 static GObjectClass *parent_class = NULL;
229 /* uncomment the following if you have defined any signals */
230 static guint signals[LAST_SIGNAL] = {0};
233 modest_header_view_get_type (void)
235 static GType my_type = 0;
237 static const GTypeInfo my_info = {
238 sizeof(ModestHeaderViewClass),
239 NULL, /* base init */
240 NULL, /* base finalize */
241 (GClassInitFunc) modest_header_view_class_init,
242 NULL, /* class finalize */
243 NULL, /* class data */
244 sizeof(ModestHeaderView),
246 (GInstanceInitFunc) modest_header_view_init,
250 static const GInterfaceInfo tny_folder_observer_info =
252 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
253 NULL, /* interface_finalize */
254 NULL /* interface_data */
256 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
260 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
261 &tny_folder_observer_info);
269 modest_header_view_class_init (ModestHeaderViewClass *klass)
271 GObjectClass *gobject_class;
272 gobject_class = (GObjectClass*) klass;
274 parent_class = g_type_class_peek_parent (klass);
275 gobject_class->finalize = modest_header_view_finalize;
276 gobject_class->dispose = modest_header_view_dispose;
278 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
280 signals[HEADER_SELECTED_SIGNAL] =
281 g_signal_new ("header_selected",
282 G_TYPE_FROM_CLASS (gobject_class),
284 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
286 g_cclosure_marshal_VOID__POINTER,
287 G_TYPE_NONE, 1, G_TYPE_POINTER);
289 signals[HEADER_ACTIVATED_SIGNAL] =
290 g_signal_new ("header_activated",
291 G_TYPE_FROM_CLASS (gobject_class),
293 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
295 gtk_marshal_VOID__POINTER_POINTER,
296 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
299 signals[ITEM_NOT_FOUND_SIGNAL] =
300 g_signal_new ("item_not_found",
301 G_TYPE_FROM_CLASS (gobject_class),
303 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
305 g_cclosure_marshal_VOID__INT,
306 G_TYPE_NONE, 1, G_TYPE_INT);
308 signals[MSG_COUNT_CHANGED_SIGNAL] =
309 g_signal_new ("msg_count_changed",
310 G_TYPE_FROM_CLASS (gobject_class),
312 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
314 modest_marshal_VOID__POINTER_POINTER,
315 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
317 signals[UPDATING_MSG_LIST_SIGNAL] =
318 g_signal_new ("updating-msg-list",
319 G_TYPE_FROM_CLASS (gobject_class),
321 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
323 g_cclosure_marshal_VOID__BOOLEAN,
324 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
326 #ifdef MODEST_TOOLKIT_HILDON2
327 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
333 tny_folder_observer_init (TnyFolderObserverIface *klass)
335 klass->update = folder_monitor_update;
338 static GtkTreeViewColumn*
339 get_new_column (const gchar *name, GtkCellRenderer *renderer,
340 gboolean resizable, gint sort_col_id, gboolean show_as_text,
341 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
343 GtkTreeViewColumn *column;
345 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
346 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
348 gtk_tree_view_column_set_resizable (column, resizable);
350 gtk_tree_view_column_set_expand (column, TRUE);
353 gtk_tree_view_column_add_attribute (column, renderer, "text",
355 if (sort_col_id >= 0)
356 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
358 gtk_tree_view_column_set_sort_indicator (column, FALSE);
359 gtk_tree_view_column_set_reorderable (column, TRUE);
362 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
369 remove_all_columns (ModestHeaderView *obj)
371 GList *columns, *cursor;
373 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
375 for (cursor = columns; cursor; cursor = cursor->next)
376 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
377 GTK_TREE_VIEW_COLUMN(cursor->data));
378 g_list_free (columns);
382 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
384 GtkTreeModel *sortable, *filter_model;
385 GtkTreeViewColumn *column=NULL;
386 GtkTreeSelection *selection = NULL;
387 GtkCellRenderer *renderer_header,
388 *renderer_attach, *renderer_compact_date_or_status;
389 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
390 *renderer_subject_box, *renderer_recpt,
392 ModestHeaderViewPrivate *priv;
393 GtkTreeViewColumn *compact_column = NULL;
396 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
397 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
399 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
401 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
403 /* TODO: check whether these renderers need to be freed */
404 renderer_attach = gtk_cell_renderer_pixbuf_new ();
405 renderer_priority = gtk_cell_renderer_pixbuf_new ();
406 renderer_header = gtk_cell_renderer_text_new ();
408 renderer_compact_header = modest_vbox_cell_renderer_new ();
409 renderer_recpt_box = modest_hbox_cell_renderer_new ();
410 renderer_subject_box = modest_hbox_cell_renderer_new ();
411 renderer_recpt = gtk_cell_renderer_text_new ();
412 priv->renderer_address = renderer_recpt;
413 priv->renderer_subject = gtk_cell_renderer_text_new ();
414 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
415 priv->renderer_date_status = renderer_compact_date_or_status;
417 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
418 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
419 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
420 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
421 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
422 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
423 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
424 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
425 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
426 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
427 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
428 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
429 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
430 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
432 #ifdef MODEST_TOOLKIT_HILDON2
433 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
435 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
436 #ifndef MODEST_TOOLKIT_GTK
437 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
438 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
440 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
441 g_object_set(G_OBJECT(renderer_header),
442 "ellipsize", PANGO_ELLIPSIZE_END,
444 g_object_set (G_OBJECT (priv->renderer_subject),
445 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
447 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
448 g_object_set (G_OBJECT (renderer_recpt),
449 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
451 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
452 g_object_set(G_OBJECT(renderer_compact_date_or_status),
453 "xalign", 1.0, "yalign", 0.1,
455 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
456 #ifdef MODEST_TOOLKIT_HILDON2
457 g_object_set (G_OBJECT (renderer_priority),
459 "xalign", 0.0, NULL);
460 g_object_set (G_OBJECT (renderer_attach),
462 "xalign", 0.0, NULL);
464 g_object_set (G_OBJECT (renderer_priority),
465 "yalign", 0.5, NULL);
466 g_object_set (G_OBJECT (renderer_attach),
467 "yalign", 0.0, NULL);
470 #ifdef MODEST_TOOLKIT_HILDON1
471 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
472 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
473 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
474 #elif MODEST_TOOLKIT_HILDON2
475 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
476 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
477 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
479 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
480 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
481 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
484 remove_all_columns (self);
486 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
487 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
489 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
490 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
491 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
494 /* Add new columns */
495 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
496 ModestHeaderViewColumn col =
497 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
499 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
500 g_printerr ("modest: invalid column %d in column list\n", col);
506 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
507 column = get_new_column (_("A"), renderer_attach, FALSE,
508 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
510 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
512 gtk_tree_view_column_set_fixed_width (column, 45);
516 case MODEST_HEADER_VIEW_COLUMN_FROM:
517 column = get_new_column (_("From"), renderer_header, TRUE,
518 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
520 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
521 GINT_TO_POINTER(TRUE));
524 case MODEST_HEADER_VIEW_COLUMN_TO:
525 column = get_new_column (_("To"), renderer_header, TRUE,
526 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
528 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
529 GINT_TO_POINTER(FALSE));
532 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
533 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
534 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
536 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
537 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
538 compact_column = column;
541 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
542 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
543 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
545 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
546 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
547 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
548 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
549 compact_column = column;
553 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
554 column = get_new_column (_("Subject"), renderer_header, TRUE,
555 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
557 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
561 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
562 column = get_new_column (_("Received"), renderer_header, TRUE,
563 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
565 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
566 GINT_TO_POINTER(TRUE));
569 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
570 column = get_new_column (_("Sent"), renderer_header, TRUE,
571 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
573 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
574 GINT_TO_POINTER(FALSE));
577 case MODEST_HEADER_VIEW_COLUMN_SIZE:
578 column = get_new_column (_("Size"), renderer_header, TRUE,
579 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
581 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
584 case MODEST_HEADER_VIEW_COLUMN_STATUS:
585 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
586 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
588 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
593 g_return_val_if_reached(FALSE);
596 /* we keep the column id around */
597 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
598 GINT_TO_POINTER(col));
600 /* we need this ptr when sorting the rows */
601 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
603 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
607 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
608 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
609 (GtkTreeIterCompareFunc) cmp_rows,
610 compact_column, NULL);
611 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
612 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
613 (GtkTreeIterCompareFunc) cmp_subject_rows,
614 compact_column, NULL);
618 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
624 datetime_format_changed (ModestDatetimeFormatter *formatter,
625 ModestHeaderView *self)
627 gtk_widget_queue_draw (GTK_WIDGET (self));
631 modest_header_view_init (ModestHeaderView *obj)
633 ModestHeaderViewPrivate *priv;
636 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
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;
659 priv->filter_string = NULL;
660 priv->filter_string_splitted = NULL;
661 priv->filter_date_range = FALSE;
662 priv->selection_changed_handler = 0;
663 priv->acc_removed_handler = 0;
665 priv->filtered_model = NULL;
666 priv->refilter_handler_id = 0;
668 /* Sort parameters */
669 for (j=0; j < 2; j++) {
670 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
671 priv->sort_colid[j][i] = -1;
672 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
676 priv->datetime_formatter = modest_datetime_formatter_new ();
677 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
678 G_CALLBACK (datetime_format_changed), (gpointer) obj);
680 setup_drag_and_drop (GTK_WIDGET(obj));
684 modest_header_view_dispose (GObject *obj)
686 ModestHeaderView *self;
687 ModestHeaderViewPrivate *priv;
688 GtkTreeSelection *sel;
690 self = MODEST_HEADER_VIEW(obj);
691 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
693 if (priv->refilter_handler_id > 0) {
694 g_source_remove (priv->refilter_handler_id);
695 priv->refilter_handler_id = 0;
696 priv->filtered_model = NULL;
699 #ifdef MODEST_TOOLKIT_HILDON2
700 if (priv->live_search_timeout > 0) {
701 g_source_remove (priv->live_search_timeout);
702 priv->live_search_timeout = 0;
706 if (priv->datetime_formatter) {
707 g_object_unref (priv->datetime_formatter);
708 priv->datetime_formatter = NULL;
711 /* Free in the dispose to avoid unref cycles */
713 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
714 g_object_unref (G_OBJECT (priv->folder));
718 /* We need to do this here in the dispose because the
719 selection won't exist when finalizing */
720 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
721 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
722 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
723 priv->selection_changed_handler = 0;
726 G_OBJECT_CLASS(parent_class)->dispose (obj);
730 modest_header_view_finalize (GObject *obj)
732 ModestHeaderView *self;
733 ModestHeaderViewPrivate *priv;
735 self = MODEST_HEADER_VIEW(obj);
736 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
738 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
739 priv->acc_removed_handler)) {
740 g_signal_handler_disconnect (modest_runtime_get_account_store (),
741 priv->acc_removed_handler);
744 /* There is no need to lock because there should not be any
745 * reference to self now. */
746 g_mutex_free(priv->observer_list_lock);
747 g_slist_free(priv->observer_list);
749 g_mutex_lock (priv->observers_lock);
751 tny_folder_monitor_stop (priv->monitor);
752 g_object_unref (G_OBJECT (priv->monitor));
754 g_mutex_unlock (priv->observers_lock);
755 g_mutex_free (priv->observers_lock);
757 /* Clear hidding array created by cut operation */
758 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
760 if (priv->autoselect_reference != NULL) {
761 gtk_tree_row_reference_free (priv->autoselect_reference);
762 priv->autoselect_reference = NULL;
765 if (priv->filter_string) {
766 g_free (priv->filter_string);
769 if (priv->filter_string_splitted) {
770 g_strfreev (priv->filter_string_splitted);
773 G_OBJECT_CLASS(parent_class)->finalize (obj);
778 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
781 GtkTreeSelection *sel;
782 ModestHeaderView *self;
783 ModestHeaderViewPrivate *priv;
785 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
788 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
789 self = MODEST_HEADER_VIEW(obj);
790 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
792 modest_header_view_set_style (self, style);
794 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
795 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
796 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
798 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
799 TRUE); /* alternating row colors */
801 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
802 priv->selection_changed_handler =
803 g_signal_connect_after (sel, "changed",
804 G_CALLBACK(on_selection_changed), self);
806 g_signal_connect (self, "row-activated",
807 G_CALLBACK (on_header_row_activated), NULL);
809 #ifndef MODEST_TOOLKIT_HILDON2
810 g_signal_connect (self, "focus-in-event",
811 G_CALLBACK(on_focus_in), NULL);
812 g_signal_connect (self, "focus-out-event",
813 G_CALLBACK(on_focus_out), NULL);
816 g_signal_connect (self, "button-press-event",
817 G_CALLBACK(on_button_press_event), NULL);
818 g_signal_connect (self, "button-release-event",
819 G_CALLBACK(on_button_release_event), NULL);
821 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
823 G_CALLBACK (on_account_removed),
826 g_signal_connect (self, "expose-event",
827 G_CALLBACK(modest_header_view_on_expose_event),
830 return GTK_WIDGET(self);
835 modest_header_view_count_selected_headers (ModestHeaderView *self)
837 GtkTreeSelection *sel;
840 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
842 /* Get selection object and check selected rows count */
843 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
844 selected_rows = gtk_tree_selection_count_selected_rows (sel);
846 return selected_rows;
850 modest_header_view_has_selected_headers (ModestHeaderView *self)
852 GtkTreeSelection *sel;
855 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
857 /* Get selection object and check selected rows count */
858 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
859 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
866 modest_header_view_get_selected_headers (ModestHeaderView *self)
868 GtkTreeSelection *sel;
869 TnyList *header_list = NULL;
871 GList *list, *tmp = NULL;
872 GtkTreeModel *tree_model = NULL;
875 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
878 /* Get selected rows */
879 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
880 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
883 header_list = tny_simple_list_new();
885 list = g_list_reverse (list);
888 /* get header from selection */
889 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
890 gtk_tree_model_get (tree_model, &iter,
891 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
893 /* Prepend to list */
894 tny_list_prepend (header_list, G_OBJECT (header));
895 g_object_unref (G_OBJECT (header));
897 tmp = g_list_next (tmp);
900 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
907 /* scroll our list view so the selected item is visible */
909 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
911 #ifdef MODEST_TOOLKIT_GTK
913 GtkTreePath *selected_path;
914 GtkTreePath *start, *end;
918 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
919 selected_path = gtk_tree_model_get_path (model, iter);
921 start = gtk_tree_path_new ();
922 end = gtk_tree_path_new ();
924 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
926 if (gtk_tree_path_compare (selected_path, start) < 0 ||
927 gtk_tree_path_compare (end, selected_path) < 0)
928 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
929 selected_path, NULL, TRUE,
932 gtk_tree_path_free (selected_path);
933 gtk_tree_path_free (start);
934 gtk_tree_path_free (end);
936 #endif /* MODEST_TOOLKIT_GTK */
941 modest_header_view_select_next (ModestHeaderView *self)
943 GtkTreeSelection *sel;
948 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
950 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
951 path = get_selected_row (GTK_TREE_VIEW(self), &model);
952 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
953 /* Unselect previous path */
954 gtk_tree_selection_unselect_path (sel, path);
956 /* Move path down and selects new one */
957 if (gtk_tree_model_iter_next (model, &iter)) {
958 gtk_tree_selection_select_iter (sel, &iter);
959 scroll_to_selected (self, &iter, FALSE);
961 gtk_tree_path_free(path);
967 modest_header_view_select_prev (ModestHeaderView *self)
969 GtkTreeSelection *sel;
974 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
976 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
977 path = get_selected_row (GTK_TREE_VIEW(self), &model);
978 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
979 /* Unselect previous path */
980 gtk_tree_selection_unselect_path (sel, path);
983 if (gtk_tree_path_prev (path)) {
984 gtk_tree_model_get_iter (model, &iter, path);
986 /* Select the new one */
987 gtk_tree_selection_select_iter (sel, &iter);
988 scroll_to_selected (self, &iter, TRUE);
991 gtk_tree_path_free (path);
996 modest_header_view_get_columns (ModestHeaderView *self)
998 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1000 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1006 modest_header_view_set_style (ModestHeaderView *self,
1007 ModestHeaderViewStyle style)
1009 ModestHeaderViewPrivate *priv;
1010 gboolean show_col_headers = FALSE;
1011 ModestHeaderViewStyle old_style;
1013 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
1014 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
1017 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1018 if (priv->style == style)
1019 return TRUE; /* nothing to do */
1022 case MODEST_HEADER_VIEW_STYLE_DETAILS:
1023 show_col_headers = TRUE;
1025 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
1028 g_return_val_if_reached (FALSE);
1030 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
1031 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
1033 old_style = priv->style;
1034 priv->style = style;
1040 ModestHeaderViewStyle
1041 modest_header_view_get_style (ModestHeaderView *self)
1043 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
1045 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
1048 /* This is used to automatically select the first header if the user
1049 * has not selected any header yet.
1052 modest_header_view_on_expose_event(GtkTreeView *header_view,
1053 GdkEventExpose *event,
1056 GtkTreeSelection *sel;
1057 GtkTreeModel *model;
1058 GtkTreeIter tree_iter;
1059 ModestHeaderViewPrivate *priv;
1061 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1062 model = gtk_tree_view_get_model(header_view);
1067 #ifdef MODEST_TOOLKIT_HILDON2
1070 sel = gtk_tree_view_get_selection(header_view);
1071 if(!gtk_tree_selection_count_selected_rows(sel)) {
1072 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1073 GtkTreePath *tree_iter_path;
1074 /* Prevent the widget from getting the focus
1075 when selecting the first item */
1076 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1077 g_object_set(header_view, "can-focus", FALSE, NULL);
1078 gtk_tree_selection_select_iter(sel, &tree_iter);
1079 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1080 g_object_set(header_view, "can-focus", TRUE, NULL);
1081 if (priv->autoselect_reference) {
1082 gtk_tree_row_reference_free (priv->autoselect_reference);
1084 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1085 gtk_tree_path_free (tree_iter_path);
1088 if (priv->autoselect_reference != NULL && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
1089 gboolean moved_selection = FALSE;
1090 GtkTreePath * last_path;
1091 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1092 moved_selection = TRUE;
1096 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1097 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1098 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1099 moved_selection = TRUE;
1100 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1102 gtk_tree_path_free (last_path);
1104 if (moved_selection) {
1105 gtk_tree_row_reference_free (priv->autoselect_reference);
1106 priv->autoselect_reference = NULL;
1109 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1110 GtkTreePath *current_path;
1111 current_path = gtk_tree_model_get_path (model, &tree_iter);
1112 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1113 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1114 g_object_set(header_view, "can-focus", FALSE, NULL);
1115 gtk_tree_selection_unselect_all (sel);
1116 gtk_tree_selection_select_iter(sel, &tree_iter);
1117 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1118 g_object_set(header_view, "can-focus", TRUE, NULL);
1119 gtk_tree_row_reference_free (priv->autoselect_reference);
1120 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1122 gtk_tree_path_free (current_path);
1123 gtk_tree_path_free (last_path);
1133 modest_header_view_get_folder (ModestHeaderView *self)
1135 ModestHeaderViewPrivate *priv;
1137 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1139 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1142 g_object_ref (priv->folder);
1144 return priv->folder;
1148 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1154 ModestHeaderView *self;
1155 ModestHeaderViewPrivate *priv;
1157 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1159 self = MODEST_HEADER_VIEW (user_data);
1160 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1162 if (cancelled || err)
1165 /* Add IDLE observer (monitor) and another folder observer for
1166 new messages (self) */
1167 g_mutex_lock (priv->observers_lock);
1168 if (priv->monitor) {
1169 tny_folder_monitor_stop (priv->monitor);
1170 g_object_unref (G_OBJECT (priv->monitor));
1172 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1173 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1174 tny_folder_monitor_start (priv->monitor);
1175 g_mutex_unlock (priv->observers_lock);
1179 modest_header_view_set_folder_intern (ModestHeaderView *self,
1185 ModestHeaderViewPrivate *priv;
1186 GList *cols, *cursor;
1187 GtkTreeModel *filter_model, *sortable;
1189 GtkSortType sort_type;
1191 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1193 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1194 tny_gtk_header_list_model_set_update_in_batches (TNY_GTK_HEADER_LIST_MODEL (headers), 300);
1196 /* Start the monitor in the callback of the
1197 tny_gtk_header_list_model_set_folder call. It's crucial to
1198 do it there and not just after the call because we want the
1199 monitor to observe only the headers returned by the
1200 tny_folder_get_headers_async call that it's inside the
1201 tny_gtk_header_list_model_set_folder call. This way the
1202 monitor infrastructure could successfully cope with
1203 duplicates. For example if a tny_folder_add_msg_async is
1204 happening while tny_gtk_header_list_model_set_folder is
1205 invoked, then the first call could add a header that will
1206 be added again by tny_gtk_header_list_model_set_folder, so
1207 we'd end up with duplicate headers. sergio */
1208 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1210 set_folder_intern_get_headers_async_cb,
1213 /* Init filter_row function to examine empty status */
1214 priv->status = HEADER_VIEW_INIT;
1216 /* Create sortable model */
1217 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (headers));
1218 g_object_unref (headers);
1220 /* Create a tree model filter to hide and show rows for cut operations */
1221 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (sortable), NULL);
1222 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1223 filter_row, self, NULL);
1224 g_object_unref (sortable);
1226 /* install our special sorting functions */
1227 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1229 /* Restore sort column id */
1231 type = modest_tny_folder_guess_folder_type (folder);
1232 if (type == TNY_FOLDER_TYPE_INVALID)
1233 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1235 sort_colid = modest_header_view_get_sort_column_id (self, type);
1236 sort_type = modest_header_view_get_sort_type (self, type);
1237 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1240 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1241 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1242 (GtkTreeIterCompareFunc) cmp_rows,
1244 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1245 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1246 (GtkTreeIterCompareFunc) cmp_subject_rows,
1251 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1252 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1253 g_object_unref (filter_model);
1260 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1262 GtkSortType sort_type)
1264 ModestHeaderViewPrivate *priv = NULL;
1265 GtkTreeModel *sortable = NULL, *filter_model = NULL;
1268 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1269 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1271 /* Get model and private data */
1272 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1273 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1274 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
1275 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
1278 /* Sort tree model */
1279 type = modest_tny_folder_guess_folder_type (priv->folder);
1280 if (type == TNY_FOLDER_TYPE_INVALID)
1281 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1283 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1286 /* Store new sort parameters */
1287 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1292 modest_header_view_set_sort_params (ModestHeaderView *self,
1294 GtkSortType sort_type,
1297 ModestHeaderViewPrivate *priv;
1298 ModestHeaderViewStyle style;
1300 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1301 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1302 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1304 style = modest_header_view_get_style (self);
1305 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1307 priv->sort_colid[style][type] = sort_colid;
1308 priv->sort_type[style][type] = sort_type;
1312 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1315 ModestHeaderViewPrivate *priv;
1316 ModestHeaderViewStyle style;
1318 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1319 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1321 style = modest_header_view_get_style (self);
1322 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1324 return priv->sort_colid[style][type];
1328 modest_header_view_get_sort_type (ModestHeaderView *self,
1331 ModestHeaderViewPrivate *priv;
1332 ModestHeaderViewStyle style;
1334 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1335 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1337 style = modest_header_view_get_style (self);
1338 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1340 return priv->sort_type[style][type];
1344 ModestHeaderView *header_view;
1345 RefreshAsyncUserCallback cb;
1350 folder_refreshed_cb (ModestMailOperation *mail_op,
1354 ModestHeaderViewPrivate *priv;
1355 SetFolderHelper *info;
1357 info = (SetFolderHelper*) user_data;
1359 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1363 info->cb (mail_op, folder, info->user_data);
1365 /* Start the folder count changes observer. We do not need it
1366 before the refresh. Note that the monitor could still be
1367 called for this refresh but now we know that the callback
1368 was previously called */
1369 g_mutex_lock (priv->observers_lock);
1370 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1371 g_mutex_unlock (priv->observers_lock);
1373 /* Notify the observers that the update is over */
1374 g_signal_emit (G_OBJECT (info->header_view),
1375 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1377 /* Allow filtering notifications from now on if the current
1378 folder is still the same (if not then the user has selected
1379 another one to refresh, we should wait until that refresh
1381 if (priv->folder == folder)
1382 priv->notify_status = TRUE;
1385 g_object_unref (info->header_view);
1390 refresh_folder_error_handler (ModestMailOperation *mail_op,
1393 const GError *error = modest_mail_operation_get_error (mail_op);
1395 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1396 error->code == TNY_IO_ERROR_WRITE ||
1397 error->code == TNY_IO_ERROR_READ) {
1398 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1399 /* If the mail op has been cancelled then it's not an error: don't show any message */
1400 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1401 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1402 modest_platform_information_banner (NULL, NULL, msg);
1409 modest_header_view_set_folder (ModestHeaderView *self,
1412 ModestWindow *progress_window,
1413 RefreshAsyncUserCallback callback,
1416 ModestHeaderViewPrivate *priv;
1418 g_return_if_fail (self);
1420 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1423 if (priv->status_timeout) {
1424 g_source_remove (priv->status_timeout);
1425 priv->status_timeout = 0;
1428 g_mutex_lock (priv->observers_lock);
1429 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1430 g_object_unref (priv->folder);
1431 priv->folder = NULL;
1432 g_mutex_unlock (priv->observers_lock);
1436 GtkTreeSelection *selection;
1437 SetFolderHelper *info;
1438 ModestMailOperation *mail_op = NULL;
1440 /* Set folder in the model */
1441 modest_header_view_set_folder_intern (self, folder, refresh);
1443 /* Pick my reference. Nothing to do with the mail operation */
1444 priv->folder = g_object_ref (folder);
1446 /* Do not notify about filterings until the refresh finishes */
1447 priv->notify_status = FALSE;
1449 /* Clear the selection if exists */
1450 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1451 gtk_tree_selection_unselect_all(selection);
1452 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1454 /* Notify the observers that the update begins */
1455 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1458 /* create the helper */
1459 info = g_malloc0 (sizeof (SetFolderHelper));
1460 info->header_view = g_object_ref (self);
1461 info->cb = callback;
1462 info->user_data = user_data;
1464 /* Create the mail operation (source will be the parent widget) */
1465 if (progress_window)
1466 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1467 refresh_folder_error_handler,
1470 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1473 /* Refresh the folder asynchronously */
1474 modest_mail_operation_refresh_folder (mail_op,
1476 folder_refreshed_cb,
1479 folder_refreshed_cb (mail_op, folder, info);
1483 g_object_unref (mail_op);
1485 g_mutex_lock (priv->observers_lock);
1487 if (priv->monitor) {
1488 tny_folder_monitor_stop (priv->monitor);
1489 g_object_unref (G_OBJECT (priv->monitor));
1490 priv->monitor = NULL;
1493 if (priv->autoselect_reference) {
1494 gtk_tree_row_reference_free (priv->autoselect_reference);
1495 priv->autoselect_reference = NULL;
1498 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1500 modest_header_view_notify_observers(self, NULL, NULL);
1502 g_mutex_unlock (priv->observers_lock);
1504 /* Notify the observers that the update is over */
1505 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1511 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1512 GtkTreeViewColumn *column, gpointer userdata)
1514 ModestHeaderView *self = NULL;
1516 GtkTreeModel *model = NULL;
1517 TnyHeader *header = NULL;
1518 TnyHeaderFlags flags;
1520 self = MODEST_HEADER_VIEW (treeview);
1522 model = gtk_tree_view_get_model (treeview);
1523 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1526 /* get the first selected item */
1527 gtk_tree_model_get (model, &iter,
1528 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1529 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1532 /* Dont open DELETED messages */
1533 if (flags & TNY_HEADER_FLAG_DELETED) {
1536 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1537 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1538 modest_platform_information_banner (NULL, NULL, msg);
1544 g_signal_emit (G_OBJECT(self),
1545 signals[HEADER_ACTIVATED_SIGNAL],
1551 g_object_unref (G_OBJECT (header));
1556 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1558 GtkTreeModel *model;
1559 TnyHeader *header = NULL;
1560 GtkTreePath *path = NULL;
1562 ModestHeaderView *self;
1563 GList *selected = NULL;
1565 g_return_if_fail (sel);
1566 g_return_if_fail (user_data);
1568 self = MODEST_HEADER_VIEW (user_data);
1570 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1571 if (selected != NULL)
1572 path = (GtkTreePath *) selected->data;
1573 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1574 return; /* msg was _un_selected */
1576 gtk_tree_model_get (model, &iter,
1577 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1581 g_signal_emit (G_OBJECT(self),
1582 signals[HEADER_SELECTED_SIGNAL],
1585 g_object_unref (G_OBJECT (header));
1587 /* free all items in 'selected' */
1588 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1589 g_list_free (selected);
1593 /* PROTECTED method. It's useful when we want to force a given
1594 selection to reload a msg. For example if we have selected a header
1595 in offline mode, when Modest become online, we want to reload the
1596 message automatically without an user click over the header */
1598 _modest_header_view_change_selection (GtkTreeSelection *selection,
1601 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1602 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1604 on_selection_changed (selection, user_data);
1608 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1615 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1619 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1623 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1631 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1639 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1640 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1644 case TNY_HEADER_FLAG_ATTACHMENTS:
1646 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1647 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1648 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1649 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1651 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1652 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1654 return cmp ? cmp : t1 - t2;
1656 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1657 TnyHeader *header1 = NULL, *header2 = NULL;
1659 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1660 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1661 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1662 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1664 /* This is for making priority values respect the intuitive sort relationship
1665 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1667 if (header1 && header2) {
1668 cmp = compare_priorities (tny_header_get_priority (header1),
1669 tny_header_get_priority (header2));
1670 g_object_unref (header1);
1671 g_object_unref (header2);
1673 return cmp ? cmp : t1 - t2;
1679 return &iter1 - &iter2; /* oughhhh */
1684 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1691 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1693 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1694 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1695 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1696 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1698 /* Do not use the prefixes for sorting. Consume all the blank
1699 spaces for sorting */
1700 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1701 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1704 /* If they're equal based on subject without prefix then just
1705 sort them by length. This will show messages like this.
1712 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1719 /* Drag and drop stuff */
1721 drag_data_get_cb (GtkWidget *widget,
1722 GdkDragContext *context,
1723 GtkSelectionData *selection_data,
1728 ModestHeaderView *self = NULL;
1729 ModestHeaderViewPrivate *priv = NULL;
1731 self = MODEST_HEADER_VIEW (widget);
1732 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1734 /* Set the data. Do not use the current selection because it
1735 could be different than the selection at the beginning of
1737 modest_dnd_selection_data_set_paths (selection_data,
1738 priv->drag_begin_cached_selected_rows);
1742 * We're caching the selected rows at the beginning because the
1743 * selection could change between drag-begin and drag-data-get, for
1744 * example if we have a set of rows already selected, and then we
1745 * click in one of them (without SHIFT key pressed) and begin a drag,
1746 * the selection at that moment contains all the selected lines, but
1747 * after dropping the selection, the release event provokes that only
1748 * the row used to begin the drag is selected, so at the end the
1749 * drag&drop affects only one rows instead of all the selected ones.
1753 drag_begin_cb (GtkWidget *widget,
1754 GdkDragContext *context,
1757 ModestHeaderView *self = NULL;
1758 ModestHeaderViewPrivate *priv = NULL;
1759 GtkTreeSelection *selection;
1761 self = MODEST_HEADER_VIEW (widget);
1762 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1764 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1765 priv->drag_begin_cached_selected_rows =
1766 gtk_tree_selection_get_selected_rows (selection, NULL);
1770 * We use the drag-end signal to clear the cached selection, we use
1771 * this because this allways happens, whether or not the d&d was a
1775 drag_end_cb (GtkWidget *widget,
1779 ModestHeaderView *self = NULL;
1780 ModestHeaderViewPrivate *priv = NULL;
1782 self = MODEST_HEADER_VIEW (widget);
1783 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1785 /* Free cached data */
1786 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1787 g_list_free (priv->drag_begin_cached_selected_rows);
1788 priv->drag_begin_cached_selected_rows = NULL;
1791 /* Header view drag types */
1792 const GtkTargetEntry header_view_drag_types[] = {
1793 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1797 enable_drag_and_drop (GtkWidget *self)
1799 #ifdef MODEST_TOOLKIT_HILDON2
1802 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1803 header_view_drag_types,
1804 G_N_ELEMENTS (header_view_drag_types),
1805 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1809 disable_drag_and_drop (GtkWidget *self)
1811 #ifdef MODEST_TOOLKIT_HILDON2
1814 gtk_drag_source_unset (self);
1818 setup_drag_and_drop (GtkWidget *self)
1820 #ifdef MODEST_TOOLKIT_HILDON2
1823 enable_drag_and_drop(self);
1824 g_signal_connect(G_OBJECT (self), "drag_data_get",
1825 G_CALLBACK(drag_data_get_cb), NULL);
1827 g_signal_connect(G_OBJECT (self), "drag_begin",
1828 G_CALLBACK(drag_begin_cb), NULL);
1830 g_signal_connect(G_OBJECT (self), "drag_end",
1831 G_CALLBACK(drag_end_cb), NULL);
1834 static GtkTreePath *
1835 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1837 GtkTreePath *path = NULL;
1838 GtkTreeSelection *sel = NULL;
1841 sel = gtk_tree_view_get_selection(self);
1842 rows = gtk_tree_selection_get_selected_rows (sel, model);
1844 if ((rows == NULL) || (g_list_length(rows) != 1))
1847 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1852 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1858 #ifndef MODEST_TOOLKIT_HILDON2
1860 * This function moves the tree view scroll to the current selected
1861 * row when the widget grabs the focus
1864 on_focus_in (GtkWidget *self,
1865 GdkEventFocus *event,
1868 GtkTreeSelection *selection;
1869 GtkTreeModel *model;
1870 GList *selected = NULL;
1871 GtkTreePath *selected_path = NULL;
1873 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1877 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1878 /* If none selected yet, pick the first one */
1879 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1883 /* Return if the model is empty */
1884 if (!gtk_tree_model_get_iter_first (model, &iter))
1887 path = gtk_tree_model_get_path (model, &iter);
1888 gtk_tree_selection_select_path (selection, path);
1889 gtk_tree_path_free (path);
1892 /* Need to get the all the rows because is selection multiple */
1893 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1894 if (selected == NULL) return FALSE;
1895 selected_path = (GtkTreePath *) selected->data;
1898 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1899 g_list_free (selected);
1905 on_focus_out (GtkWidget *self,
1906 GdkEventFocus *event,
1910 if (!gtk_widget_is_focus (self)) {
1911 GtkTreeSelection *selection = NULL;
1912 GList *selected_rows = NULL;
1913 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1914 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1915 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1916 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1917 gtk_tree_selection_unselect_all (selection);
1918 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1919 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1920 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1921 g_list_free (selected_rows);
1929 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1931 enable_drag_and_drop(self);
1936 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1938 GtkTreeSelection *selection = NULL;
1939 GtkTreePath *path = NULL;
1940 gboolean already_selected = FALSE, already_opened = FALSE;
1941 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1943 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1945 GtkTreeModel *model;
1947 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1948 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1950 /* Get header from model */
1951 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1952 if (gtk_tree_model_get_iter (model, &iter, path)) {
1953 GValue value = {0,};
1956 gtk_tree_model_get_value (model, &iter,
1957 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1959 header = (TnyHeader *) g_value_get_object (&value);
1960 if (TNY_IS_HEADER (header)) {
1961 status = modest_tny_all_send_queues_get_msg_status (header);
1962 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1965 g_value_unset (&value);
1969 /* Enable drag and drop only if the user clicks on a row that
1970 it's already selected. If not, let him select items using
1971 the pointer. If the message is in an OUTBOX and in sending
1972 status disable drag and drop as well */
1973 if (!already_selected ||
1974 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1976 disable_drag_and_drop(self);
1979 gtk_tree_path_free(path);
1981 /* If it's already opened then do not let the button-press
1982 event go on because it'll perform a message open because
1983 we're clicking on to an already selected header */
1988 folder_monitor_update (TnyFolderObserver *self,
1989 TnyFolderChange *change)
1991 ModestHeaderViewPrivate *priv = NULL;
1992 TnyFolderChangeChanged changed;
1993 TnyFolder *folder = NULL;
1995 changed = tny_folder_change_get_changed (change);
1997 /* Do not notify the observers if the folder of the header
1998 view has changed before this call to the observer
2000 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2001 folder = tny_folder_change_get_folder (change);
2002 if (folder != priv->folder)
2005 MODEST_DEBUG_BLOCK (
2006 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
2007 g_print ("ADDED %d/%d (r/t) \n",
2008 tny_folder_change_get_new_unread_count (change),
2009 tny_folder_change_get_new_all_count (change));
2010 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
2011 g_print ("ALL COUNT %d\n",
2012 tny_folder_change_get_new_all_count (change));
2013 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
2014 g_print ("UNREAD COUNT %d\n",
2015 tny_folder_change_get_new_unread_count (change));
2016 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
2017 g_print ("EXPUNGED %d/%d (r/t) \n",
2018 tny_folder_change_get_new_unread_count (change),
2019 tny_folder_change_get_new_all_count (change));
2020 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
2021 g_print ("FOLDER RENAME\n");
2022 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
2023 g_print ("MSG RECEIVED %d/%d (r/t) \n",
2024 tny_folder_change_get_new_unread_count (change),
2025 tny_folder_change_get_new_all_count (change));
2026 g_print ("---------------------------------------------------\n");
2029 /* Check folder count */
2030 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
2031 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
2033 g_mutex_lock (priv->observers_lock);
2035 /* Emit signal to evaluate how headers changes affects
2036 to the window view */
2037 g_signal_emit (G_OBJECT(self),
2038 signals[MSG_COUNT_CHANGED_SIGNAL],
2041 /* Added or removed headers, so data stored on cliboard are invalid */
2042 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
2043 modest_email_clipboard_clear (priv->clipboard);
2045 g_mutex_unlock (priv->observers_lock);
2051 g_object_unref (folder);
2055 modest_header_view_is_empty (ModestHeaderView *self)
2057 ModestHeaderViewPrivate *priv;
2059 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
2061 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2063 return priv->status == HEADER_VIEW_EMPTY;
2067 modest_header_view_clear (ModestHeaderView *self)
2069 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2071 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2075 modest_header_view_copy_selection (ModestHeaderView *header_view)
2077 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2079 /* Copy selection */
2080 _clipboard_set_selected_data (header_view, FALSE);
2084 modest_header_view_cut_selection (ModestHeaderView *header_view)
2086 ModestHeaderViewPrivate *priv = NULL;
2087 const gchar **hidding = NULL;
2088 guint i, n_selected;
2090 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2092 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2094 /* Copy selection */
2095 _clipboard_set_selected_data (header_view, TRUE);
2097 /* Get hidding ids */
2098 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2100 /* Clear hidding array created by previous cut operation */
2101 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2103 /* Copy hidding array */
2104 priv->n_selected = n_selected;
2105 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2106 for (i=0; i < n_selected; i++)
2107 priv->hidding_ids[i] = g_strdup(hidding[i]);
2109 /* Hide cut headers */
2110 modest_header_view_refilter (header_view);
2117 _clipboard_set_selected_data (ModestHeaderView *header_view,
2120 ModestHeaderViewPrivate *priv = NULL;
2121 TnyList *headers = NULL;
2123 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2124 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2126 /* Set selected data on clipboard */
2127 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2128 headers = modest_header_view_get_selected_headers (header_view);
2129 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2132 g_object_unref (headers);
2136 ModestHeaderView *self;
2141 notify_filter_change (gpointer data)
2143 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2145 g_signal_emit (info->self,
2146 signals[MSG_COUNT_CHANGED_SIGNAL],
2147 0, info->folder, NULL);
2153 notify_filter_change_destroy (gpointer data)
2155 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2156 ModestHeaderViewPrivate *priv;
2158 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2159 priv->status_timeout = 0;
2161 g_object_unref (info->self);
2162 g_object_unref (info->folder);
2163 g_slice_free (NotifyFilterInfo, info);
2167 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2169 /* For the moment we only need to filter outbox */
2170 return priv->is_outbox;
2174 header_match_string (TnyHeader *header, gchar **words)
2176 gchar *subject_fold;
2182 gchar **current_word;
2185 subject_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD);
2186 if (subject_fold == NULL) {
2188 subject = tny_header_dup_subject (header);
2189 if (subject != NULL) {
2190 subject_fold = subject?g_utf8_casefold (subject, -1):NULL;
2191 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD,
2192 subject_fold, (GDestroyNotify) g_free);
2197 from_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD);
2198 if (from_fold == NULL) {
2200 from = tny_header_dup_from (header);
2202 from_fold = from?g_utf8_casefold (from, -1):NULL;
2203 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD,
2204 from_fold, (GDestroyNotify) g_free);
2209 to_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_TO_FOLD);
2210 if (to_fold == NULL) {
2212 to = tny_header_dup_to (header);
2214 to_fold = to?g_utf8_casefold (to, -1):NULL;
2215 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_TO_FOLD,
2216 to_fold, (GDestroyNotify) g_free);
2221 cc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_CC_FOLD);
2222 if (cc_fold == NULL) {
2224 cc = tny_header_dup_cc (header);
2226 cc_fold = cc?g_utf8_casefold (cc, -1):NULL;
2227 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_CC_FOLD,
2228 cc_fold, (GDestroyNotify) g_free);
2233 bcc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD);
2234 if (bcc_fold == NULL) {
2236 bcc = tny_header_dup_bcc (header);
2238 bcc_fold = bcc?g_utf8_casefold (bcc, -1):NULL;
2239 g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD,
2240 bcc_fold, (GDestroyNotify) g_free);
2247 for (current_word = words; *current_word != NULL; current_word++) {
2249 if ((subject_fold && g_strstr_len (subject_fold, -1, *current_word))
2250 || (cc_fold && g_strstr_len (cc_fold, -1, *current_word))
2251 || (bcc_fold && g_strstr_len (bcc_fold, -1, *current_word))
2252 || (to_fold && g_strstr_len (to_fold, -1, *current_word))
2253 || (from_fold && g_strstr_len (from_fold, -1, *current_word))) {
2265 filter_row (GtkTreeModel *model,
2269 ModestHeaderViewPrivate *priv = NULL;
2270 TnyHeaderFlags flags;
2271 TnyHeader *header = NULL;
2274 gboolean visible = TRUE;
2275 gboolean found = FALSE;
2276 GValue value = {0,};
2277 HeaderViewStatus old_status;
2279 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2280 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2282 /* Get header from model */
2283 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2284 header = (TnyHeader *) g_value_get_object (&value);
2285 g_value_unset (&value);
2286 flags = tny_header_get_flags (header);
2288 /* Get message id from header (ensure is a valid id) */
2294 /* Hide deleted and mark as deleted heders */
2295 if (flags & TNY_HEADER_FLAG_DELETED ||
2296 flags & TNY_HEADER_FLAG_EXPUNGED) {
2301 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
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 & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2310 if (current_folder_needs_filtering (priv) &&
2311 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2317 if (visible && priv->filter_string) {
2318 if (!header_match_string (header, priv->filter_string_splitted)) {
2322 if (priv->filter_date_range) {
2323 if ((tny_header_get_date_sent (TNY_HEADER (header)) < priv->date_range_start) ||
2324 ((priv->date_range_end != -1) && (tny_header_get_date_sent (TNY_HEADER (header)) > priv->date_range_end))) {
2331 /* If no data on clipboard, return always TRUE */
2332 if (modest_email_clipboard_cleared(priv->clipboard)) {
2338 if (priv->hidding_ids != NULL) {
2339 id = tny_header_dup_message_id (header);
2340 for (i=0; i < priv->n_selected && !found; i++)
2341 if (priv->hidding_ids[i] != NULL && id != NULL)
2342 found = (!strcmp (priv->hidding_ids[i], id));
2349 old_status = priv->status;
2350 priv->status = ((gboolean) priv->status) && !visible;
2351 if ((priv->notify_status) && (priv->status != old_status)) {
2352 if (priv->status_timeout)
2353 g_source_remove (priv->status_timeout);
2356 NotifyFilterInfo *info;
2358 info = g_slice_new0 (NotifyFilterInfo);
2359 info->self = g_object_ref (G_OBJECT (user_data));
2361 info->folder = tny_header_get_folder (header);
2362 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2363 notify_filter_change,
2365 notify_filter_change_destroy);
2373 _clear_hidding_filter (ModestHeaderView *header_view)
2375 ModestHeaderViewPrivate *priv = NULL;
2378 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2379 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2381 if (priv->hidding_ids != NULL) {
2382 for (i=0; i < priv->n_selected; i++)
2383 g_free (priv->hidding_ids[i]);
2384 g_free(priv->hidding_ids);
2389 modest_header_view_refilter (ModestHeaderView *header_view)
2391 GtkTreeModel *filter_model = NULL;
2392 ModestHeaderViewPrivate *priv = NULL;
2394 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2395 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2397 /* Hide cut headers */
2398 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2399 if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
2400 priv->status = HEADER_VIEW_INIT;
2401 modest_header_view_refilter_by_chunks (header_view);
2406 * Called when an account is removed. If I'm showing a folder of the
2407 * account that has been removed then clear the view
2410 on_account_removed (TnyAccountStore *self,
2411 TnyAccount *account,
2414 ModestHeaderViewPrivate *priv = NULL;
2416 /* Ignore changes in transport accounts */
2417 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2420 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2423 TnyAccount *my_account;
2425 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2426 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2427 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2428 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2430 my_account = tny_folder_get_account (priv->folder);
2434 if (my_account == account)
2435 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2436 g_object_unref (my_account);
2442 modest_header_view_add_observer(ModestHeaderView *header_view,
2443 ModestHeaderViewObserver *observer)
2445 ModestHeaderViewPrivate *priv;
2447 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2448 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2450 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2452 g_mutex_lock(priv->observer_list_lock);
2453 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2454 g_mutex_unlock(priv->observer_list_lock);
2458 modest_header_view_remove_observer(ModestHeaderView *header_view,
2459 ModestHeaderViewObserver *observer)
2461 ModestHeaderViewPrivate *priv;
2463 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2464 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2466 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2468 g_mutex_lock(priv->observer_list_lock);
2469 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2470 g_mutex_unlock(priv->observer_list_lock);
2474 modest_header_view_notify_observers(ModestHeaderView *header_view,
2475 GtkTreeModel *model,
2476 const gchar *tny_folder_id)
2478 ModestHeaderViewPrivate *priv = NULL;
2480 ModestHeaderViewObserver *observer;
2483 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2485 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2487 g_mutex_lock(priv->observer_list_lock);
2488 iter = priv->observer_list;
2489 while(iter != NULL){
2490 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2491 modest_header_view_observer_update(observer, model,
2493 iter = g_slist_next(iter);
2495 g_mutex_unlock(priv->observer_list_lock);
2499 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2501 ModestHeaderViewPrivate *priv = NULL;
2503 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2504 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2508 modest_header_view_set_filter (ModestHeaderView *self,
2509 ModestHeaderViewFilter filter)
2511 ModestHeaderViewPrivate *priv;
2513 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2514 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2516 priv->filter |= filter;
2518 if (current_folder_needs_filtering (priv))
2519 modest_header_view_refilter (self);
2523 modest_header_view_unset_filter (ModestHeaderView *self,
2524 ModestHeaderViewFilter filter)
2526 ModestHeaderViewPrivate *priv;
2528 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2529 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2531 priv->filter &= ~filter;
2533 if (current_folder_needs_filtering (priv))
2534 modest_header_view_refilter (self);
2538 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2540 if (strcmp ("style", spec->name) == 0) {
2541 update_style (MODEST_HEADER_VIEW (obj));
2542 gtk_widget_queue_draw (GTK_WIDGET (obj));
2547 update_style (ModestHeaderView *self)
2549 ModestHeaderViewPrivate *priv;
2550 GdkColor style_color;
2551 GdkColor style_active_color;
2552 PangoAttrList *attr_list;
2554 PangoAttribute *attr;
2556 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2557 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2561 attr_list = pango_attr_list_new ();
2562 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
2563 gdk_color_parse (MODEST_SECONDARY_COLOR, &style_color);
2565 priv->secondary_color = style_color;
2566 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2567 pango_attr_list_insert (attr_list, attr);
2570 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2572 "SmallSystemFont", NULL,
2575 attr = pango_attr_font_desc_new (pango_font_description_copy
2576 (style->font_desc));
2577 pango_attr_list_insert (attr_list, attr);
2579 g_object_set (G_OBJECT (priv->renderer_address),
2580 "foreground-gdk", &(priv->secondary_color),
2581 "foreground-set", TRUE,
2582 "attributes", attr_list,
2584 g_object_set (G_OBJECT (priv->renderer_date_status),
2585 "foreground-gdk", &(priv->secondary_color),
2586 "foreground-set", TRUE,
2587 "attributes", attr_list,
2589 pango_attr_list_unref (attr_list);
2591 g_object_set (G_OBJECT (priv->renderer_address),
2592 "foreground-gdk", &(priv->secondary_color),
2593 "foreground-set", TRUE,
2594 "scale", PANGO_SCALE_SMALL,
2597 g_object_set (G_OBJECT (priv->renderer_date_status),
2598 "foreground-gdk", &(priv->secondary_color),
2599 "foreground-set", TRUE,
2600 "scale", PANGO_SCALE_SMALL,
2605 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
2606 priv->active_color = style_active_color;
2607 #ifdef MODEST_TOOLKIT_HILDON2
2608 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2609 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2612 #ifdef MODEST_TOOLKIT_HILDON2
2613 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2619 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2624 GtkTreeModel *tree_model;
2629 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2637 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2638 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2642 gtk_tree_model_get (tree_model, &iter,
2643 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2650 parse_date_side (const gchar *string, time_t *date_side)
2656 gboolean result = FALSE;
2658 if (string && string[0] == '\0') {
2663 casefold = g_utf8_casefold (string, -1);
2664 today = g_utf8_casefold (dgettext ("gtk20", "Today"), -1);
2665 yesterday = g_utf8_casefold (dgettext ("gtk20", "Yesterday"), -1);
2666 date = g_date_new ();
2668 if (g_utf8_collate (casefold, today) == 0) {
2669 *date_side = time (NULL);
2674 if (g_utf8_collate (casefold, yesterday) == 0) {
2675 *date_side = time (NULL) - 24*60*60;
2680 g_date_set_parse (date, string);
2681 if (g_date_valid (date)) {
2683 g_date_to_struct_tm (date, &tm);
2684 *date_side = mktime (&tm);
2699 parse_date_range (const gchar *string, time_t *date_range_start, time_t *date_range_end)
2704 parts = g_strsplit (string, "..", 2);
2707 if (g_strv_length (parts) != 2) {
2714 if (!parse_date_side (parts[0], date_range_start)) {
2719 if (parse_date_side (parts[1], date_range_end)) {
2720 if (*date_range_end == 0) {
2721 *date_range_end = (time_t) -1;
2723 *date_range_end += (24*60*60 - 1);
2736 modest_header_view_set_filter_string (ModestHeaderView *self,
2737 const gchar *filter_string)
2739 ModestHeaderViewPrivate *priv;
2741 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2742 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2744 if (priv->filter_string)
2745 g_free (priv->filter_string);
2747 priv->filter_string = g_strdup (filter_string);
2748 priv->filter_date_range = FALSE;
2750 if (priv->filter_string_splitted) {
2751 g_strfreev (priv->filter_string_splitted);
2752 priv->filter_string_splitted = NULL;
2755 if (priv->filter_string) {
2756 gchar **split, **current, **current_target;
2758 split = g_strsplit (priv->filter_string, " ", 0);
2760 priv->filter_string_splitted = g_malloc0 (sizeof (gchar *)*(g_strv_length (split) + 1));
2761 current_target = priv->filter_string_splitted;
2762 for (current = split; *current != 0; current ++) {
2763 gboolean has_date_range = FALSE;;
2764 if (g_strstr_len (*current, -1, "..") && strcmp(*current, "..")) {
2765 time_t range_start, range_end;
2766 /* It contains .. but it's not ".." so it may be a date range */
2767 if (parse_date_range (*current, &range_start, &range_end)) {
2768 priv->filter_date_range = TRUE;
2769 has_date_range = TRUE;
2770 priv->date_range_start = range_start;
2771 priv->date_range_end = range_end;
2774 if (!has_date_range) {
2775 *current_target = g_utf8_casefold (*current, -1);
2779 *current_target = '\0';
2782 modest_header_view_refilter (MODEST_HEADER_VIEW (self));
2785 #ifdef MODEST_TOOLKIT_HILDON2
2787 on_live_search_timeout (ModestHeaderView *self)
2789 const gchar *needle;
2790 ModestHeaderViewPrivate *priv;
2792 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2794 needle = hildon_live_search_get_text ((HildonLiveSearch *) priv->live_search);
2795 if (needle && needle[0] != '\0') {
2796 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), needle);
2798 modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), NULL);
2801 priv->live_search_timeout = 0;
2807 on_live_search_refilter (HildonLiveSearch *livesearch,
2808 ModestHeaderView *self)
2810 ModestHeaderViewPrivate *priv;
2811 GtkTreeModel *model, *sortable, *filter;
2813 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2815 if (priv->live_search_timeout > 0) {
2816 g_source_remove (priv->live_search_timeout);
2817 priv->live_search_timeout = 0;
2821 filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2822 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2823 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2824 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2825 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2829 if (model && tny_list_get_length (TNY_LIST (model)) > 250) {
2830 priv->live_search_timeout = g_timeout_add (1000, (GSourceFunc) on_live_search_timeout, self);
2832 on_live_search_timeout (self);
2839 modest_header_view_setup_live_search (ModestHeaderView *self)
2841 ModestHeaderViewPrivate *priv;
2843 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (self), NULL);
2844 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2845 priv->live_search = hildon_live_search_new ();
2847 g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), self);
2849 return priv->live_search;
2854 refilter_idle_handler (gpointer userdata)
2856 ModestHeaderView *self = MODEST_HEADER_VIEW (userdata);
2857 ModestHeaderViewPrivate *priv;
2858 GtkTreeModel *filter_model;
2859 GtkTreeModel *filtered_model;
2863 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2864 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2865 filtered_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
2867 if (filtered_model != priv->filtered_model) {
2868 priv->refilter_handler_id = 0;
2869 priv->filtered_model = NULL;
2873 if (!gtk_tree_model_sort_iter_is_valid (GTK_TREE_MODEL_SORT (filtered_model), &(priv->refilter_iter))) {
2874 priv->refilter_handler_id = 0;
2875 priv->filtered_model = NULL;
2876 modest_header_view_refilter_by_chunks (self);
2883 path = gtk_tree_model_get_path (priv->filtered_model, &(priv->refilter_iter));
2884 gtk_tree_model_row_changed (priv->filtered_model, path, &(priv->refilter_iter));
2885 gtk_tree_path_free (path);
2888 has_more = gtk_tree_model_iter_next (priv->filtered_model, &(priv->refilter_iter));
2889 } while (i < 100 && has_more);
2894 priv->filtered_model = NULL;
2895 priv->refilter_handler_id = 0;
2901 modest_header_view_refilter_by_chunks (ModestHeaderView *self)
2903 ModestHeaderViewPrivate *priv;
2904 GtkTreeModel *filter_model;
2906 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2907 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2909 if (priv->refilter_handler_id > 0) {
2910 g_source_remove (priv->refilter_handler_id);
2911 priv->refilter_handler_id = 0;
2914 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2915 priv->filtered_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
2917 if (gtk_tree_model_get_iter_first (priv->filtered_model, &(priv->refilter_iter))) {
2918 priv->refilter_handler_id = g_idle_add (refilter_idle_handler, self);