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>
38 #include <modest-header-view.h>
39 #include <modest-header-view-priv.h>
40 #include <modest-dnd.h>
41 #include <modest-tny-folder.h>
42 #include <modest-debug.h>
43 #include <modest-main-window.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>
53 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
54 static void modest_header_view_init (ModestHeaderView *obj);
55 static void modest_header_view_finalize (GObject *obj);
56 static void modest_header_view_dispose (GObject *obj);
58 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
59 GtkTreeViewColumn *column, gpointer userdata);
61 static gint cmp_rows (GtkTreeModel *tree_model,
66 static gint cmp_subject_rows (GtkTreeModel *tree_model,
71 static gboolean filter_row (GtkTreeModel *model,
75 static void on_account_removed (TnyAccountStore *self,
79 static void on_selection_changed (GtkTreeSelection *sel,
82 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
85 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
88 static void setup_drag_and_drop (GtkWidget *self);
90 static void enable_drag_and_drop (GtkWidget *self);
92 static void disable_drag_and_drop (GtkWidget *self);
94 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
96 static gboolean on_focus_in (GtkWidget *sef,
100 static gboolean on_focus_out (GtkWidget *self,
101 GdkEventFocus *event,
104 static void folder_monitor_update (TnyFolderObserver *self,
105 TnyFolderChange *change);
107 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
109 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
111 static void _clear_hidding_filter (ModestHeaderView *header_view);
113 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
115 const gchar *tny_folder_id);
117 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
118 GdkEventExpose *event,
122 HEADER_VIEW_NON_EMPTY,
127 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
128 struct _ModestHeaderViewPrivate {
130 ModestHeaderViewStyle style;
132 TnyFolderMonitor *monitor;
133 GMutex *observers_lock;
135 /*header-view-observer observer*/
136 GMutex *observer_list_lock;
137 GSList *observer_list;
139 /* not unref this object, its a singlenton */
140 ModestEmailClipboard *clipboard;
142 /* Filter tree model */
145 GtkTreeRowReference *autoselect_reference;
147 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
148 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
150 gulong selection_changed_handler;
151 gulong acc_removed_handler;
153 GList *drag_begin_cached_selected_rows;
155 HeaderViewStatus status;
156 guint status_timeout;
157 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
160 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
161 struct _HeadersCountChangedHelper {
162 ModestHeaderView *self;
163 TnyFolderChange *change;
167 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
168 MODEST_TYPE_HEADER_VIEW, \
169 ModestHeaderViewPrivate))
173 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
176 HEADER_SELECTED_SIGNAL,
177 HEADER_ACTIVATED_SIGNAL,
178 ITEM_NOT_FOUND_SIGNAL,
179 MSG_COUNT_CHANGED_SIGNAL,
180 UPDATING_MSG_LIST_SIGNAL,
185 static GObjectClass *parent_class = NULL;
187 /* uncomment the following if you have defined any signals */
188 static guint signals[LAST_SIGNAL] = {0};
191 modest_header_view_get_type (void)
193 static GType my_type = 0;
195 static const GTypeInfo my_info = {
196 sizeof(ModestHeaderViewClass),
197 NULL, /* base init */
198 NULL, /* base finalize */
199 (GClassInitFunc) modest_header_view_class_init,
200 NULL, /* class finalize */
201 NULL, /* class data */
202 sizeof(ModestHeaderView),
204 (GInstanceInitFunc) modest_header_view_init,
208 static const GInterfaceInfo tny_folder_observer_info =
210 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
211 NULL, /* interface_finalize */
212 NULL /* interface_data */
214 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
218 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
219 &tny_folder_observer_info);
227 modest_header_view_class_init (ModestHeaderViewClass *klass)
229 GObjectClass *gobject_class;
230 gobject_class = (GObjectClass*) klass;
232 parent_class = g_type_class_peek_parent (klass);
233 gobject_class->finalize = modest_header_view_finalize;
234 gobject_class->dispose = modest_header_view_dispose;
236 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
238 signals[HEADER_SELECTED_SIGNAL] =
239 g_signal_new ("header_selected",
240 G_TYPE_FROM_CLASS (gobject_class),
242 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
244 g_cclosure_marshal_VOID__POINTER,
245 G_TYPE_NONE, 1, G_TYPE_POINTER);
247 signals[HEADER_ACTIVATED_SIGNAL] =
248 g_signal_new ("header_activated",
249 G_TYPE_FROM_CLASS (gobject_class),
251 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
253 g_cclosure_marshal_VOID__POINTER,
254 G_TYPE_NONE, 1, G_TYPE_POINTER);
257 signals[ITEM_NOT_FOUND_SIGNAL] =
258 g_signal_new ("item_not_found",
259 G_TYPE_FROM_CLASS (gobject_class),
261 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
263 g_cclosure_marshal_VOID__INT,
264 G_TYPE_NONE, 1, G_TYPE_INT);
266 signals[MSG_COUNT_CHANGED_SIGNAL] =
267 g_signal_new ("msg_count_changed",
268 G_TYPE_FROM_CLASS (gobject_class),
270 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
272 modest_marshal_VOID__POINTER_POINTER,
273 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
275 signals[UPDATING_MSG_LIST_SIGNAL] =
276 g_signal_new ("updating-msg-list",
277 G_TYPE_FROM_CLASS (gobject_class),
279 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
281 g_cclosure_marshal_VOID__BOOLEAN,
282 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
286 tny_folder_observer_init (TnyFolderObserverIface *klass)
288 klass->update = folder_monitor_update;
291 static GtkTreeViewColumn*
292 get_new_column (const gchar *name, GtkCellRenderer *renderer,
293 gboolean resizable, gint sort_col_id, gboolean show_as_text,
294 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
296 GtkTreeViewColumn *column;
298 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
299 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
301 gtk_tree_view_column_set_resizable (column, resizable);
303 gtk_tree_view_column_set_expand (column, TRUE);
306 gtk_tree_view_column_add_attribute (column, renderer, "text",
308 if (sort_col_id >= 0)
309 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
311 gtk_tree_view_column_set_sort_indicator (column, FALSE);
312 gtk_tree_view_column_set_reorderable (column, TRUE);
315 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
322 remove_all_columns (ModestHeaderView *obj)
324 GList *columns, *cursor;
326 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
328 for (cursor = columns; cursor; cursor = cursor->next)
329 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
330 GTK_TREE_VIEW_COLUMN(cursor->data));
331 g_list_free (columns);
335 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
337 GtkTreeModel *tree_filter, *sortable;
338 GtkTreeViewColumn *column=NULL;
339 GtkTreeSelection *selection = NULL;
340 GtkCellRenderer *renderer_header,
341 *renderer_attach, *renderer_compact_date_or_status;
342 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
343 *renderer_subject, *renderer_subject_box, *renderer_recpt,
345 ModestHeaderViewPrivate *priv;
346 GtkTreeViewColumn *compact_column = NULL;
349 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
350 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
352 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
354 /* TODO: check whether these renderers need to be freed */
355 renderer_attach = gtk_cell_renderer_pixbuf_new ();
356 renderer_priority = gtk_cell_renderer_pixbuf_new ();
357 renderer_header = gtk_cell_renderer_text_new ();
359 renderer_compact_header = modest_vbox_cell_renderer_new ();
360 renderer_recpt_box = modest_hbox_cell_renderer_new ();
361 renderer_subject_box = modest_hbox_cell_renderer_new ();
362 renderer_recpt = gtk_cell_renderer_text_new ();
363 renderer_subject = gtk_cell_renderer_text_new ();
364 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
366 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
367 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
368 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
369 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
370 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
371 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
372 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
373 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
374 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
375 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
376 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
377 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
378 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
379 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
381 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
382 #ifndef MODEST_TOOLKIT_GTK
383 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
384 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
386 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
387 g_object_set(G_OBJECT(renderer_header),
388 "ellipsize", PANGO_ELLIPSIZE_END,
390 g_object_set (G_OBJECT (renderer_subject),
391 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
393 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
394 g_object_set (G_OBJECT (renderer_recpt),
395 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
397 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
398 g_object_set(G_OBJECT(renderer_compact_date_or_status),
399 "xalign", 1.0, "yalign", 0.0,
401 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
402 g_object_set (G_OBJECT (renderer_priority),
403 "yalign", 1.0, NULL);
404 g_object_set (G_OBJECT (renderer_attach),
405 "yalign", 0.0, NULL);
407 #ifndef MODEST_TOOLKIT_GTK
408 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
409 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
410 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
412 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
413 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
414 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
417 remove_all_columns (self);
419 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
420 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
421 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
422 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
424 /* Add new columns */
425 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
426 ModestHeaderViewColumn col =
427 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
429 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
430 g_printerr ("modest: invalid column %d in column list\n", col);
436 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
437 column = get_new_column (_("A"), renderer_attach, FALSE,
438 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
440 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
442 gtk_tree_view_column_set_fixed_width (column, 45);
446 case MODEST_HEADER_VIEW_COLUMN_FROM:
447 column = get_new_column (_("From"), renderer_header, TRUE,
448 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
450 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
451 GINT_TO_POINTER(TRUE));
454 case MODEST_HEADER_VIEW_COLUMN_TO:
455 column = get_new_column (_("To"), renderer_header, TRUE,
456 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
458 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
459 GINT_TO_POINTER(FALSE));
462 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
463 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
464 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
466 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
467 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
468 compact_column = column;
471 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
472 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
473 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
475 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
476 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
477 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
478 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
479 compact_column = column;
483 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
484 column = get_new_column (_("Subject"), renderer_header, TRUE,
485 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
487 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
491 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
492 column = get_new_column (_("Received"), renderer_header, TRUE,
493 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
495 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
496 GINT_TO_POINTER(TRUE));
499 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
500 column = get_new_column (_("Sent"), renderer_header, TRUE,
501 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
503 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
504 GINT_TO_POINTER(FALSE));
507 case MODEST_HEADER_VIEW_COLUMN_SIZE:
508 column = get_new_column (_("Size"), renderer_header, TRUE,
509 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
511 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
514 case MODEST_HEADER_VIEW_COLUMN_STATUS:
515 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
516 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
518 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
523 g_return_val_if_reached(FALSE);
526 /* we keep the column id around */
527 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
528 GINT_TO_POINTER(col));
530 /* we need this ptr when sorting the rows */
531 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
533 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
537 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
538 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
539 (GtkTreeIterCompareFunc) cmp_rows,
540 compact_column, NULL);
541 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
542 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
543 (GtkTreeIterCompareFunc) cmp_subject_rows,
544 compact_column, NULL);
551 modest_header_view_init (ModestHeaderView *obj)
553 ModestHeaderViewPrivate *priv;
556 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
560 priv->monitor = NULL;
561 priv->observers_lock = g_mutex_new ();
562 priv->autoselect_reference = NULL;
564 priv->status = HEADER_VIEW_INIT;
565 priv->status_timeout = 0;
566 priv->notify_status = TRUE;
568 priv->observer_list_lock = g_mutex_new();
569 priv->observer_list = NULL;
571 priv->clipboard = modest_runtime_get_email_clipboard ();
572 priv->hidding_ids = NULL;
573 priv->n_selected = 0;
574 priv->selection_changed_handler = 0;
575 priv->acc_removed_handler = 0;
577 /* Sort parameters */
578 for (j=0; j < 2; j++) {
579 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
580 priv->sort_colid[j][i] = -1;
581 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
585 setup_drag_and_drop (GTK_WIDGET(obj));
589 modest_header_view_dispose (GObject *obj)
591 ModestHeaderView *self;
592 ModestHeaderViewPrivate *priv;
593 GtkTreeSelection *sel;
595 self = MODEST_HEADER_VIEW(obj);
596 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
598 /* Free in the dispose to avoid unref cycles */
600 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
601 g_object_unref (G_OBJECT (priv->folder));
605 /* We need to do this here in the dispose because the
606 selection won't exist when finalizing */
607 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
608 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
609 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
610 priv->selection_changed_handler = 0;
613 G_OBJECT_CLASS(parent_class)->dispose (obj);
617 modest_header_view_finalize (GObject *obj)
619 ModestHeaderView *self;
620 ModestHeaderViewPrivate *priv;
622 self = MODEST_HEADER_VIEW(obj);
623 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
625 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
626 priv->acc_removed_handler)) {
627 g_signal_handler_disconnect (modest_runtime_get_account_store (),
628 priv->acc_removed_handler);
631 /* There is no need to lock because there should not be any
632 * reference to self now. */
633 g_mutex_free(priv->observer_list_lock);
634 g_slist_free(priv->observer_list);
636 g_mutex_lock (priv->observers_lock);
638 tny_folder_monitor_stop (priv->monitor);
639 g_object_unref (G_OBJECT (priv->monitor));
641 g_mutex_unlock (priv->observers_lock);
642 g_mutex_free (priv->observers_lock);
644 /* Clear hidding array created by cut operation */
645 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
647 if (priv->autoselect_reference != NULL) {
648 gtk_tree_row_reference_free (priv->autoselect_reference);
649 priv->autoselect_reference = NULL;
652 G_OBJECT_CLASS(parent_class)->finalize (obj);
657 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
660 GtkTreeSelection *sel;
661 ModestHeaderView *self;
662 ModestHeaderViewPrivate *priv;
664 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
667 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
668 self = MODEST_HEADER_VIEW(obj);
669 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
671 modest_header_view_set_style (self, style);
673 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
674 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
675 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
677 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
678 TRUE); /* alternating row colors */
680 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
681 priv->selection_changed_handler =
682 g_signal_connect_after (sel, "changed",
683 G_CALLBACK(on_selection_changed), self);
685 g_signal_connect (self, "row-activated",
686 G_CALLBACK (on_header_row_activated), NULL);
688 g_signal_connect (self, "focus-in-event",
689 G_CALLBACK(on_focus_in), NULL);
690 g_signal_connect (self, "focus-out-event",
691 G_CALLBACK(on_focus_out), NULL);
693 g_signal_connect (self, "button-press-event",
694 G_CALLBACK(on_button_press_event), NULL);
695 g_signal_connect (self, "button-release-event",
696 G_CALLBACK(on_button_release_event), NULL);
698 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
700 G_CALLBACK (on_account_removed),
703 g_signal_connect (self, "expose-event",
704 G_CALLBACK(modest_header_view_on_expose_event),
707 return GTK_WIDGET(self);
712 modest_header_view_count_selected_headers (ModestHeaderView *self)
714 GtkTreeSelection *sel;
717 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
719 /* Get selection object and check selected rows count */
720 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
721 selected_rows = gtk_tree_selection_count_selected_rows (sel);
723 return selected_rows;
727 modest_header_view_has_selected_headers (ModestHeaderView *self)
729 GtkTreeSelection *sel;
732 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
734 /* Get selection object and check selected rows count */
735 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
736 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
743 modest_header_view_get_selected_headers (ModestHeaderView *self)
745 GtkTreeSelection *sel;
746 ModestHeaderViewPrivate *priv;
747 TnyList *header_list = NULL;
749 GList *list, *tmp = NULL;
750 GtkTreeModel *tree_model = NULL;
753 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
755 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
757 /* Get selected rows */
758 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
759 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
762 header_list = tny_simple_list_new();
764 list = g_list_reverse (list);
767 /* get header from selection */
768 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
769 gtk_tree_model_get (tree_model, &iter,
770 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
772 /* Prepend to list */
773 tny_list_prepend (header_list, G_OBJECT (header));
774 g_object_unref (G_OBJECT (header));
776 tmp = g_list_next (tmp);
779 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
786 /* scroll our list view so the selected item is visible */
788 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
790 #ifdef MODEST_TOOLKIT_GTK
792 GtkTreePath *selected_path;
793 GtkTreePath *start, *end;
797 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
798 selected_path = gtk_tree_model_get_path (model, iter);
800 start = gtk_tree_path_new ();
801 end = gtk_tree_path_new ();
803 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
805 if (gtk_tree_path_compare (selected_path, start) < 0 ||
806 gtk_tree_path_compare (end, selected_path) < 0)
807 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
808 selected_path, NULL, TRUE,
811 gtk_tree_path_free (selected_path);
812 gtk_tree_path_free (start);
813 gtk_tree_path_free (end);
815 #endif /* MODEST_TOOLKIT_GTK */
820 modest_header_view_select_next (ModestHeaderView *self)
822 GtkTreeSelection *sel;
827 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
829 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
830 path = get_selected_row (GTK_TREE_VIEW(self), &model);
831 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
832 /* Unselect previous path */
833 gtk_tree_selection_unselect_path (sel, path);
835 /* Move path down and selects new one */
836 if (gtk_tree_model_iter_next (model, &iter)) {
837 gtk_tree_selection_select_iter (sel, &iter);
838 scroll_to_selected (self, &iter, FALSE);
840 gtk_tree_path_free(path);
846 modest_header_view_select_prev (ModestHeaderView *self)
848 GtkTreeSelection *sel;
853 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
855 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
856 path = get_selected_row (GTK_TREE_VIEW(self), &model);
857 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
858 /* Unselect previous path */
859 gtk_tree_selection_unselect_path (sel, path);
862 if (gtk_tree_path_prev (path)) {
863 gtk_tree_model_get_iter (model, &iter, path);
865 /* Select the new one */
866 gtk_tree_selection_select_iter (sel, &iter);
867 scroll_to_selected (self, &iter, TRUE);
870 gtk_tree_path_free (path);
875 modest_header_view_get_columns (ModestHeaderView *self)
877 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
879 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
885 modest_header_view_set_style (ModestHeaderView *self,
886 ModestHeaderViewStyle style)
888 ModestHeaderViewPrivate *priv;
889 gboolean show_col_headers = FALSE;
890 ModestHeaderViewStyle old_style;
892 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
893 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
896 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
897 if (priv->style == style)
898 return TRUE; /* nothing to do */
901 case MODEST_HEADER_VIEW_STYLE_DETAILS:
902 show_col_headers = TRUE;
904 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
907 g_return_val_if_reached (FALSE);
909 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
910 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
912 old_style = priv->style;
919 ModestHeaderViewStyle
920 modest_header_view_get_style (ModestHeaderView *self)
922 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
924 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
927 /* This is used to automatically select the first header if the user
928 * has not selected any header yet.
931 modest_header_view_on_expose_event(GtkTreeView *header_view,
932 GdkEventExpose *event,
935 GtkTreeSelection *sel;
937 GtkTreeIter tree_iter;
938 ModestHeaderViewPrivate *priv;
940 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
941 model = gtk_tree_view_get_model(header_view);
946 sel = gtk_tree_view_get_selection(header_view);
947 if(!gtk_tree_selection_count_selected_rows(sel)) {
948 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
949 GtkTreePath *tree_iter_path;
950 /* Prevent the widget from getting the focus
951 when selecting the first item */
952 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
953 g_object_set(header_view, "can-focus", FALSE, NULL);
954 gtk_tree_selection_select_iter(sel, &tree_iter);
955 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
956 g_object_set(header_view, "can-focus", TRUE, NULL);
957 if (priv->autoselect_reference) {
958 gtk_tree_row_reference_free (priv->autoselect_reference);
960 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
961 gtk_tree_path_free (tree_iter_path);
964 if (priv->autoselect_reference != NULL) {
965 gboolean moved_selection = FALSE;
966 GtkTreePath * last_path;
967 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
968 moved_selection = TRUE;
972 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
973 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
974 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
975 moved_selection = TRUE;
976 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
978 gtk_tree_path_free (last_path);
980 if (moved_selection) {
981 gtk_tree_row_reference_free (priv->autoselect_reference);
982 priv->autoselect_reference = NULL;
985 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
986 GtkTreePath *current_path;
987 current_path = gtk_tree_model_get_path (model, &tree_iter);
988 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
989 if (gtk_tree_path_compare (current_path, last_path) != 0) {
990 g_object_set(header_view, "can-focus", FALSE, NULL);
991 gtk_tree_selection_unselect_all (sel);
992 gtk_tree_selection_select_iter(sel, &tree_iter);
993 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
994 g_object_set(header_view, "can-focus", TRUE, NULL);
995 gtk_tree_row_reference_free (priv->autoselect_reference);
996 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
998 gtk_tree_path_free (current_path);
999 gtk_tree_path_free (last_path);
1009 modest_header_view_get_folder (ModestHeaderView *self)
1011 ModestHeaderViewPrivate *priv;
1013 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1015 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1018 g_object_ref (priv->folder);
1020 return priv->folder;
1024 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1030 ModestHeaderView *self;
1031 ModestHeaderViewPrivate *priv;
1033 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1035 self = MODEST_HEADER_VIEW (user_data);
1036 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1038 if (cancelled || err)
1041 /* Add IDLE observer (monitor) and another folder observer for
1042 new messages (self) */
1043 g_mutex_lock (priv->observers_lock);
1044 if (priv->monitor) {
1045 tny_folder_monitor_stop (priv->monitor);
1046 g_object_unref (G_OBJECT (priv->monitor));
1048 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1049 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1050 tny_folder_monitor_start (priv->monitor);
1051 g_mutex_unlock (priv->observers_lock);
1055 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1059 ModestHeaderViewPrivate *priv;
1060 GList *cols, *cursor;
1061 GtkTreeModel *filter_model, *sortable;
1063 GtkSortType sort_type;
1065 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1067 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1069 /* Start the monitor in the callback of the
1070 tny_gtk_header_list_model_set_folder call. It's crucial to
1071 do it there and not just after the call because we want the
1072 monitor to observe only the headers returned by the
1073 tny_folder_get_headers_async call that it's inside the
1074 tny_gtk_header_list_model_set_folder call. This way the
1075 monitor infrastructure could successfully cope with
1076 duplicates. For example if a tny_folder_add_msg_async is
1077 happening while tny_gtk_header_list_model_set_folder is
1078 invoked, then the first call could add a header that will
1079 be added again by tny_gtk_header_list_model_set_folder, so
1080 we'd end up with duplicate headers. sergio */
1081 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1083 set_folder_intern_get_headers_async_cb,
1086 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1087 g_object_unref (G_OBJECT (headers));
1089 /* Init filter_row function to examine empty status */
1090 priv->status = HEADER_VIEW_INIT;
1092 /* Create a tree model filter to hide and show rows for cut operations */
1093 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1094 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1098 g_object_unref (G_OBJECT (sortable));
1100 /* install our special sorting functions */
1101 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1103 /* Restore sort column id */
1105 type = modest_tny_folder_guess_folder_type (folder);
1106 if (type == TNY_FOLDER_TYPE_INVALID)
1107 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1109 sort_colid = modest_header_view_get_sort_column_id (self, type);
1110 sort_type = modest_header_view_get_sort_type (self, type);
1111 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1114 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1115 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1116 (GtkTreeIterCompareFunc) cmp_rows,
1118 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1119 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1120 (GtkTreeIterCompareFunc) cmp_subject_rows,
1125 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1126 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1127 tny_folder_get_id(folder));
1128 g_object_unref (G_OBJECT (filter_model));
1135 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1137 GtkSortType sort_type)
1139 ModestHeaderViewPrivate *priv = NULL;
1140 GtkTreeModel *tree_filter, *sortable = NULL;
1143 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1144 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1146 /* Get model and private data */
1147 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1148 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1149 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1150 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1152 /* Sort tree model */
1153 type = modest_tny_folder_guess_folder_type (priv->folder);
1154 if (type == TNY_FOLDER_TYPE_INVALID)
1155 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1157 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1160 /* Store new sort parameters */
1161 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1166 modest_header_view_set_sort_params (ModestHeaderView *self,
1168 GtkSortType sort_type,
1171 ModestHeaderViewPrivate *priv;
1172 ModestHeaderViewStyle style;
1174 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1175 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1176 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1178 style = modest_header_view_get_style (self);
1179 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1181 priv->sort_colid[style][type] = sort_colid;
1182 priv->sort_type[style][type] = sort_type;
1186 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1189 ModestHeaderViewPrivate *priv;
1190 ModestHeaderViewStyle style;
1192 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1193 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1195 style = modest_header_view_get_style (self);
1196 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1198 return priv->sort_colid[style][type];
1202 modest_header_view_get_sort_type (ModestHeaderView *self,
1205 ModestHeaderViewPrivate *priv;
1206 ModestHeaderViewStyle style;
1208 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1209 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1211 style = modest_header_view_get_style (self);
1212 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1214 return priv->sort_type[style][type];
1218 ModestHeaderView *header_view;
1219 RefreshAsyncUserCallback cb;
1224 folder_refreshed_cb (ModestMailOperation *mail_op,
1228 ModestHeaderViewPrivate *priv;
1229 SetFolderHelper *info;
1231 info = (SetFolderHelper*) user_data;
1233 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1237 info->cb (mail_op, folder, info->user_data);
1239 /* Start the folder count changes observer. We do not need it
1240 before the refresh. Note that the monitor could still be
1241 called for this refresh but now we know that the callback
1242 was previously called */
1243 g_mutex_lock (priv->observers_lock);
1244 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1245 g_mutex_unlock (priv->observers_lock);
1247 /* Notify the observers that the update is over */
1248 g_signal_emit (G_OBJECT (info->header_view),
1249 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1251 /* Allow filtering notifications from now on if the current
1252 folder is still the same (if not then the user has selected
1253 another one to refresh, we should wait until that refresh
1255 if (priv->folder == folder)
1256 priv->notify_status = TRUE;
1259 g_object_unref (info->header_view);
1264 refresh_folder_error_handler (ModestMailOperation *mail_op,
1267 const GError *error = modest_mail_operation_get_error (mail_op);
1269 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1270 error->code == TNY_IO_ERROR_WRITE ||
1271 error->code == TNY_IO_ERROR_READ) {
1272 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1273 /* If the mail op has been cancelled then it's not an error: don't show any message */
1274 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1275 modest_platform_information_banner (NULL, NULL,
1277 "cerm_device_memory_full"));
1283 modest_header_view_set_folder (ModestHeaderView *self,
1286 RefreshAsyncUserCallback callback,
1289 ModestHeaderViewPrivate *priv;
1290 ModestWindow *main_win;
1292 g_return_if_fail (self);
1294 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1296 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1297 FALSE); /* don't create */
1299 g_warning ("%s: BUG: no main window", __FUNCTION__);
1304 if (priv->status_timeout) {
1305 g_source_remove (priv->status_timeout);
1306 priv->status_timeout = 0;
1309 g_mutex_lock (priv->observers_lock);
1310 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1311 g_object_unref (priv->folder);
1312 priv->folder = NULL;
1313 g_mutex_unlock (priv->observers_lock);
1317 GtkTreeSelection *selection;
1318 SetFolderHelper *info;
1319 ModestMailOperation *mail_op = NULL;
1321 /* Set folder in the model */
1322 modest_header_view_set_folder_intern (self, folder);
1324 /* Pick my reference. Nothing to do with the mail operation */
1325 priv->folder = g_object_ref (folder);
1327 /* Do not notify about filterings until the refresh finishes */
1328 priv->notify_status = FALSE;
1330 /* Clear the selection if exists */
1331 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1332 gtk_tree_selection_unselect_all(selection);
1333 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1335 /* Notify the observers that the update begins */
1336 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1339 /* create the helper */
1340 info = g_malloc0 (sizeof (SetFolderHelper));
1341 info->header_view = g_object_ref (self);
1342 info->cb = callback;
1343 info->user_data = user_data;
1345 /* Create the mail operation (source will be the parent widget) */
1346 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1347 refresh_folder_error_handler,
1350 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1353 /* Refresh the folder asynchronously */
1354 modest_mail_operation_refresh_folder (mail_op,
1356 folder_refreshed_cb,
1359 folder_refreshed_cb (mail_op, folder, info);
1362 g_object_unref (mail_op);
1364 g_mutex_lock (priv->observers_lock);
1366 if (priv->monitor) {
1367 tny_folder_monitor_stop (priv->monitor);
1368 g_object_unref (G_OBJECT (priv->monitor));
1369 priv->monitor = NULL;
1372 if (priv->autoselect_reference) {
1373 gtk_tree_row_reference_free (priv->autoselect_reference);
1374 priv->autoselect_reference = NULL;
1377 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1379 modest_header_view_notify_observers(self, NULL, NULL);
1381 g_mutex_unlock (priv->observers_lock);
1383 /* Notify the observers that the update is over */
1384 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1390 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1391 GtkTreeViewColumn *column, gpointer userdata)
1393 ModestHeaderView *self = NULL;
1394 ModestHeaderViewPrivate *priv = NULL;
1396 GtkTreeModel *model = NULL;
1397 TnyHeader *header = NULL;
1398 TnyHeaderFlags flags;
1400 self = MODEST_HEADER_VIEW (treeview);
1401 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1403 model = gtk_tree_view_get_model (treeview);
1404 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1407 /* get the first selected item */
1408 gtk_tree_model_get (model, &iter,
1409 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1410 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1413 /* Dont open DELETED messages */
1414 if (flags & TNY_HEADER_FLAG_DELETED) {
1417 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1418 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1419 modest_platform_information_banner (NULL, NULL, msg);
1425 g_signal_emit (G_OBJECT(self),
1426 signals[HEADER_ACTIVATED_SIGNAL],
1432 g_object_unref (G_OBJECT (header));
1437 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1439 GtkTreeModel *model;
1440 TnyHeader *header = NULL;
1441 GtkTreePath *path = NULL;
1443 ModestHeaderView *self;
1444 ModestHeaderViewPrivate *priv;
1445 GList *selected = NULL;
1447 g_return_if_fail (sel);
1448 g_return_if_fail (user_data);
1450 self = MODEST_HEADER_VIEW (user_data);
1451 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1453 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1454 if (selected != NULL)
1455 path = (GtkTreePath *) selected->data;
1456 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1457 return; /* msg was _un_selected */
1459 gtk_tree_model_get (model, &iter,
1460 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1464 g_signal_emit (G_OBJECT(self),
1465 signals[HEADER_SELECTED_SIGNAL],
1468 g_object_unref (G_OBJECT (header));
1470 /* free all items in 'selected' */
1471 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1472 g_list_free (selected);
1476 /* PROTECTED method. It's useful when we want to force a given
1477 selection to reload a msg. For example if we have selected a header
1478 in offline mode, when Modest become online, we want to reload the
1479 message automatically without an user click over the header */
1481 _modest_header_view_change_selection (GtkTreeSelection *selection,
1484 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1485 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1487 on_selection_changed (selection, user_data);
1491 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1498 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1502 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1506 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1514 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1521 /* static int counter = 0; */
1523 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1524 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1525 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1529 case TNY_HEADER_FLAG_ATTACHMENTS:
1531 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1532 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1533 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1534 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1536 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1537 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1539 return cmp ? cmp : t1 - t2;
1541 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1542 TnyHeader *header1 = NULL, *header2 = NULL;
1544 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1545 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1546 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1547 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1549 /* This is for making priority values respect the intuitive sort relationship
1550 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1552 if (header1 && header2) {
1553 cmp = compare_priorities (tny_header_get_priority (header1),
1554 tny_header_get_priority (header2));
1555 g_object_unref (header1);
1556 g_object_unref (header2);
1558 return cmp ? cmp : t1 - t2;
1564 return &iter1 - &iter2; /* oughhhh */
1569 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1575 /* static int counter = 0; */
1577 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1579 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1580 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1581 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1582 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1584 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1585 val2 + modest_text_utils_get_subject_prefix_len(val2),
1592 /* Drag and drop stuff */
1594 drag_data_get_cb (GtkWidget *widget,
1595 GdkDragContext *context,
1596 GtkSelectionData *selection_data,
1601 ModestHeaderView *self = NULL;
1602 ModestHeaderViewPrivate *priv = NULL;
1604 self = MODEST_HEADER_VIEW (widget);
1605 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1607 /* Set the data. Do not use the current selection because it
1608 could be different than the selection at the beginning of
1610 modest_dnd_selection_data_set_paths (selection_data,
1611 priv->drag_begin_cached_selected_rows);
1615 * We're caching the selected rows at the beginning because the
1616 * selection could change between drag-begin and drag-data-get, for
1617 * example if we have a set of rows already selected, and then we
1618 * click in one of them (without SHIFT key pressed) and begin a drag,
1619 * the selection at that moment contains all the selected lines, but
1620 * after dropping the selection, the release event provokes that only
1621 * the row used to begin the drag is selected, so at the end the
1622 * drag&drop affects only one rows instead of all the selected ones.
1626 drag_begin_cb (GtkWidget *widget,
1627 GdkDragContext *context,
1630 ModestHeaderView *self = NULL;
1631 ModestHeaderViewPrivate *priv = NULL;
1632 GtkTreeSelection *selection;
1634 self = MODEST_HEADER_VIEW (widget);
1635 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1637 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1638 priv->drag_begin_cached_selected_rows =
1639 gtk_tree_selection_get_selected_rows (selection, NULL);
1643 * We use the drag-end signal to clear the cached selection, we use
1644 * this because this allways happens, whether or not the d&d was a
1648 drag_end_cb (GtkWidget *widget,
1652 ModestHeaderView *self = NULL;
1653 ModestHeaderViewPrivate *priv = NULL;
1655 self = MODEST_HEADER_VIEW (widget);
1656 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1658 /* Free cached data */
1659 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1660 g_list_free (priv->drag_begin_cached_selected_rows);
1661 priv->drag_begin_cached_selected_rows = NULL;
1664 /* Header view drag types */
1665 const GtkTargetEntry header_view_drag_types[] = {
1666 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1670 enable_drag_and_drop (GtkWidget *self)
1672 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1673 header_view_drag_types,
1674 G_N_ELEMENTS (header_view_drag_types),
1675 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1679 disable_drag_and_drop (GtkWidget *self)
1681 gtk_drag_source_unset (self);
1685 setup_drag_and_drop (GtkWidget *self)
1687 enable_drag_and_drop(self);
1688 g_signal_connect(G_OBJECT (self), "drag_data_get",
1689 G_CALLBACK(drag_data_get_cb), NULL);
1691 g_signal_connect(G_OBJECT (self), "drag_begin",
1692 G_CALLBACK(drag_begin_cb), NULL);
1694 g_signal_connect(G_OBJECT (self), "drag_end",
1695 G_CALLBACK(drag_end_cb), NULL);
1698 static GtkTreePath *
1699 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1701 GtkTreePath *path = NULL;
1702 GtkTreeSelection *sel = NULL;
1705 sel = gtk_tree_view_get_selection(self);
1706 rows = gtk_tree_selection_get_selected_rows (sel, model);
1708 if ((rows == NULL) || (g_list_length(rows) != 1))
1711 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1716 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1723 * This function moves the tree view scroll to the current selected
1724 * row when the widget grabs the focus
1727 on_focus_in (GtkWidget *self,
1728 GdkEventFocus *event,
1731 GtkTreeSelection *selection;
1732 GtkTreeModel *model;
1733 GList *selected = NULL;
1734 GtkTreePath *selected_path = NULL;
1736 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1740 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1741 /* If none selected yet, pick the first one */
1742 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1746 /* Return if the model is empty */
1747 if (!gtk_tree_model_get_iter_first (model, &iter))
1750 path = gtk_tree_model_get_path (model, &iter);
1751 gtk_tree_selection_select_path (selection, path);
1752 gtk_tree_path_free (path);
1755 /* Need to get the all the rows because is selection multiple */
1756 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1757 if (selected == NULL) return FALSE;
1758 selected_path = (GtkTreePath *) selected->data;
1761 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1762 g_list_free (selected);
1768 on_focus_out (GtkWidget *self,
1769 GdkEventFocus *event,
1773 if (!gtk_widget_is_focus (self)) {
1774 GtkTreeSelection *selection = NULL;
1775 GList *selected_rows = NULL;
1776 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1777 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1778 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1779 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1780 gtk_tree_selection_unselect_all (selection);
1781 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1782 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1783 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1784 g_list_free (selected_rows);
1791 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1793 enable_drag_and_drop(self);
1798 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1800 GtkTreeSelection *selection = NULL;
1801 GtkTreePath *path = NULL;
1802 gboolean already_selected = FALSE, already_opened = FALSE;
1803 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1805 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1807 GtkTreeModel *model;
1809 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1810 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1812 /* Get header from model */
1813 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1814 if (gtk_tree_model_get_iter (model, &iter, path)) {
1815 GValue value = {0,};
1818 gtk_tree_model_get_value (model, &iter,
1819 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1821 header = (TnyHeader *) g_value_get_object (&value);
1822 if (TNY_IS_HEADER (header)) {
1823 status = modest_tny_all_send_queues_get_msg_status (header);
1824 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1827 g_value_unset (&value);
1831 /* Enable drag and drop only if the user clicks on a row that
1832 it's already selected. If not, let him select items using
1833 the pointer. If the message is in an OUTBOX and in sending
1834 status disable drag and drop as well */
1835 if (!already_selected ||
1836 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1838 disable_drag_and_drop(self);
1841 gtk_tree_path_free(path);
1843 /* If it's already opened then do not let the button-press
1844 event go on because it'll perform a message open because
1845 we're clicking on to an already selected header */
1850 folder_monitor_update (TnyFolderObserver *self,
1851 TnyFolderChange *change)
1853 ModestHeaderViewPrivate *priv = NULL;
1854 TnyFolderChangeChanged changed;
1855 TnyFolder *folder = NULL;
1857 changed = tny_folder_change_get_changed (change);
1859 /* Do not notify the observers if the folder of the header
1860 view has changed before this call to the observer
1862 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1863 folder = tny_folder_change_get_folder (change);
1864 if (folder != priv->folder)
1867 MODEST_DEBUG_BLOCK (
1868 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1869 g_print ("ADDED %d/%d (r/t) \n",
1870 tny_folder_change_get_new_unread_count (change),
1871 tny_folder_change_get_new_all_count (change));
1872 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1873 g_print ("ALL COUNT %d\n",
1874 tny_folder_change_get_new_all_count (change));
1875 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1876 g_print ("UNREAD COUNT %d\n",
1877 tny_folder_change_get_new_unread_count (change));
1878 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1879 g_print ("EXPUNGED %d/%d (r/t) \n",
1880 tny_folder_change_get_new_unread_count (change),
1881 tny_folder_change_get_new_all_count (change));
1882 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1883 g_print ("FOLDER RENAME\n");
1884 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1885 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1886 tny_folder_change_get_new_unread_count (change),
1887 tny_folder_change_get_new_all_count (change));
1888 g_print ("---------------------------------------------------\n");
1891 /* Check folder count */
1892 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1893 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1895 g_mutex_lock (priv->observers_lock);
1897 /* Emit signal to evaluate how headers changes affects
1898 to the window view */
1899 g_signal_emit (G_OBJECT(self),
1900 signals[MSG_COUNT_CHANGED_SIGNAL],
1903 /* Added or removed headers, so data stored on cliboard are invalid */
1904 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1905 modest_email_clipboard_clear (priv->clipboard);
1907 g_mutex_unlock (priv->observers_lock);
1913 g_object_unref (folder);
1917 modest_header_view_is_empty (ModestHeaderView *self)
1919 ModestHeaderViewPrivate *priv;
1921 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1923 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1925 return priv->status == HEADER_VIEW_EMPTY;
1929 modest_header_view_clear (ModestHeaderView *self)
1931 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1933 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1937 modest_header_view_copy_selection (ModestHeaderView *header_view)
1939 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1941 /* Copy selection */
1942 _clipboard_set_selected_data (header_view, FALSE);
1946 modest_header_view_cut_selection (ModestHeaderView *header_view)
1948 ModestHeaderViewPrivate *priv = NULL;
1949 const gchar **hidding = NULL;
1950 guint i, n_selected;
1952 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1954 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1956 /* Copy selection */
1957 _clipboard_set_selected_data (header_view, TRUE);
1959 /* Get hidding ids */
1960 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1962 /* Clear hidding array created by previous cut operation */
1963 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1965 /* Copy hidding array */
1966 priv->n_selected = n_selected;
1967 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1968 for (i=0; i < n_selected; i++)
1969 priv->hidding_ids[i] = g_strdup(hidding[i]);
1971 /* Hide cut headers */
1972 modest_header_view_refilter (header_view);
1979 _clipboard_set_selected_data (ModestHeaderView *header_view,
1982 ModestHeaderViewPrivate *priv = NULL;
1983 TnyList *headers = NULL;
1985 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1986 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1988 /* Set selected data on clipboard */
1989 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1990 headers = modest_header_view_get_selected_headers (header_view);
1991 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1994 g_object_unref (headers);
1998 ModestHeaderView *self;
2003 notify_filter_change (gpointer data)
2005 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2007 g_signal_emit (info->self,
2008 signals[MSG_COUNT_CHANGED_SIGNAL],
2009 0, info->folder, NULL);
2015 notify_filter_change_destroy (gpointer data)
2017 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2018 ModestHeaderViewPrivate *priv;
2020 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2021 priv->status_timeout = 0;
2023 g_object_unref (info->self);
2024 g_object_unref (info->folder);
2025 g_slice_free (NotifyFilterInfo, info);
2029 filter_row (GtkTreeModel *model,
2033 ModestHeaderViewPrivate *priv = NULL;
2034 TnyHeaderFlags flags;
2035 TnyHeader *header = NULL;
2038 gboolean visible = TRUE;
2039 gboolean found = FALSE;
2040 GValue value = {0,};
2041 HeaderViewStatus old_status;
2043 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2044 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2046 /* Get header from model */
2047 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2048 flags = (TnyHeaderFlags) g_value_get_int (&value);
2049 g_value_unset (&value);
2050 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2051 header = (TnyHeader *) g_value_get_object (&value);
2052 g_value_unset (&value);
2054 /* Hide deleted and mark as deleted heders */
2055 if (flags & TNY_HEADER_FLAG_DELETED ||
2056 flags & TNY_HEADER_FLAG_EXPUNGED) {
2061 /* If no data on clipboard, return always TRUE */
2062 if (modest_email_clipboard_cleared(priv->clipboard)) {
2067 /* Get message id from header (ensure is a valid id) */
2074 if (priv->hidding_ids != NULL) {
2075 id = tny_header_dup_message_id (header);
2076 for (i=0; i < priv->n_selected && !found; i++)
2077 if (priv->hidding_ids[i] != NULL && id != NULL)
2078 found = (!strcmp (priv->hidding_ids[i], id));
2085 old_status = priv->status;
2086 priv->status = ((gboolean) priv->status) && !visible;
2087 if ((priv->notify_status) && (priv->status != old_status)) {
2088 NotifyFilterInfo *info;
2090 if (priv->status_timeout)
2091 g_source_remove (priv->status_timeout);
2093 info = g_slice_new0 (NotifyFilterInfo);
2094 info->self = g_object_ref (G_OBJECT (user_data));
2095 info->folder = tny_header_get_folder (header);
2096 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2097 notify_filter_change,
2099 notify_filter_change_destroy);
2106 _clear_hidding_filter (ModestHeaderView *header_view)
2108 ModestHeaderViewPrivate *priv = NULL;
2111 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2112 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2114 if (priv->hidding_ids != NULL) {
2115 for (i=0; i < priv->n_selected; i++)
2116 g_free (priv->hidding_ids[i]);
2117 g_free(priv->hidding_ids);
2122 modest_header_view_refilter (ModestHeaderView *header_view)
2124 GtkTreeModel *model = NULL;
2125 ModestHeaderViewPrivate *priv = NULL;
2127 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2128 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2130 /* Hide cut headers */
2131 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2132 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2133 priv->status = HEADER_VIEW_INIT;
2134 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2139 * Called when an account is removed. If I'm showing a folder of the
2140 * account that has been removed then clear the view
2143 on_account_removed (TnyAccountStore *self,
2144 TnyAccount *account,
2147 ModestHeaderViewPrivate *priv = NULL;
2149 /* Ignore changes in transport accounts */
2150 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2153 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2156 TnyAccount *my_account;
2158 my_account = tny_folder_get_account (priv->folder);
2159 if (my_account == account)
2160 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2161 g_object_unref (my_account);
2166 modest_header_view_add_observer(ModestHeaderView *header_view,
2167 ModestHeaderViewObserver *observer)
2169 ModestHeaderViewPrivate *priv;
2171 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2172 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2174 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2176 g_mutex_lock(priv->observer_list_lock);
2177 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2178 g_mutex_unlock(priv->observer_list_lock);
2182 modest_header_view_remove_observer(ModestHeaderView *header_view,
2183 ModestHeaderViewObserver *observer)
2185 ModestHeaderViewPrivate *priv;
2187 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2188 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2190 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2192 g_mutex_lock(priv->observer_list_lock);
2193 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2194 g_mutex_unlock(priv->observer_list_lock);
2198 modest_header_view_notify_observers(ModestHeaderView *header_view,
2199 GtkTreeModel *model,
2200 const gchar *tny_folder_id)
2202 ModestHeaderViewPrivate *priv = NULL;
2204 ModestHeaderViewObserver *observer;
2207 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2209 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2211 g_mutex_lock(priv->observer_list_lock);
2212 iter = priv->observer_list;
2213 while(iter != NULL){
2214 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2215 modest_header_view_observer_update(observer, model,
2217 iter = g_slist_next(iter);
2219 g_mutex_unlock(priv->observer_list_lock);