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(
119 GtkTreeView *header_view,
120 GdkEventExpose *event,
124 HEADER_VIEW_NON_EMPTY,
129 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
130 struct _ModestHeaderViewPrivate {
132 ModestHeaderViewStyle style;
134 TnyFolderMonitor *monitor;
135 GMutex *observers_lock;
137 /*header-view-observer observer*/
138 GMutex *observer_list_lock;
139 GSList *observer_list;
141 /* not unref this object, its a singlenton */
142 ModestEmailClipboard *clipboard;
144 /* Filter tree model */
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 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
385 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
386 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
387 g_object_set(G_OBJECT(renderer_header),
388 "ellipsize", PANGO_ELLIPSIZE_END,
390 g_object_set (G_OBJECT (renderer_subject),
391 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
393 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
394 g_object_set (G_OBJECT (renderer_recpt),
395 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
397 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
398 g_object_set(G_OBJECT(renderer_compact_date_or_status),
399 "xalign", 1.0, "yalign", 0.0,
401 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
402 g_object_set (G_OBJECT (renderer_priority),
403 "yalign", 1.0, NULL);
404 g_object_set (G_OBJECT (renderer_attach),
405 "yalign", 0.0, NULL);
407 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
408 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
409 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
411 remove_all_columns (self);
413 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
414 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
415 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
416 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
418 /* Add new columns */
419 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
420 ModestHeaderViewColumn col =
421 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
423 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
424 g_printerr ("modest: invalid column %d in column list\n", col);
430 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
431 column = get_new_column (_("M"), renderer_msgtype, FALSE,
432 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
434 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
436 gtk_tree_view_column_set_fixed_width (column, 45);
439 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
440 column = get_new_column (_("A"), renderer_attach, FALSE,
441 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
443 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
445 gtk_tree_view_column_set_fixed_width (column, 45);
449 case MODEST_HEADER_VIEW_COLUMN_FROM:
450 column = get_new_column (_("From"), renderer_header, TRUE,
451 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
453 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
454 GINT_TO_POINTER(TRUE));
457 case MODEST_HEADER_VIEW_COLUMN_TO:
458 column = get_new_column (_("To"), renderer_header, TRUE,
459 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
461 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
462 GINT_TO_POINTER(FALSE));
465 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
466 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
467 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
469 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
470 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
471 compact_column = column;
474 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
475 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
476 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
478 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
479 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
480 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
481 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
482 compact_column = column;
486 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
487 column = get_new_column (_("Subject"), renderer_header, TRUE,
488 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
490 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
494 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
495 column = get_new_column (_("Received"), renderer_header, TRUE,
496 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
498 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
499 GINT_TO_POINTER(TRUE));
502 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
503 column = get_new_column (_("Sent"), renderer_header, TRUE,
504 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
506 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
507 GINT_TO_POINTER(FALSE));
510 case MODEST_HEADER_VIEW_COLUMN_SIZE:
511 column = get_new_column (_("Size"), renderer_header, TRUE,
512 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
514 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
517 case MODEST_HEADER_VIEW_COLUMN_STATUS:
518 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
519 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
521 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
526 g_return_val_if_reached(FALSE);
529 /* we keep the column id around */
530 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
531 GINT_TO_POINTER(col));
533 /* we need this ptr when sorting the rows */
534 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
536 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
540 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
541 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
542 (GtkTreeIterCompareFunc) cmp_rows,
543 compact_column, NULL);
544 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
545 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
546 (GtkTreeIterCompareFunc) cmp_subject_rows,
547 compact_column, NULL);
555 modest_header_view_init (ModestHeaderView *obj)
557 ModestHeaderViewPrivate *priv;
560 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
564 priv->monitor = NULL;
565 priv->observers_lock = g_mutex_new ();
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 G_OBJECT_CLASS(parent_class)->finalize (obj);
655 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
658 GtkTreeSelection *sel;
659 ModestHeaderView *self;
660 ModestHeaderViewPrivate *priv;
662 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
665 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
666 self = MODEST_HEADER_VIEW(obj);
667 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
669 modest_header_view_set_style (self, style);
671 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
672 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
673 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
675 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
676 TRUE); /* alternating row colors */
678 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
679 priv->selection_changed_handler =
680 g_signal_connect_after (sel, "changed",
681 G_CALLBACK(on_selection_changed), self);
683 g_signal_connect (self, "row-activated",
684 G_CALLBACK (on_header_row_activated), NULL);
686 g_signal_connect (self, "focus-in-event",
687 G_CALLBACK(on_focus_in), NULL);
688 g_signal_connect (self, "focus-out-event",
689 G_CALLBACK(on_focus_out), NULL);
691 g_signal_connect (self, "button-press-event",
692 G_CALLBACK(on_button_press_event), NULL);
693 g_signal_connect (self, "button-release-event",
694 G_CALLBACK(on_button_release_event), NULL);
696 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
698 G_CALLBACK (on_account_removed),
701 g_signal_connect (self, "expose-event",
702 G_CALLBACK(modest_header_view_on_expose_event),
705 return GTK_WIDGET(self);
710 modest_header_view_count_selected_headers (ModestHeaderView *self)
712 GtkTreeSelection *sel;
715 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
717 /* Get selection object and check selected rows count */
718 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
719 selected_rows = gtk_tree_selection_count_selected_rows (sel);
721 return selected_rows;
725 modest_header_view_has_selected_headers (ModestHeaderView *self)
727 GtkTreeSelection *sel;
730 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
732 /* Get selection object and check selected rows count */
733 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
734 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
741 modest_header_view_get_selected_headers (ModestHeaderView *self)
743 GtkTreeSelection *sel;
744 ModestHeaderViewPrivate *priv;
745 TnyList *header_list = NULL;
747 GList *list, *tmp = NULL;
748 GtkTreeModel *tree_model = NULL;
751 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
753 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
755 /* Get selected rows */
756 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
757 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
760 header_list = tny_simple_list_new();
762 list = g_list_reverse (list);
765 /* get header from selection */
766 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
767 gtk_tree_model_get (tree_model, &iter,
768 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
770 /* Prepend to list */
771 tny_list_prepend (header_list, G_OBJECT (header));
772 g_object_unref (G_OBJECT (header));
774 tmp = g_list_next (tmp);
777 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
784 /* scroll our list view so the selected item is visible */
786 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
788 #ifdef MODEST_PLATFORM_GNOME
790 GtkTreePath *selected_path;
791 GtkTreePath *start, *end;
795 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
796 selected_path = gtk_tree_model_get_path (model, iter);
798 start = gtk_tree_path_new ();
799 end = gtk_tree_path_new ();
801 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
803 if (gtk_tree_path_compare (selected_path, start) < 0 ||
804 gtk_tree_path_compare (end, selected_path) < 0)
805 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
806 selected_path, NULL, TRUE,
809 gtk_tree_path_free (selected_path);
810 gtk_tree_path_free (start);
811 gtk_tree_path_free (end);
813 #endif /* MODEST_PLATFORM_GNOME */
818 modest_header_view_select_next (ModestHeaderView *self)
820 GtkTreeSelection *sel;
825 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
827 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
828 path = get_selected_row (GTK_TREE_VIEW(self), &model);
829 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
830 /* Unselect previous path */
831 gtk_tree_selection_unselect_path (sel, path);
833 /* Move path down and selects new one */
834 if (gtk_tree_model_iter_next (model, &iter)) {
835 gtk_tree_selection_select_iter (sel, &iter);
836 scroll_to_selected (self, &iter, FALSE);
838 gtk_tree_path_free(path);
844 modest_header_view_select_prev (ModestHeaderView *self)
846 GtkTreeSelection *sel;
851 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
853 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
854 path = get_selected_row (GTK_TREE_VIEW(self), &model);
855 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
856 /* Unselect previous path */
857 gtk_tree_selection_unselect_path (sel, path);
860 if (gtk_tree_path_prev (path)) {
861 gtk_tree_model_get_iter (model, &iter, path);
863 /* Select the new one */
864 gtk_tree_selection_select_iter (sel, &iter);
865 scroll_to_selected (self, &iter, TRUE);
868 gtk_tree_path_free (path);
873 modest_header_view_get_columns (ModestHeaderView *self)
875 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
877 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
883 modest_header_view_set_style (ModestHeaderView *self,
884 ModestHeaderViewStyle style)
886 ModestHeaderViewPrivate *priv;
887 gboolean show_col_headers = FALSE;
888 ModestHeaderViewStyle old_style;
890 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
891 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
894 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
895 if (priv->style == style)
896 return TRUE; /* nothing to do */
899 case MODEST_HEADER_VIEW_STYLE_DETAILS:
900 show_col_headers = TRUE;
902 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
905 g_return_val_if_reached (FALSE);
907 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
908 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
910 old_style = priv->style;
917 ModestHeaderViewStyle
918 modest_header_view_get_style (ModestHeaderView *self)
920 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
922 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
925 /* This is used to automatically select the first header if the user
926 * has not selected any header yet.
929 modest_header_view_on_expose_event(GtkTreeView *header_view,
930 GdkEventExpose *event,
933 GtkTreeSelection *sel;
935 GtkTreeIter tree_iter;
937 model = gtk_tree_view_get_model(header_view);
942 sel = gtk_tree_view_get_selection(header_view);
943 if(!gtk_tree_selection_count_selected_rows(sel))
944 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
945 /* Prevent the widget from getting the focus
946 when selecting the first item */
947 g_object_set(header_view, "can-focus", FALSE, NULL);
948 gtk_tree_selection_select_iter(sel, &tree_iter);
949 g_object_set(header_view, "can-focus", TRUE, NULL);
956 * This function sets a sortable model in the header view. It's just
957 * used for developing purposes, because it only does a
958 * gtk_tree_view_set_model
961 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
963 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
964 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
965 /* GtkTreeModel *old_model; */
966 /* ModestHeaderViewPrivate *priv; */
967 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
968 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
970 /* /\* Set new model *\/ */
971 /* gtk_tree_view_set_model (header_view, model); */
973 gtk_tree_view_set_model (header_view, model);
977 modest_header_view_get_folder (ModestHeaderView *self)
979 ModestHeaderViewPrivate *priv;
981 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
983 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
986 g_object_ref (priv->folder);
992 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
998 ModestHeaderView *self;
999 ModestHeaderViewPrivate *priv;
1001 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1003 self = MODEST_HEADER_VIEW (user_data);
1004 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1006 if (cancelled || err)
1009 /* Add IDLE observer (monitor) and another folder observer for
1010 new messages (self) */
1011 g_mutex_lock (priv->observers_lock);
1012 if (priv->monitor) {
1013 tny_folder_monitor_stop (priv->monitor);
1014 g_object_unref (G_OBJECT (priv->monitor));
1016 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1017 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1018 tny_folder_monitor_start (priv->monitor);
1019 g_mutex_unlock (priv->observers_lock);
1023 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1027 ModestHeaderViewPrivate *priv;
1028 GList *cols, *cursor;
1029 GtkTreeModel *filter_model, *sortable;
1031 GtkSortType sort_type;
1033 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1035 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1037 /* Start the monitor in the callback of the
1038 tny_gtk_header_list_model_set_folder call. It's crucial to
1039 do it there and not just after the call because we want the
1040 monitor to observe only the headers returned by the
1041 tny_folder_get_headers_async call that it's inside the
1042 tny_gtk_header_list_model_set_folder call. This way the
1043 monitor infrastructure could successfully cope with
1044 duplicates. For example if a tny_folder_add_msg_async is
1045 happening while tny_gtk_header_list_model_set_folder is
1046 invoked, then the first call could add a header that will
1047 be added again by tny_gtk_header_list_model_set_folder, so
1048 we'd end up with duplicate headers. sergio */
1049 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1051 set_folder_intern_get_headers_async_cb,
1054 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1055 g_object_unref (G_OBJECT (headers));
1057 /* Init filter_row function to examine empty status */
1058 priv->status = HEADER_VIEW_INIT;
1060 /* Create a tree model filter to hide and show rows for cut operations */
1061 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1062 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1066 g_object_unref (G_OBJECT (sortable));
1068 /* install our special sorting functions */
1069 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1071 /* Restore sort column id */
1073 type = modest_tny_folder_guess_folder_type (folder);
1074 if (type == TNY_FOLDER_TYPE_INVALID)
1075 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1077 sort_colid = modest_header_view_get_sort_column_id (self, type);
1078 sort_type = modest_header_view_get_sort_type (self, type);
1079 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1082 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1083 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1084 (GtkTreeIterCompareFunc) cmp_rows,
1086 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1087 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1088 (GtkTreeIterCompareFunc) cmp_subject_rows,
1093 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1094 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1095 tny_folder_get_id(folder));
1096 g_object_unref (G_OBJECT (filter_model));
1097 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1098 /* g_object_unref (G_OBJECT (sortable)); */
1105 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1107 GtkSortType sort_type)
1109 ModestHeaderViewPrivate *priv = NULL;
1110 GtkTreeModel *tree_filter, *sortable = NULL;
1113 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1114 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1116 /* Get model and private data */
1117 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1118 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1119 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1120 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1122 /* Sort tree model */
1123 type = modest_tny_folder_guess_folder_type (priv->folder);
1124 if (type == TNY_FOLDER_TYPE_INVALID)
1125 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1127 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1130 /* Store new sort parameters */
1131 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1136 modest_header_view_set_sort_params (ModestHeaderView *self,
1138 GtkSortType sort_type,
1141 ModestHeaderViewPrivate *priv;
1142 ModestHeaderViewStyle style;
1144 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1145 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1146 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1148 style = modest_header_view_get_style (self);
1149 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1151 priv->sort_colid[style][type] = sort_colid;
1152 priv->sort_type[style][type] = sort_type;
1156 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1159 ModestHeaderViewPrivate *priv;
1160 ModestHeaderViewStyle style;
1162 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1163 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1165 style = modest_header_view_get_style (self);
1166 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1168 return priv->sort_colid[style][type];
1172 modest_header_view_get_sort_type (ModestHeaderView *self,
1175 ModestHeaderViewPrivate *priv;
1176 ModestHeaderViewStyle style;
1178 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1179 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1181 style = modest_header_view_get_style (self);
1182 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1184 return priv->sort_type[style][type];
1188 ModestHeaderView *header_view;
1189 RefreshAsyncUserCallback cb;
1194 folder_refreshed_cb (ModestMailOperation *mail_op,
1198 ModestHeaderViewPrivate *priv;
1199 SetFolderHelper *info;
1201 info = (SetFolderHelper*) user_data;
1203 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1207 info->cb (mail_op, folder, info->user_data);
1209 /* Start the folder count changes observer. We do not need it
1210 before the refresh. Note that the monitor could still be
1211 called for this refresh but now we know that the callback
1212 was previously called */
1213 g_mutex_lock (priv->observers_lock);
1214 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1215 g_mutex_unlock (priv->observers_lock);
1217 /* Notify the observers that the update is over */
1218 g_signal_emit (G_OBJECT (info->header_view),
1219 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1221 /* Allow filtering notifications from now on if the current
1222 folder is still the same (if not then the user has selected
1223 another one to refresh, we should wait until that refresh
1225 if (priv->folder == folder)
1226 priv->notify_status = TRUE;
1229 g_object_unref (info->header_view);
1234 refresh_folder_error_handler (ModestMailOperation *mail_op,
1237 const GError *error = modest_mail_operation_get_error (mail_op);
1239 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1240 error->code == TNY_IO_ERROR_WRITE ||
1241 error->code == TNY_IO_ERROR_READ) {
1242 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1243 /* If the mail op has been cancelled then it's not an error: don't show any message */
1244 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1245 modest_platform_information_banner (NULL, NULL,
1247 "cerm_device_memory_full"));
1253 modest_header_view_set_folder (ModestHeaderView *self,
1256 RefreshAsyncUserCallback callback,
1259 ModestHeaderViewPrivate *priv;
1260 ModestWindow *main_win;
1262 g_return_if_fail (self);
1264 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1266 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1267 FALSE); /* don't create */
1269 g_warning ("%s: BUG: no main window", __FUNCTION__);
1274 if (priv->status_timeout) {
1275 g_source_remove (priv->status_timeout);
1276 priv->status_timeout = 0;
1279 g_mutex_lock (priv->observers_lock);
1280 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1281 g_object_unref (priv->folder);
1282 priv->folder = NULL;
1283 g_mutex_unlock (priv->observers_lock);
1287 GtkTreeSelection *selection;
1288 SetFolderHelper *info;
1289 ModestMailOperation *mail_op = NULL;
1291 /* Set folder in the model */
1292 modest_header_view_set_folder_intern (self, folder);
1294 /* Pick my reference. Nothing to do with the mail operation */
1295 priv->folder = g_object_ref (folder);
1297 /* Do not notify about filterings until the refresh finishes */
1298 priv->notify_status = FALSE;
1300 /* Clear the selection if exists */
1301 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1302 gtk_tree_selection_unselect_all(selection);
1303 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1305 /* Notify the observers that the update begins */
1306 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1309 /* create the helper */
1310 info = g_malloc0 (sizeof (SetFolderHelper));
1311 info->header_view = g_object_ref (self);
1312 info->cb = callback;
1313 info->user_data = user_data;
1315 /* Create the mail operation (source will be the parent widget) */
1316 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1317 refresh_folder_error_handler,
1320 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1323 /* Refresh the folder asynchronously */
1324 modest_mail_operation_refresh_folder (mail_op,
1326 folder_refreshed_cb,
1329 folder_refreshed_cb (mail_op, folder, info);
1332 g_object_unref (mail_op);
1334 g_mutex_lock (priv->observers_lock);
1336 if (priv->monitor) {
1337 tny_folder_monitor_stop (priv->monitor);
1338 g_object_unref (G_OBJECT (priv->monitor));
1339 priv->monitor = NULL;
1341 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1343 modest_header_view_notify_observers(self, NULL, NULL);
1345 g_mutex_unlock (priv->observers_lock);
1347 /* Notify the observers that the update is over */
1348 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1354 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1355 GtkTreeViewColumn *column, gpointer userdata)
1357 ModestHeaderView *self = NULL;
1358 ModestHeaderViewPrivate *priv = NULL;
1360 GtkTreeModel *model = NULL;
1361 TnyHeader *header = NULL;
1362 TnyHeaderFlags flags;
1364 self = MODEST_HEADER_VIEW (treeview);
1365 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1367 model = gtk_tree_view_get_model (treeview);
1368 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1371 /* get the first selected item */
1372 gtk_tree_model_get (model, &iter,
1373 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1374 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1377 /* Dont open DELETED messages */
1378 if (flags & TNY_HEADER_FLAG_DELETED) {
1381 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1382 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1383 modest_platform_information_banner (NULL, NULL, msg);
1389 g_signal_emit (G_OBJECT(self),
1390 signals[HEADER_ACTIVATED_SIGNAL],
1396 g_object_unref (G_OBJECT (header));
1401 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1403 GtkTreeModel *model;
1404 TnyHeader *header = NULL;
1405 GtkTreePath *path = NULL;
1407 ModestHeaderView *self;
1408 ModestHeaderViewPrivate *priv;
1409 GList *selected = NULL;
1411 g_return_if_fail (sel);
1412 g_return_if_fail (user_data);
1414 self = MODEST_HEADER_VIEW (user_data);
1415 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1417 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1418 if (selected != NULL)
1419 path = (GtkTreePath *) selected->data;
1420 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1421 return; /* msg was _un_selected */
1423 gtk_tree_model_get (model, &iter,
1424 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1428 g_signal_emit (G_OBJECT(self),
1429 signals[HEADER_SELECTED_SIGNAL],
1432 g_object_unref (G_OBJECT (header));
1434 /* free all items in 'selected' */
1435 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1436 g_list_free (selected);
1440 /* PROTECTED method. It's useful when we want to force a given
1441 selection to reload a msg. For example if we have selected a header
1442 in offline mode, when Modest become online, we want to reload the
1443 message automatically without an user click over the header */
1445 _modest_header_view_change_selection (GtkTreeSelection *selection,
1448 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1449 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1451 on_selection_changed (selection, user_data);
1455 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1462 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1466 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1470 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1478 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1485 /* static int counter = 0; */
1487 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1488 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1489 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1493 case TNY_HEADER_FLAG_ATTACHMENTS:
1495 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1496 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1497 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1498 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1500 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1501 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1503 return cmp ? cmp : t1 - t2;
1505 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1506 TnyHeader *header1 = NULL, *header2 = NULL;
1508 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1509 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1510 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1511 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1513 /* This is for making priority values respect the intuitive sort relationship
1514 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1516 if (header1 && header2) {
1517 cmp = compare_priorities (tny_header_get_priority (header1),
1518 tny_header_get_priority (header2));
1519 g_object_unref (header1);
1520 g_object_unref (header2);
1522 return cmp ? cmp : t1 - t2;
1528 return &iter1 - &iter2; /* oughhhh */
1533 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1539 /* static int counter = 0; */
1541 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1543 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1544 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1545 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1546 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1548 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1549 val2 + modest_text_utils_get_subject_prefix_len(val2),
1556 /* Drag and drop stuff */
1558 drag_data_get_cb (GtkWidget *widget,
1559 GdkDragContext *context,
1560 GtkSelectionData *selection_data,
1565 ModestHeaderView *self = NULL;
1566 ModestHeaderViewPrivate *priv = NULL;
1568 self = MODEST_HEADER_VIEW (widget);
1569 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1571 /* Set the data. Do not use the current selection because it
1572 could be different than the selection at the beginning of
1574 modest_dnd_selection_data_set_paths (selection_data,
1575 priv->drag_begin_cached_selected_rows);
1579 * We're caching the selected rows at the beginning because the
1580 * selection could change between drag-begin and drag-data-get, for
1581 * example if we have a set of rows already selected, and then we
1582 * click in one of them (without SHIFT key pressed) and begin a drag,
1583 * the selection at that moment contains all the selected lines, but
1584 * after dropping the selection, the release event provokes that only
1585 * the row used to begin the drag is selected, so at the end the
1586 * drag&drop affects only one rows instead of all the selected ones.
1590 drag_begin_cb (GtkWidget *widget,
1591 GdkDragContext *context,
1594 ModestHeaderView *self = NULL;
1595 ModestHeaderViewPrivate *priv = NULL;
1596 GtkTreeSelection *selection;
1598 self = MODEST_HEADER_VIEW (widget);
1599 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1601 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1602 priv->drag_begin_cached_selected_rows =
1603 gtk_tree_selection_get_selected_rows (selection, NULL);
1607 * We use the drag-end signal to clear the cached selection, we use
1608 * this because this allways happens, whether or not the d&d was a
1612 drag_end_cb (GtkWidget *widget,
1616 ModestHeaderView *self = NULL;
1617 ModestHeaderViewPrivate *priv = NULL;
1619 self = MODEST_HEADER_VIEW (widget);
1620 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1622 /* Free cached data */
1623 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1624 g_list_free (priv->drag_begin_cached_selected_rows);
1625 priv->drag_begin_cached_selected_rows = NULL;
1628 /* Header view drag types */
1629 const GtkTargetEntry header_view_drag_types[] = {
1630 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1634 enable_drag_and_drop (GtkWidget *self)
1636 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1637 header_view_drag_types,
1638 G_N_ELEMENTS (header_view_drag_types),
1639 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1643 disable_drag_and_drop (GtkWidget *self)
1645 gtk_drag_source_unset (self);
1649 setup_drag_and_drop (GtkWidget *self)
1651 enable_drag_and_drop(self);
1652 g_signal_connect(G_OBJECT (self), "drag_data_get",
1653 G_CALLBACK(drag_data_get_cb), NULL);
1655 g_signal_connect(G_OBJECT (self), "drag_begin",
1656 G_CALLBACK(drag_begin_cb), NULL);
1658 g_signal_connect(G_OBJECT (self), "drag_end",
1659 G_CALLBACK(drag_end_cb), NULL);
1662 static GtkTreePath *
1663 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1665 GtkTreePath *path = NULL;
1666 GtkTreeSelection *sel = NULL;
1669 sel = gtk_tree_view_get_selection(self);
1670 rows = gtk_tree_selection_get_selected_rows (sel, model);
1672 if ((rows == NULL) || (g_list_length(rows) != 1))
1675 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1680 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1687 * This function moves the tree view scroll to the current selected
1688 * row when the widget grabs the focus
1691 on_focus_in (GtkWidget *self,
1692 GdkEventFocus *event,
1695 GtkTreeSelection *selection;
1696 GtkTreeModel *model;
1697 GList *selected = NULL;
1698 GtkTreePath *selected_path = NULL;
1700 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1704 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1705 /* If none selected yet, pick the first one */
1706 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1710 /* Return if the model is empty */
1711 if (!gtk_tree_model_get_iter_first (model, &iter))
1714 path = gtk_tree_model_get_path (model, &iter);
1715 gtk_tree_selection_select_path (selection, path);
1716 gtk_tree_path_free (path);
1719 /* Need to get the all the rows because is selection multiple */
1720 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1721 if (selected == NULL) return FALSE;
1722 selected_path = (GtkTreePath *) selected->data;
1724 /* Check if we need to scroll */
1725 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1726 GtkTreePath *start_path = NULL;
1727 GtkTreePath *end_path = NULL;
1728 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1732 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1733 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1735 /* Scroll to first path */
1736 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1745 gtk_tree_path_free (start_path);
1747 gtk_tree_path_free (end_path);
1749 #endif /* GTK_CHECK_VERSION */
1752 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1753 g_list_free (selected);
1759 on_focus_out (GtkWidget *self,
1760 GdkEventFocus *event,
1764 if (!gtk_widget_is_focus (self)) {
1765 GtkTreeSelection *selection = NULL;
1766 GList *selected_rows = NULL;
1767 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1768 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1769 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1770 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1771 gtk_tree_selection_unselect_all (selection);
1772 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1773 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1774 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1775 g_list_free (selected_rows);
1782 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1784 enable_drag_and_drop(self);
1789 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1791 GtkTreeSelection *selection = NULL;
1792 GtkTreePath *path = NULL;
1793 gboolean already_selected = FALSE, already_opened = FALSE;
1794 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1796 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1798 GtkTreeModel *model;
1800 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1801 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1803 /* Get header from model */
1804 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1805 if (gtk_tree_model_get_iter (model, &iter, path)) {
1806 GValue value = {0,};
1809 gtk_tree_model_get_value (model, &iter,
1810 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1812 header = (TnyHeader *) g_value_get_object (&value);
1813 if (TNY_IS_HEADER (header)) {
1814 status = modest_tny_all_send_queues_get_msg_status (header);
1815 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1818 g_value_unset (&value);
1822 /* Enable drag and drop only if the user clicks on a row that
1823 it's already selected. If not, let him select items using
1824 the pointer. If the message is in an OUTBOX and in sending
1825 status disable drag and drop as well */
1826 if (!already_selected ||
1827 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1829 disable_drag_and_drop(self);
1832 gtk_tree_path_free(path);
1834 /* If it's already opened then do not let the button-press
1835 event go on because it'll perform a message open because
1836 we're clicking on to an already selected header */
1841 folder_monitor_update (TnyFolderObserver *self,
1842 TnyFolderChange *change)
1844 ModestHeaderViewPrivate *priv = NULL;
1845 TnyFolderChangeChanged changed;
1846 TnyFolder *folder = NULL;
1848 changed = tny_folder_change_get_changed (change);
1850 /* Do not notify the observers if the folder of the header
1851 view has changed before this call to the observer
1853 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1854 folder = tny_folder_change_get_folder (change);
1855 if (folder != priv->folder)
1858 MODEST_DEBUG_BLOCK (
1859 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1860 g_print ("ADDED %d/%d (r/t) \n",
1861 tny_folder_change_get_new_unread_count (change),
1862 tny_folder_change_get_new_all_count (change));
1863 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1864 g_print ("ALL COUNT %d\n",
1865 tny_folder_change_get_new_all_count (change));
1866 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1867 g_print ("UNREAD COUNT %d\n",
1868 tny_folder_change_get_new_unread_count (change));
1869 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1870 g_print ("EXPUNGED %d/%d (r/t) \n",
1871 tny_folder_change_get_new_unread_count (change),
1872 tny_folder_change_get_new_all_count (change));
1873 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1874 g_print ("FOLDER RENAME\n");
1875 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1876 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1877 tny_folder_change_get_new_unread_count (change),
1878 tny_folder_change_get_new_all_count (change));
1879 g_print ("---------------------------------------------------\n");
1882 /* Check folder count */
1883 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1884 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1886 g_mutex_lock (priv->observers_lock);
1888 /* Emit signal to evaluate how headers changes affects
1889 to the window view */
1890 g_signal_emit (G_OBJECT(self),
1891 signals[MSG_COUNT_CHANGED_SIGNAL],
1894 /* Added or removed headers, so data stored on cliboard are invalid */
1895 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1896 modest_email_clipboard_clear (priv->clipboard);
1898 g_mutex_unlock (priv->observers_lock);
1904 g_object_unref (folder);
1908 modest_header_view_is_empty (ModestHeaderView *self)
1910 ModestHeaderViewPrivate *priv;
1912 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1914 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1916 return priv->status == HEADER_VIEW_EMPTY;
1920 modest_header_view_clear (ModestHeaderView *self)
1922 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1924 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1928 modest_header_view_copy_selection (ModestHeaderView *header_view)
1930 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1932 /* Copy selection */
1933 _clipboard_set_selected_data (header_view, FALSE);
1937 modest_header_view_cut_selection (ModestHeaderView *header_view)
1939 ModestHeaderViewPrivate *priv = NULL;
1940 const gchar **hidding = NULL;
1941 guint i, n_selected;
1943 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1945 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1947 /* Copy selection */
1948 _clipboard_set_selected_data (header_view, TRUE);
1950 /* Get hidding ids */
1951 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1953 /* Clear hidding array created by previous cut operation */
1954 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1956 /* Copy hidding array */
1957 priv->n_selected = n_selected;
1958 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1959 for (i=0; i < n_selected; i++)
1960 priv->hidding_ids[i] = g_strdup(hidding[i]);
1962 /* Hide cut headers */
1963 modest_header_view_refilter (header_view);
1970 _clipboard_set_selected_data (ModestHeaderView *header_view,
1973 ModestHeaderViewPrivate *priv = NULL;
1974 TnyList *headers = NULL;
1976 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1977 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1979 /* Set selected data on clipboard */
1980 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1981 headers = modest_header_view_get_selected_headers (header_view);
1982 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1985 g_object_unref (headers);
1989 ModestHeaderView *self;
1994 notify_filter_change (gpointer data)
1996 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1998 g_signal_emit (info->self,
1999 signals[MSG_COUNT_CHANGED_SIGNAL],
2000 0, info->folder, NULL);
2006 notify_filter_change_destroy (gpointer data)
2008 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2009 ModestHeaderViewPrivate *priv;
2011 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2012 priv->status_timeout = 0;
2014 g_object_unref (info->self);
2015 g_object_unref (info->folder);
2016 g_slice_free (NotifyFilterInfo, info);
2020 filter_row (GtkTreeModel *model,
2024 ModestHeaderViewPrivate *priv = NULL;
2025 TnyHeaderFlags flags;
2026 TnyHeader *header = NULL;
2029 gboolean visible = TRUE;
2030 gboolean found = FALSE;
2031 GValue value = {0,};
2032 HeaderViewStatus old_status;
2034 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2035 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2037 /* Get header from model */
2038 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2039 flags = (TnyHeaderFlags) g_value_get_int (&value);
2040 g_value_unset (&value);
2041 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2042 header = (TnyHeader *) g_value_get_object (&value);
2043 g_value_unset (&value);
2045 /* Hide deleted and mark as deleted heders */
2046 if (flags & TNY_HEADER_FLAG_DELETED ||
2047 flags & TNY_HEADER_FLAG_EXPUNGED) {
2052 /* If no data on clipboard, return always TRUE */
2053 if (modest_email_clipboard_cleared(priv->clipboard)) {
2058 /* Get message id from header (ensure is a valid id) */
2065 if (priv->hidding_ids != NULL) {
2066 id = tny_header_dup_message_id (header);
2067 for (i=0; i < priv->n_selected && !found; i++)
2068 if (priv->hidding_ids[i] != NULL && id != NULL)
2069 found = (!strcmp (priv->hidding_ids[i], id));
2076 old_status = priv->status;
2077 priv->status = ((gboolean) priv->status) && !visible;
2078 if ((priv->notify_status) && (priv->status != old_status)) {
2079 NotifyFilterInfo *info;
2081 if (priv->status_timeout)
2082 g_source_remove (priv->status_timeout);
2084 info = g_slice_new0 (NotifyFilterInfo);
2085 info->self = g_object_ref (G_OBJECT (user_data));
2086 info->folder = tny_header_get_folder (header);
2087 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2088 notify_filter_change,
2090 notify_filter_change_destroy);
2097 _clear_hidding_filter (ModestHeaderView *header_view)
2099 ModestHeaderViewPrivate *priv = NULL;
2102 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2103 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2105 if (priv->hidding_ids != NULL) {
2106 for (i=0; i < priv->n_selected; i++)
2107 g_free (priv->hidding_ids[i]);
2108 g_free(priv->hidding_ids);
2113 modest_header_view_refilter (ModestHeaderView *header_view)
2115 GtkTreeModel *model = NULL;
2116 ModestHeaderViewPrivate *priv = NULL;
2118 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2119 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2121 /* Hide cut headers */
2122 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2123 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2124 priv->status = HEADER_VIEW_INIT;
2125 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2130 * Called when an account is removed. If I'm showing a folder of the
2131 * account that has been removed then clear the view
2134 on_account_removed (TnyAccountStore *self,
2135 TnyAccount *account,
2138 ModestHeaderViewPrivate *priv = NULL;
2140 /* Ignore changes in transport accounts */
2141 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2144 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2147 TnyAccount *my_account;
2149 my_account = tny_folder_get_account (priv->folder);
2150 if (my_account == account)
2151 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2152 g_object_unref (my_account);
2157 modest_header_view_add_observer(ModestHeaderView *header_view,
2158 ModestHeaderViewObserver *observer)
2160 ModestHeaderViewPrivate *priv;
2162 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2163 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2165 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2167 g_mutex_lock(priv->observer_list_lock);
2168 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2169 g_mutex_unlock(priv->observer_list_lock);
2173 modest_header_view_remove_observer(ModestHeaderView *header_view,
2174 ModestHeaderViewObserver *observer)
2176 ModestHeaderViewPrivate *priv;
2178 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2179 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2181 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2183 g_mutex_lock(priv->observer_list_lock);
2184 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2185 g_mutex_unlock(priv->observer_list_lock);
2189 modest_header_view_notify_observers(ModestHeaderView *header_view,
2190 GtkTreeModel *model,
2191 const gchar *tny_folder_id)
2193 ModestHeaderViewPrivate *priv = NULL;
2195 ModestHeaderViewObserver *observer;
2198 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2200 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2202 g_mutex_lock(priv->observer_list_lock);
2203 iter = priv->observer_list;
2204 while(iter != NULL){
2205 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2206 modest_header_view_observer_update(observer, model,
2208 iter = g_slist_next(iter);
2210 g_mutex_unlock(priv->observer_list_lock);