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 /* Add IDLE observer (monitor) and another folder observer for
1007 new messages (self) */
1008 g_mutex_lock (priv->observers_lock);
1009 if (priv->monitor) {
1010 tny_folder_monitor_stop (priv->monitor);
1011 g_object_unref (G_OBJECT (priv->monitor));
1013 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1014 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1015 tny_folder_monitor_start (priv->monitor);
1016 g_mutex_unlock (priv->observers_lock);
1020 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1024 ModestHeaderViewPrivate *priv;
1025 GList *cols, *cursor;
1026 GtkTreeModel *filter_model, *sortable;
1028 GtkSortType sort_type;
1030 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1032 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1034 /* Start the monitor in the callback of the
1035 tny_gtk_header_list_model_set_folder call. It's crucial to
1036 do it there and not just after the call because we want the
1037 monitor to observe only the headers returned by the
1038 tny_folder_get_headers_async call that it's inside the
1039 tny_gtk_header_list_model_set_folder call. This way the
1040 monitor infrastructure could successfully cope with
1041 duplicates. For example if a tny_folder_add_msg_async is
1042 happening while tny_gtk_header_list_model_set_folder is
1043 invoked, then the first call could add a header that will
1044 be added again by tny_gtk_header_list_model_set_folder, so
1045 we'd end up with duplicate headers. sergio */
1046 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1048 set_folder_intern_get_headers_async_cb,
1051 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1052 g_object_unref (G_OBJECT (headers));
1054 /* Init filter_row function to examine empty status */
1055 priv->status = HEADER_VIEW_INIT;
1057 /* Create a tree model filter to hide and show rows for cut operations */
1058 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1059 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1063 g_object_unref (G_OBJECT (sortable));
1065 /* install our special sorting functions */
1066 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1068 /* Restore sort column id */
1070 type = modest_tny_folder_guess_folder_type (folder);
1071 if (type == TNY_FOLDER_TYPE_INVALID)
1072 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1074 sort_colid = modest_header_view_get_sort_column_id (self, type);
1075 sort_type = modest_header_view_get_sort_type (self, type);
1076 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1079 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1080 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1081 (GtkTreeIterCompareFunc) cmp_rows,
1083 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1084 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1085 (GtkTreeIterCompareFunc) cmp_subject_rows,
1090 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1091 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1092 tny_folder_get_id(folder));
1093 g_object_unref (G_OBJECT (filter_model));
1094 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1095 /* g_object_unref (G_OBJECT (sortable)); */
1102 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1104 GtkSortType sort_type)
1106 ModestHeaderViewPrivate *priv = NULL;
1107 GtkTreeModel *tree_filter, *sortable = NULL;
1110 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1111 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1113 /* Get model and private data */
1114 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1115 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1116 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1117 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1119 /* Sort tree model */
1120 type = modest_tny_folder_guess_folder_type (priv->folder);
1121 if (type == TNY_FOLDER_TYPE_INVALID)
1122 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1124 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1127 /* Store new sort parameters */
1128 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1133 modest_header_view_set_sort_params (ModestHeaderView *self,
1135 GtkSortType sort_type,
1138 ModestHeaderViewPrivate *priv;
1139 ModestHeaderViewStyle style;
1141 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1142 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1143 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1145 style = modest_header_view_get_style (self);
1146 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1148 priv->sort_colid[style][type] = sort_colid;
1149 priv->sort_type[style][type] = sort_type;
1153 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1156 ModestHeaderViewPrivate *priv;
1157 ModestHeaderViewStyle style;
1159 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1160 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1162 style = modest_header_view_get_style (self);
1163 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1165 return priv->sort_colid[style][type];
1169 modest_header_view_get_sort_type (ModestHeaderView *self,
1172 ModestHeaderViewPrivate *priv;
1173 ModestHeaderViewStyle style;
1175 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1176 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1178 style = modest_header_view_get_style (self);
1179 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1181 return priv->sort_type[style][type];
1185 ModestHeaderView *header_view;
1186 RefreshAsyncUserCallback cb;
1191 folder_refreshed_cb (ModestMailOperation *mail_op,
1195 ModestHeaderViewPrivate *priv;
1196 SetFolderHelper *info;
1198 info = (SetFolderHelper*) user_data;
1200 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1204 info->cb (mail_op, folder, info->user_data);
1206 /* Start the folder count changes observer. We do not need it
1207 before the refresh. Note that the monitor could still be
1208 called for this refresh but now we know that the callback
1209 was previously called */
1210 g_mutex_lock (priv->observers_lock);
1211 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1212 g_mutex_unlock (priv->observers_lock);
1214 /* Notify the observers that the update is over */
1215 g_signal_emit (G_OBJECT (info->header_view),
1216 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1218 /* Allow filtering notifications from now on if the current
1219 folder is still the same (if not then the user has selected
1220 another one to refresh, we should wait until that refresh
1222 if (priv->folder == folder)
1223 priv->notify_status = TRUE;
1226 g_object_unref (info->header_view);
1231 refresh_folder_error_handler (ModestMailOperation *mail_op,
1234 const GError *error = modest_mail_operation_get_error (mail_op);
1236 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1237 error->code == TNY_IO_ERROR_WRITE ||
1238 error->code == TNY_IO_ERROR_READ) {
1239 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1240 /* If the mail op has been cancelled then it's not an error: don't show any message */
1241 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1242 modest_platform_information_banner (NULL, NULL,
1244 "cerm_device_memory_full"));
1250 modest_header_view_set_folder (ModestHeaderView *self,
1252 RefreshAsyncUserCallback callback,
1255 ModestHeaderViewPrivate *priv;
1256 SetFolderHelper *info;
1257 ModestWindow *main_win;
1259 g_return_if_fail (self);
1261 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1263 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1264 FALSE); /* don't create */
1266 g_warning ("%s: BUG: no main window", __FUNCTION__);
1271 if (priv->status_timeout) {
1272 g_source_remove (priv->status_timeout);
1273 priv->status_timeout = 0;
1276 g_mutex_lock (priv->observers_lock);
1277 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1278 g_object_unref (priv->folder);
1279 priv->folder = NULL;
1280 g_mutex_unlock (priv->observers_lock);
1284 ModestMailOperation *mail_op = NULL;
1285 GtkTreeSelection *selection;
1287 /* Set folder in the model */
1288 modest_header_view_set_folder_intern (self, folder);
1290 /* Pick my reference. Nothing to do with the mail operation */
1291 priv->folder = g_object_ref (folder);
1293 /* Do not notify about filterings until the refresh finishes */
1294 priv->notify_status = FALSE;
1296 /* Clear the selection if exists */
1297 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1298 gtk_tree_selection_unselect_all(selection);
1299 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1301 /* Notify the observers that the update begins */
1302 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1305 /* create the helper */
1306 info = g_malloc0 (sizeof(SetFolderHelper));
1307 info->header_view = g_object_ref (self);
1308 info->cb = callback;
1309 info->user_data = user_data;
1311 /* Create the mail operation (source will be the parent widget) */
1312 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1313 refresh_folder_error_handler,
1315 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1318 /* Refresh the folder asynchronously */
1319 modest_mail_operation_refresh_folder (mail_op,
1321 folder_refreshed_cb,
1325 g_object_unref (mail_op);
1327 g_mutex_lock (priv->observers_lock);
1329 if (priv->monitor) {
1330 tny_folder_monitor_stop (priv->monitor);
1331 g_object_unref (G_OBJECT (priv->monitor));
1332 priv->monitor = NULL;
1334 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1336 modest_header_view_notify_observers(self, NULL, NULL);
1338 g_mutex_unlock (priv->observers_lock);
1340 /* Notify the observers that the update is over */
1341 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1347 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1348 GtkTreeViewColumn *column, gpointer userdata)
1350 ModestHeaderView *self = NULL;
1351 ModestHeaderViewPrivate *priv = NULL;
1353 GtkTreeModel *model = NULL;
1354 TnyHeader *header = NULL;
1355 TnyHeaderFlags flags;
1357 self = MODEST_HEADER_VIEW (treeview);
1358 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1360 model = gtk_tree_view_get_model (treeview);
1361 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1364 /* get the first selected item */
1365 gtk_tree_model_get (model, &iter,
1366 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1367 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1370 /* Dont open DELETED messages */
1371 if (flags & TNY_HEADER_FLAG_DELETED) {
1374 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1375 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1376 modest_platform_information_banner (NULL, NULL, msg);
1382 g_signal_emit (G_OBJECT(self),
1383 signals[HEADER_ACTIVATED_SIGNAL],
1389 g_object_unref (G_OBJECT (header));
1394 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1396 GtkTreeModel *model;
1397 TnyHeader *header = NULL;
1398 GtkTreePath *path = NULL;
1400 ModestHeaderView *self;
1401 ModestHeaderViewPrivate *priv;
1402 GList *selected = NULL;
1404 g_return_if_fail (sel);
1405 g_return_if_fail (user_data);
1407 self = MODEST_HEADER_VIEW (user_data);
1408 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1410 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1411 if (selected != NULL)
1412 path = (GtkTreePath *) selected->data;
1413 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1414 return; /* msg was _un_selected */
1416 gtk_tree_model_get (model, &iter,
1417 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1421 g_signal_emit (G_OBJECT(self),
1422 signals[HEADER_SELECTED_SIGNAL],
1425 g_object_unref (G_OBJECT (header));
1427 /* free all items in 'selected' */
1428 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1429 g_list_free (selected);
1433 /* PROTECTED method. It's useful when we want to force a given
1434 selection to reload a msg. For example if we have selected a header
1435 in offline mode, when Modest become online, we want to reload the
1436 message automatically without an user click over the header */
1438 _modest_header_view_change_selection (GtkTreeSelection *selection,
1441 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1442 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1444 on_selection_changed (selection, user_data);
1448 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1455 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1459 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1463 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1471 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1478 /* static int counter = 0; */
1480 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1481 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1482 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1486 case TNY_HEADER_FLAG_ATTACHMENTS:
1488 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1489 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1490 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1491 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1493 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1494 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1496 return cmp ? cmp : t1 - t2;
1498 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1499 TnyHeader *header1 = NULL, *header2 = NULL;
1501 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1502 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1503 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1504 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1506 /* This is for making priority values respect the intuitive sort relationship
1507 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1509 if (header1 && header2) {
1510 cmp = compare_priorities (tny_header_get_priority (header1),
1511 tny_header_get_priority (header2));
1512 g_object_unref (header1);
1513 g_object_unref (header2);
1515 return cmp ? cmp : t1 - t2;
1521 return &iter1 - &iter2; /* oughhhh */
1526 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1532 /* static int counter = 0; */
1534 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1536 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1537 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1538 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1539 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1541 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1542 val2 + modest_text_utils_get_subject_prefix_len(val2),
1549 /* Drag and drop stuff */
1551 drag_data_get_cb (GtkWidget *widget,
1552 GdkDragContext *context,
1553 GtkSelectionData *selection_data,
1558 ModestHeaderView *self = NULL;
1559 ModestHeaderViewPrivate *priv = NULL;
1561 self = MODEST_HEADER_VIEW (widget);
1562 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1564 /* Set the data. Do not use the current selection because it
1565 could be different than the selection at the beginning of
1567 modest_dnd_selection_data_set_paths (selection_data,
1568 priv->drag_begin_cached_selected_rows);
1572 * We're caching the selected rows at the beginning because the
1573 * selection could change between drag-begin and drag-data-get, for
1574 * example if we have a set of rows already selected, and then we
1575 * click in one of them (without SHIFT key pressed) and begin a drag,
1576 * the selection at that moment contains all the selected lines, but
1577 * after dropping the selection, the release event provokes that only
1578 * the row used to begin the drag is selected, so at the end the
1579 * drag&drop affects only one rows instead of all the selected ones.
1583 drag_begin_cb (GtkWidget *widget,
1584 GdkDragContext *context,
1587 ModestHeaderView *self = NULL;
1588 ModestHeaderViewPrivate *priv = NULL;
1589 GtkTreeSelection *selection;
1591 self = MODEST_HEADER_VIEW (widget);
1592 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1594 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1595 priv->drag_begin_cached_selected_rows =
1596 gtk_tree_selection_get_selected_rows (selection, NULL);
1600 * We use the drag-end signal to clear the cached selection, we use
1601 * this because this allways happens, whether or not the d&d was a
1605 drag_end_cb (GtkWidget *widget,
1609 ModestHeaderView *self = NULL;
1610 ModestHeaderViewPrivate *priv = NULL;
1612 self = MODEST_HEADER_VIEW (widget);
1613 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1615 /* Free cached data */
1616 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1617 g_list_free (priv->drag_begin_cached_selected_rows);
1618 priv->drag_begin_cached_selected_rows = NULL;
1621 /* Header view drag types */
1622 const GtkTargetEntry header_view_drag_types[] = {
1623 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1627 enable_drag_and_drop (GtkWidget *self)
1629 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1630 header_view_drag_types,
1631 G_N_ELEMENTS (header_view_drag_types),
1632 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1636 disable_drag_and_drop (GtkWidget *self)
1638 gtk_drag_source_unset (self);
1642 setup_drag_and_drop (GtkWidget *self)
1644 enable_drag_and_drop(self);
1645 g_signal_connect(G_OBJECT (self), "drag_data_get",
1646 G_CALLBACK(drag_data_get_cb), NULL);
1648 g_signal_connect(G_OBJECT (self), "drag_begin",
1649 G_CALLBACK(drag_begin_cb), NULL);
1651 g_signal_connect(G_OBJECT (self), "drag_end",
1652 G_CALLBACK(drag_end_cb), NULL);
1655 static GtkTreePath *
1656 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1658 GtkTreePath *path = NULL;
1659 GtkTreeSelection *sel = NULL;
1662 sel = gtk_tree_view_get_selection(self);
1663 rows = gtk_tree_selection_get_selected_rows (sel, model);
1665 if ((rows == NULL) || (g_list_length(rows) != 1))
1668 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1673 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1680 * This function moves the tree view scroll to the current selected
1681 * row when the widget grabs the focus
1684 on_focus_in (GtkWidget *self,
1685 GdkEventFocus *event,
1688 GtkTreeSelection *selection;
1689 GtkTreeModel *model;
1690 GList *selected = NULL;
1691 GtkTreePath *selected_path = NULL;
1693 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1697 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1698 /* If none selected yet, pick the first one */
1699 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1703 /* Return if the model is empty */
1704 if (!gtk_tree_model_get_iter_first (model, &iter))
1707 path = gtk_tree_model_get_path (model, &iter);
1708 gtk_tree_selection_select_path (selection, path);
1709 gtk_tree_path_free (path);
1712 /* Need to get the all the rows because is selection multiple */
1713 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1714 if (selected == NULL) return FALSE;
1715 selected_path = (GtkTreePath *) selected->data;
1717 /* Check if we need to scroll */
1718 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1719 GtkTreePath *start_path = NULL;
1720 GtkTreePath *end_path = NULL;
1721 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1725 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1726 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1728 /* Scroll to first path */
1729 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1738 gtk_tree_path_free (start_path);
1740 gtk_tree_path_free (end_path);
1742 #endif /* GTK_CHECK_VERSION */
1745 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1746 g_list_free (selected);
1752 on_focus_out (GtkWidget *self,
1753 GdkEventFocus *event,
1757 if (!gtk_widget_is_focus (self)) {
1758 GtkTreeSelection *selection = NULL;
1759 GList *selected_rows = NULL;
1760 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1761 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1762 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1763 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1764 gtk_tree_selection_unselect_all (selection);
1765 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1766 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1767 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1768 g_list_free (selected_rows);
1775 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1777 enable_drag_and_drop(self);
1782 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1784 GtkTreeSelection *selection = NULL;
1785 GtkTreePath *path = NULL;
1786 gboolean already_selected = FALSE, already_opened = FALSE;
1787 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1789 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1791 GtkTreeModel *model;
1793 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1794 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1796 /* Get header from model */
1797 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1798 if (gtk_tree_model_get_iter (model, &iter, path)) {
1799 GValue value = {0,};
1802 gtk_tree_model_get_value (model, &iter,
1803 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1805 header = (TnyHeader *) g_value_get_object (&value);
1806 if (TNY_IS_HEADER (header)) {
1807 status = modest_tny_all_send_queues_get_msg_status (header);
1808 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1811 g_value_unset (&value);
1815 /* Enable drag and drop only if the user clicks on a row that
1816 it's already selected. If not, let him select items using
1817 the pointer. If the message is in an OUTBOX and in sending
1818 status disable drag and drop as well */
1819 if (!already_selected ||
1820 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1822 disable_drag_and_drop(self);
1825 gtk_tree_path_free(path);
1827 /* If it's already opened then do not let the button-press
1828 event go on because it'll perform a message open because
1829 we're clicking on to an already selected header */
1830 return (already_opened) ? TRUE : FALSE;
1834 folder_monitor_update (TnyFolderObserver *self,
1835 TnyFolderChange *change)
1837 ModestHeaderViewPrivate *priv = NULL;
1838 TnyFolderChangeChanged changed;
1839 TnyFolder *folder = NULL;
1841 changed = tny_folder_change_get_changed (change);
1843 /* Do not notify the observers if the folder of the header
1844 view has changed before this call to the observer
1846 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1847 folder = tny_folder_change_get_folder (change);
1848 if (folder != priv->folder)
1851 MODEST_DEBUG_BLOCK (
1852 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1853 g_print ("ADDED %d/%d (r/t) \n",
1854 tny_folder_change_get_new_unread_count (change),
1855 tny_folder_change_get_new_all_count (change));
1856 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1857 g_print ("ALL COUNT %d\n",
1858 tny_folder_change_get_new_all_count (change));
1859 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1860 g_print ("UNREAD COUNT %d\n",
1861 tny_folder_change_get_new_unread_count (change));
1862 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1863 g_print ("EXPUNGED %d/%d (r/t) \n",
1864 tny_folder_change_get_new_unread_count (change),
1865 tny_folder_change_get_new_all_count (change));
1866 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1867 g_print ("FOLDER RENAME\n");
1868 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1869 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1870 tny_folder_change_get_new_unread_count (change),
1871 tny_folder_change_get_new_all_count (change));
1872 g_print ("---------------------------------------------------\n");
1875 /* Check folder count */
1876 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1877 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1879 g_mutex_lock (priv->observers_lock);
1881 /* Emit signal to evaluate how headers changes affects
1882 to the window view */
1883 g_signal_emit (G_OBJECT(self),
1884 signals[MSG_COUNT_CHANGED_SIGNAL],
1887 /* Added or removed headers, so data stored on cliboard are invalid */
1888 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1889 modest_email_clipboard_clear (priv->clipboard);
1891 g_mutex_unlock (priv->observers_lock);
1897 g_object_unref (folder);
1901 modest_header_view_is_empty (ModestHeaderView *self)
1903 ModestHeaderViewPrivate *priv;
1905 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1907 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1909 return priv->status == HEADER_VIEW_EMPTY;
1913 modest_header_view_clear (ModestHeaderView *self)
1915 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1917 modest_header_view_set_folder (self, NULL, NULL, NULL);
1921 modest_header_view_copy_selection (ModestHeaderView *header_view)
1923 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1925 /* Copy selection */
1926 _clipboard_set_selected_data (header_view, FALSE);
1930 modest_header_view_cut_selection (ModestHeaderView *header_view)
1932 ModestHeaderViewPrivate *priv = NULL;
1933 const gchar **hidding = NULL;
1934 guint i, n_selected;
1936 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1938 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1940 /* Copy selection */
1941 _clipboard_set_selected_data (header_view, TRUE);
1943 /* Get hidding ids */
1944 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1946 /* Clear hidding array created by previous cut operation */
1947 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1949 /* Copy hidding array */
1950 priv->n_selected = n_selected;
1951 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1952 for (i=0; i < n_selected; i++)
1953 priv->hidding_ids[i] = g_strdup(hidding[i]);
1955 /* Hide cut headers */
1956 modest_header_view_refilter (header_view);
1963 _clipboard_set_selected_data (ModestHeaderView *header_view,
1966 ModestHeaderViewPrivate *priv = NULL;
1967 TnyList *headers = NULL;
1969 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1970 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1972 /* Set selected data on clipboard */
1973 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1974 headers = modest_header_view_get_selected_headers (header_view);
1975 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1978 g_object_unref (headers);
1982 ModestHeaderView *self;
1987 notify_filter_change (gpointer data)
1989 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
1991 g_signal_emit (info->self,
1992 signals[MSG_COUNT_CHANGED_SIGNAL],
1993 0, info->folder, NULL);
1999 notify_filter_change_destroy (gpointer data)
2001 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2002 ModestHeaderViewPrivate *priv;
2004 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2005 priv->status_timeout = 0;
2007 g_object_unref (info->self);
2008 g_object_unref (info->folder);
2009 g_slice_free (NotifyFilterInfo, info);
2013 filter_row (GtkTreeModel *model,
2017 ModestHeaderViewPrivate *priv = NULL;
2018 TnyHeaderFlags flags;
2019 TnyHeader *header = NULL;
2022 gboolean visible = TRUE;
2023 gboolean found = FALSE;
2024 GValue value = {0,};
2025 HeaderViewStatus old_status;
2027 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2028 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2030 /* Get header from model */
2031 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2032 flags = (TnyHeaderFlags) g_value_get_int (&value);
2033 g_value_unset (&value);
2034 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2035 header = (TnyHeader *) g_value_get_object (&value);
2036 g_value_unset (&value);
2038 /* Hide deleted and mark as deleted heders */
2039 if (flags & TNY_HEADER_FLAG_DELETED ||
2040 flags & TNY_HEADER_FLAG_EXPUNGED) {
2045 /* If no data on clipboard, return always TRUE */
2046 if (modest_email_clipboard_cleared(priv->clipboard)) {
2051 /* Get message id from header (ensure is a valid id) */
2058 if (priv->hidding_ids != NULL) {
2059 id = tny_header_dup_message_id (header);
2060 for (i=0; i < priv->n_selected && !found; i++)
2061 if (priv->hidding_ids[i] != NULL && id != NULL)
2062 found = (!strcmp (priv->hidding_ids[i], id));
2069 old_status = priv->status;
2070 priv->status = ((gboolean) priv->status) && !visible;
2071 if ((priv->notify_status) && (priv->status != old_status)) {
2072 NotifyFilterInfo *info;
2074 if (priv->status_timeout)
2075 g_source_remove (priv->status_timeout);
2077 info = g_slice_new0 (NotifyFilterInfo);
2078 info->self = g_object_ref (G_OBJECT (user_data));
2079 info->folder = tny_header_get_folder (header);
2080 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2081 notify_filter_change,
2083 notify_filter_change_destroy);
2090 _clear_hidding_filter (ModestHeaderView *header_view)
2092 ModestHeaderViewPrivate *priv = NULL;
2095 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2096 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2098 if (priv->hidding_ids != NULL) {
2099 for (i=0; i < priv->n_selected; i++)
2100 g_free (priv->hidding_ids[i]);
2101 g_free(priv->hidding_ids);
2106 modest_header_view_refilter (ModestHeaderView *header_view)
2108 GtkTreeModel *model = NULL;
2109 ModestHeaderViewPrivate *priv = NULL;
2111 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2112 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2114 /* Hide cut headers */
2115 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2116 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2117 priv->status = HEADER_VIEW_INIT;
2118 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2123 * Called when an account is removed. If I'm showing a folder of the
2124 * account that has been removed then clear the view
2127 on_account_removed (TnyAccountStore *self,
2128 TnyAccount *account,
2131 ModestHeaderViewPrivate *priv = NULL;
2133 /* Ignore changes in transport accounts */
2134 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2137 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2140 TnyAccount *my_account;
2142 my_account = tny_folder_get_account (priv->folder);
2143 if (my_account == account)
2144 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2145 g_object_unref (my_account);
2150 modest_header_view_add_observer(ModestHeaderView *header_view,
2151 ModestHeaderViewObserver *observer)
2153 ModestHeaderViewPrivate *priv;
2155 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2156 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2158 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2160 g_mutex_lock(priv->observer_list_lock);
2161 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2162 g_mutex_unlock(priv->observer_list_lock);
2166 modest_header_view_remove_observer(ModestHeaderView *header_view,
2167 ModestHeaderViewObserver *observer)
2169 ModestHeaderViewPrivate *priv;
2171 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2172 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2174 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2176 g_mutex_lock(priv->observer_list_lock);
2177 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2178 g_mutex_unlock(priv->observer_list_lock);
2182 modest_header_view_notify_observers(ModestHeaderView *header_view,
2183 GtkTreeModel *model,
2184 const gchar *tny_folder_id)
2186 ModestHeaderViewPrivate *priv = NULL;
2188 ModestHeaderViewObserver *observer;
2191 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2193 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2195 g_mutex_lock(priv->observer_list_lock);
2196 iter = priv->observer_list;
2197 while(iter != NULL){
2198 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2199 modest_header_view_observer_update(observer, model,
2201 iter = g_slist_next(iter);
2203 g_mutex_unlock(priv->observer_list_lock);