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_msgtype,*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 /* FIXME: check whether these renderers need to be freed */
355 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
356 renderer_attach = gtk_cell_renderer_pixbuf_new ();
357 renderer_priority = gtk_cell_renderer_pixbuf_new ();
358 renderer_header = gtk_cell_renderer_text_new ();
360 renderer_compact_header = modest_vbox_cell_renderer_new ();
361 renderer_recpt_box = modest_hbox_cell_renderer_new ();
362 renderer_subject_box = modest_hbox_cell_renderer_new ();
363 renderer_recpt = gtk_cell_renderer_text_new ();
364 renderer_subject = gtk_cell_renderer_text_new ();
365 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
367 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
368 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
369 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
370 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
371 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
372 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
373 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
374 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
375 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
376 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
377 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
378 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
379 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
380 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
382 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
383 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
384 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
385 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
386 g_object_set(G_OBJECT(renderer_header),
387 "ellipsize", PANGO_ELLIPSIZE_END,
389 g_object_set (G_OBJECT (renderer_subject),
390 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
392 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
393 g_object_set (G_OBJECT (renderer_recpt),
394 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
396 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
397 g_object_set(G_OBJECT(renderer_compact_date_or_status),
398 "xalign", 1.0, "yalign", 0.0,
400 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
401 g_object_set (G_OBJECT (renderer_priority),
402 "yalign", 1.0, NULL);
403 g_object_set (G_OBJECT (renderer_attach),
404 "yalign", 0.0, NULL);
406 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
407 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
408 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
410 remove_all_columns (self);
412 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
413 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
414 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
415 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
417 /* Add new columns */
418 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
419 ModestHeaderViewColumn col =
420 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
422 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
423 g_printerr ("modest: invalid column %d in column list\n", col);
429 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
430 column = get_new_column (_("M"), renderer_msgtype, FALSE,
431 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
433 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
435 gtk_tree_view_column_set_fixed_width (column, 45);
438 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
439 column = get_new_column (_("A"), renderer_attach, FALSE,
440 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
442 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
444 gtk_tree_view_column_set_fixed_width (column, 45);
448 case MODEST_HEADER_VIEW_COLUMN_FROM:
449 column = get_new_column (_("From"), renderer_header, TRUE,
450 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
452 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
453 GINT_TO_POINTER(TRUE));
456 case MODEST_HEADER_VIEW_COLUMN_TO:
457 column = get_new_column (_("To"), renderer_header, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
461 GINT_TO_POINTER(FALSE));
464 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
465 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
466 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
468 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
469 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
470 compact_column = column;
473 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
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((type == TNY_FOLDER_TYPE_OUTBOX)?
479 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
480 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
481 compact_column = column;
485 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
486 column = get_new_column (_("Subject"), renderer_header, TRUE,
487 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
489 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
493 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
494 column = get_new_column (_("Received"), renderer_header, TRUE,
495 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
497 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
498 GINT_TO_POINTER(TRUE));
501 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
502 column = get_new_column (_("Sent"), renderer_header, TRUE,
503 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
505 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
506 GINT_TO_POINTER(FALSE));
509 case MODEST_HEADER_VIEW_COLUMN_SIZE:
510 column = get_new_column (_("Size"), renderer_header, TRUE,
511 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
513 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
516 case MODEST_HEADER_VIEW_COLUMN_STATUS:
517 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
518 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
520 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
525 g_return_val_if_reached(FALSE);
528 /* we keep the column id around */
529 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
530 GINT_TO_POINTER(col));
532 /* we need this ptr when sorting the rows */
533 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
535 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
539 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
540 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
541 (GtkTreeIterCompareFunc) cmp_rows,
542 compact_column, NULL);
543 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
544 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
545 (GtkTreeIterCompareFunc) cmp_subject_rows,
546 compact_column, NULL);
554 modest_header_view_init (ModestHeaderView *obj)
556 ModestHeaderViewPrivate *priv;
559 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
563 priv->monitor = NULL;
564 priv->observers_lock = g_mutex_new ();
565 priv->autoselect_reference = NULL;
567 priv->status = HEADER_VIEW_INIT;
568 priv->status_timeout = 0;
569 priv->notify_status = TRUE;
571 priv->observer_list_lock = g_mutex_new();
572 priv->observer_list = NULL;
574 priv->clipboard = modest_runtime_get_email_clipboard ();
575 priv->hidding_ids = NULL;
576 priv->n_selected = 0;
577 priv->selection_changed_handler = 0;
578 priv->acc_removed_handler = 0;
580 /* Sort parameters */
581 for (j=0; j < 2; j++) {
582 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
583 priv->sort_colid[j][i] = -1;
584 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
588 setup_drag_and_drop (GTK_WIDGET(obj));
592 modest_header_view_dispose (GObject *obj)
594 ModestHeaderView *self;
595 ModestHeaderViewPrivate *priv;
596 GtkTreeSelection *sel;
598 self = MODEST_HEADER_VIEW(obj);
599 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
601 /* Free in the dispose to avoid unref cycles */
603 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
604 g_object_unref (G_OBJECT (priv->folder));
608 /* We need to do this here in the dispose because the
609 selection won't exist when finalizing */
610 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
611 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
612 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
613 priv->selection_changed_handler = 0;
616 G_OBJECT_CLASS(parent_class)->dispose (obj);
620 modest_header_view_finalize (GObject *obj)
622 ModestHeaderView *self;
623 ModestHeaderViewPrivate *priv;
625 self = MODEST_HEADER_VIEW(obj);
626 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
628 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
629 priv->acc_removed_handler)) {
630 g_signal_handler_disconnect (modest_runtime_get_account_store (),
631 priv->acc_removed_handler);
634 /* There is no need to lock because there should not be any
635 * reference to self now. */
636 g_mutex_free(priv->observer_list_lock);
637 g_slist_free(priv->observer_list);
639 g_mutex_lock (priv->observers_lock);
641 tny_folder_monitor_stop (priv->monitor);
642 g_object_unref (G_OBJECT (priv->monitor));
644 g_mutex_unlock (priv->observers_lock);
645 g_mutex_free (priv->observers_lock);
647 /* Clear hidding array created by cut operation */
648 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
650 if (priv->autoselect_reference != NULL) {
651 gtk_tree_row_reference_free (priv->autoselect_reference);
652 priv->autoselect_reference = NULL;
655 G_OBJECT_CLASS(parent_class)->finalize (obj);
660 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
663 GtkTreeSelection *sel;
664 ModestHeaderView *self;
665 ModestHeaderViewPrivate *priv;
667 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
670 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
671 self = MODEST_HEADER_VIEW(obj);
672 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
674 modest_header_view_set_style (self, style);
676 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
677 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
678 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
680 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
681 TRUE); /* alternating row colors */
683 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
684 priv->selection_changed_handler =
685 g_signal_connect_after (sel, "changed",
686 G_CALLBACK(on_selection_changed), self);
688 g_signal_connect (self, "row-activated",
689 G_CALLBACK (on_header_row_activated), NULL);
691 g_signal_connect (self, "focus-in-event",
692 G_CALLBACK(on_focus_in), NULL);
693 g_signal_connect (self, "focus-out-event",
694 G_CALLBACK(on_focus_out), NULL);
696 g_signal_connect (self, "button-press-event",
697 G_CALLBACK(on_button_press_event), NULL);
698 g_signal_connect (self, "button-release-event",
699 G_CALLBACK(on_button_release_event), NULL);
701 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
703 G_CALLBACK (on_account_removed),
706 g_signal_connect (self, "expose-event",
707 G_CALLBACK(modest_header_view_on_expose_event),
710 return GTK_WIDGET(self);
715 modest_header_view_count_selected_headers (ModestHeaderView *self)
717 GtkTreeSelection *sel;
720 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
722 /* Get selection object and check selected rows count */
723 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
724 selected_rows = gtk_tree_selection_count_selected_rows (sel);
726 return selected_rows;
730 modest_header_view_has_selected_headers (ModestHeaderView *self)
732 GtkTreeSelection *sel;
735 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
737 /* Get selection object and check selected rows count */
738 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
739 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
746 modest_header_view_get_selected_headers (ModestHeaderView *self)
748 GtkTreeSelection *sel;
749 ModestHeaderViewPrivate *priv;
750 TnyList *header_list = NULL;
752 GList *list, *tmp = NULL;
753 GtkTreeModel *tree_model = NULL;
756 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
758 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
760 /* Get selected rows */
761 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
762 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
765 header_list = tny_simple_list_new();
767 list = g_list_reverse (list);
770 /* get header from selection */
771 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
772 gtk_tree_model_get (tree_model, &iter,
773 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
775 /* Prepend to list */
776 tny_list_prepend (header_list, G_OBJECT (header));
777 g_object_unref (G_OBJECT (header));
779 tmp = g_list_next (tmp);
782 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
789 /* scroll our list view so the selected item is visible */
791 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
793 #ifdef MODEST_PLATFORM_GNOME
795 GtkTreePath *selected_path;
796 GtkTreePath *start, *end;
800 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
801 selected_path = gtk_tree_model_get_path (model, iter);
803 start = gtk_tree_path_new ();
804 end = gtk_tree_path_new ();
806 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
808 if (gtk_tree_path_compare (selected_path, start) < 0 ||
809 gtk_tree_path_compare (end, selected_path) < 0)
810 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
811 selected_path, NULL, TRUE,
814 gtk_tree_path_free (selected_path);
815 gtk_tree_path_free (start);
816 gtk_tree_path_free (end);
818 #endif /* MODEST_PLATFORM_GNOME */
823 modest_header_view_select_next (ModestHeaderView *self)
825 GtkTreeSelection *sel;
830 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
832 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
833 path = get_selected_row (GTK_TREE_VIEW(self), &model);
834 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
835 /* Unselect previous path */
836 gtk_tree_selection_unselect_path (sel, path);
838 /* Move path down and selects new one */
839 if (gtk_tree_model_iter_next (model, &iter)) {
840 gtk_tree_selection_select_iter (sel, &iter);
841 scroll_to_selected (self, &iter, FALSE);
843 gtk_tree_path_free(path);
849 modest_header_view_select_prev (ModestHeaderView *self)
851 GtkTreeSelection *sel;
856 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
858 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
859 path = get_selected_row (GTK_TREE_VIEW(self), &model);
860 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
861 /* Unselect previous path */
862 gtk_tree_selection_unselect_path (sel, path);
865 if (gtk_tree_path_prev (path)) {
866 gtk_tree_model_get_iter (model, &iter, path);
868 /* Select the new one */
869 gtk_tree_selection_select_iter (sel, &iter);
870 scroll_to_selected (self, &iter, TRUE);
873 gtk_tree_path_free (path);
878 modest_header_view_get_columns (ModestHeaderView *self)
880 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
882 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
888 modest_header_view_set_style (ModestHeaderView *self,
889 ModestHeaderViewStyle style)
891 ModestHeaderViewPrivate *priv;
892 gboolean show_col_headers = FALSE;
893 ModestHeaderViewStyle old_style;
895 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
896 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
899 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
900 if (priv->style == style)
901 return TRUE; /* nothing to do */
904 case MODEST_HEADER_VIEW_STYLE_DETAILS:
905 show_col_headers = TRUE;
907 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
910 g_return_val_if_reached (FALSE);
912 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
913 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
915 old_style = priv->style;
922 ModestHeaderViewStyle
923 modest_header_view_get_style (ModestHeaderView *self)
925 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
927 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
930 /* This is used to automatically select the first header if the user
931 * has not selected any header yet.
934 modest_header_view_on_expose_event(GtkTreeView *header_view,
935 GdkEventExpose *event,
938 GtkTreeSelection *sel;
940 GtkTreeIter tree_iter;
941 ModestHeaderViewPrivate *priv;
943 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
944 model = gtk_tree_view_get_model(header_view);
949 sel = gtk_tree_view_get_selection(header_view);
950 if(!gtk_tree_selection_count_selected_rows(sel)) {
951 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
952 GtkTreePath *tree_iter_path;
953 /* Prevent the widget from getting the focus
954 when selecting the first item */
955 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
956 g_object_set(header_view, "can-focus", FALSE, NULL);
957 gtk_tree_selection_select_iter(sel, &tree_iter);
958 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
959 g_object_set(header_view, "can-focus", TRUE, NULL);
960 if (priv->autoselect_reference) {
961 gtk_tree_row_reference_free (priv->autoselect_reference);
963 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
964 gtk_tree_path_free (tree_iter_path);
967 if (priv->autoselect_reference != NULL) {
968 gboolean moved_selection = FALSE;
969 GtkTreePath * last_path;
970 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
971 moved_selection = TRUE;
972 g_message ("MULTISELECTION %d -> MOVED", gtk_tree_selection_count_selected_rows (sel));
976 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
977 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
978 g_message ("SELECTION PATH %s LAST PATH %s", gtk_tree_path_to_string (rows->data), gtk_tree_path_to_string (last_path));
979 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
980 moved_selection = TRUE;
981 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
984 if (moved_selection) {
985 gtk_tree_row_reference_free (priv->autoselect_reference);
986 priv->autoselect_reference = NULL;
989 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
990 GtkTreePath *current_path;
991 current_path = gtk_tree_model_get_path (model, &tree_iter);
992 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
993 g_message ("CURRENT PATH %s LAST PATH %s", gtk_tree_path_to_string (current_path), gtk_tree_path_to_string (last_path));
994 if (gtk_tree_path_compare (current_path, last_path) != 0) {
995 g_object_set(header_view, "can-focus", FALSE, NULL);
996 gtk_tree_selection_unselect_all (sel);
997 gtk_tree_selection_select_iter(sel, &tree_iter);
998 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
999 g_object_set(header_view, "can-focus", TRUE, NULL);
1000 gtk_tree_row_reference_free (priv->autoselect_reference);
1001 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1003 gtk_tree_path_free (current_path);
1004 gtk_tree_path_free (last_path);
1014 * This function sets a sortable model in the header view. It's just
1015 * used for developing purposes, because it only does a
1016 * gtk_tree_view_set_model
1019 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
1021 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
1022 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
1023 /* GtkTreeModel *old_model; */
1024 /* ModestHeaderViewPrivate *priv; */
1025 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
1026 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
1028 /* /\* Set new model *\/ */
1029 /* gtk_tree_view_set_model (header_view, model); */
1031 gtk_tree_view_set_model (header_view, model);
1035 modest_header_view_get_folder (ModestHeaderView *self)
1037 ModestHeaderViewPrivate *priv;
1039 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1041 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1044 g_object_ref (priv->folder);
1046 return priv->folder;
1050 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1056 ModestHeaderView *self;
1057 ModestHeaderViewPrivate *priv;
1059 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1061 self = MODEST_HEADER_VIEW (user_data);
1062 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1064 if (cancelled || err)
1067 /* Add IDLE observer (monitor) and another folder observer for
1068 new messages (self) */
1069 g_mutex_lock (priv->observers_lock);
1070 if (priv->monitor) {
1071 tny_folder_monitor_stop (priv->monitor);
1072 g_object_unref (G_OBJECT (priv->monitor));
1074 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1075 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1076 tny_folder_monitor_start (priv->monitor);
1077 g_mutex_unlock (priv->observers_lock);
1081 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1085 ModestHeaderViewPrivate *priv;
1086 GList *cols, *cursor;
1087 GtkTreeModel *filter_model, *sortable;
1089 GtkSortType sort_type;
1091 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1093 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1095 /* Start the monitor in the callback of the
1096 tny_gtk_header_list_model_set_folder call. It's crucial to
1097 do it there and not just after the call because we want the
1098 monitor to observe only the headers returned by the
1099 tny_folder_get_headers_async call that it's inside the
1100 tny_gtk_header_list_model_set_folder call. This way the
1101 monitor infrastructure could successfully cope with
1102 duplicates. For example if a tny_folder_add_msg_async is
1103 happening while tny_gtk_header_list_model_set_folder is
1104 invoked, then the first call could add a header that will
1105 be added again by tny_gtk_header_list_model_set_folder, so
1106 we'd end up with duplicate headers. sergio */
1107 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1109 set_folder_intern_get_headers_async_cb,
1112 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1113 g_object_unref (G_OBJECT (headers));
1115 /* Init filter_row function to examine empty status */
1116 priv->status = HEADER_VIEW_INIT;
1118 /* Create a tree model filter to hide and show rows for cut operations */
1119 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1120 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1124 g_object_unref (G_OBJECT (sortable));
1126 /* install our special sorting functions */
1127 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1129 /* Restore sort column id */
1131 type = modest_tny_folder_guess_folder_type (folder);
1132 if (type == TNY_FOLDER_TYPE_INVALID)
1133 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1135 sort_colid = modest_header_view_get_sort_column_id (self, type);
1136 sort_type = modest_header_view_get_sort_type (self, type);
1137 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1140 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1141 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1142 (GtkTreeIterCompareFunc) cmp_rows,
1144 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1145 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1146 (GtkTreeIterCompareFunc) cmp_subject_rows,
1151 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1152 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1153 tny_folder_get_id(folder));
1154 g_object_unref (G_OBJECT (filter_model));
1155 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1156 /* g_object_unref (G_OBJECT (sortable)); */
1163 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1165 GtkSortType sort_type)
1167 ModestHeaderViewPrivate *priv = NULL;
1168 GtkTreeModel *tree_filter, *sortable = NULL;
1171 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1172 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1174 /* Get model and private data */
1175 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1176 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1177 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1178 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1180 /* Sort tree model */
1181 type = modest_tny_folder_guess_folder_type (priv->folder);
1182 if (type == TNY_FOLDER_TYPE_INVALID)
1183 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1185 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1188 /* Store new sort parameters */
1189 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1194 modest_header_view_set_sort_params (ModestHeaderView *self,
1196 GtkSortType sort_type,
1199 ModestHeaderViewPrivate *priv;
1200 ModestHeaderViewStyle style;
1202 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1203 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1204 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1206 style = modest_header_view_get_style (self);
1207 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1209 priv->sort_colid[style][type] = sort_colid;
1210 priv->sort_type[style][type] = sort_type;
1214 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1217 ModestHeaderViewPrivate *priv;
1218 ModestHeaderViewStyle style;
1220 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1221 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1223 style = modest_header_view_get_style (self);
1224 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1226 return priv->sort_colid[style][type];
1230 modest_header_view_get_sort_type (ModestHeaderView *self,
1233 ModestHeaderViewPrivate *priv;
1234 ModestHeaderViewStyle style;
1236 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1237 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1239 style = modest_header_view_get_style (self);
1240 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1242 return priv->sort_type[style][type];
1246 ModestHeaderView *header_view;
1247 RefreshAsyncUserCallback cb;
1252 folder_refreshed_cb (ModestMailOperation *mail_op,
1256 ModestHeaderViewPrivate *priv;
1257 SetFolderHelper *info;
1259 info = (SetFolderHelper*) user_data;
1261 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1265 info->cb (mail_op, folder, info->user_data);
1267 /* Start the folder count changes observer. We do not need it
1268 before the refresh. Note that the monitor could still be
1269 called for this refresh but now we know that the callback
1270 was previously called */
1271 g_mutex_lock (priv->observers_lock);
1272 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1273 g_mutex_unlock (priv->observers_lock);
1275 /* Notify the observers that the update is over */
1276 g_signal_emit (G_OBJECT (info->header_view),
1277 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1279 /* Allow filtering notifications from now on if the current
1280 folder is still the same (if not then the user has selected
1281 another one to refresh, we should wait until that refresh
1283 if (priv->folder == folder)
1284 priv->notify_status = TRUE;
1287 g_object_unref (info->header_view);
1292 refresh_folder_error_handler (ModestMailOperation *mail_op,
1295 const GError *error = modest_mail_operation_get_error (mail_op);
1297 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1298 error->code == TNY_IO_ERROR_WRITE ||
1299 error->code == TNY_IO_ERROR_READ) {
1300 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1301 /* If the mail op has been cancelled then it's not an error: don't show any message */
1302 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1303 modest_platform_information_banner (NULL, NULL,
1305 "cerm_device_memory_full"));
1311 modest_header_view_set_folder (ModestHeaderView *self,
1314 RefreshAsyncUserCallback callback,
1317 ModestHeaderViewPrivate *priv;
1318 ModestWindow *main_win;
1320 g_return_if_fail (self);
1322 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1324 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1325 FALSE); /* don't create */
1327 g_warning ("%s: BUG: no main window", __FUNCTION__);
1332 if (priv->status_timeout) {
1333 g_source_remove (priv->status_timeout);
1334 priv->status_timeout = 0;
1337 g_mutex_lock (priv->observers_lock);
1338 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1339 g_object_unref (priv->folder);
1340 priv->folder = NULL;
1341 g_mutex_unlock (priv->observers_lock);
1345 GtkTreeSelection *selection;
1346 SetFolderHelper *info;
1347 ModestMailOperation *mail_op = NULL;
1349 /* Set folder in the model */
1350 modest_header_view_set_folder_intern (self, folder);
1352 /* Pick my reference. Nothing to do with the mail operation */
1353 priv->folder = g_object_ref (folder);
1355 /* Do not notify about filterings until the refresh finishes */
1356 priv->notify_status = FALSE;
1358 /* Clear the selection if exists */
1359 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1360 gtk_tree_selection_unselect_all(selection);
1361 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1363 /* Notify the observers that the update begins */
1364 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1367 /* create the helper */
1368 info = g_malloc0 (sizeof (SetFolderHelper));
1369 info->header_view = g_object_ref (self);
1370 info->cb = callback;
1371 info->user_data = user_data;
1373 /* Create the mail operation (source will be the parent widget) */
1374 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1375 refresh_folder_error_handler,
1378 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1381 /* Refresh the folder asynchronously */
1382 modest_mail_operation_refresh_folder (mail_op,
1384 folder_refreshed_cb,
1387 folder_refreshed_cb (mail_op, folder, info);
1390 g_object_unref (mail_op);
1392 g_mutex_lock (priv->observers_lock);
1394 if (priv->monitor) {
1395 tny_folder_monitor_stop (priv->monitor);
1396 g_object_unref (G_OBJECT (priv->monitor));
1397 priv->monitor = NULL;
1400 if (priv->autoselect_reference) {
1401 gtk_tree_row_reference_free (priv->autoselect_reference);
1402 priv->autoselect_reference = NULL;
1405 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1407 modest_header_view_notify_observers(self, NULL, NULL);
1409 g_mutex_unlock (priv->observers_lock);
1411 /* Notify the observers that the update is over */
1412 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1418 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1419 GtkTreeViewColumn *column, gpointer userdata)
1421 ModestHeaderView *self = NULL;
1422 ModestHeaderViewPrivate *priv = NULL;
1424 GtkTreeModel *model = NULL;
1425 TnyHeader *header = NULL;
1426 TnyHeaderFlags flags;
1428 self = MODEST_HEADER_VIEW (treeview);
1429 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1431 model = gtk_tree_view_get_model (treeview);
1432 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1435 /* get the first selected item */
1436 gtk_tree_model_get (model, &iter,
1437 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1438 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1441 /* Dont open DELETED messages */
1442 if (flags & TNY_HEADER_FLAG_DELETED) {
1445 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1446 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1447 modest_platform_information_banner (NULL, NULL, msg);
1453 g_signal_emit (G_OBJECT(self),
1454 signals[HEADER_ACTIVATED_SIGNAL],
1460 g_object_unref (G_OBJECT (header));
1465 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1467 GtkTreeModel *model;
1468 TnyHeader *header = NULL;
1469 GtkTreePath *path = NULL;
1471 ModestHeaderView *self;
1472 ModestHeaderViewPrivate *priv;
1473 GList *selected = NULL;
1475 g_return_if_fail (sel);
1476 g_return_if_fail (user_data);
1478 self = MODEST_HEADER_VIEW (user_data);
1479 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1481 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1482 if (selected != NULL)
1483 path = (GtkTreePath *) selected->data;
1484 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1485 return; /* msg was _un_selected */
1487 gtk_tree_model_get (model, &iter,
1488 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1492 g_signal_emit (G_OBJECT(self),
1493 signals[HEADER_SELECTED_SIGNAL],
1496 g_object_unref (G_OBJECT (header));
1498 /* free all items in 'selected' */
1499 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1500 g_list_free (selected);
1504 /* PROTECTED method. It's useful when we want to force a given
1505 selection to reload a msg. For example if we have selected a header
1506 in offline mode, when Modest become online, we want to reload the
1507 message automatically without an user click over the header */
1509 _modest_header_view_change_selection (GtkTreeSelection *selection,
1512 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1513 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1515 on_selection_changed (selection, user_data);
1519 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1526 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1530 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1534 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1542 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1549 /* static int counter = 0; */
1551 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1552 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1553 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1557 case TNY_HEADER_FLAG_ATTACHMENTS:
1559 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1560 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1561 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1562 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1564 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1565 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1567 return cmp ? cmp : t1 - t2;
1569 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1570 TnyHeader *header1 = NULL, *header2 = NULL;
1572 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1573 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1574 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1575 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1577 /* This is for making priority values respect the intuitive sort relationship
1578 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1580 if (header1 && header2) {
1581 cmp = compare_priorities (tny_header_get_priority (header1),
1582 tny_header_get_priority (header2));
1583 g_object_unref (header1);
1584 g_object_unref (header2);
1586 return cmp ? cmp : t1 - t2;
1592 return &iter1 - &iter2; /* oughhhh */
1597 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1603 /* static int counter = 0; */
1605 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1607 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1608 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1609 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1610 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1612 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1613 val2 + modest_text_utils_get_subject_prefix_len(val2),
1620 /* Drag and drop stuff */
1622 drag_data_get_cb (GtkWidget *widget,
1623 GdkDragContext *context,
1624 GtkSelectionData *selection_data,
1629 ModestHeaderView *self = NULL;
1630 ModestHeaderViewPrivate *priv = NULL;
1632 self = MODEST_HEADER_VIEW (widget);
1633 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1635 /* Set the data. Do not use the current selection because it
1636 could be different than the selection at the beginning of
1638 modest_dnd_selection_data_set_paths (selection_data,
1639 priv->drag_begin_cached_selected_rows);
1643 * We're caching the selected rows at the beginning because the
1644 * selection could change between drag-begin and drag-data-get, for
1645 * example if we have a set of rows already selected, and then we
1646 * click in one of them (without SHIFT key pressed) and begin a drag,
1647 * the selection at that moment contains all the selected lines, but
1648 * after dropping the selection, the release event provokes that only
1649 * the row used to begin the drag is selected, so at the end the
1650 * drag&drop affects only one rows instead of all the selected ones.
1654 drag_begin_cb (GtkWidget *widget,
1655 GdkDragContext *context,
1658 ModestHeaderView *self = NULL;
1659 ModestHeaderViewPrivate *priv = NULL;
1660 GtkTreeSelection *selection;
1662 self = MODEST_HEADER_VIEW (widget);
1663 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1665 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1666 priv->drag_begin_cached_selected_rows =
1667 gtk_tree_selection_get_selected_rows (selection, NULL);
1671 * We use the drag-end signal to clear the cached selection, we use
1672 * this because this allways happens, whether or not the d&d was a
1676 drag_end_cb (GtkWidget *widget,
1680 ModestHeaderView *self = NULL;
1681 ModestHeaderViewPrivate *priv = NULL;
1683 self = MODEST_HEADER_VIEW (widget);
1684 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1686 /* Free cached data */
1687 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1688 g_list_free (priv->drag_begin_cached_selected_rows);
1689 priv->drag_begin_cached_selected_rows = NULL;
1692 /* Header view drag types */
1693 const GtkTargetEntry header_view_drag_types[] = {
1694 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1698 enable_drag_and_drop (GtkWidget *self)
1700 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1701 header_view_drag_types,
1702 G_N_ELEMENTS (header_view_drag_types),
1703 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1707 disable_drag_and_drop (GtkWidget *self)
1709 gtk_drag_source_unset (self);
1713 setup_drag_and_drop (GtkWidget *self)
1715 enable_drag_and_drop(self);
1716 g_signal_connect(G_OBJECT (self), "drag_data_get",
1717 G_CALLBACK(drag_data_get_cb), NULL);
1719 g_signal_connect(G_OBJECT (self), "drag_begin",
1720 G_CALLBACK(drag_begin_cb), NULL);
1722 g_signal_connect(G_OBJECT (self), "drag_end",
1723 G_CALLBACK(drag_end_cb), NULL);
1726 static GtkTreePath *
1727 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1729 GtkTreePath *path = NULL;
1730 GtkTreeSelection *sel = NULL;
1733 sel = gtk_tree_view_get_selection(self);
1734 rows = gtk_tree_selection_get_selected_rows (sel, model);
1736 if ((rows == NULL) || (g_list_length(rows) != 1))
1739 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1744 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1751 * This function moves the tree view scroll to the current selected
1752 * row when the widget grabs the focus
1755 on_focus_in (GtkWidget *self,
1756 GdkEventFocus *event,
1759 GtkTreeSelection *selection;
1760 GtkTreeModel *model;
1761 GList *selected = NULL;
1762 GtkTreePath *selected_path = NULL;
1764 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1768 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1769 /* If none selected yet, pick the first one */
1770 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1774 /* Return if the model is empty */
1775 if (!gtk_tree_model_get_iter_first (model, &iter))
1778 path = gtk_tree_model_get_path (model, &iter);
1779 gtk_tree_selection_select_path (selection, path);
1780 gtk_tree_path_free (path);
1783 /* Need to get the all the rows because is selection multiple */
1784 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1785 if (selected == NULL) return FALSE;
1786 selected_path = (GtkTreePath *) selected->data;
1788 /* Check if we need to scroll */
1789 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1790 GtkTreePath *start_path = NULL;
1791 GtkTreePath *end_path = NULL;
1792 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1796 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1797 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1799 /* Scroll to first path */
1800 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1809 gtk_tree_path_free (start_path);
1811 gtk_tree_path_free (end_path);
1813 #endif /* GTK_CHECK_VERSION */
1816 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1817 g_list_free (selected);
1823 on_focus_out (GtkWidget *self,
1824 GdkEventFocus *event,
1828 if (!gtk_widget_is_focus (self)) {
1829 GtkTreeSelection *selection = NULL;
1830 GList *selected_rows = NULL;
1831 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1832 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1833 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1834 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1835 gtk_tree_selection_unselect_all (selection);
1836 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1837 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1838 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1839 g_list_free (selected_rows);
1846 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1848 enable_drag_and_drop(self);
1853 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1855 GtkTreeSelection *selection = NULL;
1856 GtkTreePath *path = NULL;
1857 gboolean already_selected = FALSE, already_opened = FALSE;
1858 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1860 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1862 GtkTreeModel *model;
1864 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1865 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1867 /* Get header from model */
1868 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1869 if (gtk_tree_model_get_iter (model, &iter, path)) {
1870 GValue value = {0,};
1873 gtk_tree_model_get_value (model, &iter,
1874 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1876 header = (TnyHeader *) g_value_get_object (&value);
1877 if (TNY_IS_HEADER (header)) {
1878 status = modest_tny_all_send_queues_get_msg_status (header);
1879 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1882 g_value_unset (&value);
1886 /* Enable drag and drop only if the user clicks on a row that
1887 it's already selected. If not, let him select items using
1888 the pointer. If the message is in an OUTBOX and in sending
1889 status disable drag and drop as well */
1890 if (!already_selected ||
1891 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1893 disable_drag_and_drop(self);
1896 gtk_tree_path_free(path);
1898 /* If it's already opened then do not let the button-press
1899 event go on because it'll perform a message open because
1900 we're clicking on to an already selected header */
1905 folder_monitor_update (TnyFolderObserver *self,
1906 TnyFolderChange *change)
1908 ModestHeaderViewPrivate *priv = NULL;
1909 TnyFolderChangeChanged changed;
1910 TnyFolder *folder = NULL;
1912 changed = tny_folder_change_get_changed (change);
1914 /* Do not notify the observers if the folder of the header
1915 view has changed before this call to the observer
1917 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1918 folder = tny_folder_change_get_folder (change);
1919 if (folder != priv->folder)
1922 MODEST_DEBUG_BLOCK (
1923 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1924 g_print ("ADDED %d/%d (r/t) \n",
1925 tny_folder_change_get_new_unread_count (change),
1926 tny_folder_change_get_new_all_count (change));
1927 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1928 g_print ("ALL COUNT %d\n",
1929 tny_folder_change_get_new_all_count (change));
1930 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1931 g_print ("UNREAD COUNT %d\n",
1932 tny_folder_change_get_new_unread_count (change));
1933 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1934 g_print ("EXPUNGED %d/%d (r/t) \n",
1935 tny_folder_change_get_new_unread_count (change),
1936 tny_folder_change_get_new_all_count (change));
1937 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1938 g_print ("FOLDER RENAME\n");
1939 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1940 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1941 tny_folder_change_get_new_unread_count (change),
1942 tny_folder_change_get_new_all_count (change));
1943 g_print ("---------------------------------------------------\n");
1946 /* Check folder count */
1947 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1948 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1950 g_mutex_lock (priv->observers_lock);
1952 /* Emit signal to evaluate how headers changes affects
1953 to the window view */
1954 g_signal_emit (G_OBJECT(self),
1955 signals[MSG_COUNT_CHANGED_SIGNAL],
1958 /* Added or removed headers, so data stored on cliboard are invalid */
1959 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1960 modest_email_clipboard_clear (priv->clipboard);
1962 g_mutex_unlock (priv->observers_lock);
1968 g_object_unref (folder);
1972 modest_header_view_is_empty (ModestHeaderView *self)
1974 ModestHeaderViewPrivate *priv;
1976 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1978 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1980 return priv->status == HEADER_VIEW_EMPTY;
1984 modest_header_view_clear (ModestHeaderView *self)
1986 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1988 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1992 modest_header_view_copy_selection (ModestHeaderView *header_view)
1994 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1996 /* Copy selection */
1997 _clipboard_set_selected_data (header_view, FALSE);
2001 modest_header_view_cut_selection (ModestHeaderView *header_view)
2003 ModestHeaderViewPrivate *priv = NULL;
2004 const gchar **hidding = NULL;
2005 guint i, n_selected;
2007 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2009 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2011 /* Copy selection */
2012 _clipboard_set_selected_data (header_view, TRUE);
2014 /* Get hidding ids */
2015 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2017 /* Clear hidding array created by previous cut operation */
2018 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2020 /* Copy hidding array */
2021 priv->n_selected = n_selected;
2022 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2023 for (i=0; i < n_selected; i++)
2024 priv->hidding_ids[i] = g_strdup(hidding[i]);
2026 /* Hide cut headers */
2027 modest_header_view_refilter (header_view);
2034 _clipboard_set_selected_data (ModestHeaderView *header_view,
2037 ModestHeaderViewPrivate *priv = NULL;
2038 TnyList *headers = NULL;
2040 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2041 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2043 /* Set selected data on clipboard */
2044 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2045 headers = modest_header_view_get_selected_headers (header_view);
2046 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2049 g_object_unref (headers);
2053 ModestHeaderView *self;
2058 notify_filter_change (gpointer data)
2060 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2062 g_signal_emit (info->self,
2063 signals[MSG_COUNT_CHANGED_SIGNAL],
2064 0, info->folder, NULL);
2070 notify_filter_change_destroy (gpointer data)
2072 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2073 ModestHeaderViewPrivate *priv;
2075 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2076 priv->status_timeout = 0;
2078 g_object_unref (info->self);
2079 g_object_unref (info->folder);
2080 g_slice_free (NotifyFilterInfo, info);
2084 filter_row (GtkTreeModel *model,
2088 ModestHeaderViewPrivate *priv = NULL;
2089 TnyHeaderFlags flags;
2090 TnyHeader *header = NULL;
2093 gboolean visible = TRUE;
2094 gboolean found = FALSE;
2095 GValue value = {0,};
2096 HeaderViewStatus old_status;
2098 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2099 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2101 /* Get header from model */
2102 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2103 flags = (TnyHeaderFlags) g_value_get_int (&value);
2104 g_value_unset (&value);
2105 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2106 header = (TnyHeader *) g_value_get_object (&value);
2107 g_value_unset (&value);
2109 /* Hide deleted and mark as deleted heders */
2110 if (flags & TNY_HEADER_FLAG_DELETED ||
2111 flags & TNY_HEADER_FLAG_EXPUNGED) {
2116 /* If no data on clipboard, return always TRUE */
2117 if (modest_email_clipboard_cleared(priv->clipboard)) {
2122 /* Get message id from header (ensure is a valid id) */
2129 if (priv->hidding_ids != NULL) {
2130 id = tny_header_dup_message_id (header);
2131 for (i=0; i < priv->n_selected && !found; i++)
2132 if (priv->hidding_ids[i] != NULL && id != NULL)
2133 found = (!strcmp (priv->hidding_ids[i], id));
2140 old_status = priv->status;
2141 priv->status = ((gboolean) priv->status) && !visible;
2142 if ((priv->notify_status) && (priv->status != old_status)) {
2143 NotifyFilterInfo *info;
2145 if (priv->status_timeout)
2146 g_source_remove (priv->status_timeout);
2148 info = g_slice_new0 (NotifyFilterInfo);
2149 info->self = g_object_ref (G_OBJECT (user_data));
2150 info->folder = tny_header_get_folder (header);
2151 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2152 notify_filter_change,
2154 notify_filter_change_destroy);
2161 _clear_hidding_filter (ModestHeaderView *header_view)
2163 ModestHeaderViewPrivate *priv = NULL;
2166 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2167 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2169 if (priv->hidding_ids != NULL) {
2170 for (i=0; i < priv->n_selected; i++)
2171 g_free (priv->hidding_ids[i]);
2172 g_free(priv->hidding_ids);
2177 modest_header_view_refilter (ModestHeaderView *header_view)
2179 GtkTreeModel *model = NULL;
2180 ModestHeaderViewPrivate *priv = NULL;
2182 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2183 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2185 /* Hide cut headers */
2186 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2187 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2188 priv->status = HEADER_VIEW_INIT;
2189 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2194 * Called when an account is removed. If I'm showing a folder of the
2195 * account that has been removed then clear the view
2198 on_account_removed (TnyAccountStore *self,
2199 TnyAccount *account,
2202 ModestHeaderViewPrivate *priv = NULL;
2204 /* Ignore changes in transport accounts */
2205 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2208 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2211 TnyAccount *my_account;
2213 my_account = tny_folder_get_account (priv->folder);
2214 if (my_account == account)
2215 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2216 g_object_unref (my_account);
2221 modest_header_view_add_observer(ModestHeaderView *header_view,
2222 ModestHeaderViewObserver *observer)
2224 ModestHeaderViewPrivate *priv;
2226 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2227 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2229 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2231 g_mutex_lock(priv->observer_list_lock);
2232 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2233 g_mutex_unlock(priv->observer_list_lock);
2237 modest_header_view_remove_observer(ModestHeaderView *header_view,
2238 ModestHeaderViewObserver *observer)
2240 ModestHeaderViewPrivate *priv;
2242 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2243 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2245 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2247 g_mutex_lock(priv->observer_list_lock);
2248 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2249 g_mutex_unlock(priv->observer_list_lock);
2253 modest_header_view_notify_observers(ModestHeaderView *header_view,
2254 GtkTreeModel *model,
2255 const gchar *tny_folder_id)
2257 ModestHeaderViewPrivate *priv = NULL;
2259 ModestHeaderViewObserver *observer;
2262 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2264 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2266 g_mutex_lock(priv->observer_list_lock);
2267 iter = priv->observer_list;
2268 while(iter != NULL){
2269 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2270 modest_header_view_observer_update(observer, model,
2272 iter = g_slist_next(iter);
2274 g_mutex_unlock(priv->observer_list_lock);