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>
52 #include <modest-datetime-formatter.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 #ifndef MODEST_TOOLKIT_HILDON2
98 static gboolean on_focus_in (GtkWidget *sef,
102 static gboolean on_focus_out (GtkWidget *self,
103 GdkEventFocus *event,
107 static void folder_monitor_update (TnyFolderObserver *self,
108 TnyFolderChange *change);
110 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
112 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
114 static void _clear_hidding_filter (ModestHeaderView *header_view);
116 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
118 const gchar *tny_folder_id);
120 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
121 GdkEventExpose *event,
125 HEADER_VIEW_NON_EMPTY,
130 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
131 struct _ModestHeaderViewPrivate {
133 ModestHeaderViewStyle style;
136 TnyFolderMonitor *monitor;
137 GMutex *observers_lock;
139 /*header-view-observer observer*/
140 GMutex *observer_list_lock;
141 GSList *observer_list;
143 /* not unref this object, its a singlenton */
144 ModestEmailClipboard *clipboard;
146 /* Filter tree model */
149 GtkTreeRowReference *autoselect_reference;
150 ModestHeaderViewFilter filter;
152 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
153 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
155 gulong selection_changed_handler;
156 gulong acc_removed_handler;
158 GList *drag_begin_cached_selected_rows;
160 HeaderViewStatus status;
161 guint status_timeout;
162 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
164 ModestDatetimeFormatter *datetime_formatter;
167 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
168 struct _HeadersCountChangedHelper {
169 ModestHeaderView *self;
170 TnyFolderChange *change;
174 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
175 MODEST_TYPE_HEADER_VIEW, \
176 ModestHeaderViewPrivate))
180 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
183 HEADER_SELECTED_SIGNAL,
184 HEADER_ACTIVATED_SIGNAL,
185 ITEM_NOT_FOUND_SIGNAL,
186 MSG_COUNT_CHANGED_SIGNAL,
187 UPDATING_MSG_LIST_SIGNAL,
192 static GObjectClass *parent_class = NULL;
194 /* uncomment the following if you have defined any signals */
195 static guint signals[LAST_SIGNAL] = {0};
198 modest_header_view_get_type (void)
200 static GType my_type = 0;
202 static const GTypeInfo my_info = {
203 sizeof(ModestHeaderViewClass),
204 NULL, /* base init */
205 NULL, /* base finalize */
206 (GClassInitFunc) modest_header_view_class_init,
207 NULL, /* class finalize */
208 NULL, /* class data */
209 sizeof(ModestHeaderView),
211 (GInstanceInitFunc) modest_header_view_init,
215 static const GInterfaceInfo tny_folder_observer_info =
217 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
218 NULL, /* interface_finalize */
219 NULL /* interface_data */
221 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
225 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
226 &tny_folder_observer_info);
234 modest_header_view_class_init (ModestHeaderViewClass *klass)
236 GObjectClass *gobject_class;
237 gobject_class = (GObjectClass*) klass;
239 parent_class = g_type_class_peek_parent (klass);
240 gobject_class->finalize = modest_header_view_finalize;
241 gobject_class->dispose = modest_header_view_dispose;
243 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
245 signals[HEADER_SELECTED_SIGNAL] =
246 g_signal_new ("header_selected",
247 G_TYPE_FROM_CLASS (gobject_class),
249 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
251 g_cclosure_marshal_VOID__POINTER,
252 G_TYPE_NONE, 1, G_TYPE_POINTER);
254 signals[HEADER_ACTIVATED_SIGNAL] =
255 g_signal_new ("header_activated",
256 G_TYPE_FROM_CLASS (gobject_class),
258 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
260 gtk_marshal_VOID__POINTER_POINTER,
261 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
264 signals[ITEM_NOT_FOUND_SIGNAL] =
265 g_signal_new ("item_not_found",
266 G_TYPE_FROM_CLASS (gobject_class),
268 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
270 g_cclosure_marshal_VOID__INT,
271 G_TYPE_NONE, 1, G_TYPE_INT);
273 signals[MSG_COUNT_CHANGED_SIGNAL] =
274 g_signal_new ("msg_count_changed",
275 G_TYPE_FROM_CLASS (gobject_class),
277 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
279 modest_marshal_VOID__POINTER_POINTER,
280 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
282 signals[UPDATING_MSG_LIST_SIGNAL] =
283 g_signal_new ("updating-msg-list",
284 G_TYPE_FROM_CLASS (gobject_class),
286 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
288 g_cclosure_marshal_VOID__BOOLEAN,
289 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
291 #ifdef MODEST_TOOLKIT_HILDON2
292 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
298 tny_folder_observer_init (TnyFolderObserverIface *klass)
300 klass->update = folder_monitor_update;
303 static GtkTreeViewColumn*
304 get_new_column (const gchar *name, GtkCellRenderer *renderer,
305 gboolean resizable, gint sort_col_id, gboolean show_as_text,
306 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
308 GtkTreeViewColumn *column;
310 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
311 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
313 gtk_tree_view_column_set_resizable (column, resizable);
315 gtk_tree_view_column_set_expand (column, TRUE);
318 gtk_tree_view_column_add_attribute (column, renderer, "text",
320 if (sort_col_id >= 0)
321 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
323 gtk_tree_view_column_set_sort_indicator (column, FALSE);
324 gtk_tree_view_column_set_reorderable (column, TRUE);
327 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
334 remove_all_columns (ModestHeaderView *obj)
336 GList *columns, *cursor;
338 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
340 for (cursor = columns; cursor; cursor = cursor->next)
341 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
342 GTK_TREE_VIEW_COLUMN(cursor->data));
343 g_list_free (columns);
347 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
349 GtkTreeModel *tree_filter, *sortable;
350 GtkTreeViewColumn *column=NULL;
351 GtkTreeSelection *selection = NULL;
352 GtkCellRenderer *renderer_header,
353 *renderer_attach, *renderer_compact_date_or_status;
354 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
355 *renderer_subject, *renderer_subject_box, *renderer_recpt,
357 ModestHeaderViewPrivate *priv;
358 GtkTreeViewColumn *compact_column = NULL;
361 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
362 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
364 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
366 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
368 /* TODO: check whether these renderers need to be freed */
369 renderer_attach = gtk_cell_renderer_pixbuf_new ();
370 renderer_priority = gtk_cell_renderer_pixbuf_new ();
371 renderer_header = gtk_cell_renderer_text_new ();
373 renderer_compact_header = modest_vbox_cell_renderer_new ();
374 renderer_recpt_box = modest_hbox_cell_renderer_new ();
375 renderer_subject_box = modest_hbox_cell_renderer_new ();
376 renderer_recpt = gtk_cell_renderer_text_new ();
377 renderer_subject = gtk_cell_renderer_text_new ();
378 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
380 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
381 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
382 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
383 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
384 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
385 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
386 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
387 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
388 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
389 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
390 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
391 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
392 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
393 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
395 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
396 #ifndef MODEST_TOOLKIT_GTK
397 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
398 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
400 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
401 g_object_set(G_OBJECT(renderer_header),
402 "ellipsize", PANGO_ELLIPSIZE_END,
404 g_object_set (G_OBJECT (renderer_subject),
405 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
407 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
408 g_object_set (G_OBJECT (renderer_recpt),
409 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
411 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
412 g_object_set(G_OBJECT(renderer_compact_date_or_status),
413 "xalign", 1.0, "yalign", 0.0,
415 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
416 g_object_set (G_OBJECT (renderer_priority),
417 "yalign", 1.0, NULL);
418 g_object_set (G_OBJECT (renderer_attach),
419 "yalign", 0.0, NULL);
421 #ifndef MODEST_TOOLKIT_GTK
422 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
423 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
424 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
426 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
427 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
428 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
431 remove_all_columns (self);
433 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
434 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
435 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
436 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
438 /* Add new columns */
439 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
440 ModestHeaderViewColumn col =
441 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
443 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
444 g_printerr ("modest: invalid column %d in column list\n", col);
450 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
451 column = get_new_column (_("A"), renderer_attach, FALSE,
452 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
454 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
456 gtk_tree_view_column_set_fixed_width (column, 45);
460 case MODEST_HEADER_VIEW_COLUMN_FROM:
461 column = get_new_column (_("From"), renderer_header, TRUE,
462 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
464 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
465 GINT_TO_POINTER(TRUE));
468 case MODEST_HEADER_VIEW_COLUMN_TO:
469 column = get_new_column (_("To"), renderer_header, TRUE,
470 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
472 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
473 GINT_TO_POINTER(FALSE));
476 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
477 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
478 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
480 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
481 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
482 compact_column = column;
485 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
486 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
487 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
489 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
490 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
491 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
492 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
493 compact_column = column;
497 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
498 column = get_new_column (_("Subject"), renderer_header, TRUE,
499 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
501 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
505 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
506 column = get_new_column (_("Received"), renderer_header, TRUE,
507 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
509 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
510 GINT_TO_POINTER(TRUE));
513 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
514 column = get_new_column (_("Sent"), renderer_header, TRUE,
515 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
517 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
518 GINT_TO_POINTER(FALSE));
521 case MODEST_HEADER_VIEW_COLUMN_SIZE:
522 column = get_new_column (_("Size"), renderer_header, TRUE,
523 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
525 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
528 case MODEST_HEADER_VIEW_COLUMN_STATUS:
529 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
530 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
532 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
537 g_return_val_if_reached(FALSE);
540 /* we keep the column id around */
541 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
542 GINT_TO_POINTER(col));
544 /* we need this ptr when sorting the rows */
545 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
547 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
551 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
552 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
553 (GtkTreeIterCompareFunc) cmp_rows,
554 compact_column, NULL);
555 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
556 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
557 (GtkTreeIterCompareFunc) cmp_subject_rows,
558 compact_column, NULL);
565 datetime_format_changed (ModestDatetimeFormatter *formatter,
566 ModestHeaderView *self)
568 gtk_widget_queue_draw (GTK_WIDGET (self));
572 modest_header_view_init (ModestHeaderView *obj)
574 ModestHeaderViewPrivate *priv;
577 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
580 priv->is_outbox = FALSE;
582 priv->monitor = NULL;
583 priv->observers_lock = g_mutex_new ();
584 priv->autoselect_reference = NULL;
586 priv->status = HEADER_VIEW_INIT;
587 priv->status_timeout = 0;
588 priv->notify_status = TRUE;
590 priv->observer_list_lock = g_mutex_new();
591 priv->observer_list = NULL;
593 priv->clipboard = modest_runtime_get_email_clipboard ();
594 priv->hidding_ids = NULL;
595 priv->n_selected = 0;
596 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
597 priv->selection_changed_handler = 0;
598 priv->acc_removed_handler = 0;
600 /* Sort parameters */
601 for (j=0; j < 2; j++) {
602 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
603 priv->sort_colid[j][i] = -1;
604 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
608 priv->datetime_formatter = modest_datetime_formatter_new ();
609 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
610 G_CALLBACK (datetime_format_changed), (gpointer) obj);
612 setup_drag_and_drop (GTK_WIDGET(obj));
616 modest_header_view_dispose (GObject *obj)
618 ModestHeaderView *self;
619 ModestHeaderViewPrivate *priv;
620 GtkTreeSelection *sel;
622 self = MODEST_HEADER_VIEW(obj);
623 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
625 if (priv->datetime_formatter) {
626 g_object_unref (priv->datetime_formatter);
627 priv->datetime_formatter = NULL;
630 /* Free in the dispose to avoid unref cycles */
632 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
633 g_object_unref (G_OBJECT (priv->folder));
637 /* We need to do this here in the dispose because the
638 selection won't exist when finalizing */
639 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
640 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
641 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
642 priv->selection_changed_handler = 0;
645 G_OBJECT_CLASS(parent_class)->dispose (obj);
649 modest_header_view_finalize (GObject *obj)
651 ModestHeaderView *self;
652 ModestHeaderViewPrivate *priv;
654 self = MODEST_HEADER_VIEW(obj);
655 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
657 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
658 priv->acc_removed_handler)) {
659 g_signal_handler_disconnect (modest_runtime_get_account_store (),
660 priv->acc_removed_handler);
663 /* There is no need to lock because there should not be any
664 * reference to self now. */
665 g_mutex_free(priv->observer_list_lock);
666 g_slist_free(priv->observer_list);
668 g_mutex_lock (priv->observers_lock);
670 tny_folder_monitor_stop (priv->monitor);
671 g_object_unref (G_OBJECT (priv->monitor));
673 g_mutex_unlock (priv->observers_lock);
674 g_mutex_free (priv->observers_lock);
676 /* Clear hidding array created by cut operation */
677 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
679 if (priv->autoselect_reference != NULL) {
680 gtk_tree_row_reference_free (priv->autoselect_reference);
681 priv->autoselect_reference = NULL;
684 G_OBJECT_CLASS(parent_class)->finalize (obj);
689 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
692 GtkTreeSelection *sel;
693 ModestHeaderView *self;
694 ModestHeaderViewPrivate *priv;
696 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
699 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
700 self = MODEST_HEADER_VIEW(obj);
701 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
703 modest_header_view_set_style (self, style);
705 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
706 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
707 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
709 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
710 TRUE); /* alternating row colors */
712 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
713 priv->selection_changed_handler =
714 g_signal_connect_after (sel, "changed",
715 G_CALLBACK(on_selection_changed), self);
717 g_signal_connect (self, "row-activated",
718 G_CALLBACK (on_header_row_activated), NULL);
720 #ifndef MODEST_TOOLKIT_HILDON2
721 g_signal_connect (self, "focus-in-event",
722 G_CALLBACK(on_focus_in), NULL);
723 g_signal_connect (self, "focus-out-event",
724 G_CALLBACK(on_focus_out), NULL);
727 g_signal_connect (self, "button-press-event",
728 G_CALLBACK(on_button_press_event), NULL);
729 g_signal_connect (self, "button-release-event",
730 G_CALLBACK(on_button_release_event), NULL);
732 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
734 G_CALLBACK (on_account_removed),
737 g_signal_connect (self, "expose-event",
738 G_CALLBACK(modest_header_view_on_expose_event),
741 return GTK_WIDGET(self);
746 modest_header_view_count_selected_headers (ModestHeaderView *self)
748 GtkTreeSelection *sel;
751 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
753 /* Get selection object and check selected rows count */
754 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
755 selected_rows = gtk_tree_selection_count_selected_rows (sel);
757 return selected_rows;
761 modest_header_view_has_selected_headers (ModestHeaderView *self)
763 GtkTreeSelection *sel;
766 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
768 /* Get selection object and check selected rows count */
769 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
770 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
777 modest_header_view_get_selected_headers (ModestHeaderView *self)
779 GtkTreeSelection *sel;
780 ModestHeaderViewPrivate *priv;
781 TnyList *header_list = NULL;
783 GList *list, *tmp = NULL;
784 GtkTreeModel *tree_model = NULL;
787 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
789 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
791 /* Get selected rows */
792 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
793 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
796 header_list = tny_simple_list_new();
798 list = g_list_reverse (list);
801 /* get header from selection */
802 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
803 gtk_tree_model_get (tree_model, &iter,
804 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
806 /* Prepend to list */
807 tny_list_prepend (header_list, G_OBJECT (header));
808 g_object_unref (G_OBJECT (header));
810 tmp = g_list_next (tmp);
813 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
820 /* scroll our list view so the selected item is visible */
822 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
824 #ifdef MODEST_TOOLKIT_GTK
826 GtkTreePath *selected_path;
827 GtkTreePath *start, *end;
831 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
832 selected_path = gtk_tree_model_get_path (model, iter);
834 start = gtk_tree_path_new ();
835 end = gtk_tree_path_new ();
837 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
839 if (gtk_tree_path_compare (selected_path, start) < 0 ||
840 gtk_tree_path_compare (end, selected_path) < 0)
841 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
842 selected_path, NULL, TRUE,
845 gtk_tree_path_free (selected_path);
846 gtk_tree_path_free (start);
847 gtk_tree_path_free (end);
849 #endif /* MODEST_TOOLKIT_GTK */
854 modest_header_view_select_next (ModestHeaderView *self)
856 GtkTreeSelection *sel;
861 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
863 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
864 path = get_selected_row (GTK_TREE_VIEW(self), &model);
865 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
866 /* Unselect previous path */
867 gtk_tree_selection_unselect_path (sel, path);
869 /* Move path down and selects new one */
870 if (gtk_tree_model_iter_next (model, &iter)) {
871 gtk_tree_selection_select_iter (sel, &iter);
872 scroll_to_selected (self, &iter, FALSE);
874 gtk_tree_path_free(path);
880 modest_header_view_select_prev (ModestHeaderView *self)
882 GtkTreeSelection *sel;
887 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
889 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
890 path = get_selected_row (GTK_TREE_VIEW(self), &model);
891 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
892 /* Unselect previous path */
893 gtk_tree_selection_unselect_path (sel, path);
896 if (gtk_tree_path_prev (path)) {
897 gtk_tree_model_get_iter (model, &iter, path);
899 /* Select the new one */
900 gtk_tree_selection_select_iter (sel, &iter);
901 scroll_to_selected (self, &iter, TRUE);
904 gtk_tree_path_free (path);
909 modest_header_view_get_columns (ModestHeaderView *self)
911 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
913 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
919 modest_header_view_set_style (ModestHeaderView *self,
920 ModestHeaderViewStyle style)
922 ModestHeaderViewPrivate *priv;
923 gboolean show_col_headers = FALSE;
924 ModestHeaderViewStyle old_style;
926 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
927 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
930 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
931 if (priv->style == style)
932 return TRUE; /* nothing to do */
935 case MODEST_HEADER_VIEW_STYLE_DETAILS:
936 show_col_headers = TRUE;
938 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
941 g_return_val_if_reached (FALSE);
943 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
944 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
946 old_style = priv->style;
953 ModestHeaderViewStyle
954 modest_header_view_get_style (ModestHeaderView *self)
956 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
958 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
961 /* This is used to automatically select the first header if the user
962 * has not selected any header yet.
965 modest_header_view_on_expose_event(GtkTreeView *header_view,
966 GdkEventExpose *event,
969 GtkTreeSelection *sel;
971 GtkTreeIter tree_iter;
972 ModestHeaderViewPrivate *priv;
974 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
975 model = gtk_tree_view_get_model(header_view);
980 #ifdef MODEST_TOOLKIT_HILDON2
983 sel = gtk_tree_view_get_selection(header_view);
984 if(!gtk_tree_selection_count_selected_rows(sel)) {
985 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
986 GtkTreePath *tree_iter_path;
987 /* Prevent the widget from getting the focus
988 when selecting the first item */
989 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
990 g_object_set(header_view, "can-focus", FALSE, NULL);
991 gtk_tree_selection_select_iter(sel, &tree_iter);
992 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
993 g_object_set(header_view, "can-focus", TRUE, NULL);
994 if (priv->autoselect_reference) {
995 gtk_tree_row_reference_free (priv->autoselect_reference);
997 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
998 gtk_tree_path_free (tree_iter_path);
1001 if (priv->autoselect_reference != NULL) {
1002 gboolean moved_selection = FALSE;
1003 GtkTreePath * last_path;
1004 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1005 moved_selection = TRUE;
1009 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1010 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1011 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1012 moved_selection = TRUE;
1013 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1015 gtk_tree_path_free (last_path);
1017 if (moved_selection) {
1018 gtk_tree_row_reference_free (priv->autoselect_reference);
1019 priv->autoselect_reference = NULL;
1022 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1023 GtkTreePath *current_path;
1024 current_path = gtk_tree_model_get_path (model, &tree_iter);
1025 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1026 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1027 g_object_set(header_view, "can-focus", FALSE, NULL);
1028 gtk_tree_selection_unselect_all (sel);
1029 gtk_tree_selection_select_iter(sel, &tree_iter);
1030 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1031 g_object_set(header_view, "can-focus", TRUE, NULL);
1032 gtk_tree_row_reference_free (priv->autoselect_reference);
1033 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1035 gtk_tree_path_free (current_path);
1036 gtk_tree_path_free (last_path);
1046 modest_header_view_get_folder (ModestHeaderView *self)
1048 ModestHeaderViewPrivate *priv;
1050 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1052 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1055 g_object_ref (priv->folder);
1057 return priv->folder;
1061 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1067 ModestHeaderView *self;
1068 ModestHeaderViewPrivate *priv;
1070 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1072 self = MODEST_HEADER_VIEW (user_data);
1073 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1075 if (cancelled || err)
1078 /* Add IDLE observer (monitor) and another folder observer for
1079 new messages (self) */
1080 g_mutex_lock (priv->observers_lock);
1081 if (priv->monitor) {
1082 tny_folder_monitor_stop (priv->monitor);
1083 g_object_unref (G_OBJECT (priv->monitor));
1085 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1086 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1087 tny_folder_monitor_start (priv->monitor);
1088 g_mutex_unlock (priv->observers_lock);
1092 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1096 ModestHeaderViewPrivate *priv;
1097 GList *cols, *cursor;
1098 GtkTreeModel *filter_model, *sortable;
1100 GtkSortType sort_type;
1102 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1104 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1106 /* Start the monitor in the callback of the
1107 tny_gtk_header_list_model_set_folder call. It's crucial to
1108 do it there and not just after the call because we want the
1109 monitor to observe only the headers returned by the
1110 tny_folder_get_headers_async call that it's inside the
1111 tny_gtk_header_list_model_set_folder call. This way the
1112 monitor infrastructure could successfully cope with
1113 duplicates. For example if a tny_folder_add_msg_async is
1114 happening while tny_gtk_header_list_model_set_folder is
1115 invoked, then the first call could add a header that will
1116 be added again by tny_gtk_header_list_model_set_folder, so
1117 we'd end up with duplicate headers. sergio */
1118 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1120 set_folder_intern_get_headers_async_cb,
1123 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1124 g_object_unref (G_OBJECT (headers));
1126 /* Init filter_row function to examine empty status */
1127 priv->status = HEADER_VIEW_INIT;
1129 /* Create a tree model filter to hide and show rows for cut operations */
1130 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1131 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1135 g_object_unref (G_OBJECT (sortable));
1137 /* install our special sorting functions */
1138 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1140 /* Restore sort column id */
1142 type = modest_tny_folder_guess_folder_type (folder);
1143 if (type == TNY_FOLDER_TYPE_INVALID)
1144 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1146 sort_colid = modest_header_view_get_sort_column_id (self, type);
1147 sort_type = modest_header_view_get_sort_type (self, type);
1148 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1151 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1152 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1153 (GtkTreeIterCompareFunc) cmp_rows,
1155 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1156 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1157 (GtkTreeIterCompareFunc) cmp_subject_rows,
1162 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1163 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1164 tny_folder_get_id(folder));
1165 g_object_unref (G_OBJECT (filter_model));
1172 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1174 GtkSortType sort_type)
1176 ModestHeaderViewPrivate *priv = NULL;
1177 GtkTreeModel *tree_filter, *sortable = NULL;
1180 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1181 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1183 /* Get model and private data */
1184 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1185 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1186 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1187 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1189 /* Sort tree model */
1190 type = modest_tny_folder_guess_folder_type (priv->folder);
1191 if (type == TNY_FOLDER_TYPE_INVALID)
1192 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1194 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1197 /* Store new sort parameters */
1198 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1203 modest_header_view_set_sort_params (ModestHeaderView *self,
1205 GtkSortType sort_type,
1208 ModestHeaderViewPrivate *priv;
1209 ModestHeaderViewStyle style;
1211 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1212 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1213 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1215 style = modest_header_view_get_style (self);
1216 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1218 priv->sort_colid[style][type] = sort_colid;
1219 priv->sort_type[style][type] = sort_type;
1223 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1226 ModestHeaderViewPrivate *priv;
1227 ModestHeaderViewStyle style;
1229 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1230 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1232 style = modest_header_view_get_style (self);
1233 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1235 return priv->sort_colid[style][type];
1239 modest_header_view_get_sort_type (ModestHeaderView *self,
1242 ModestHeaderViewPrivate *priv;
1243 ModestHeaderViewStyle style;
1245 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1246 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1248 style = modest_header_view_get_style (self);
1249 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1251 return priv->sort_type[style][type];
1255 ModestHeaderView *header_view;
1256 RefreshAsyncUserCallback cb;
1261 folder_refreshed_cb (ModestMailOperation *mail_op,
1265 ModestHeaderViewPrivate *priv;
1266 SetFolderHelper *info;
1268 info = (SetFolderHelper*) user_data;
1270 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1274 info->cb (mail_op, folder, info->user_data);
1276 /* Start the folder count changes observer. We do not need it
1277 before the refresh. Note that the monitor could still be
1278 called for this refresh but now we know that the callback
1279 was previously called */
1280 g_mutex_lock (priv->observers_lock);
1281 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1282 g_mutex_unlock (priv->observers_lock);
1284 /* Notify the observers that the update is over */
1285 g_signal_emit (G_OBJECT (info->header_view),
1286 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1288 /* Allow filtering notifications from now on if the current
1289 folder is still the same (if not then the user has selected
1290 another one to refresh, we should wait until that refresh
1292 if (priv->folder == folder)
1293 priv->notify_status = TRUE;
1296 g_object_unref (info->header_view);
1301 refresh_folder_error_handler (ModestMailOperation *mail_op,
1304 const GError *error = modest_mail_operation_get_error (mail_op);
1306 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1307 error->code == TNY_IO_ERROR_WRITE ||
1308 error->code == TNY_IO_ERROR_READ) {
1309 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1310 /* If the mail op has been cancelled then it's not an error: don't show any message */
1311 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1312 modest_platform_information_banner (NULL, NULL,
1314 "cerm_device_memory_full"));
1320 modest_header_view_set_folder (ModestHeaderView *self,
1323 ModestWindow *progress_window,
1324 RefreshAsyncUserCallback callback,
1327 ModestHeaderViewPrivate *priv;
1329 g_return_if_fail (self);
1331 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1334 if (priv->status_timeout) {
1335 g_source_remove (priv->status_timeout);
1336 priv->status_timeout = 0;
1339 g_mutex_lock (priv->observers_lock);
1340 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1341 g_object_unref (priv->folder);
1342 priv->folder = NULL;
1343 g_mutex_unlock (priv->observers_lock);
1347 GtkTreeSelection *selection;
1348 SetFolderHelper *info;
1349 ModestMailOperation *mail_op = NULL;
1351 /* Set folder in the model */
1352 modest_header_view_set_folder_intern (self, folder);
1354 /* Pick my reference. Nothing to do with the mail operation */
1355 priv->folder = g_object_ref (folder);
1357 /* Do not notify about filterings until the refresh finishes */
1358 priv->notify_status = FALSE;
1360 /* Clear the selection if exists */
1361 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1362 gtk_tree_selection_unselect_all(selection);
1363 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1365 /* Notify the observers that the update begins */
1366 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1369 /* create the helper */
1370 info = g_malloc0 (sizeof (SetFolderHelper));
1371 info->header_view = g_object_ref (self);
1372 info->cb = callback;
1373 info->user_data = user_data;
1375 /* Create the mail operation (source will be the parent widget) */
1376 if (progress_window)
1377 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1378 refresh_folder_error_handler,
1381 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1384 /* Refresh the folder asynchronously */
1385 modest_mail_operation_refresh_folder (mail_op,
1387 folder_refreshed_cb,
1390 folder_refreshed_cb (mail_op, folder, info);
1393 g_object_unref (mail_op);
1395 g_mutex_lock (priv->observers_lock);
1397 if (priv->monitor) {
1398 tny_folder_monitor_stop (priv->monitor);
1399 g_object_unref (G_OBJECT (priv->monitor));
1400 priv->monitor = NULL;
1403 if (priv->autoselect_reference) {
1404 gtk_tree_row_reference_free (priv->autoselect_reference);
1405 priv->autoselect_reference = NULL;
1408 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1410 modest_header_view_notify_observers(self, NULL, NULL);
1412 g_mutex_unlock (priv->observers_lock);
1414 /* Notify the observers that the update is over */
1415 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1421 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1422 GtkTreeViewColumn *column, gpointer userdata)
1424 ModestHeaderView *self = NULL;
1425 ModestHeaderViewPrivate *priv = NULL;
1427 GtkTreeModel *model = NULL;
1428 TnyHeader *header = NULL;
1429 TnyHeaderFlags flags;
1431 self = MODEST_HEADER_VIEW (treeview);
1432 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1434 model = gtk_tree_view_get_model (treeview);
1435 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1438 /* get the first selected item */
1439 gtk_tree_model_get (model, &iter,
1440 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1441 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1444 /* Dont open DELETED messages */
1445 if (flags & TNY_HEADER_FLAG_DELETED) {
1448 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1449 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1450 modest_platform_information_banner (NULL, NULL, msg);
1456 g_signal_emit (G_OBJECT(self),
1457 signals[HEADER_ACTIVATED_SIGNAL],
1463 g_object_unref (G_OBJECT (header));
1468 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1470 GtkTreeModel *model;
1471 TnyHeader *header = NULL;
1472 GtkTreePath *path = NULL;
1474 ModestHeaderView *self;
1475 ModestHeaderViewPrivate *priv;
1476 GList *selected = NULL;
1478 g_return_if_fail (sel);
1479 g_return_if_fail (user_data);
1481 self = MODEST_HEADER_VIEW (user_data);
1482 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1484 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1485 if (selected != NULL)
1486 path = (GtkTreePath *) selected->data;
1487 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1488 return; /* msg was _un_selected */
1490 gtk_tree_model_get (model, &iter,
1491 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1495 g_signal_emit (G_OBJECT(self),
1496 signals[HEADER_SELECTED_SIGNAL],
1499 g_object_unref (G_OBJECT (header));
1501 /* free all items in 'selected' */
1502 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1503 g_list_free (selected);
1507 /* PROTECTED method. It's useful when we want to force a given
1508 selection to reload a msg. For example if we have selected a header
1509 in offline mode, when Modest become online, we want to reload the
1510 message automatically without an user click over the header */
1512 _modest_header_view_change_selection (GtkTreeSelection *selection,
1515 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1516 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1518 on_selection_changed (selection, user_data);
1522 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1529 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1533 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1537 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1545 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1552 /* static int counter = 0; */
1554 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1555 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1556 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1560 case TNY_HEADER_FLAG_ATTACHMENTS:
1562 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1563 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1564 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1565 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1567 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1568 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1570 return cmp ? cmp : t1 - t2;
1572 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1573 TnyHeader *header1 = NULL, *header2 = NULL;
1575 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1576 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1577 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1578 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1580 /* This is for making priority values respect the intuitive sort relationship
1581 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1583 if (header1 && header2) {
1584 cmp = compare_priorities (tny_header_get_priority (header1),
1585 tny_header_get_priority (header2));
1586 g_object_unref (header1);
1587 g_object_unref (header2);
1589 return cmp ? cmp : t1 - t2;
1595 return &iter1 - &iter2; /* oughhhh */
1600 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1606 /* static int counter = 0; */
1608 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1610 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1611 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1612 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1613 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1615 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1616 val2 + modest_text_utils_get_subject_prefix_len(val2),
1623 /* Drag and drop stuff */
1625 drag_data_get_cb (GtkWidget *widget,
1626 GdkDragContext *context,
1627 GtkSelectionData *selection_data,
1632 ModestHeaderView *self = NULL;
1633 ModestHeaderViewPrivate *priv = NULL;
1635 self = MODEST_HEADER_VIEW (widget);
1636 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1638 /* Set the data. Do not use the current selection because it
1639 could be different than the selection at the beginning of
1641 modest_dnd_selection_data_set_paths (selection_data,
1642 priv->drag_begin_cached_selected_rows);
1646 * We're caching the selected rows at the beginning because the
1647 * selection could change between drag-begin and drag-data-get, for
1648 * example if we have a set of rows already selected, and then we
1649 * click in one of them (without SHIFT key pressed) and begin a drag,
1650 * the selection at that moment contains all the selected lines, but
1651 * after dropping the selection, the release event provokes that only
1652 * the row used to begin the drag is selected, so at the end the
1653 * drag&drop affects only one rows instead of all the selected ones.
1657 drag_begin_cb (GtkWidget *widget,
1658 GdkDragContext *context,
1661 ModestHeaderView *self = NULL;
1662 ModestHeaderViewPrivate *priv = NULL;
1663 GtkTreeSelection *selection;
1665 self = MODEST_HEADER_VIEW (widget);
1666 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1668 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1669 priv->drag_begin_cached_selected_rows =
1670 gtk_tree_selection_get_selected_rows (selection, NULL);
1674 * We use the drag-end signal to clear the cached selection, we use
1675 * this because this allways happens, whether or not the d&d was a
1679 drag_end_cb (GtkWidget *widget,
1683 ModestHeaderView *self = NULL;
1684 ModestHeaderViewPrivate *priv = NULL;
1686 self = MODEST_HEADER_VIEW (widget);
1687 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1689 /* Free cached data */
1690 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1691 g_list_free (priv->drag_begin_cached_selected_rows);
1692 priv->drag_begin_cached_selected_rows = NULL;
1695 /* Header view drag types */
1696 const GtkTargetEntry header_view_drag_types[] = {
1697 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1701 enable_drag_and_drop (GtkWidget *self)
1703 #ifdef MODEST_TOOLKIT_HILDON2
1706 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1707 header_view_drag_types,
1708 G_N_ELEMENTS (header_view_drag_types),
1709 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1713 disable_drag_and_drop (GtkWidget *self)
1715 #ifdef MODEST_TOOLKIT_HILDON2
1718 gtk_drag_source_unset (self);
1722 setup_drag_and_drop (GtkWidget *self)
1724 #ifdef MODEST_TOOLKIT_HILDON2
1727 enable_drag_and_drop(self);
1728 g_signal_connect(G_OBJECT (self), "drag_data_get",
1729 G_CALLBACK(drag_data_get_cb), NULL);
1731 g_signal_connect(G_OBJECT (self), "drag_begin",
1732 G_CALLBACK(drag_begin_cb), NULL);
1734 g_signal_connect(G_OBJECT (self), "drag_end",
1735 G_CALLBACK(drag_end_cb), NULL);
1738 static GtkTreePath *
1739 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1741 GtkTreePath *path = NULL;
1742 GtkTreeSelection *sel = NULL;
1745 sel = gtk_tree_view_get_selection(self);
1746 rows = gtk_tree_selection_get_selected_rows (sel, model);
1748 if ((rows == NULL) || (g_list_length(rows) != 1))
1751 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1756 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1762 #ifndef MODEST_TOOLKIT_HILDON2
1764 * This function moves the tree view scroll to the current selected
1765 * row when the widget grabs the focus
1768 on_focus_in (GtkWidget *self,
1769 GdkEventFocus *event,
1772 GtkTreeSelection *selection;
1773 GtkTreeModel *model;
1774 GList *selected = NULL;
1775 GtkTreePath *selected_path = NULL;
1777 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1781 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1782 /* If none selected yet, pick the first one */
1783 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1787 /* Return if the model is empty */
1788 if (!gtk_tree_model_get_iter_first (model, &iter))
1791 path = gtk_tree_model_get_path (model, &iter);
1792 gtk_tree_selection_select_path (selection, path);
1793 gtk_tree_path_free (path);
1796 /* Need to get the all the rows because is selection multiple */
1797 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1798 if (selected == NULL) return FALSE;
1799 selected_path = (GtkTreePath *) selected->data;
1802 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1803 g_list_free (selected);
1809 on_focus_out (GtkWidget *self,
1810 GdkEventFocus *event,
1814 if (!gtk_widget_is_focus (self)) {
1815 GtkTreeSelection *selection = NULL;
1816 GList *selected_rows = NULL;
1817 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1818 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1819 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1820 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1821 gtk_tree_selection_unselect_all (selection);
1822 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1823 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1824 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1825 g_list_free (selected_rows);
1833 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1835 enable_drag_and_drop(self);
1840 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1842 GtkTreeSelection *selection = NULL;
1843 GtkTreePath *path = NULL;
1844 gboolean already_selected = FALSE, already_opened = FALSE;
1845 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1847 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1849 GtkTreeModel *model;
1851 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1852 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1854 /* Get header from model */
1855 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1856 if (gtk_tree_model_get_iter (model, &iter, path)) {
1857 GValue value = {0,};
1860 gtk_tree_model_get_value (model, &iter,
1861 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1863 header = (TnyHeader *) g_value_get_object (&value);
1864 if (TNY_IS_HEADER (header)) {
1865 status = modest_tny_all_send_queues_get_msg_status (header);
1866 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1869 g_value_unset (&value);
1873 /* Enable drag and drop only if the user clicks on a row that
1874 it's already selected. If not, let him select items using
1875 the pointer. If the message is in an OUTBOX and in sending
1876 status disable drag and drop as well */
1877 if (!already_selected ||
1878 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1880 disable_drag_and_drop(self);
1883 gtk_tree_path_free(path);
1885 /* If it's already opened then do not let the button-press
1886 event go on because it'll perform a message open because
1887 we're clicking on to an already selected header */
1892 folder_monitor_update (TnyFolderObserver *self,
1893 TnyFolderChange *change)
1895 ModestHeaderViewPrivate *priv = NULL;
1896 TnyFolderChangeChanged changed;
1897 TnyFolder *folder = NULL;
1899 changed = tny_folder_change_get_changed (change);
1901 /* Do not notify the observers if the folder of the header
1902 view has changed before this call to the observer
1904 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1905 folder = tny_folder_change_get_folder (change);
1906 if (folder != priv->folder)
1909 MODEST_DEBUG_BLOCK (
1910 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1911 g_print ("ADDED %d/%d (r/t) \n",
1912 tny_folder_change_get_new_unread_count (change),
1913 tny_folder_change_get_new_all_count (change));
1914 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1915 g_print ("ALL COUNT %d\n",
1916 tny_folder_change_get_new_all_count (change));
1917 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1918 g_print ("UNREAD COUNT %d\n",
1919 tny_folder_change_get_new_unread_count (change));
1920 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1921 g_print ("EXPUNGED %d/%d (r/t) \n",
1922 tny_folder_change_get_new_unread_count (change),
1923 tny_folder_change_get_new_all_count (change));
1924 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1925 g_print ("FOLDER RENAME\n");
1926 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1927 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1928 tny_folder_change_get_new_unread_count (change),
1929 tny_folder_change_get_new_all_count (change));
1930 g_print ("---------------------------------------------------\n");
1933 /* Check folder count */
1934 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1935 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1937 g_mutex_lock (priv->observers_lock);
1939 /* Emit signal to evaluate how headers changes affects
1940 to the window view */
1941 g_signal_emit (G_OBJECT(self),
1942 signals[MSG_COUNT_CHANGED_SIGNAL],
1945 /* Added or removed headers, so data stored on cliboard are invalid */
1946 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1947 modest_email_clipboard_clear (priv->clipboard);
1949 g_mutex_unlock (priv->observers_lock);
1955 g_object_unref (folder);
1959 modest_header_view_is_empty (ModestHeaderView *self)
1961 ModestHeaderViewPrivate *priv;
1963 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1965 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1967 return priv->status == HEADER_VIEW_EMPTY;
1971 modest_header_view_clear (ModestHeaderView *self)
1973 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1975 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
1979 modest_header_view_copy_selection (ModestHeaderView *header_view)
1981 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1983 /* Copy selection */
1984 _clipboard_set_selected_data (header_view, FALSE);
1988 modest_header_view_cut_selection (ModestHeaderView *header_view)
1990 ModestHeaderViewPrivate *priv = NULL;
1991 const gchar **hidding = NULL;
1992 guint i, n_selected;
1994 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
1996 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1998 /* Copy selection */
1999 _clipboard_set_selected_data (header_view, TRUE);
2001 /* Get hidding ids */
2002 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2004 /* Clear hidding array created by previous cut operation */
2005 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2007 /* Copy hidding array */
2008 priv->n_selected = n_selected;
2009 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2010 for (i=0; i < n_selected; i++)
2011 priv->hidding_ids[i] = g_strdup(hidding[i]);
2013 /* Hide cut headers */
2014 modest_header_view_refilter (header_view);
2021 _clipboard_set_selected_data (ModestHeaderView *header_view,
2024 ModestHeaderViewPrivate *priv = NULL;
2025 TnyList *headers = NULL;
2027 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2028 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2030 /* Set selected data on clipboard */
2031 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2032 headers = modest_header_view_get_selected_headers (header_view);
2033 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2036 g_object_unref (headers);
2040 ModestHeaderView *self;
2045 notify_filter_change (gpointer data)
2047 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2049 g_signal_emit (info->self,
2050 signals[MSG_COUNT_CHANGED_SIGNAL],
2051 0, info->folder, NULL);
2057 notify_filter_change_destroy (gpointer data)
2059 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2060 ModestHeaderViewPrivate *priv;
2062 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2063 priv->status_timeout = 0;
2065 g_object_unref (info->self);
2066 g_object_unref (info->folder);
2067 g_slice_free (NotifyFilterInfo, info);
2071 filter_row (GtkTreeModel *model,
2075 ModestHeaderViewPrivate *priv = NULL;
2076 TnyHeaderFlags flags;
2077 TnyHeader *header = NULL;
2080 gboolean visible = TRUE;
2081 gboolean found = FALSE;
2082 GValue value = {0,};
2083 HeaderViewStatus old_status;
2085 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2086 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2088 /* Get header from model */
2089 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2090 flags = (TnyHeaderFlags) g_value_get_int (&value);
2091 g_value_unset (&value);
2092 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2093 header = (TnyHeader *) g_value_get_object (&value);
2094 g_value_unset (&value);
2096 /* Hide deleted and mark as deleted heders */
2097 if (flags & TNY_HEADER_FLAG_DELETED ||
2098 flags & TNY_HEADER_FLAG_EXPUNGED) {
2103 /* If no data on clipboard, return always TRUE */
2104 if (modest_email_clipboard_cleared(priv->clipboard)) {
2109 /* Get message id from header (ensure is a valid id) */
2116 if (priv->hidding_ids != NULL) {
2117 id = tny_header_dup_message_id (header);
2118 for (i=0; i < priv->n_selected && !found; i++)
2119 if (priv->hidding_ids[i] != NULL && id != NULL)
2120 found = (!strcmp (priv->hidding_ids[i], id));
2126 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2127 if (priv->is_outbox &&
2128 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING)
2132 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2133 if (priv->is_outbox &&
2134 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING)
2139 old_status = priv->status;
2140 priv->status = ((gboolean) priv->status) && !visible;
2141 if ((priv->notify_status) && (priv->status != old_status)) {
2142 NotifyFilterInfo *info;
2144 if (priv->status_timeout)
2145 g_source_remove (priv->status_timeout);
2147 info = g_slice_new0 (NotifyFilterInfo);
2148 info->self = g_object_ref (G_OBJECT (user_data));
2149 info->folder = tny_header_get_folder (header);
2150 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2151 notify_filter_change,
2153 notify_filter_change_destroy);
2160 _clear_hidding_filter (ModestHeaderView *header_view)
2162 ModestHeaderViewPrivate *priv = NULL;
2165 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2166 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2168 if (priv->hidding_ids != NULL) {
2169 for (i=0; i < priv->n_selected; i++)
2170 g_free (priv->hidding_ids[i]);
2171 g_free(priv->hidding_ids);
2176 modest_header_view_refilter (ModestHeaderView *header_view)
2178 GtkTreeModel *model = NULL;
2179 ModestHeaderViewPrivate *priv = NULL;
2181 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2182 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2184 /* Hide cut headers */
2185 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2186 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2187 priv->status = HEADER_VIEW_INIT;
2188 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2193 * Called when an account is removed. If I'm showing a folder of the
2194 * account that has been removed then clear the view
2197 on_account_removed (TnyAccountStore *self,
2198 TnyAccount *account,
2201 ModestHeaderViewPrivate *priv = NULL;
2203 /* Ignore changes in transport accounts */
2204 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2207 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2210 TnyAccount *my_account;
2212 my_account = tny_folder_get_account (priv->folder);
2213 if (my_account == account)
2214 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2215 g_object_unref (my_account);
2220 modest_header_view_add_observer(ModestHeaderView *header_view,
2221 ModestHeaderViewObserver *observer)
2223 ModestHeaderViewPrivate *priv;
2225 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2226 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2228 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2230 g_mutex_lock(priv->observer_list_lock);
2231 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2232 g_mutex_unlock(priv->observer_list_lock);
2236 modest_header_view_remove_observer(ModestHeaderView *header_view,
2237 ModestHeaderViewObserver *observer)
2239 ModestHeaderViewPrivate *priv;
2241 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2242 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2244 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2246 g_mutex_lock(priv->observer_list_lock);
2247 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2248 g_mutex_unlock(priv->observer_list_lock);
2252 modest_header_view_notify_observers(ModestHeaderView *header_view,
2253 GtkTreeModel *model,
2254 const gchar *tny_folder_id)
2256 ModestHeaderViewPrivate *priv = NULL;
2258 ModestHeaderViewObserver *observer;
2261 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2263 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2265 g_mutex_lock(priv->observer_list_lock);
2266 iter = priv->observer_list;
2267 while(iter != NULL){
2268 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2269 modest_header_view_observer_update(observer, model,
2271 iter = g_slist_next(iter);
2273 g_mutex_unlock(priv->observer_list_lock);
2277 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2279 ModestHeaderViewPrivate *priv = NULL;
2281 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2282 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2286 modest_header_view_set_filter (ModestHeaderView *self,
2287 ModestHeaderViewFilter filter)
2289 ModestHeaderViewPrivate *priv;
2290 GtkTreeModel *filter_model;
2292 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2293 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2295 priv->filter |= filter;
2297 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2298 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2299 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2304 modest_header_view_unset_filter (ModestHeaderView *self,
2305 ModestHeaderViewFilter filter)
2307 ModestHeaderViewPrivate *priv;
2308 GtkTreeModel *filter_model;
2310 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2311 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2313 priv->filter &= ~filter;
2315 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2316 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2317 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));