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(
114 ModestHeaderView *header_view,
116 const gchar *tny_folder_id);
118 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
119 GdkEventExpose *event,
123 HEADER_VIEW_NON_EMPTY,
128 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
129 struct _ModestHeaderViewPrivate {
131 ModestHeaderViewStyle style;
133 TnyFolderMonitor *monitor;
134 GMutex *observers_lock;
136 /*header-view-observer observer*/
137 GMutex *observer_list_lock;
138 GSList *observer_list;
140 /* not unref this object, its a singlenton */
141 ModestEmailClipboard *clipboard;
143 /* Filter tree model */
146 GtkTreeRowReference *autoselect_reference;
148 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
149 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
151 gulong selection_changed_handler;
152 gulong acc_removed_handler;
154 GList *drag_begin_cached_selected_rows;
156 HeaderViewStatus status;
157 guint status_timeout;
158 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
161 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
162 struct _HeadersCountChangedHelper {
163 ModestHeaderView *self;
164 TnyFolderChange *change;
168 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
169 MODEST_TYPE_HEADER_VIEW, \
170 ModestHeaderViewPrivate))
174 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
177 HEADER_SELECTED_SIGNAL,
178 HEADER_ACTIVATED_SIGNAL,
179 ITEM_NOT_FOUND_SIGNAL,
180 MSG_COUNT_CHANGED_SIGNAL,
181 UPDATING_MSG_LIST_SIGNAL,
186 static GObjectClass *parent_class = NULL;
188 /* uncomment the following if you have defined any signals */
189 static guint signals[LAST_SIGNAL] = {0};
192 modest_header_view_get_type (void)
194 static GType my_type = 0;
196 static const GTypeInfo my_info = {
197 sizeof(ModestHeaderViewClass),
198 NULL, /* base init */
199 NULL, /* base finalize */
200 (GClassInitFunc) modest_header_view_class_init,
201 NULL, /* class finalize */
202 NULL, /* class data */
203 sizeof(ModestHeaderView),
205 (GInstanceInitFunc) modest_header_view_init,
209 static const GInterfaceInfo tny_folder_observer_info =
211 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
212 NULL, /* interface_finalize */
213 NULL /* interface_data */
215 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
219 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
220 &tny_folder_observer_info);
228 modest_header_view_class_init (ModestHeaderViewClass *klass)
230 GObjectClass *gobject_class;
231 gobject_class = (GObjectClass*) klass;
233 parent_class = g_type_class_peek_parent (klass);
234 gobject_class->finalize = modest_header_view_finalize;
235 gobject_class->dispose = modest_header_view_dispose;
237 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
239 signals[HEADER_SELECTED_SIGNAL] =
240 g_signal_new ("header_selected",
241 G_TYPE_FROM_CLASS (gobject_class),
243 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
245 g_cclosure_marshal_VOID__POINTER,
246 G_TYPE_NONE, 1, G_TYPE_POINTER);
248 signals[HEADER_ACTIVATED_SIGNAL] =
249 g_signal_new ("header_activated",
250 G_TYPE_FROM_CLASS (gobject_class),
252 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
254 g_cclosure_marshal_VOID__POINTER,
255 G_TYPE_NONE, 1, G_TYPE_POINTER);
258 signals[ITEM_NOT_FOUND_SIGNAL] =
259 g_signal_new ("item_not_found",
260 G_TYPE_FROM_CLASS (gobject_class),
262 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
264 g_cclosure_marshal_VOID__INT,
265 G_TYPE_NONE, 1, G_TYPE_INT);
267 signals[MSG_COUNT_CHANGED_SIGNAL] =
268 g_signal_new ("msg_count_changed",
269 G_TYPE_FROM_CLASS (gobject_class),
271 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
273 modest_marshal_VOID__POINTER_POINTER,
274 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
276 signals[UPDATING_MSG_LIST_SIGNAL] =
277 g_signal_new ("updating-msg-list",
278 G_TYPE_FROM_CLASS (gobject_class),
280 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
282 g_cclosure_marshal_VOID__BOOLEAN,
283 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
287 tny_folder_observer_init (TnyFolderObserverIface *klass)
289 klass->update = folder_monitor_update;
292 static GtkTreeViewColumn*
293 get_new_column (const gchar *name, GtkCellRenderer *renderer,
294 gboolean resizable, gint sort_col_id, gboolean show_as_text,
295 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
297 GtkTreeViewColumn *column;
299 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
300 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
302 gtk_tree_view_column_set_resizable (column, resizable);
304 gtk_tree_view_column_set_expand (column, TRUE);
307 gtk_tree_view_column_add_attribute (column, renderer, "text",
309 if (sort_col_id >= 0)
310 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
312 gtk_tree_view_column_set_sort_indicator (column, FALSE);
313 gtk_tree_view_column_set_reorderable (column, TRUE);
316 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
323 remove_all_columns (ModestHeaderView *obj)
325 GList *columns, *cursor;
327 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
329 for (cursor = columns; cursor; cursor = cursor->next)
330 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
331 GTK_TREE_VIEW_COLUMN(cursor->data));
332 g_list_free (columns);
336 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
338 GtkTreeModel *tree_filter, *sortable;
339 GtkTreeViewColumn *column=NULL;
340 GtkTreeSelection *selection = NULL;
341 GtkCellRenderer *renderer_msgtype,*renderer_header,
342 *renderer_attach, *renderer_compact_date_or_status;
343 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
344 *renderer_subject, *renderer_subject_box, *renderer_recpt,
346 ModestHeaderViewPrivate *priv;
347 GtkTreeViewColumn *compact_column = NULL;
350 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
351 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
353 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
355 /* FIXME: check whether these renderers need to be freed */
356 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
357 renderer_attach = gtk_cell_renderer_pixbuf_new ();
358 renderer_priority = gtk_cell_renderer_pixbuf_new ();
359 renderer_header = gtk_cell_renderer_text_new ();
361 renderer_compact_header = modest_vbox_cell_renderer_new ();
362 renderer_recpt_box = modest_hbox_cell_renderer_new ();
363 renderer_subject_box = modest_hbox_cell_renderer_new ();
364 renderer_recpt = gtk_cell_renderer_text_new ();
365 renderer_subject = gtk_cell_renderer_text_new ();
366 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
368 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
369 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
370 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
371 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
372 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
373 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
374 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
375 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
376 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
377 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
378 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
379 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
380 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
381 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
383 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
384 #ifdef MODEST_PLATFORM_MAEMO
385 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
386 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
388 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
389 g_object_set(G_OBJECT(renderer_header),
390 "ellipsize", PANGO_ELLIPSIZE_END,
392 g_object_set (G_OBJECT (renderer_subject),
393 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
395 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
396 g_object_set (G_OBJECT (renderer_recpt),
397 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
399 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
400 g_object_set(G_OBJECT(renderer_compact_date_or_status),
401 "xalign", 1.0, "yalign", 0.0,
403 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
404 g_object_set (G_OBJECT (renderer_priority),
405 "yalign", 1.0, NULL);
406 g_object_set (G_OBJECT (renderer_attach),
407 "yalign", 0.0, NULL);
409 #ifdef MODEST_PLATFORM_MAEMO
410 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
411 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
412 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
414 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
415 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
416 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
419 remove_all_columns (self);
421 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
422 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
423 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
424 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
426 /* Add new columns */
427 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
428 ModestHeaderViewColumn col =
429 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
431 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
432 g_printerr ("modest: invalid column %d in column list\n", col);
438 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
439 column = get_new_column (_("M"), renderer_msgtype, FALSE,
440 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
442 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
444 gtk_tree_view_column_set_fixed_width (column, 45);
447 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
448 column = get_new_column (_("A"), renderer_attach, FALSE,
449 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
451 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
453 gtk_tree_view_column_set_fixed_width (column, 45);
457 case MODEST_HEADER_VIEW_COLUMN_FROM:
458 column = get_new_column (_("From"), renderer_header, TRUE,
459 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
461 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
462 GINT_TO_POINTER(TRUE));
465 case MODEST_HEADER_VIEW_COLUMN_TO:
466 column = get_new_column (_("To"), renderer_header, TRUE,
467 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
469 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
470 GINT_TO_POINTER(FALSE));
473 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
474 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
475 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
477 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
478 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
479 compact_column = column;
482 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
483 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
484 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
486 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
487 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
488 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
489 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
490 compact_column = column;
494 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
495 column = get_new_column (_("Subject"), renderer_header, TRUE,
496 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
498 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
502 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
503 column = get_new_column (_("Received"), renderer_header, TRUE,
504 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
506 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
507 GINT_TO_POINTER(TRUE));
510 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
511 column = get_new_column (_("Sent"), renderer_header, TRUE,
512 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
514 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
515 GINT_TO_POINTER(FALSE));
518 case MODEST_HEADER_VIEW_COLUMN_SIZE:
519 column = get_new_column (_("Size"), renderer_header, TRUE,
520 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
522 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
525 case MODEST_HEADER_VIEW_COLUMN_STATUS:
526 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
527 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
529 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
534 g_return_val_if_reached(FALSE);
537 /* we keep the column id around */
538 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
539 GINT_TO_POINTER(col));
541 /* we need this ptr when sorting the rows */
542 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
544 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
548 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
549 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
550 (GtkTreeIterCompareFunc) cmp_rows,
551 compact_column, NULL);
552 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
553 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
554 (GtkTreeIterCompareFunc) cmp_subject_rows,
555 compact_column, NULL);
563 modest_header_view_init (ModestHeaderView *obj)
565 ModestHeaderViewPrivate *priv;
568 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
572 priv->monitor = NULL;
573 priv->observers_lock = g_mutex_new ();
574 priv->autoselect_reference = NULL;
576 priv->status = HEADER_VIEW_INIT;
577 priv->status_timeout = 0;
578 priv->notify_status = TRUE;
580 priv->observer_list_lock = g_mutex_new();
581 priv->observer_list = NULL;
583 priv->clipboard = modest_runtime_get_email_clipboard ();
584 priv->hidding_ids = NULL;
585 priv->n_selected = 0;
586 priv->selection_changed_handler = 0;
587 priv->acc_removed_handler = 0;
589 /* Sort parameters */
590 for (j=0; j < 2; j++) {
591 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
592 priv->sort_colid[j][i] = -1;
593 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
597 setup_drag_and_drop (GTK_WIDGET(obj));
601 modest_header_view_dispose (GObject *obj)
603 ModestHeaderView *self;
604 ModestHeaderViewPrivate *priv;
605 GtkTreeSelection *sel;
607 self = MODEST_HEADER_VIEW(obj);
608 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
610 /* Free in the dispose to avoid unref cycles */
612 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
613 g_object_unref (G_OBJECT (priv->folder));
617 /* We need to do this here in the dispose because the
618 selection won't exist when finalizing */
619 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
620 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
621 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
622 priv->selection_changed_handler = 0;
625 G_OBJECT_CLASS(parent_class)->dispose (obj);
629 modest_header_view_finalize (GObject *obj)
631 ModestHeaderView *self;
632 ModestHeaderViewPrivate *priv;
634 self = MODEST_HEADER_VIEW(obj);
635 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
637 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
638 priv->acc_removed_handler)) {
639 g_signal_handler_disconnect (modest_runtime_get_account_store (),
640 priv->acc_removed_handler);
643 /* There is no need to lock because there should not be any
644 * reference to self now. */
645 g_mutex_free(priv->observer_list_lock);
646 g_slist_free(priv->observer_list);
648 g_mutex_lock (priv->observers_lock);
650 tny_folder_monitor_stop (priv->monitor);
651 g_object_unref (G_OBJECT (priv->monitor));
653 g_mutex_unlock (priv->observers_lock);
654 g_mutex_free (priv->observers_lock);
656 /* Clear hidding array created by cut operation */
657 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
659 if (priv->autoselect_reference != NULL) {
660 gtk_tree_row_reference_free (priv->autoselect_reference);
661 priv->autoselect_reference = NULL;
664 G_OBJECT_CLASS(parent_class)->finalize (obj);
669 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
672 GtkTreeSelection *sel;
673 ModestHeaderView *self;
674 ModestHeaderViewPrivate *priv;
676 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
679 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
680 self = MODEST_HEADER_VIEW(obj);
681 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
683 modest_header_view_set_style (self, style);
685 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
686 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
687 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
689 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
690 TRUE); /* alternating row colors */
692 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
693 priv->selection_changed_handler =
694 g_signal_connect_after (sel, "changed",
695 G_CALLBACK(on_selection_changed), self);
697 g_signal_connect (self, "row-activated",
698 G_CALLBACK (on_header_row_activated), NULL);
700 g_signal_connect (self, "focus-in-event",
701 G_CALLBACK(on_focus_in), NULL);
702 g_signal_connect (self, "focus-out-event",
703 G_CALLBACK(on_focus_out), NULL);
705 g_signal_connect (self, "button-press-event",
706 G_CALLBACK(on_button_press_event), NULL);
707 g_signal_connect (self, "button-release-event",
708 G_CALLBACK(on_button_release_event), NULL);
710 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
712 G_CALLBACK (on_account_removed),
715 g_signal_connect (self, "expose-event",
716 G_CALLBACK(modest_header_view_on_expose_event),
719 return GTK_WIDGET(self);
724 modest_header_view_count_selected_headers (ModestHeaderView *self)
726 GtkTreeSelection *sel;
729 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
731 /* Get selection object and check selected rows count */
732 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
733 selected_rows = gtk_tree_selection_count_selected_rows (sel);
735 return selected_rows;
739 modest_header_view_has_selected_headers (ModestHeaderView *self)
741 GtkTreeSelection *sel;
744 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
746 /* Get selection object and check selected rows count */
747 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
748 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
755 modest_header_view_get_selected_headers (ModestHeaderView *self)
757 GtkTreeSelection *sel;
758 ModestHeaderViewPrivate *priv;
759 TnyList *header_list = NULL;
761 GList *list, *tmp = NULL;
762 GtkTreeModel *tree_model = NULL;
765 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
767 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
769 /* Get selected rows */
770 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
771 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
774 header_list = tny_simple_list_new();
776 list = g_list_reverse (list);
779 /* get header from selection */
780 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
781 gtk_tree_model_get (tree_model, &iter,
782 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
784 /* Prepend to list */
785 tny_list_prepend (header_list, G_OBJECT (header));
786 g_object_unref (G_OBJECT (header));
788 tmp = g_list_next (tmp);
791 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
798 /* scroll our list view so the selected item is visible */
800 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
802 #ifdef MODEST_PLATFORM_GNOME
804 GtkTreePath *selected_path;
805 GtkTreePath *start, *end;
809 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
810 selected_path = gtk_tree_model_get_path (model, iter);
812 start = gtk_tree_path_new ();
813 end = gtk_tree_path_new ();
815 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
817 if (gtk_tree_path_compare (selected_path, start) < 0 ||
818 gtk_tree_path_compare (end, selected_path) < 0)
819 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
820 selected_path, NULL, TRUE,
823 gtk_tree_path_free (selected_path);
824 gtk_tree_path_free (start);
825 gtk_tree_path_free (end);
827 #endif /* MODEST_PLATFORM_GNOME */
832 modest_header_view_select_next (ModestHeaderView *self)
834 GtkTreeSelection *sel;
839 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
841 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
842 path = get_selected_row (GTK_TREE_VIEW(self), &model);
843 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
844 /* Unselect previous path */
845 gtk_tree_selection_unselect_path (sel, path);
847 /* Move path down and selects new one */
848 if (gtk_tree_model_iter_next (model, &iter)) {
849 gtk_tree_selection_select_iter (sel, &iter);
850 scroll_to_selected (self, &iter, FALSE);
852 gtk_tree_path_free(path);
858 modest_header_view_select_prev (ModestHeaderView *self)
860 GtkTreeSelection *sel;
865 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
867 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
868 path = get_selected_row (GTK_TREE_VIEW(self), &model);
869 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
870 /* Unselect previous path */
871 gtk_tree_selection_unselect_path (sel, path);
874 if (gtk_tree_path_prev (path)) {
875 gtk_tree_model_get_iter (model, &iter, path);
877 /* Select the new one */
878 gtk_tree_selection_select_iter (sel, &iter);
879 scroll_to_selected (self, &iter, TRUE);
882 gtk_tree_path_free (path);
887 modest_header_view_get_columns (ModestHeaderView *self)
889 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
891 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
897 modest_header_view_set_style (ModestHeaderView *self,
898 ModestHeaderViewStyle style)
900 ModestHeaderViewPrivate *priv;
901 gboolean show_col_headers = FALSE;
902 ModestHeaderViewStyle old_style;
904 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
905 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
908 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
909 if (priv->style == style)
910 return TRUE; /* nothing to do */
913 case MODEST_HEADER_VIEW_STYLE_DETAILS:
914 show_col_headers = TRUE;
916 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
919 g_return_val_if_reached (FALSE);
921 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
922 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
924 old_style = priv->style;
931 ModestHeaderViewStyle
932 modest_header_view_get_style (ModestHeaderView *self)
934 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
936 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
939 /* This is used to automatically select the first header if the user
940 * has not selected any header yet.
943 modest_header_view_on_expose_event(GtkTreeView *header_view,
944 GdkEventExpose *event,
947 GtkTreeSelection *sel;
949 GtkTreeIter tree_iter;
950 ModestHeaderViewPrivate *priv;
952 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
953 model = gtk_tree_view_get_model(header_view);
958 sel = gtk_tree_view_get_selection(header_view);
959 if(!gtk_tree_selection_count_selected_rows(sel)) {
960 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
961 GtkTreePath *tree_iter_path;
962 /* Prevent the widget from getting the focus
963 when selecting the first item */
964 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
965 g_object_set(header_view, "can-focus", FALSE, NULL);
966 gtk_tree_selection_select_iter(sel, &tree_iter);
967 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
968 g_object_set(header_view, "can-focus", TRUE, NULL);
969 if (priv->autoselect_reference) {
970 gtk_tree_row_reference_free (priv->autoselect_reference);
972 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
973 gtk_tree_path_free (tree_iter_path);
976 if (priv->autoselect_reference != NULL) {
977 gboolean moved_selection = FALSE;
978 GtkTreePath * last_path;
979 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
980 moved_selection = TRUE;
984 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
985 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
986 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
987 moved_selection = TRUE;
988 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
991 if (moved_selection) {
992 gtk_tree_row_reference_free (priv->autoselect_reference);
993 priv->autoselect_reference = NULL;
996 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
997 GtkTreePath *current_path;
998 current_path = gtk_tree_model_get_path (model, &tree_iter);
999 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1000 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1001 g_object_set(header_view, "can-focus", FALSE, NULL);
1002 gtk_tree_selection_unselect_all (sel);
1003 gtk_tree_selection_select_iter(sel, &tree_iter);
1004 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1005 g_object_set(header_view, "can-focus", TRUE, NULL);
1006 gtk_tree_row_reference_free (priv->autoselect_reference);
1007 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1009 gtk_tree_path_free (current_path);
1010 gtk_tree_path_free (last_path);
1020 modest_header_view_get_folder (ModestHeaderView *self)
1022 ModestHeaderViewPrivate *priv;
1024 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1026 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1029 g_object_ref (priv->folder);
1031 return priv->folder;
1035 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1041 ModestHeaderView *self;
1042 ModestHeaderViewPrivate *priv;
1044 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1046 self = MODEST_HEADER_VIEW (user_data);
1047 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1049 if (cancelled || err)
1052 /* Add IDLE observer (monitor) and another folder observer for
1053 new messages (self) */
1054 g_mutex_lock (priv->observers_lock);
1055 if (priv->monitor) {
1056 tny_folder_monitor_stop (priv->monitor);
1057 g_object_unref (G_OBJECT (priv->monitor));
1059 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1060 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1061 tny_folder_monitor_start (priv->monitor);
1062 g_mutex_unlock (priv->observers_lock);
1066 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1070 ModestHeaderViewPrivate *priv;
1071 GList *cols, *cursor;
1072 GtkTreeModel *filter_model, *sortable;
1074 GtkSortType sort_type;
1076 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1078 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1080 /* Start the monitor in the callback of the
1081 tny_gtk_header_list_model_set_folder call. It's crucial to
1082 do it there and not just after the call because we want the
1083 monitor to observe only the headers returned by the
1084 tny_folder_get_headers_async call that it's inside the
1085 tny_gtk_header_list_model_set_folder call. This way the
1086 monitor infrastructure could successfully cope with
1087 duplicates. For example if a tny_folder_add_msg_async is
1088 happening while tny_gtk_header_list_model_set_folder is
1089 invoked, then the first call could add a header that will
1090 be added again by tny_gtk_header_list_model_set_folder, so
1091 we'd end up with duplicate headers. sergio */
1092 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1094 set_folder_intern_get_headers_async_cb,
1097 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1098 g_object_unref (G_OBJECT (headers));
1100 /* Init filter_row function to examine empty status */
1101 priv->status = HEADER_VIEW_INIT;
1103 /* Create a tree model filter to hide and show rows for cut operations */
1104 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1105 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1109 g_object_unref (G_OBJECT (sortable));
1111 /* install our special sorting functions */
1112 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1114 /* Restore sort column id */
1116 type = modest_tny_folder_guess_folder_type (folder);
1117 if (type == TNY_FOLDER_TYPE_INVALID)
1118 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1120 sort_colid = modest_header_view_get_sort_column_id (self, type);
1121 sort_type = modest_header_view_get_sort_type (self, type);
1122 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1125 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1126 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1127 (GtkTreeIterCompareFunc) cmp_rows,
1129 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1130 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1131 (GtkTreeIterCompareFunc) cmp_subject_rows,
1136 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1137 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1138 tny_folder_get_id(folder));
1139 g_object_unref (G_OBJECT (filter_model));
1146 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1148 GtkSortType sort_type)
1150 ModestHeaderViewPrivate *priv = NULL;
1151 GtkTreeModel *tree_filter, *sortable = NULL;
1154 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1155 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1157 /* Get model and private data */
1158 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1159 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1160 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1161 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1163 /* Sort tree model */
1164 type = modest_tny_folder_guess_folder_type (priv->folder);
1165 if (type == TNY_FOLDER_TYPE_INVALID)
1166 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1168 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1171 /* Store new sort parameters */
1172 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1177 modest_header_view_set_sort_params (ModestHeaderView *self,
1179 GtkSortType sort_type,
1182 ModestHeaderViewPrivate *priv;
1183 ModestHeaderViewStyle style;
1185 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1186 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1187 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1189 style = modest_header_view_get_style (self);
1190 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1192 priv->sort_colid[style][type] = sort_colid;
1193 priv->sort_type[style][type] = sort_type;
1197 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1200 ModestHeaderViewPrivate *priv;
1201 ModestHeaderViewStyle style;
1203 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1204 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1206 style = modest_header_view_get_style (self);
1207 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1209 return priv->sort_colid[style][type];
1213 modest_header_view_get_sort_type (ModestHeaderView *self,
1216 ModestHeaderViewPrivate *priv;
1217 ModestHeaderViewStyle style;
1219 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1220 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1222 style = modest_header_view_get_style (self);
1223 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1225 return priv->sort_type[style][type];
1229 ModestHeaderView *header_view;
1230 RefreshAsyncUserCallback cb;
1235 folder_refreshed_cb (ModestMailOperation *mail_op,
1239 ModestHeaderViewPrivate *priv;
1240 SetFolderHelper *info;
1242 info = (SetFolderHelper*) user_data;
1244 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1248 info->cb (mail_op, folder, info->user_data);
1250 /* Start the folder count changes observer. We do not need it
1251 before the refresh. Note that the monitor could still be
1252 called for this refresh but now we know that the callback
1253 was previously called */
1254 g_mutex_lock (priv->observers_lock);
1255 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1256 g_mutex_unlock (priv->observers_lock);
1258 /* Notify the observers that the update is over */
1259 g_signal_emit (G_OBJECT (info->header_view),
1260 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1262 /* Allow filtering notifications from now on if the current
1263 folder is still the same (if not then the user has selected
1264 another one to refresh, we should wait until that refresh
1266 if (priv->folder == folder)
1267 priv->notify_status = TRUE;
1270 g_object_unref (info->header_view);
1275 refresh_folder_error_handler (ModestMailOperation *mail_op,
1278 const GError *error = modest_mail_operation_get_error (mail_op);
1280 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1281 error->code == TNY_IO_ERROR_WRITE ||
1282 error->code == TNY_IO_ERROR_READ) {
1283 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1284 /* If the mail op has been cancelled then it's not an error: don't show any message */
1285 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1286 modest_platform_information_banner (NULL, NULL,
1288 "cerm_device_memory_full"));
1294 modest_header_view_set_folder (ModestHeaderView *self,
1297 RefreshAsyncUserCallback callback,
1300 ModestHeaderViewPrivate *priv;
1301 ModestWindow *main_win;
1303 g_return_if_fail (self);
1305 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1307 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1308 FALSE); /* don't create */
1310 g_warning ("%s: BUG: no main window", __FUNCTION__);
1315 if (priv->status_timeout) {
1316 g_source_remove (priv->status_timeout);
1317 priv->status_timeout = 0;
1320 g_mutex_lock (priv->observers_lock);
1321 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1322 g_object_unref (priv->folder);
1323 priv->folder = NULL;
1324 g_mutex_unlock (priv->observers_lock);
1328 GtkTreeSelection *selection;
1329 SetFolderHelper *info;
1330 ModestMailOperation *mail_op = NULL;
1332 /* Set folder in the model */
1333 modest_header_view_set_folder_intern (self, folder);
1335 /* Pick my reference. Nothing to do with the mail operation */
1336 priv->folder = g_object_ref (folder);
1338 /* Do not notify about filterings until the refresh finishes */
1339 priv->notify_status = FALSE;
1341 /* Clear the selection if exists */
1342 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1343 gtk_tree_selection_unselect_all(selection);
1344 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1346 /* Notify the observers that the update begins */
1347 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1350 /* create the helper */
1351 info = g_malloc0 (sizeof (SetFolderHelper));
1352 info->header_view = g_object_ref (self);
1353 info->cb = callback;
1354 info->user_data = user_data;
1356 /* Create the mail operation (source will be the parent widget) */
1357 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1358 refresh_folder_error_handler,
1361 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1364 /* Refresh the folder asynchronously */
1365 modest_mail_operation_refresh_folder (mail_op,
1367 folder_refreshed_cb,
1370 folder_refreshed_cb (mail_op, folder, info);
1373 g_object_unref (mail_op);
1375 g_mutex_lock (priv->observers_lock);
1377 if (priv->monitor) {
1378 tny_folder_monitor_stop (priv->monitor);
1379 g_object_unref (G_OBJECT (priv->monitor));
1380 priv->monitor = NULL;
1382 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1384 modest_header_view_notify_observers(self, NULL, NULL);
1386 g_mutex_unlock (priv->observers_lock);
1388 /* Notify the observers that the update is over */
1389 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1395 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1396 GtkTreeViewColumn *column, gpointer userdata)
1398 ModestHeaderView *self = NULL;
1399 ModestHeaderViewPrivate *priv = NULL;
1401 GtkTreeModel *model = NULL;
1402 TnyHeader *header = NULL;
1403 TnyHeaderFlags flags;
1405 self = MODEST_HEADER_VIEW (treeview);
1406 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1408 model = gtk_tree_view_get_model (treeview);
1409 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1412 /* get the first selected item */
1413 gtk_tree_model_get (model, &iter,
1414 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1415 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1418 /* Dont open DELETED messages */
1419 if (flags & TNY_HEADER_FLAG_DELETED) {
1422 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1423 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1424 modest_platform_information_banner (NULL, NULL, msg);
1430 g_signal_emit (G_OBJECT(self),
1431 signals[HEADER_ACTIVATED_SIGNAL],
1437 g_object_unref (G_OBJECT (header));
1442 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1444 GtkTreeModel *model;
1445 TnyHeader *header = NULL;
1446 GtkTreePath *path = NULL;
1448 ModestHeaderView *self;
1449 ModestHeaderViewPrivate *priv;
1450 GList *selected = NULL;
1452 g_return_if_fail (sel);
1453 g_return_if_fail (user_data);
1455 self = MODEST_HEADER_VIEW (user_data);
1456 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1458 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1459 if (selected != NULL)
1460 path = (GtkTreePath *) selected->data;
1461 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1462 return; /* msg was _un_selected */
1464 gtk_tree_model_get (model, &iter,
1465 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1469 g_signal_emit (G_OBJECT(self),
1470 signals[HEADER_SELECTED_SIGNAL],
1473 g_object_unref (G_OBJECT (header));
1475 /* free all items in 'selected' */
1476 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1477 g_list_free (selected);
1481 /* PROTECTED method. It's useful when we want to force a given
1482 selection to reload a msg. For example if we have selected a header
1483 in offline mode, when Modest become online, we want to reload the
1484 message automatically without an user click over the header */
1486 _modest_header_view_change_selection (GtkTreeSelection *selection,
1489 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1490 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1492 on_selection_changed (selection, user_data);
1496 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1503 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1507 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1511 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1519 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1526 /* static int counter = 0; */
1528 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1529 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1530 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1534 case TNY_HEADER_FLAG_ATTACHMENTS:
1536 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1537 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1538 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1539 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1541 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1542 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1544 return cmp ? cmp : t1 - t2;
1546 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1547 TnyHeader *header1 = NULL, *header2 = NULL;
1549 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1550 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1551 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1552 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1554 /* This is for making priority values respect the intuitive sort relationship
1555 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1557 if (header1 && header2) {
1558 cmp = compare_priorities (tny_header_get_priority (header1),
1559 tny_header_get_priority (header2));
1560 g_object_unref (header1);
1561 g_object_unref (header2);
1563 return cmp ? cmp : t1 - t2;
1569 return &iter1 - &iter2; /* oughhhh */
1574 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1580 /* static int counter = 0; */
1582 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1584 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1585 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1586 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1587 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1589 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1590 val2 + modest_text_utils_get_subject_prefix_len(val2),
1597 /* Drag and drop stuff */
1599 drag_data_get_cb (GtkWidget *widget,
1600 GdkDragContext *context,
1601 GtkSelectionData *selection_data,
1606 ModestHeaderView *self = NULL;
1607 ModestHeaderViewPrivate *priv = NULL;
1609 self = MODEST_HEADER_VIEW (widget);
1610 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1612 /* Set the data. Do not use the current selection because it
1613 could be different than the selection at the beginning of
1615 modest_dnd_selection_data_set_paths (selection_data,
1616 priv->drag_begin_cached_selected_rows);
1620 * We're caching the selected rows at the beginning because the
1621 * selection could change between drag-begin and drag-data-get, for
1622 * example if we have a set of rows already selected, and then we
1623 * click in one of them (without SHIFT key pressed) and begin a drag,
1624 * the selection at that moment contains all the selected lines, but
1625 * after dropping the selection, the release event provokes that only
1626 * the row used to begin the drag is selected, so at the end the
1627 * drag&drop affects only one rows instead of all the selected ones.
1631 drag_begin_cb (GtkWidget *widget,
1632 GdkDragContext *context,
1635 ModestHeaderView *self = NULL;
1636 ModestHeaderViewPrivate *priv = NULL;
1637 GtkTreeSelection *selection;
1639 self = MODEST_HEADER_VIEW (widget);
1640 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1642 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1643 priv->drag_begin_cached_selected_rows =
1644 gtk_tree_selection_get_selected_rows (selection, NULL);
1648 * We use the drag-end signal to clear the cached selection, we use
1649 * this because this allways happens, whether or not the d&d was a
1653 drag_end_cb (GtkWidget *widget,
1657 ModestHeaderView *self = NULL;
1658 ModestHeaderViewPrivate *priv = NULL;
1660 self = MODEST_HEADER_VIEW (widget);
1661 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1663 /* Free cached data */
1664 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1665 g_list_free (priv->drag_begin_cached_selected_rows);
1666 priv->drag_begin_cached_selected_rows = NULL;
1669 /* Header view drag types */
1670 const GtkTargetEntry header_view_drag_types[] = {
1671 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1675 enable_drag_and_drop (GtkWidget *self)
1677 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1678 header_view_drag_types,
1679 G_N_ELEMENTS (header_view_drag_types),
1680 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1684 disable_drag_and_drop (GtkWidget *self)
1686 gtk_drag_source_unset (self);
1690 setup_drag_and_drop (GtkWidget *self)
1692 enable_drag_and_drop(self);
1693 g_signal_connect(G_OBJECT (self), "drag_data_get",
1694 G_CALLBACK(drag_data_get_cb), NULL);
1696 g_signal_connect(G_OBJECT (self), "drag_begin",
1697 G_CALLBACK(drag_begin_cb), NULL);
1699 g_signal_connect(G_OBJECT (self), "drag_end",
1700 G_CALLBACK(drag_end_cb), NULL);
1703 static GtkTreePath *
1704 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1706 GtkTreePath *path = NULL;
1707 GtkTreeSelection *sel = NULL;
1710 sel = gtk_tree_view_get_selection(self);
1711 rows = gtk_tree_selection_get_selected_rows (sel, model);
1713 if ((rows == NULL) || (g_list_length(rows) != 1))
1716 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1721 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1728 * This function moves the tree view scroll to the current selected
1729 * row when the widget grabs the focus
1732 on_focus_in (GtkWidget *self,
1733 GdkEventFocus *event,
1736 GtkTreeSelection *selection;
1737 GtkTreeModel *model;
1738 GList *selected = NULL;
1739 GtkTreePath *selected_path = NULL;
1741 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1745 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1746 /* If none selected yet, pick the first one */
1747 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1751 /* Return if the model is empty */
1752 if (!gtk_tree_model_get_iter_first (model, &iter))
1755 path = gtk_tree_model_get_path (model, &iter);
1756 gtk_tree_selection_select_path (selection, path);
1757 gtk_tree_path_free (path);
1760 /* Need to get the all the rows because is selection multiple */
1761 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1762 if (selected == NULL) return FALSE;
1763 selected_path = (GtkTreePath *) selected->data;
1765 /* Check if we need to scroll */
1766 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1767 GtkTreePath *start_path = NULL;
1768 GtkTreePath *end_path = NULL;
1769 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1773 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1774 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1776 /* Scroll to first path */
1777 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1786 gtk_tree_path_free (start_path);
1788 gtk_tree_path_free (end_path);
1790 #endif /* GTK_CHECK_VERSION */
1793 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1794 g_list_free (selected);
1800 on_focus_out (GtkWidget *self,
1801 GdkEventFocus *event,
1805 if (!gtk_widget_is_focus (self)) {
1806 GtkTreeSelection *selection = NULL;
1807 GList *selected_rows = NULL;
1808 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1809 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1810 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1811 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1812 gtk_tree_selection_unselect_all (selection);
1813 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1814 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1815 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1816 g_list_free (selected_rows);
1823 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1825 enable_drag_and_drop(self);
1830 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1832 GtkTreeSelection *selection = NULL;
1833 GtkTreePath *path = NULL;
1834 gboolean already_selected = FALSE, already_opened = FALSE;
1835 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1837 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1839 GtkTreeModel *model;
1841 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1842 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1844 /* Get header from model */
1845 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1846 if (gtk_tree_model_get_iter (model, &iter, path)) {
1847 GValue value = {0,};
1850 gtk_tree_model_get_value (model, &iter,
1851 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1853 header = (TnyHeader *) g_value_get_object (&value);
1854 if (TNY_IS_HEADER (header)) {
1855 status = modest_tny_all_send_queues_get_msg_status (header);
1856 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1859 g_value_unset (&value);
1863 /* Enable drag and drop only if the user clicks on a row that
1864 it's already selected. If not, let him select items using
1865 the pointer. If the message is in an OUTBOX and in sending
1866 status disable drag and drop as well */
1867 if (!already_selected ||
1868 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1870 disable_drag_and_drop(self);
1873 gtk_tree_path_free(path);
1875 /* If it's already opened then do not let the button-press
1876 event go on because it'll perform a message open because
1877 we're clicking on to an already selected header */
1882 folder_monitor_update (TnyFolderObserver *self,
1883 TnyFolderChange *change)
1885 ModestHeaderViewPrivate *priv = NULL;
1886 TnyFolderChangeChanged changed;
1887 TnyFolder *folder = NULL;
1889 changed = tny_folder_change_get_changed (change);
1891 /* Do not notify the observers if the folder of the header
1892 view has changed before this call to the observer
1894 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1895 folder = tny_folder_change_get_folder (change);
1896 if (folder != priv->folder)
1899 MODEST_DEBUG_BLOCK (
1900 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1901 g_print ("ADDED %d/%d (r/t) \n",
1902 tny_folder_change_get_new_unread_count (change),
1903 tny_folder_change_get_new_all_count (change));
1904 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1905 g_print ("ALL COUNT %d\n",
1906 tny_folder_change_get_new_all_count (change));
1907 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1908 g_print ("UNREAD COUNT %d\n",
1909 tny_folder_change_get_new_unread_count (change));
1910 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1911 g_print ("EXPUNGED %d/%d (r/t) \n",
1912 tny_folder_change_get_new_unread_count (change),
1913 tny_folder_change_get_new_all_count (change));
1914 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1915 g_print ("FOLDER RENAME\n");
1916 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1917 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1918 tny_folder_change_get_new_unread_count (change),
1919 tny_folder_change_get_new_all_count (change));
1920 g_print ("---------------------------------------------------\n");
1923 /* Check folder count */
1924 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1925 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1927 g_mutex_lock (priv->observers_lock);
1929 /* Emit signal to evaluate how headers changes affects
1930 to the window view */
1931 g_signal_emit (G_OBJECT(self),
1932 signals[MSG_COUNT_CHANGED_SIGNAL],
1935 /* Added or removed headers, so data stored on cliboard are invalid */
1936 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1937 modest_email_clipboard_clear (priv->clipboard);
1939 g_mutex_unlock (priv->observers_lock);
1945 g_object_unref (folder);
1949 modest_header_view_is_empty (ModestHeaderView *self)
1951 ModestHeaderViewPrivate *priv;
1953 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1955 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1957 return priv->status == HEADER_VIEW_EMPTY;
1961 modest_header_view_clear (ModestHeaderView *self)
1963 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1965 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1969 modest_header_view_copy_selection (ModestHeaderView *header_view)
1971 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1973 /* Copy selection */
1974 _clipboard_set_selected_data (header_view, FALSE);
1978 modest_header_view_cut_selection (ModestHeaderView *header_view)
1980 ModestHeaderViewPrivate *priv = NULL;
1981 const gchar **hidding = NULL;
1982 guint i, n_selected;
1984 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1986 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1988 /* Copy selection */
1989 _clipboard_set_selected_data (header_view, TRUE);
1991 /* Get hidding ids */
1992 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1994 /* Clear hidding array created by previous cut operation */
1995 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1997 /* Copy hidding array */
1998 priv->n_selected = n_selected;
1999 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2000 for (i=0; i < n_selected; i++)
2001 priv->hidding_ids[i] = g_strdup(hidding[i]);
2003 /* Hide cut headers */
2004 modest_header_view_refilter (header_view);
2011 _clipboard_set_selected_data (ModestHeaderView *header_view,
2014 ModestHeaderViewPrivate *priv = NULL;
2015 TnyList *headers = NULL;
2017 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2018 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2020 /* Set selected data on clipboard */
2021 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2022 headers = modest_header_view_get_selected_headers (header_view);
2023 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2026 g_object_unref (headers);
2030 ModestHeaderView *self;
2035 notify_filter_change (gpointer data)
2037 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2039 g_signal_emit (info->self,
2040 signals[MSG_COUNT_CHANGED_SIGNAL],
2041 0, info->folder, NULL);
2047 notify_filter_change_destroy (gpointer data)
2049 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2050 ModestHeaderViewPrivate *priv;
2052 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2053 priv->status_timeout = 0;
2055 g_object_unref (info->self);
2056 g_object_unref (info->folder);
2057 g_slice_free (NotifyFilterInfo, info);
2061 filter_row (GtkTreeModel *model,
2065 ModestHeaderViewPrivate *priv = NULL;
2066 TnyHeaderFlags flags;
2067 TnyHeader *header = NULL;
2070 gboolean visible = TRUE;
2071 gboolean found = FALSE;
2072 GValue value = {0,};
2073 HeaderViewStatus old_status;
2075 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2076 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2078 /* Get header from model */
2079 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2080 flags = (TnyHeaderFlags) g_value_get_int (&value);
2081 g_value_unset (&value);
2082 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2083 header = (TnyHeader *) g_value_get_object (&value);
2084 g_value_unset (&value);
2086 /* Hide deleted and mark as deleted heders */
2087 if (flags & TNY_HEADER_FLAG_DELETED ||
2088 flags & TNY_HEADER_FLAG_EXPUNGED) {
2093 /* If no data on clipboard, return always TRUE */
2094 if (modest_email_clipboard_cleared(priv->clipboard)) {
2099 /* Get message id from header (ensure is a valid id) */
2106 if (priv->hidding_ids != NULL) {
2107 id = tny_header_dup_message_id (header);
2108 for (i=0; i < priv->n_selected && !found; i++)
2109 if (priv->hidding_ids[i] != NULL && id != NULL)
2110 found = (!strcmp (priv->hidding_ids[i], id));
2117 old_status = priv->status;
2118 priv->status = ((gboolean) priv->status) && !visible;
2119 if ((priv->notify_status) && (priv->status != old_status)) {
2120 NotifyFilterInfo *info;
2122 if (priv->status_timeout)
2123 g_source_remove (priv->status_timeout);
2125 info = g_slice_new0 (NotifyFilterInfo);
2126 info->self = g_object_ref (G_OBJECT (user_data));
2127 info->folder = tny_header_get_folder (header);
2128 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2129 notify_filter_change,
2131 notify_filter_change_destroy);
2138 _clear_hidding_filter (ModestHeaderView *header_view)
2140 ModestHeaderViewPrivate *priv = NULL;
2143 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2144 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2146 if (priv->hidding_ids != NULL) {
2147 for (i=0; i < priv->n_selected; i++)
2148 g_free (priv->hidding_ids[i]);
2149 g_free(priv->hidding_ids);
2154 modest_header_view_refilter (ModestHeaderView *header_view)
2156 GtkTreeModel *model = NULL;
2157 ModestHeaderViewPrivate *priv = NULL;
2159 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2160 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2162 /* Hide cut headers */
2163 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2164 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2165 priv->status = HEADER_VIEW_INIT;
2166 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2171 * Called when an account is removed. If I'm showing a folder of the
2172 * account that has been removed then clear the view
2175 on_account_removed (TnyAccountStore *self,
2176 TnyAccount *account,
2179 ModestHeaderViewPrivate *priv = NULL;
2181 /* Ignore changes in transport accounts */
2182 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2185 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2188 TnyAccount *my_account;
2190 my_account = tny_folder_get_account (priv->folder);
2191 if (my_account == account)
2192 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2193 g_object_unref (my_account);
2198 modest_header_view_add_observer(ModestHeaderView *header_view,
2199 ModestHeaderViewObserver *observer)
2201 ModestHeaderViewPrivate *priv;
2203 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2204 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2206 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2208 g_mutex_lock(priv->observer_list_lock);
2209 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2210 g_mutex_unlock(priv->observer_list_lock);
2214 modest_header_view_remove_observer(ModestHeaderView *header_view,
2215 ModestHeaderViewObserver *observer)
2217 ModestHeaderViewPrivate *priv;
2219 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2220 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2222 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2224 g_mutex_lock(priv->observer_list_lock);
2225 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2226 g_mutex_unlock(priv->observer_list_lock);
2230 modest_header_view_notify_observers(ModestHeaderView *header_view,
2231 GtkTreeModel *model,
2232 const gchar *tny_folder_id)
2234 ModestHeaderViewPrivate *priv = NULL;
2236 ModestHeaderViewObserver *observer;
2239 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2241 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2243 g_mutex_lock(priv->observer_list_lock);
2244 iter = priv->observer_list;
2245 while(iter != NULL){
2246 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2247 modest_header_view_observer_update(observer, model,
2249 iter = g_slist_next(iter);
2251 g_mutex_unlock(priv->observer_list_lock);