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-merge-folder.h>
36 #include <tny-error.h>
39 #include <modest-header-view.h>
40 #include <modest-header-view-priv.h>
41 #include <modest-dnd.h>
42 #include <modest-tny-folder.h>
43 #include <modest-debug.h>
44 #include <modest-main-window.h>
45 #include <modest-ui-actions.h>
46 #include <modest-marshal.h>
47 #include <modest-text-utils.h>
48 #include <modest-icon-names.h>
49 #include <modest-runtime.h>
50 #include "modest-platform.h"
51 #include <modest-hbox-cell-renderer.h>
52 #include <modest-vbox-cell-renderer.h>
54 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
55 static void modest_header_view_init (ModestHeaderView *obj);
56 static void modest_header_view_finalize (GObject *obj);
57 static void modest_header_view_dispose (GObject *obj);
59 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
60 GtkTreeViewColumn *column, gpointer userdata);
62 static gint cmp_rows (GtkTreeModel *tree_model,
67 static gint cmp_subject_rows (GtkTreeModel *tree_model,
72 static gboolean filter_row (GtkTreeModel *model,
76 static void on_account_removed (TnyAccountStore *self,
80 static void on_selection_changed (GtkTreeSelection *sel,
83 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
86 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
89 static void setup_drag_and_drop (GtkWidget *self);
91 static void enable_drag_and_drop (GtkWidget *self);
93 static void disable_drag_and_drop (GtkWidget *self);
95 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
97 static gboolean on_focus_in (GtkWidget *sef,
101 static gboolean on_focus_out (GtkWidget *self,
102 GdkEventFocus *event,
105 static void folder_monitor_update (TnyFolderObserver *self,
106 TnyFolderChange *change);
108 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
110 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
112 static void _clear_hidding_filter (ModestHeaderView *header_view);
114 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
116 const gchar *tny_folder_id);
118 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
119 GdkEventExpose *event,
123 HEADER_VIEW_NON_EMPTY,
128 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
129 struct _ModestHeaderViewPrivate {
131 ModestHeaderViewStyle style;
133 TnyFolderMonitor *monitor;
134 GMutex *observers_lock;
136 /*header-view-observer observer*/
137 GMutex *observer_list_lock;
138 GSList *observer_list;
140 /* not unref this object, its a singlenton */
141 ModestEmailClipboard *clipboard;
143 /* Filter tree model */
146 GtkTreeRowReference *autoselect_reference;
148 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
149 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
151 gulong selection_changed_handler;
152 gulong acc_removed_handler;
154 GList *drag_begin_cached_selected_rows;
156 HeaderViewStatus status;
157 guint status_timeout;
158 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
161 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
162 struct _HeadersCountChangedHelper {
163 ModestHeaderView *self;
164 TnyFolderChange *change;
168 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
169 MODEST_TYPE_HEADER_VIEW, \
170 ModestHeaderViewPrivate))
174 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
177 HEADER_SELECTED_SIGNAL,
178 HEADER_ACTIVATED_SIGNAL,
179 ITEM_NOT_FOUND_SIGNAL,
180 MSG_COUNT_CHANGED_SIGNAL,
181 UPDATING_MSG_LIST_SIGNAL,
186 static GObjectClass *parent_class = NULL;
188 /* uncomment the following if you have defined any signals */
189 static guint signals[LAST_SIGNAL] = {0};
192 modest_header_view_get_type (void)
194 static GType my_type = 0;
196 static const GTypeInfo my_info = {
197 sizeof(ModestHeaderViewClass),
198 NULL, /* base init */
199 NULL, /* base finalize */
200 (GClassInitFunc) modest_header_view_class_init,
201 NULL, /* class finalize */
202 NULL, /* class data */
203 sizeof(ModestHeaderView),
205 (GInstanceInitFunc) modest_header_view_init,
209 static const GInterfaceInfo tny_folder_observer_info =
211 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
212 NULL, /* interface_finalize */
213 NULL /* interface_data */
215 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
219 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
220 &tny_folder_observer_info);
228 modest_header_view_class_init (ModestHeaderViewClass *klass)
230 GObjectClass *gobject_class;
231 gobject_class = (GObjectClass*) klass;
233 parent_class = g_type_class_peek_parent (klass);
234 gobject_class->finalize = modest_header_view_finalize;
235 gobject_class->dispose = modest_header_view_dispose;
237 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
239 signals[HEADER_SELECTED_SIGNAL] =
240 g_signal_new ("header_selected",
241 G_TYPE_FROM_CLASS (gobject_class),
243 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
245 g_cclosure_marshal_VOID__POINTER,
246 G_TYPE_NONE, 1, G_TYPE_POINTER);
248 signals[HEADER_ACTIVATED_SIGNAL] =
249 g_signal_new ("header_activated",
250 G_TYPE_FROM_CLASS (gobject_class),
252 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
254 g_cclosure_marshal_VOID__POINTER,
255 G_TYPE_NONE, 1, G_TYPE_POINTER);
258 signals[ITEM_NOT_FOUND_SIGNAL] =
259 g_signal_new ("item_not_found",
260 G_TYPE_FROM_CLASS (gobject_class),
262 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
264 g_cclosure_marshal_VOID__INT,
265 G_TYPE_NONE, 1, G_TYPE_INT);
267 signals[MSG_COUNT_CHANGED_SIGNAL] =
268 g_signal_new ("msg_count_changed",
269 G_TYPE_FROM_CLASS (gobject_class),
271 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
273 modest_marshal_VOID__POINTER_POINTER,
274 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
276 signals[UPDATING_MSG_LIST_SIGNAL] =
277 g_signal_new ("updating-msg-list",
278 G_TYPE_FROM_CLASS (gobject_class),
280 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
282 g_cclosure_marshal_VOID__BOOLEAN,
283 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
287 tny_folder_observer_init (TnyFolderObserverIface *klass)
289 klass->update = folder_monitor_update;
292 static GtkTreeViewColumn*
293 get_new_column (const gchar *name, GtkCellRenderer *renderer,
294 gboolean resizable, gint sort_col_id, gboolean show_as_text,
295 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
297 GtkTreeViewColumn *column;
299 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
300 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
302 gtk_tree_view_column_set_resizable (column, resizable);
304 gtk_tree_view_column_set_expand (column, TRUE);
307 gtk_tree_view_column_add_attribute (column, renderer, "text",
309 if (sort_col_id >= 0)
310 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
312 gtk_tree_view_column_set_sort_indicator (column, FALSE);
313 gtk_tree_view_column_set_reorderable (column, TRUE);
316 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
323 remove_all_columns (ModestHeaderView *obj)
325 GList *columns, *cursor;
327 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
329 for (cursor = columns; cursor; cursor = cursor->next)
330 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
331 GTK_TREE_VIEW_COLUMN(cursor->data));
332 g_list_free (columns);
336 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
338 GtkTreeModel *tree_filter, *sortable;
339 GtkTreeViewColumn *column=NULL;
340 GtkTreeSelection *selection = NULL;
341 GtkCellRenderer *renderer_msgtype,*renderer_header,
342 *renderer_attach, *renderer_compact_date_or_status;
343 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
344 *renderer_subject, *renderer_subject_box, *renderer_recpt,
346 ModestHeaderViewPrivate *priv;
347 GtkTreeViewColumn *compact_column = NULL;
350 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
351 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
353 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
355 /* FIXME: check whether these renderers need to be freed */
356 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
357 renderer_attach = gtk_cell_renderer_pixbuf_new ();
358 renderer_priority = gtk_cell_renderer_pixbuf_new ();
359 renderer_header = gtk_cell_renderer_text_new ();
361 renderer_compact_header = modest_vbox_cell_renderer_new ();
362 renderer_recpt_box = modest_hbox_cell_renderer_new ();
363 renderer_subject_box = modest_hbox_cell_renderer_new ();
364 renderer_recpt = gtk_cell_renderer_text_new ();
365 renderer_subject = gtk_cell_renderer_text_new ();
366 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
368 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
369 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
370 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
371 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
372 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
373 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
374 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
375 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
376 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
377 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
378 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
379 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
380 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
381 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
383 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
384 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 ();
566 priv->autoselect_reference = NULL;
568 priv->status = HEADER_VIEW_INIT;
569 priv->status_timeout = 0;
570 priv->notify_status = TRUE;
572 priv->observer_list_lock = g_mutex_new();
573 priv->observer_list = NULL;
575 priv->clipboard = modest_runtime_get_email_clipboard ();
576 priv->hidding_ids = NULL;
577 priv->n_selected = 0;
578 priv->selection_changed_handler = 0;
579 priv->acc_removed_handler = 0;
581 /* Sort parameters */
582 for (j=0; j < 2; j++) {
583 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
584 priv->sort_colid[j][i] = -1;
585 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
589 setup_drag_and_drop (GTK_WIDGET(obj));
593 modest_header_view_dispose (GObject *obj)
595 ModestHeaderView *self;
596 ModestHeaderViewPrivate *priv;
597 GtkTreeSelection *sel;
599 self = MODEST_HEADER_VIEW(obj);
600 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
602 /* Free in the dispose to avoid unref cycles */
604 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
605 g_object_unref (G_OBJECT (priv->folder));
609 /* We need to do this here in the dispose because the
610 selection won't exist when finalizing */
611 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
612 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
613 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
614 priv->selection_changed_handler = 0;
617 G_OBJECT_CLASS(parent_class)->dispose (obj);
621 modest_header_view_finalize (GObject *obj)
623 ModestHeaderView *self;
624 ModestHeaderViewPrivate *priv;
626 self = MODEST_HEADER_VIEW(obj);
627 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
629 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
630 priv->acc_removed_handler)) {
631 g_signal_handler_disconnect (modest_runtime_get_account_store (),
632 priv->acc_removed_handler);
635 /* There is no need to lock because there should not be any
636 * reference to self now. */
637 g_mutex_free(priv->observer_list_lock);
638 g_slist_free(priv->observer_list);
640 g_mutex_lock (priv->observers_lock);
642 tny_folder_monitor_stop (priv->monitor);
643 g_object_unref (G_OBJECT (priv->monitor));
645 g_mutex_unlock (priv->observers_lock);
646 g_mutex_free (priv->observers_lock);
648 /* Clear hidding array created by cut operation */
649 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
651 if (priv->autoselect_reference != NULL) {
652 gtk_tree_row_reference_free (priv->autoselect_reference);
653 priv->autoselect_reference = NULL;
656 G_OBJECT_CLASS(parent_class)->finalize (obj);
661 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
664 GtkTreeSelection *sel;
665 ModestHeaderView *self;
666 ModestHeaderViewPrivate *priv;
668 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
671 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
672 self = MODEST_HEADER_VIEW(obj);
673 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
675 modest_header_view_set_style (self, style);
677 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
678 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
679 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
681 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
682 TRUE); /* alternating row colors */
684 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
685 priv->selection_changed_handler =
686 g_signal_connect_after (sel, "changed",
687 G_CALLBACK(on_selection_changed), self);
689 g_signal_connect (self, "row-activated",
690 G_CALLBACK (on_header_row_activated), NULL);
692 g_signal_connect (self, "focus-in-event",
693 G_CALLBACK(on_focus_in), NULL);
694 g_signal_connect (self, "focus-out-event",
695 G_CALLBACK(on_focus_out), NULL);
697 g_signal_connect (self, "button-press-event",
698 G_CALLBACK(on_button_press_event), NULL);
699 g_signal_connect (self, "button-release-event",
700 G_CALLBACK(on_button_release_event), NULL);
702 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
704 G_CALLBACK (on_account_removed),
707 g_signal_connect (self, "expose-event",
708 G_CALLBACK(modest_header_view_on_expose_event),
711 return GTK_WIDGET(self);
716 modest_header_view_count_selected_headers (ModestHeaderView *self)
718 GtkTreeSelection *sel;
721 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
723 /* Get selection object and check selected rows count */
724 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
725 selected_rows = gtk_tree_selection_count_selected_rows (sel);
727 return selected_rows;
731 modest_header_view_has_selected_headers (ModestHeaderView *self)
733 GtkTreeSelection *sel;
736 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
738 /* Get selection object and check selected rows count */
739 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
740 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
747 modest_header_view_get_selected_headers (ModestHeaderView *self)
749 GtkTreeSelection *sel;
750 ModestHeaderViewPrivate *priv;
751 TnyList *header_list = NULL;
753 GList *list, *tmp = NULL;
754 GtkTreeModel *tree_model = NULL;
757 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
759 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
761 /* Get selected rows */
762 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
763 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
766 header_list = tny_simple_list_new();
768 list = g_list_reverse (list);
771 /* get header from selection */
772 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
773 gtk_tree_model_get (tree_model, &iter,
774 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
776 /* Prepend to list */
777 tny_list_prepend (header_list, G_OBJECT (header));
778 g_object_unref (G_OBJECT (header));
780 tmp = g_list_next (tmp);
783 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
790 /* scroll our list view so the selected item is visible */
792 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
794 #ifdef MODEST_PLATFORM_GNOME
796 GtkTreePath *selected_path;
797 GtkTreePath *start, *end;
801 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
802 selected_path = gtk_tree_model_get_path (model, iter);
804 start = gtk_tree_path_new ();
805 end = gtk_tree_path_new ();
807 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
809 if (gtk_tree_path_compare (selected_path, start) < 0 ||
810 gtk_tree_path_compare (end, selected_path) < 0)
811 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
812 selected_path, NULL, TRUE,
815 gtk_tree_path_free (selected_path);
816 gtk_tree_path_free (start);
817 gtk_tree_path_free (end);
819 #endif /* MODEST_PLATFORM_GNOME */
824 modest_header_view_select_next (ModestHeaderView *self)
826 GtkTreeSelection *sel;
831 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
833 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
834 path = get_selected_row (GTK_TREE_VIEW(self), &model);
835 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
836 /* Unselect previous path */
837 gtk_tree_selection_unselect_path (sel, path);
839 /* Move path down and selects new one */
840 if (gtk_tree_model_iter_next (model, &iter)) {
841 gtk_tree_selection_select_iter (sel, &iter);
842 scroll_to_selected (self, &iter, FALSE);
844 gtk_tree_path_free(path);
850 modest_header_view_select_prev (ModestHeaderView *self)
852 GtkTreeSelection *sel;
857 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
859 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
860 path = get_selected_row (GTK_TREE_VIEW(self), &model);
861 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
862 /* Unselect previous path */
863 gtk_tree_selection_unselect_path (sel, path);
866 if (gtk_tree_path_prev (path)) {
867 gtk_tree_model_get_iter (model, &iter, path);
869 /* Select the new one */
870 gtk_tree_selection_select_iter (sel, &iter);
871 scroll_to_selected (self, &iter, TRUE);
874 gtk_tree_path_free (path);
879 modest_header_view_get_columns (ModestHeaderView *self)
881 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
883 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
889 modest_header_view_set_style (ModestHeaderView *self,
890 ModestHeaderViewStyle style)
892 ModestHeaderViewPrivate *priv;
893 gboolean show_col_headers = FALSE;
894 ModestHeaderViewStyle old_style;
896 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
897 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
900 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
901 if (priv->style == style)
902 return TRUE; /* nothing to do */
905 case MODEST_HEADER_VIEW_STYLE_DETAILS:
906 show_col_headers = TRUE;
908 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
911 g_return_val_if_reached (FALSE);
913 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
914 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
916 old_style = priv->style;
923 ModestHeaderViewStyle
924 modest_header_view_get_style (ModestHeaderView *self)
926 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
928 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
931 /* This is used to automatically select the first header if the user
932 * has not selected any header yet.
935 modest_header_view_on_expose_event(GtkTreeView *header_view,
936 GdkEventExpose *event,
939 GtkTreeSelection *sel;
941 GtkTreeIter tree_iter;
942 ModestHeaderViewPrivate *priv;
944 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
945 model = gtk_tree_view_get_model(header_view);
950 sel = gtk_tree_view_get_selection(header_view);
951 if(!gtk_tree_selection_count_selected_rows(sel)) {
952 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
953 GtkTreePath *tree_iter_path;
954 /* Prevent the widget from getting the focus
955 when selecting the first item */
956 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
957 g_object_set(header_view, "can-focus", FALSE, NULL);
958 gtk_tree_selection_select_iter(sel, &tree_iter);
959 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
960 g_object_set(header_view, "can-focus", TRUE, NULL);
961 if (priv->autoselect_reference) {
962 gtk_tree_row_reference_free (priv->autoselect_reference);
964 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
965 gtk_tree_path_free (tree_iter_path);
968 if (priv->autoselect_reference != NULL) {
969 gboolean moved_selection = FALSE;
970 GtkTreePath * last_path;
971 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
972 moved_selection = TRUE;
976 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
977 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
978 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
979 moved_selection = TRUE;
980 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
983 if (moved_selection) {
984 gtk_tree_row_reference_free (priv->autoselect_reference);
985 priv->autoselect_reference = NULL;
988 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
989 GtkTreePath *current_path;
990 current_path = gtk_tree_model_get_path (model, &tree_iter);
991 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
992 if (gtk_tree_path_compare (current_path, last_path) != 0) {
993 g_object_set(header_view, "can-focus", FALSE, NULL);
994 gtk_tree_selection_unselect_all (sel);
995 gtk_tree_selection_select_iter(sel, &tree_iter);
996 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
997 g_object_set(header_view, "can-focus", TRUE, NULL);
998 gtk_tree_row_reference_free (priv->autoselect_reference);
999 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1001 gtk_tree_path_free (current_path);
1002 gtk_tree_path_free (last_path);
1012 * This function sets a sortable model in the header view. It's just
1013 * used for developing purposes, because it only does a
1014 * gtk_tree_view_set_model
1017 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
1019 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
1020 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
1021 /* GtkTreeModel *old_model; */
1022 /* ModestHeaderViewPrivate *priv; */
1023 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
1024 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
1026 /* /\* Set new model *\/ */
1027 /* gtk_tree_view_set_model (header_view, model); */
1029 gtk_tree_view_set_model (header_view, model);
1033 modest_header_view_get_folder (ModestHeaderView *self)
1035 ModestHeaderViewPrivate *priv;
1037 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1039 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1042 g_object_ref (priv->folder);
1044 return priv->folder;
1048 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1054 ModestHeaderView *self;
1055 ModestHeaderViewPrivate *priv;
1057 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1059 self = MODEST_HEADER_VIEW (user_data);
1060 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1062 if (cancelled || err)
1065 /* Add IDLE observer (monitor) and another folder observer for
1066 new messages (self) */
1067 g_mutex_lock (priv->observers_lock);
1068 if (priv->monitor) {
1069 tny_folder_monitor_stop (priv->monitor);
1070 g_object_unref (G_OBJECT (priv->monitor));
1072 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1073 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1074 tny_folder_monitor_start (priv->monitor);
1075 g_mutex_unlock (priv->observers_lock);
1079 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1083 ModestHeaderViewPrivate *priv;
1084 GList *cols, *cursor;
1085 GtkTreeModel *filter_model, *sortable;
1087 GtkSortType sort_type;
1089 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1091 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1093 /* Start the monitor in the callback of the
1094 tny_gtk_header_list_model_set_folder call. It's crucial to
1095 do it there and not just after the call because we want the
1096 monitor to observe only the headers returned by the
1097 tny_folder_get_headers_async call that it's inside the
1098 tny_gtk_header_list_model_set_folder call. This way the
1099 monitor infrastructure could successfully cope with
1100 duplicates. For example if a tny_folder_add_msg_async is
1101 happening while tny_gtk_header_list_model_set_folder is
1102 invoked, then the first call could add a header that will
1103 be added again by tny_gtk_header_list_model_set_folder, so
1104 we'd end up with duplicate headers. sergio */
1105 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1107 set_folder_intern_get_headers_async_cb,
1110 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1111 g_object_unref (G_OBJECT (headers));
1113 /* Init filter_row function to examine empty status */
1114 priv->status = HEADER_VIEW_INIT;
1116 /* Create a tree model filter to hide and show rows for cut operations */
1117 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1118 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1122 g_object_unref (G_OBJECT (sortable));
1124 /* install our special sorting functions */
1125 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1127 /* Restore sort column id */
1129 type = modest_tny_folder_guess_folder_type (folder);
1130 if (type == TNY_FOLDER_TYPE_INVALID)
1131 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1133 sort_colid = modest_header_view_get_sort_column_id (self, type);
1134 sort_type = modest_header_view_get_sort_type (self, type);
1135 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1138 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1139 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1140 (GtkTreeIterCompareFunc) cmp_rows,
1142 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1143 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1144 (GtkTreeIterCompareFunc) cmp_subject_rows,
1149 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1150 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1151 tny_folder_get_id(folder));
1152 g_object_unref (G_OBJECT (filter_model));
1153 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1154 /* g_object_unref (G_OBJECT (sortable)); */
1161 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1163 GtkSortType sort_type)
1165 ModestHeaderViewPrivate *priv = NULL;
1166 GtkTreeModel *tree_filter, *sortable = NULL;
1169 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1170 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1172 /* Get model and private data */
1173 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1174 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1175 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1176 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1178 /* Sort tree model */
1179 type = modest_tny_folder_guess_folder_type (priv->folder);
1180 if (type == TNY_FOLDER_TYPE_INVALID)
1181 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1183 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1186 /* Store new sort parameters */
1187 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1192 modest_header_view_set_sort_params (ModestHeaderView *self,
1194 GtkSortType sort_type,
1197 ModestHeaderViewPrivate *priv;
1198 ModestHeaderViewStyle style;
1200 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1201 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1202 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1204 style = modest_header_view_get_style (self);
1205 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1207 priv->sort_colid[style][type] = sort_colid;
1208 priv->sort_type[style][type] = sort_type;
1212 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1215 ModestHeaderViewPrivate *priv;
1216 ModestHeaderViewStyle style;
1218 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1219 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1221 style = modest_header_view_get_style (self);
1222 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1224 return priv->sort_colid[style][type];
1228 modest_header_view_get_sort_type (ModestHeaderView *self,
1231 ModestHeaderViewPrivate *priv;
1232 ModestHeaderViewStyle style;
1234 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1235 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1237 style = modest_header_view_get_style (self);
1238 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1240 return priv->sort_type[style][type];
1244 ModestHeaderView *header_view;
1245 RefreshAsyncUserCallback cb;
1250 folder_refreshed_cb (ModestMailOperation *mail_op,
1254 ModestHeaderViewPrivate *priv;
1255 SetFolderHelper *info;
1257 info = (SetFolderHelper*) user_data;
1259 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1263 info->cb (mail_op, folder, info->user_data);
1265 /* Start the folder count changes observer. We do not need it
1266 before the refresh. Note that the monitor could still be
1267 called for this refresh but now we know that the callback
1268 was previously called */
1269 g_mutex_lock (priv->observers_lock);
1270 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1271 g_mutex_unlock (priv->observers_lock);
1273 /* Notify the observers that the update is over */
1274 g_signal_emit (G_OBJECT (info->header_view),
1275 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1277 /* Allow filtering notifications from now on if the current
1278 folder is still the same (if not then the user has selected
1279 another one to refresh, we should wait until that refresh
1281 if (priv->folder == folder)
1282 priv->notify_status = TRUE;
1285 g_object_unref (info->header_view);
1290 refresh_folder_error_handler (ModestMailOperation *mail_op,
1293 const GError *error = modest_mail_operation_get_error (mail_op);
1295 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1296 error->code == TNY_IO_ERROR_WRITE ||
1297 error->code == TNY_IO_ERROR_READ) {
1298 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1299 /* If the mail op has been cancelled then it's not an error: don't show any message */
1300 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1301 modest_platform_information_banner (NULL, NULL,
1303 "cerm_device_memory_full"));
1309 modest_header_view_set_folder (ModestHeaderView *self,
1312 RefreshAsyncUserCallback callback,
1315 ModestHeaderViewPrivate *priv;
1316 ModestWindow *main_win;
1318 g_return_if_fail (self);
1320 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1322 main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
1323 FALSE); /* don't create */
1325 g_warning ("%s: BUG: no main window", __FUNCTION__);
1330 if (priv->status_timeout) {
1331 g_source_remove (priv->status_timeout);
1332 priv->status_timeout = 0;
1335 g_mutex_lock (priv->observers_lock);
1336 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1337 g_object_unref (priv->folder);
1338 priv->folder = NULL;
1339 g_mutex_unlock (priv->observers_lock);
1343 GtkTreeSelection *selection;
1344 SetFolderHelper *info;
1345 ModestMailOperation *mail_op = NULL;
1347 /* Set folder in the model */
1348 modest_header_view_set_folder_intern (self, folder);
1350 /* Pick my reference. Nothing to do with the mail operation */
1351 priv->folder = g_object_ref (folder);
1353 /* Do not notify about filterings until the refresh finishes */
1354 priv->notify_status = FALSE;
1356 /* Clear the selection if exists */
1357 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1358 gtk_tree_selection_unselect_all(selection);
1359 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1361 /* Notify the observers that the update begins */
1362 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1365 /* create the helper */
1366 info = g_malloc0 (sizeof (SetFolderHelper));
1367 info->header_view = g_object_ref (self);
1368 info->cb = callback;
1369 info->user_data = user_data;
1371 /* Create the mail operation (source will be the parent widget) */
1372 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(main_win),
1373 refresh_folder_error_handler,
1376 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1379 /* Refresh the folder asynchronously */
1380 modest_mail_operation_refresh_folder (mail_op,
1382 folder_refreshed_cb,
1385 folder_refreshed_cb (mail_op, folder, info);
1388 g_object_unref (mail_op);
1390 g_mutex_lock (priv->observers_lock);
1392 if (priv->monitor) {
1393 tny_folder_monitor_stop (priv->monitor);
1394 g_object_unref (G_OBJECT (priv->monitor));
1395 priv->monitor = NULL;
1398 if (priv->autoselect_reference) {
1399 gtk_tree_row_reference_free (priv->autoselect_reference);
1400 priv->autoselect_reference = NULL;
1403 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1405 modest_header_view_notify_observers(self, NULL, NULL);
1407 g_mutex_unlock (priv->observers_lock);
1409 /* Notify the observers that the update is over */
1410 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1416 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1417 GtkTreeViewColumn *column, gpointer userdata)
1419 ModestHeaderView *self = NULL;
1420 ModestHeaderViewPrivate *priv = NULL;
1422 GtkTreeModel *model = NULL;
1423 TnyHeader *header = NULL;
1424 TnyHeaderFlags flags;
1426 self = MODEST_HEADER_VIEW (treeview);
1427 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1429 model = gtk_tree_view_get_model (treeview);
1430 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1433 /* get the first selected item */
1434 gtk_tree_model_get (model, &iter,
1435 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1436 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1439 /* Dont open DELETED messages */
1440 if (flags & TNY_HEADER_FLAG_DELETED) {
1443 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1444 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1445 modest_platform_information_banner (NULL, NULL, msg);
1451 g_signal_emit (G_OBJECT(self),
1452 signals[HEADER_ACTIVATED_SIGNAL],
1458 g_object_unref (G_OBJECT (header));
1463 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1465 GtkTreeModel *model;
1466 TnyHeader *header = NULL;
1467 GtkTreePath *path = NULL;
1469 ModestHeaderView *self;
1470 ModestHeaderViewPrivate *priv;
1471 GList *selected = NULL;
1473 g_return_if_fail (sel);
1474 g_return_if_fail (user_data);
1476 self = MODEST_HEADER_VIEW (user_data);
1477 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1479 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1480 if (selected != NULL)
1481 path = (GtkTreePath *) selected->data;
1482 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1483 return; /* msg was _un_selected */
1485 gtk_tree_model_get (model, &iter,
1486 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1490 g_signal_emit (G_OBJECT(self),
1491 signals[HEADER_SELECTED_SIGNAL],
1494 g_object_unref (G_OBJECT (header));
1496 /* free all items in 'selected' */
1497 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1498 g_list_free (selected);
1502 /* PROTECTED method. It's useful when we want to force a given
1503 selection to reload a msg. For example if we have selected a header
1504 in offline mode, when Modest become online, we want to reload the
1505 message automatically without an user click over the header */
1507 _modest_header_view_change_selection (GtkTreeSelection *selection,
1510 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1511 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1513 on_selection_changed (selection, user_data);
1517 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1524 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1528 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1532 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1540 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1547 /* static int counter = 0; */
1549 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1550 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1551 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1555 case TNY_HEADER_FLAG_ATTACHMENTS:
1557 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1558 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1559 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1560 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1562 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1563 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1565 return cmp ? cmp : t1 - t2;
1567 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1568 TnyHeader *header1 = NULL, *header2 = NULL;
1570 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1571 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1572 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1573 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1575 /* This is for making priority values respect the intuitive sort relationship
1576 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1578 if (header1 && header2) {
1579 cmp = compare_priorities (tny_header_get_priority (header1),
1580 tny_header_get_priority (header2));
1581 g_object_unref (header1);
1582 g_object_unref (header2);
1584 return cmp ? cmp : t1 - t2;
1590 return &iter1 - &iter2; /* oughhhh */
1595 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1601 /* static int counter = 0; */
1603 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1605 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1606 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1607 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1608 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1610 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1611 val2 + modest_text_utils_get_subject_prefix_len(val2),
1618 /* Drag and drop stuff */
1620 drag_data_get_cb (GtkWidget *widget,
1621 GdkDragContext *context,
1622 GtkSelectionData *selection_data,
1627 ModestHeaderView *self = NULL;
1628 ModestHeaderViewPrivate *priv = NULL;
1630 self = MODEST_HEADER_VIEW (widget);
1631 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1633 /* Set the data. Do not use the current selection because it
1634 could be different than the selection at the beginning of
1636 modest_dnd_selection_data_set_paths (selection_data,
1637 priv->drag_begin_cached_selected_rows);
1641 * We're caching the selected rows at the beginning because the
1642 * selection could change between drag-begin and drag-data-get, for
1643 * example if we have a set of rows already selected, and then we
1644 * click in one of them (without SHIFT key pressed) and begin a drag,
1645 * the selection at that moment contains all the selected lines, but
1646 * after dropping the selection, the release event provokes that only
1647 * the row used to begin the drag is selected, so at the end the
1648 * drag&drop affects only one rows instead of all the selected ones.
1652 drag_begin_cb (GtkWidget *widget,
1653 GdkDragContext *context,
1656 ModestHeaderView *self = NULL;
1657 ModestHeaderViewPrivate *priv = NULL;
1658 GtkTreeSelection *selection;
1660 self = MODEST_HEADER_VIEW (widget);
1661 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1663 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1664 priv->drag_begin_cached_selected_rows =
1665 gtk_tree_selection_get_selected_rows (selection, NULL);
1669 * We use the drag-end signal to clear the cached selection, we use
1670 * this because this allways happens, whether or not the d&d was a
1674 drag_end_cb (GtkWidget *widget,
1678 ModestHeaderView *self = NULL;
1679 ModestHeaderViewPrivate *priv = NULL;
1681 self = MODEST_HEADER_VIEW (widget);
1682 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1684 /* Free cached data */
1685 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1686 g_list_free (priv->drag_begin_cached_selected_rows);
1687 priv->drag_begin_cached_selected_rows = NULL;
1690 /* Header view drag types */
1691 const GtkTargetEntry header_view_drag_types[] = {
1692 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1696 enable_drag_and_drop (GtkWidget *self)
1698 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1699 header_view_drag_types,
1700 G_N_ELEMENTS (header_view_drag_types),
1701 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1705 disable_drag_and_drop (GtkWidget *self)
1707 gtk_drag_source_unset (self);
1711 setup_drag_and_drop (GtkWidget *self)
1713 enable_drag_and_drop(self);
1714 g_signal_connect(G_OBJECT (self), "drag_data_get",
1715 G_CALLBACK(drag_data_get_cb), NULL);
1717 g_signal_connect(G_OBJECT (self), "drag_begin",
1718 G_CALLBACK(drag_begin_cb), NULL);
1720 g_signal_connect(G_OBJECT (self), "drag_end",
1721 G_CALLBACK(drag_end_cb), NULL);
1724 static GtkTreePath *
1725 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1727 GtkTreePath *path = NULL;
1728 GtkTreeSelection *sel = NULL;
1731 sel = gtk_tree_view_get_selection(self);
1732 rows = gtk_tree_selection_get_selected_rows (sel, model);
1734 if ((rows == NULL) || (g_list_length(rows) != 1))
1737 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1742 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1749 * This function moves the tree view scroll to the current selected
1750 * row when the widget grabs the focus
1753 on_focus_in (GtkWidget *self,
1754 GdkEventFocus *event,
1757 GtkTreeSelection *selection;
1758 GtkTreeModel *model;
1759 GList *selected = NULL;
1760 GtkTreePath *selected_path = NULL;
1762 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1766 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1767 /* If none selected yet, pick the first one */
1768 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1772 /* Return if the model is empty */
1773 if (!gtk_tree_model_get_iter_first (model, &iter))
1776 path = gtk_tree_model_get_path (model, &iter);
1777 gtk_tree_selection_select_path (selection, path);
1778 gtk_tree_path_free (path);
1781 /* Need to get the all the rows because is selection multiple */
1782 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1783 if (selected == NULL) return FALSE;
1784 selected_path = (GtkTreePath *) selected->data;
1786 /* Check if we need to scroll */
1787 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1788 GtkTreePath *start_path = NULL;
1789 GtkTreePath *end_path = NULL;
1790 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1794 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1795 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1797 /* Scroll to first path */
1798 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1807 gtk_tree_path_free (start_path);
1809 gtk_tree_path_free (end_path);
1811 #endif /* GTK_CHECK_VERSION */
1814 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1815 g_list_free (selected);
1821 on_focus_out (GtkWidget *self,
1822 GdkEventFocus *event,
1826 if (!gtk_widget_is_focus (self)) {
1827 GtkTreeSelection *selection = NULL;
1828 GList *selected_rows = NULL;
1829 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1830 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1831 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1832 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1833 gtk_tree_selection_unselect_all (selection);
1834 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1835 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1836 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1837 g_list_free (selected_rows);
1844 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1846 enable_drag_and_drop(self);
1851 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1853 GtkTreeSelection *selection = NULL;
1854 GtkTreePath *path = NULL;
1855 gboolean already_selected = FALSE, already_opened = FALSE;
1856 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1858 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1860 GtkTreeModel *model;
1862 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1863 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1865 /* Get header from model */
1866 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1867 if (gtk_tree_model_get_iter (model, &iter, path)) {
1868 GValue value = {0,};
1871 gtk_tree_model_get_value (model, &iter,
1872 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1874 header = (TnyHeader *) g_value_get_object (&value);
1875 if (TNY_IS_HEADER (header)) {
1876 status = modest_tny_all_send_queues_get_msg_status (header);
1877 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1880 g_value_unset (&value);
1884 /* Enable drag and drop only if the user clicks on a row that
1885 it's already selected. If not, let him select items using
1886 the pointer. If the message is in an OUTBOX and in sending
1887 status disable drag and drop as well */
1888 if (!already_selected ||
1889 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1891 disable_drag_and_drop(self);
1894 gtk_tree_path_free(path);
1896 /* If it's already opened then do not let the button-press
1897 event go on because it'll perform a message open because
1898 we're clicking on to an already selected header */
1899 return (already_opened) ? TRUE : FALSE;
1903 folder_monitor_update (TnyFolderObserver *self,
1904 TnyFolderChange *change)
1906 ModestHeaderViewPrivate *priv = NULL;
1907 TnyFolderChangeChanged changed;
1908 TnyFolder *folder = NULL;
1910 changed = tny_folder_change_get_changed (change);
1912 /* Do not notify the observers if the folder of the header
1913 view has changed before this call to the observer
1915 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1916 folder = tny_folder_change_get_folder (change);
1917 if (folder != priv->folder)
1920 MODEST_DEBUG_BLOCK (
1921 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1922 g_print ("ADDED %d/%d (r/t) \n",
1923 tny_folder_change_get_new_unread_count (change),
1924 tny_folder_change_get_new_all_count (change));
1925 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1926 g_print ("ALL COUNT %d\n",
1927 tny_folder_change_get_new_all_count (change));
1928 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1929 g_print ("UNREAD COUNT %d\n",
1930 tny_folder_change_get_new_unread_count (change));
1931 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1932 g_print ("EXPUNGED %d/%d (r/t) \n",
1933 tny_folder_change_get_new_unread_count (change),
1934 tny_folder_change_get_new_all_count (change));
1935 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1936 g_print ("FOLDER RENAME\n");
1937 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1938 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1939 tny_folder_change_get_new_unread_count (change),
1940 tny_folder_change_get_new_all_count (change));
1941 g_print ("---------------------------------------------------\n");
1944 /* Check folder count */
1945 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1946 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1948 g_mutex_lock (priv->observers_lock);
1950 /* Emit signal to evaluate how headers changes affects
1951 to the window view */
1952 g_signal_emit (G_OBJECT(self),
1953 signals[MSG_COUNT_CHANGED_SIGNAL],
1956 /* Added or removed headers, so data stored on cliboard are invalid */
1957 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1958 modest_email_clipboard_clear (priv->clipboard);
1960 g_mutex_unlock (priv->observers_lock);
1966 g_object_unref (folder);
1970 modest_header_view_is_empty (ModestHeaderView *self)
1972 ModestHeaderViewPrivate *priv;
1974 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1976 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1978 return priv->status == HEADER_VIEW_EMPTY;
1982 modest_header_view_clear (ModestHeaderView *self)
1984 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1986 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL);
1990 modest_header_view_copy_selection (ModestHeaderView *header_view)
1992 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1994 /* Copy selection */
1995 _clipboard_set_selected_data (header_view, FALSE);
1999 modest_header_view_cut_selection (ModestHeaderView *header_view)
2001 ModestHeaderViewPrivate *priv = NULL;
2002 const gchar **hidding = NULL;
2003 guint i, n_selected;
2005 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2007 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2009 /* Copy selection */
2010 _clipboard_set_selected_data (header_view, TRUE);
2012 /* Get hidding ids */
2013 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2015 /* Clear hidding array created by previous cut operation */
2016 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2018 /* Copy hidding array */
2019 priv->n_selected = n_selected;
2020 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2021 for (i=0; i < n_selected; i++)
2022 priv->hidding_ids[i] = g_strdup(hidding[i]);
2024 /* Hide cut headers */
2025 modest_header_view_refilter (header_view);
2032 _clipboard_set_selected_data (ModestHeaderView *header_view,
2035 ModestHeaderViewPrivate *priv = NULL;
2036 TnyList *headers = NULL;
2038 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2039 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2041 /* Set selected data on clipboard */
2042 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2043 headers = modest_header_view_get_selected_headers (header_view);
2044 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2047 g_object_unref (headers);
2051 ModestHeaderView *self;
2056 notify_filter_change (gpointer data)
2058 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2060 g_signal_emit (info->self,
2061 signals[MSG_COUNT_CHANGED_SIGNAL],
2062 0, info->folder, NULL);
2068 notify_filter_change_destroy (gpointer data)
2070 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2071 ModestHeaderViewPrivate *priv;
2073 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2074 priv->status_timeout = 0;
2076 g_object_unref (info->self);
2077 g_object_unref (info->folder);
2078 g_slice_free (NotifyFilterInfo, info);
2082 filter_row (GtkTreeModel *model,
2086 ModestHeaderViewPrivate *priv = NULL;
2087 TnyHeaderFlags flags;
2088 TnyHeader *header = NULL;
2091 gboolean visible = TRUE;
2092 gboolean found = FALSE;
2093 GValue value = {0,};
2094 HeaderViewStatus old_status;
2096 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2097 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2099 /* Get header from model */
2100 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2101 flags = (TnyHeaderFlags) g_value_get_int (&value);
2102 g_value_unset (&value);
2103 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2104 header = (TnyHeader *) g_value_get_object (&value);
2105 g_value_unset (&value);
2107 /* Hide deleted and mark as deleted heders */
2108 if (flags & TNY_HEADER_FLAG_DELETED ||
2109 flags & TNY_HEADER_FLAG_EXPUNGED) {
2114 /* If no data on clipboard, return always TRUE */
2115 if (modest_email_clipboard_cleared(priv->clipboard)) {
2120 /* Get message id from header (ensure is a valid id) */
2127 if (priv->hidding_ids != NULL) {
2128 id = tny_header_dup_message_id (header);
2129 for (i=0; i < priv->n_selected && !found; i++)
2130 if (priv->hidding_ids[i] != NULL && id != NULL)
2131 found = (!strcmp (priv->hidding_ids[i], id));
2138 old_status = priv->status;
2139 priv->status = ((gboolean) priv->status) && !visible;
2140 if ((priv->notify_status) && (priv->status != old_status)) {
2141 NotifyFilterInfo *info;
2143 if (priv->status_timeout)
2144 g_source_remove (priv->status_timeout);
2146 info = g_slice_new0 (NotifyFilterInfo);
2147 info->self = g_object_ref (G_OBJECT (user_data));
2148 info->folder = tny_header_get_folder (header);
2149 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2150 notify_filter_change,
2152 notify_filter_change_destroy);
2159 _clear_hidding_filter (ModestHeaderView *header_view)
2161 ModestHeaderViewPrivate *priv = NULL;
2164 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2165 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2167 if (priv->hidding_ids != NULL) {
2168 for (i=0; i < priv->n_selected; i++)
2169 g_free (priv->hidding_ids[i]);
2170 g_free(priv->hidding_ids);
2175 modest_header_view_refilter (ModestHeaderView *header_view)
2177 GtkTreeModel *model = NULL;
2178 ModestHeaderViewPrivate *priv = NULL;
2180 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2181 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2183 /* Hide cut headers */
2184 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2185 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2186 priv->status = HEADER_VIEW_INIT;
2187 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2192 * Called when an account is removed. If I'm showing a folder of the
2193 * account that has been removed then clear the view
2196 on_account_removed (TnyAccountStore *self,
2197 TnyAccount *account,
2200 ModestHeaderViewPrivate *priv = NULL;
2202 /* Ignore changes in transport accounts */
2203 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2206 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2208 if (priv->folder && !TNY_IS_MERGE_FOLDER (priv->folder)) {
2209 TnyAccount *my_account = NULL;
2211 my_account = tny_folder_get_account (priv->folder);
2212 if (my_account == account)
2213 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2214 g_object_unref (my_account);
2219 modest_header_view_add_observer(ModestHeaderView *header_view,
2220 ModestHeaderViewObserver *observer)
2222 ModestHeaderViewPrivate *priv;
2224 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2225 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2227 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2229 g_mutex_lock(priv->observer_list_lock);
2230 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2231 g_mutex_unlock(priv->observer_list_lock);
2235 modest_header_view_remove_observer(ModestHeaderView *header_view,
2236 ModestHeaderViewObserver *observer)
2238 ModestHeaderViewPrivate *priv;
2240 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2241 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2243 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2245 g_mutex_lock(priv->observer_list_lock);
2246 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2247 g_mutex_unlock(priv->observer_list_lock);
2251 modest_header_view_notify_observers(ModestHeaderView *header_view,
2252 GtkTreeModel *model,
2253 const gchar *tny_folder_id)
2255 ModestHeaderViewPrivate *priv = NULL;
2257 ModestHeaderViewObserver *observer;
2260 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2262 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2264 g_mutex_lock(priv->observer_list_lock);
2265 iter = priv->observer_list;
2266 while(iter != NULL){
2267 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2268 modest_header_view_observer_update(observer, model,
2270 iter = g_slist_next(iter);
2272 g_mutex_unlock(priv->observer_list_lock);