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>
53 #include <modest-ui-constants.h>
55 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
56 static void modest_header_view_init (ModestHeaderView *obj);
57 static void modest_header_view_finalize (GObject *obj);
58 static void modest_header_view_dispose (GObject *obj);
60 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
61 GtkTreeViewColumn *column, gpointer userdata);
63 static gint cmp_rows (GtkTreeModel *tree_model,
68 static gint cmp_subject_rows (GtkTreeModel *tree_model,
73 static gboolean filter_row (GtkTreeModel *model,
77 static void on_account_removed (TnyAccountStore *self,
81 static void on_selection_changed (GtkTreeSelection *sel,
84 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
87 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
90 static void setup_drag_and_drop (GtkWidget *self);
92 static void enable_drag_and_drop (GtkWidget *self);
94 static void disable_drag_and_drop (GtkWidget *self);
96 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
98 #ifndef MODEST_TOOLKIT_HILDON2
99 static gboolean on_focus_in (GtkWidget *sef,
100 GdkEventFocus *event,
103 static gboolean on_focus_out (GtkWidget *self,
104 GdkEventFocus *event,
108 static void folder_monitor_update (TnyFolderObserver *self,
109 TnyFolderChange *change);
111 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
113 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
115 static void _clear_hidding_filter (ModestHeaderView *header_view);
117 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
119 const gchar *tny_folder_id);
121 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
122 GdkEventExpose *event,
125 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
126 static void update_style (ModestHeaderView *self);
129 HEADER_VIEW_NON_EMPTY,
134 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
135 struct _ModestHeaderViewPrivate {
137 ModestHeaderViewStyle style;
140 TnyFolderMonitor *monitor;
141 GMutex *observers_lock;
143 /*header-view-observer observer*/
144 GMutex *observer_list_lock;
145 GSList *observer_list;
147 /* not unref this object, its a singlenton */
148 ModestEmailClipboard *clipboard;
150 /* Filter tree model */
153 GtkTreeRowReference *autoselect_reference;
154 ModestHeaderViewFilter filter;
156 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
157 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
159 gulong selection_changed_handler;
160 gulong acc_removed_handler;
162 GList *drag_begin_cached_selected_rows;
164 HeaderViewStatus status;
165 guint status_timeout;
166 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
168 ModestDatetimeFormatter *datetime_formatter;
170 GtkCellRenderer *renderer_address;
171 GtkCellRenderer *renderer_date_status;
174 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
175 struct _HeadersCountChangedHelper {
176 ModestHeaderView *self;
177 TnyFolderChange *change;
181 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
182 MODEST_TYPE_HEADER_VIEW, \
183 ModestHeaderViewPrivate))
187 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
190 HEADER_SELECTED_SIGNAL,
191 HEADER_ACTIVATED_SIGNAL,
192 ITEM_NOT_FOUND_SIGNAL,
193 MSG_COUNT_CHANGED_SIGNAL,
194 UPDATING_MSG_LIST_SIGNAL,
199 static GObjectClass *parent_class = NULL;
201 /* uncomment the following if you have defined any signals */
202 static guint signals[LAST_SIGNAL] = {0};
205 modest_header_view_get_type (void)
207 static GType my_type = 0;
209 static const GTypeInfo my_info = {
210 sizeof(ModestHeaderViewClass),
211 NULL, /* base init */
212 NULL, /* base finalize */
213 (GClassInitFunc) modest_header_view_class_init,
214 NULL, /* class finalize */
215 NULL, /* class data */
216 sizeof(ModestHeaderView),
218 (GInstanceInitFunc) modest_header_view_init,
222 static const GInterfaceInfo tny_folder_observer_info =
224 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
225 NULL, /* interface_finalize */
226 NULL /* interface_data */
228 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
232 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
233 &tny_folder_observer_info);
241 modest_header_view_class_init (ModestHeaderViewClass *klass)
243 GObjectClass *gobject_class;
244 gobject_class = (GObjectClass*) klass;
246 parent_class = g_type_class_peek_parent (klass);
247 gobject_class->finalize = modest_header_view_finalize;
248 gobject_class->dispose = modest_header_view_dispose;
250 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
252 signals[HEADER_SELECTED_SIGNAL] =
253 g_signal_new ("header_selected",
254 G_TYPE_FROM_CLASS (gobject_class),
256 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
258 g_cclosure_marshal_VOID__POINTER,
259 G_TYPE_NONE, 1, G_TYPE_POINTER);
261 signals[HEADER_ACTIVATED_SIGNAL] =
262 g_signal_new ("header_activated",
263 G_TYPE_FROM_CLASS (gobject_class),
265 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
267 gtk_marshal_VOID__POINTER_POINTER,
268 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
271 signals[ITEM_NOT_FOUND_SIGNAL] =
272 g_signal_new ("item_not_found",
273 G_TYPE_FROM_CLASS (gobject_class),
275 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
277 g_cclosure_marshal_VOID__INT,
278 G_TYPE_NONE, 1, G_TYPE_INT);
280 signals[MSG_COUNT_CHANGED_SIGNAL] =
281 g_signal_new ("msg_count_changed",
282 G_TYPE_FROM_CLASS (gobject_class),
284 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
286 modest_marshal_VOID__POINTER_POINTER,
287 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
289 signals[UPDATING_MSG_LIST_SIGNAL] =
290 g_signal_new ("updating-msg-list",
291 G_TYPE_FROM_CLASS (gobject_class),
293 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
295 g_cclosure_marshal_VOID__BOOLEAN,
296 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
298 #ifdef MODEST_TOOLKIT_HILDON2
299 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
305 tny_folder_observer_init (TnyFolderObserverIface *klass)
307 klass->update = folder_monitor_update;
310 static GtkTreeViewColumn*
311 get_new_column (const gchar *name, GtkCellRenderer *renderer,
312 gboolean resizable, gint sort_col_id, gboolean show_as_text,
313 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
315 GtkTreeViewColumn *column;
317 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
318 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
320 gtk_tree_view_column_set_resizable (column, resizable);
322 gtk_tree_view_column_set_expand (column, TRUE);
325 gtk_tree_view_column_add_attribute (column, renderer, "text",
327 if (sort_col_id >= 0)
328 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
330 gtk_tree_view_column_set_sort_indicator (column, FALSE);
331 gtk_tree_view_column_set_reorderable (column, TRUE);
334 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
341 remove_all_columns (ModestHeaderView *obj)
343 GList *columns, *cursor;
345 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
347 for (cursor = columns; cursor; cursor = cursor->next)
348 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
349 GTK_TREE_VIEW_COLUMN(cursor->data));
350 g_list_free (columns);
354 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
356 GtkTreeModel *tree_filter, *sortable;
357 GtkTreeViewColumn *column=NULL;
358 GtkTreeSelection *selection = NULL;
359 GtkCellRenderer *renderer_header,
360 *renderer_attach, *renderer_compact_date_or_status;
361 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
362 *renderer_subject, *renderer_subject_box, *renderer_recpt,
364 ModestHeaderViewPrivate *priv;
365 GtkTreeViewColumn *compact_column = NULL;
368 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
369 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
371 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
373 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
375 /* TODO: check whether these renderers need to be freed */
376 renderer_attach = gtk_cell_renderer_pixbuf_new ();
377 renderer_priority = gtk_cell_renderer_pixbuf_new ();
378 renderer_header = gtk_cell_renderer_text_new ();
380 renderer_compact_header = modest_vbox_cell_renderer_new ();
381 renderer_recpt_box = modest_hbox_cell_renderer_new ();
382 renderer_subject_box = modest_hbox_cell_renderer_new ();
383 renderer_recpt = gtk_cell_renderer_text_new ();
384 priv->renderer_address = renderer_recpt;
385 renderer_subject = gtk_cell_renderer_text_new ();
386 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
387 priv->renderer_date_status = renderer_compact_date_or_status;
389 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
390 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
391 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
392 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
393 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
394 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
395 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
396 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
397 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
398 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
399 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
400 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
401 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
402 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
404 #ifdef MODEST_TOOLKIT_HILDON2
405 g_object_set (G_OBJECT (renderer_compact_header), "xpad", MODEST_MARGIN_DOUBLE, NULL);
407 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
408 #ifndef MODEST_TOOLKIT_GTK
409 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
410 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
412 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
413 g_object_set(G_OBJECT(renderer_header),
414 "ellipsize", PANGO_ELLIPSIZE_END,
416 g_object_set (G_OBJECT (renderer_subject),
417 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
419 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
420 g_object_set (G_OBJECT (renderer_recpt),
421 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
423 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
424 g_object_set(G_OBJECT(renderer_compact_date_or_status),
425 "xalign", 1.0, "yalign", 0.1,
427 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
428 #ifdef MODEST_TOOLKIT_HILDON2
429 g_object_set (G_OBJECT (renderer_priority),
431 "xalign", 0.0, NULL);
432 g_object_set (G_OBJECT (renderer_attach),
434 "xalign", 0.0, NULL);
436 g_object_set (G_OBJECT (renderer_priority),
437 "yalign", 0.5, NULL);
438 g_object_set (G_OBJECT (renderer_attach),
439 "yalign", 0.0, NULL);
442 #ifdef MODEST_TOOLKIT_HILDON1
443 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
444 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
445 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
446 #elif MODEST_TOOLKIT_HILDON2
447 gtk_cell_renderer_set_fixed_size (renderer_attach, 24, 26);
448 gtk_cell_renderer_set_fixed_size (renderer_priority, 24, 26);
449 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
451 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
452 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
453 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
456 remove_all_columns (self);
458 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
459 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
460 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
461 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
463 /* Add new columns */
464 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
465 ModestHeaderViewColumn col =
466 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
468 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
469 g_printerr ("modest: invalid column %d in column list\n", col);
475 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
476 column = get_new_column (_("A"), renderer_attach, FALSE,
477 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
479 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
481 gtk_tree_view_column_set_fixed_width (column, 45);
485 case MODEST_HEADER_VIEW_COLUMN_FROM:
486 column = get_new_column (_("From"), renderer_header, TRUE,
487 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
489 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
490 GINT_TO_POINTER(TRUE));
493 case MODEST_HEADER_VIEW_COLUMN_TO:
494 column = get_new_column (_("To"), renderer_header, TRUE,
495 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
497 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
498 GINT_TO_POINTER(FALSE));
501 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
502 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
503 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
505 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
506 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
507 compact_column = column;
510 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
511 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
512 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
514 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
515 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
516 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
517 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
518 compact_column = column;
522 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
523 column = get_new_column (_("Subject"), renderer_header, TRUE,
524 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
526 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
530 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
531 column = get_new_column (_("Received"), renderer_header, TRUE,
532 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
534 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
535 GINT_TO_POINTER(TRUE));
538 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
539 column = get_new_column (_("Sent"), renderer_header, TRUE,
540 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
542 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
543 GINT_TO_POINTER(FALSE));
546 case MODEST_HEADER_VIEW_COLUMN_SIZE:
547 column = get_new_column (_("Size"), renderer_header, TRUE,
548 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
550 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
553 case MODEST_HEADER_VIEW_COLUMN_STATUS:
554 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
555 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
557 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
562 g_return_val_if_reached(FALSE);
565 /* we keep the column id around */
566 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
567 GINT_TO_POINTER(col));
569 /* we need this ptr when sorting the rows */
570 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
572 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
576 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
577 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
578 (GtkTreeIterCompareFunc) cmp_rows,
579 compact_column, NULL);
580 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
581 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
582 (GtkTreeIterCompareFunc) cmp_subject_rows,
583 compact_column, NULL);
587 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
593 datetime_format_changed (ModestDatetimeFormatter *formatter,
594 ModestHeaderView *self)
596 gtk_widget_queue_draw (GTK_WIDGET (self));
600 modest_header_view_init (ModestHeaderView *obj)
602 ModestHeaderViewPrivate *priv;
605 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
608 priv->is_outbox = FALSE;
610 priv->monitor = NULL;
611 priv->observers_lock = g_mutex_new ();
612 priv->autoselect_reference = NULL;
614 priv->status = HEADER_VIEW_INIT;
615 priv->status_timeout = 0;
616 priv->notify_status = TRUE;
618 priv->observer_list_lock = g_mutex_new();
619 priv->observer_list = NULL;
621 priv->clipboard = modest_runtime_get_email_clipboard ();
622 priv->hidding_ids = NULL;
623 priv->n_selected = 0;
624 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
625 priv->selection_changed_handler = 0;
626 priv->acc_removed_handler = 0;
628 /* Sort parameters */
629 for (j=0; j < 2; j++) {
630 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
631 priv->sort_colid[j][i] = -1;
632 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
636 priv->datetime_formatter = modest_datetime_formatter_new ();
637 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
638 G_CALLBACK (datetime_format_changed), (gpointer) obj);
640 setup_drag_and_drop (GTK_WIDGET(obj));
644 modest_header_view_dispose (GObject *obj)
646 ModestHeaderView *self;
647 ModestHeaderViewPrivate *priv;
648 GtkTreeSelection *sel;
650 self = MODEST_HEADER_VIEW(obj);
651 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
653 if (priv->datetime_formatter) {
654 g_object_unref (priv->datetime_formatter);
655 priv->datetime_formatter = NULL;
658 /* Free in the dispose to avoid unref cycles */
660 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
661 g_object_unref (G_OBJECT (priv->folder));
665 /* We need to do this here in the dispose because the
666 selection won't exist when finalizing */
667 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
668 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
669 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
670 priv->selection_changed_handler = 0;
673 G_OBJECT_CLASS(parent_class)->dispose (obj);
677 modest_header_view_finalize (GObject *obj)
679 ModestHeaderView *self;
680 ModestHeaderViewPrivate *priv;
682 self = MODEST_HEADER_VIEW(obj);
683 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
685 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
686 priv->acc_removed_handler)) {
687 g_signal_handler_disconnect (modest_runtime_get_account_store (),
688 priv->acc_removed_handler);
691 /* There is no need to lock because there should not be any
692 * reference to self now. */
693 g_mutex_free(priv->observer_list_lock);
694 g_slist_free(priv->observer_list);
696 g_mutex_lock (priv->observers_lock);
698 tny_folder_monitor_stop (priv->monitor);
699 g_object_unref (G_OBJECT (priv->monitor));
701 g_mutex_unlock (priv->observers_lock);
702 g_mutex_free (priv->observers_lock);
704 /* Clear hidding array created by cut operation */
705 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
707 if (priv->autoselect_reference != NULL) {
708 gtk_tree_row_reference_free (priv->autoselect_reference);
709 priv->autoselect_reference = NULL;
712 G_OBJECT_CLASS(parent_class)->finalize (obj);
717 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
720 GtkTreeSelection *sel;
721 ModestHeaderView *self;
722 ModestHeaderViewPrivate *priv;
724 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
727 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
728 self = MODEST_HEADER_VIEW(obj);
729 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
731 modest_header_view_set_style (self, style);
733 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
734 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
735 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
737 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
738 TRUE); /* alternating row colors */
740 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
741 priv->selection_changed_handler =
742 g_signal_connect_after (sel, "changed",
743 G_CALLBACK(on_selection_changed), self);
745 g_signal_connect (self, "row-activated",
746 G_CALLBACK (on_header_row_activated), NULL);
748 #ifndef MODEST_TOOLKIT_HILDON2
749 g_signal_connect (self, "focus-in-event",
750 G_CALLBACK(on_focus_in), NULL);
751 g_signal_connect (self, "focus-out-event",
752 G_CALLBACK(on_focus_out), NULL);
755 g_signal_connect (self, "button-press-event",
756 G_CALLBACK(on_button_press_event), NULL);
757 g_signal_connect (self, "button-release-event",
758 G_CALLBACK(on_button_release_event), NULL);
760 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
762 G_CALLBACK (on_account_removed),
765 g_signal_connect (self, "expose-event",
766 G_CALLBACK(modest_header_view_on_expose_event),
769 return GTK_WIDGET(self);
774 modest_header_view_count_selected_headers (ModestHeaderView *self)
776 GtkTreeSelection *sel;
779 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
781 /* Get selection object and check selected rows count */
782 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
783 selected_rows = gtk_tree_selection_count_selected_rows (sel);
785 return selected_rows;
789 modest_header_view_has_selected_headers (ModestHeaderView *self)
791 GtkTreeSelection *sel;
794 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
796 /* Get selection object and check selected rows count */
797 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
798 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
805 modest_header_view_get_selected_headers (ModestHeaderView *self)
807 GtkTreeSelection *sel;
808 ModestHeaderViewPrivate *priv;
809 TnyList *header_list = NULL;
811 GList *list, *tmp = NULL;
812 GtkTreeModel *tree_model = NULL;
815 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
817 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
819 /* Get selected rows */
820 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
821 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
824 header_list = tny_simple_list_new();
826 list = g_list_reverse (list);
829 /* get header from selection */
830 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
831 gtk_tree_model_get (tree_model, &iter,
832 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
834 /* Prepend to list */
835 tny_list_prepend (header_list, G_OBJECT (header));
836 g_object_unref (G_OBJECT (header));
838 tmp = g_list_next (tmp);
841 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
848 /* scroll our list view so the selected item is visible */
850 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
852 #ifdef MODEST_TOOLKIT_GTK
854 GtkTreePath *selected_path;
855 GtkTreePath *start, *end;
859 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
860 selected_path = gtk_tree_model_get_path (model, iter);
862 start = gtk_tree_path_new ();
863 end = gtk_tree_path_new ();
865 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
867 if (gtk_tree_path_compare (selected_path, start) < 0 ||
868 gtk_tree_path_compare (end, selected_path) < 0)
869 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
870 selected_path, NULL, TRUE,
873 gtk_tree_path_free (selected_path);
874 gtk_tree_path_free (start);
875 gtk_tree_path_free (end);
877 #endif /* MODEST_TOOLKIT_GTK */
882 modest_header_view_select_next (ModestHeaderView *self)
884 GtkTreeSelection *sel;
889 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
891 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
892 path = get_selected_row (GTK_TREE_VIEW(self), &model);
893 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
894 /* Unselect previous path */
895 gtk_tree_selection_unselect_path (sel, path);
897 /* Move path down and selects new one */
898 if (gtk_tree_model_iter_next (model, &iter)) {
899 gtk_tree_selection_select_iter (sel, &iter);
900 scroll_to_selected (self, &iter, FALSE);
902 gtk_tree_path_free(path);
908 modest_header_view_select_prev (ModestHeaderView *self)
910 GtkTreeSelection *sel;
915 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
917 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
918 path = get_selected_row (GTK_TREE_VIEW(self), &model);
919 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
920 /* Unselect previous path */
921 gtk_tree_selection_unselect_path (sel, path);
924 if (gtk_tree_path_prev (path)) {
925 gtk_tree_model_get_iter (model, &iter, path);
927 /* Select the new one */
928 gtk_tree_selection_select_iter (sel, &iter);
929 scroll_to_selected (self, &iter, TRUE);
932 gtk_tree_path_free (path);
937 modest_header_view_get_columns (ModestHeaderView *self)
939 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
941 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
947 modest_header_view_set_style (ModestHeaderView *self,
948 ModestHeaderViewStyle style)
950 ModestHeaderViewPrivate *priv;
951 gboolean show_col_headers = FALSE;
952 ModestHeaderViewStyle old_style;
954 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
955 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
958 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
959 if (priv->style == style)
960 return TRUE; /* nothing to do */
963 case MODEST_HEADER_VIEW_STYLE_DETAILS:
964 show_col_headers = TRUE;
966 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
969 g_return_val_if_reached (FALSE);
971 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
972 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
974 old_style = priv->style;
981 ModestHeaderViewStyle
982 modest_header_view_get_style (ModestHeaderView *self)
984 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
986 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
989 /* This is used to automatically select the first header if the user
990 * has not selected any header yet.
993 modest_header_view_on_expose_event(GtkTreeView *header_view,
994 GdkEventExpose *event,
997 GtkTreeSelection *sel;
999 GtkTreeIter tree_iter;
1000 ModestHeaderViewPrivate *priv;
1002 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1003 model = gtk_tree_view_get_model(header_view);
1008 #ifdef MODEST_TOOLKIT_HILDON2
1011 sel = gtk_tree_view_get_selection(header_view);
1012 if(!gtk_tree_selection_count_selected_rows(sel)) {
1013 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1014 GtkTreePath *tree_iter_path;
1015 /* Prevent the widget from getting the focus
1016 when selecting the first item */
1017 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1018 g_object_set(header_view, "can-focus", FALSE, NULL);
1019 gtk_tree_selection_select_iter(sel, &tree_iter);
1020 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1021 g_object_set(header_view, "can-focus", TRUE, NULL);
1022 if (priv->autoselect_reference) {
1023 gtk_tree_row_reference_free (priv->autoselect_reference);
1025 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1026 gtk_tree_path_free (tree_iter_path);
1029 if (priv->autoselect_reference != NULL) {
1030 gboolean moved_selection = FALSE;
1031 GtkTreePath * last_path;
1032 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1033 moved_selection = TRUE;
1037 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1038 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1039 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1040 moved_selection = TRUE;
1041 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1043 gtk_tree_path_free (last_path);
1045 if (moved_selection) {
1046 gtk_tree_row_reference_free (priv->autoselect_reference);
1047 priv->autoselect_reference = NULL;
1050 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1051 GtkTreePath *current_path;
1052 current_path = gtk_tree_model_get_path (model, &tree_iter);
1053 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1054 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1055 g_object_set(header_view, "can-focus", FALSE, NULL);
1056 gtk_tree_selection_unselect_all (sel);
1057 gtk_tree_selection_select_iter(sel, &tree_iter);
1058 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1059 g_object_set(header_view, "can-focus", TRUE, NULL);
1060 gtk_tree_row_reference_free (priv->autoselect_reference);
1061 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1063 gtk_tree_path_free (current_path);
1064 gtk_tree_path_free (last_path);
1074 modest_header_view_get_folder (ModestHeaderView *self)
1076 ModestHeaderViewPrivate *priv;
1078 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1080 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1083 g_object_ref (priv->folder);
1085 return priv->folder;
1089 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1095 ModestHeaderView *self;
1096 ModestHeaderViewPrivate *priv;
1098 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1100 self = MODEST_HEADER_VIEW (user_data);
1101 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1103 if (cancelled || err)
1106 /* Add IDLE observer (monitor) and another folder observer for
1107 new messages (self) */
1108 g_mutex_lock (priv->observers_lock);
1109 if (priv->monitor) {
1110 tny_folder_monitor_stop (priv->monitor);
1111 g_object_unref (G_OBJECT (priv->monitor));
1113 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1114 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1115 tny_folder_monitor_start (priv->monitor);
1116 g_mutex_unlock (priv->observers_lock);
1120 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1124 ModestHeaderViewPrivate *priv;
1125 GList *cols, *cursor;
1126 GtkTreeModel *filter_model, *sortable;
1128 GtkSortType sort_type;
1130 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1132 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1134 /* Start the monitor in the callback of the
1135 tny_gtk_header_list_model_set_folder call. It's crucial to
1136 do it there and not just after the call because we want the
1137 monitor to observe only the headers returned by the
1138 tny_folder_get_headers_async call that it's inside the
1139 tny_gtk_header_list_model_set_folder call. This way the
1140 monitor infrastructure could successfully cope with
1141 duplicates. For example if a tny_folder_add_msg_async is
1142 happening while tny_gtk_header_list_model_set_folder is
1143 invoked, then the first call could add a header that will
1144 be added again by tny_gtk_header_list_model_set_folder, so
1145 we'd end up with duplicate headers. sergio */
1146 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1148 set_folder_intern_get_headers_async_cb,
1151 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1152 g_object_unref (G_OBJECT (headers));
1154 /* Init filter_row function to examine empty status */
1155 priv->status = HEADER_VIEW_INIT;
1157 /* Create a tree model filter to hide and show rows for cut operations */
1158 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1159 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1163 g_object_unref (G_OBJECT (sortable));
1165 /* install our special sorting functions */
1166 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1168 /* Restore sort column id */
1170 type = modest_tny_folder_guess_folder_type (folder);
1171 if (type == TNY_FOLDER_TYPE_INVALID)
1172 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1174 sort_colid = modest_header_view_get_sort_column_id (self, type);
1175 sort_type = modest_header_view_get_sort_type (self, type);
1176 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1179 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1180 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1181 (GtkTreeIterCompareFunc) cmp_rows,
1183 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1184 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1185 (GtkTreeIterCompareFunc) cmp_subject_rows,
1190 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1191 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1192 tny_folder_get_id(folder));
1193 g_object_unref (G_OBJECT (filter_model));
1200 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1202 GtkSortType sort_type)
1204 ModestHeaderViewPrivate *priv = NULL;
1205 GtkTreeModel *tree_filter, *sortable = NULL;
1208 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1209 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1211 /* Get model and private data */
1212 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1213 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1214 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1215 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1217 /* Sort tree model */
1218 type = modest_tny_folder_guess_folder_type (priv->folder);
1219 if (type == TNY_FOLDER_TYPE_INVALID)
1220 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1222 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1225 /* Store new sort parameters */
1226 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1231 modest_header_view_set_sort_params (ModestHeaderView *self,
1233 GtkSortType sort_type,
1236 ModestHeaderViewPrivate *priv;
1237 ModestHeaderViewStyle style;
1239 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1240 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1241 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1243 style = modest_header_view_get_style (self);
1244 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1246 priv->sort_colid[style][type] = sort_colid;
1247 priv->sort_type[style][type] = sort_type;
1251 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1254 ModestHeaderViewPrivate *priv;
1255 ModestHeaderViewStyle style;
1257 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1258 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1260 style = modest_header_view_get_style (self);
1261 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1263 return priv->sort_colid[style][type];
1267 modest_header_view_get_sort_type (ModestHeaderView *self,
1270 ModestHeaderViewPrivate *priv;
1271 ModestHeaderViewStyle style;
1273 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1274 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1276 style = modest_header_view_get_style (self);
1277 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1279 return priv->sort_type[style][type];
1283 ModestHeaderView *header_view;
1284 RefreshAsyncUserCallback cb;
1289 folder_refreshed_cb (ModestMailOperation *mail_op,
1293 ModestHeaderViewPrivate *priv;
1294 SetFolderHelper *info;
1296 info = (SetFolderHelper*) user_data;
1298 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1302 info->cb (mail_op, folder, info->user_data);
1304 /* Start the folder count changes observer. We do not need it
1305 before the refresh. Note that the monitor could still be
1306 called for this refresh but now we know that the callback
1307 was previously called */
1308 g_mutex_lock (priv->observers_lock);
1309 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1310 g_mutex_unlock (priv->observers_lock);
1312 /* Notify the observers that the update is over */
1313 g_signal_emit (G_OBJECT (info->header_view),
1314 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1316 /* Allow filtering notifications from now on if the current
1317 folder is still the same (if not then the user has selected
1318 another one to refresh, we should wait until that refresh
1320 if (priv->folder == folder)
1321 priv->notify_status = TRUE;
1324 g_object_unref (info->header_view);
1329 refresh_folder_error_handler (ModestMailOperation *mail_op,
1332 const GError *error = modest_mail_operation_get_error (mail_op);
1334 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1335 error->code == TNY_IO_ERROR_WRITE ||
1336 error->code == TNY_IO_ERROR_READ) {
1337 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1338 /* If the mail op has been cancelled then it's not an error: don't show any message */
1339 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1340 modest_platform_information_banner (NULL, NULL,
1342 "cerm_device_memory_full"));
1348 modest_header_view_set_folder (ModestHeaderView *self,
1351 ModestWindow *progress_window,
1352 RefreshAsyncUserCallback callback,
1355 ModestHeaderViewPrivate *priv;
1357 g_return_if_fail (self);
1359 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1362 if (priv->status_timeout) {
1363 g_source_remove (priv->status_timeout);
1364 priv->status_timeout = 0;
1367 g_mutex_lock (priv->observers_lock);
1368 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1369 g_object_unref (priv->folder);
1370 priv->folder = NULL;
1371 g_mutex_unlock (priv->observers_lock);
1375 GtkTreeSelection *selection;
1376 SetFolderHelper *info;
1377 ModestMailOperation *mail_op = NULL;
1379 /* Set folder in the model */
1380 modest_header_view_set_folder_intern (self, folder);
1382 /* Pick my reference. Nothing to do with the mail operation */
1383 priv->folder = g_object_ref (folder);
1385 /* Do not notify about filterings until the refresh finishes */
1386 priv->notify_status = FALSE;
1388 /* Clear the selection if exists */
1389 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1390 gtk_tree_selection_unselect_all(selection);
1391 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1393 /* Notify the observers that the update begins */
1394 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1397 /* create the helper */
1398 info = g_malloc0 (sizeof (SetFolderHelper));
1399 info->header_view = g_object_ref (self);
1400 info->cb = callback;
1401 info->user_data = user_data;
1403 /* Create the mail operation (source will be the parent widget) */
1404 if (progress_window)
1405 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1406 refresh_folder_error_handler,
1409 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1412 /* Refresh the folder asynchronously */
1413 modest_mail_operation_refresh_folder (mail_op,
1415 folder_refreshed_cb,
1418 folder_refreshed_cb (mail_op, folder, info);
1421 g_object_unref (mail_op);
1423 g_mutex_lock (priv->observers_lock);
1425 if (priv->monitor) {
1426 tny_folder_monitor_stop (priv->monitor);
1427 g_object_unref (G_OBJECT (priv->monitor));
1428 priv->monitor = NULL;
1431 if (priv->autoselect_reference) {
1432 gtk_tree_row_reference_free (priv->autoselect_reference);
1433 priv->autoselect_reference = NULL;
1436 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1438 modest_header_view_notify_observers(self, NULL, NULL);
1440 g_mutex_unlock (priv->observers_lock);
1442 /* Notify the observers that the update is over */
1443 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1449 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1450 GtkTreeViewColumn *column, gpointer userdata)
1452 ModestHeaderView *self = NULL;
1453 ModestHeaderViewPrivate *priv = NULL;
1455 GtkTreeModel *model = NULL;
1456 TnyHeader *header = NULL;
1457 TnyHeaderFlags flags;
1459 self = MODEST_HEADER_VIEW (treeview);
1460 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1462 model = gtk_tree_view_get_model (treeview);
1463 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1466 /* get the first selected item */
1467 gtk_tree_model_get (model, &iter,
1468 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1469 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1472 /* Dont open DELETED messages */
1473 if (flags & TNY_HEADER_FLAG_DELETED) {
1476 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1477 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1478 modest_platform_information_banner (NULL, NULL, msg);
1484 g_signal_emit (G_OBJECT(self),
1485 signals[HEADER_ACTIVATED_SIGNAL],
1491 g_object_unref (G_OBJECT (header));
1496 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1498 GtkTreeModel *model;
1499 TnyHeader *header = NULL;
1500 GtkTreePath *path = NULL;
1502 ModestHeaderView *self;
1503 ModestHeaderViewPrivate *priv;
1504 GList *selected = NULL;
1506 g_return_if_fail (sel);
1507 g_return_if_fail (user_data);
1509 self = MODEST_HEADER_VIEW (user_data);
1510 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1512 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1513 if (selected != NULL)
1514 path = (GtkTreePath *) selected->data;
1515 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1516 return; /* msg was _un_selected */
1518 gtk_tree_model_get (model, &iter,
1519 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1523 g_signal_emit (G_OBJECT(self),
1524 signals[HEADER_SELECTED_SIGNAL],
1527 g_object_unref (G_OBJECT (header));
1529 /* free all items in 'selected' */
1530 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1531 g_list_free (selected);
1535 /* PROTECTED method. It's useful when we want to force a given
1536 selection to reload a msg. For example if we have selected a header
1537 in offline mode, when Modest become online, we want to reload the
1538 message automatically without an user click over the header */
1540 _modest_header_view_change_selection (GtkTreeSelection *selection,
1543 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1544 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1546 on_selection_changed (selection, user_data);
1550 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1557 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1561 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1565 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1573 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1580 /* static int counter = 0; */
1582 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1583 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1584 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1588 case TNY_HEADER_FLAG_ATTACHMENTS:
1590 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1591 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1592 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1593 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1595 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1596 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1598 return cmp ? cmp : t1 - t2;
1600 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1601 TnyHeader *header1 = NULL, *header2 = NULL;
1603 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1604 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1605 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1606 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1608 /* This is for making priority values respect the intuitive sort relationship
1609 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1611 if (header1 && header2) {
1612 cmp = compare_priorities (tny_header_get_priority (header1),
1613 tny_header_get_priority (header2));
1614 g_object_unref (header1);
1615 g_object_unref (header2);
1617 return cmp ? cmp : t1 - t2;
1623 return &iter1 - &iter2; /* oughhhh */
1628 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1634 /* static int counter = 0; */
1636 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1638 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1639 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1640 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1641 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1643 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1644 val2 + modest_text_utils_get_subject_prefix_len(val2),
1651 /* Drag and drop stuff */
1653 drag_data_get_cb (GtkWidget *widget,
1654 GdkDragContext *context,
1655 GtkSelectionData *selection_data,
1660 ModestHeaderView *self = NULL;
1661 ModestHeaderViewPrivate *priv = NULL;
1663 self = MODEST_HEADER_VIEW (widget);
1664 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1666 /* Set the data. Do not use the current selection because it
1667 could be different than the selection at the beginning of
1669 modest_dnd_selection_data_set_paths (selection_data,
1670 priv->drag_begin_cached_selected_rows);
1674 * We're caching the selected rows at the beginning because the
1675 * selection could change between drag-begin and drag-data-get, for
1676 * example if we have a set of rows already selected, and then we
1677 * click in one of them (without SHIFT key pressed) and begin a drag,
1678 * the selection at that moment contains all the selected lines, but
1679 * after dropping the selection, the release event provokes that only
1680 * the row used to begin the drag is selected, so at the end the
1681 * drag&drop affects only one rows instead of all the selected ones.
1685 drag_begin_cb (GtkWidget *widget,
1686 GdkDragContext *context,
1689 ModestHeaderView *self = NULL;
1690 ModestHeaderViewPrivate *priv = NULL;
1691 GtkTreeSelection *selection;
1693 self = MODEST_HEADER_VIEW (widget);
1694 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1696 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1697 priv->drag_begin_cached_selected_rows =
1698 gtk_tree_selection_get_selected_rows (selection, NULL);
1702 * We use the drag-end signal to clear the cached selection, we use
1703 * this because this allways happens, whether or not the d&d was a
1707 drag_end_cb (GtkWidget *widget,
1711 ModestHeaderView *self = NULL;
1712 ModestHeaderViewPrivate *priv = NULL;
1714 self = MODEST_HEADER_VIEW (widget);
1715 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1717 /* Free cached data */
1718 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1719 g_list_free (priv->drag_begin_cached_selected_rows);
1720 priv->drag_begin_cached_selected_rows = NULL;
1723 /* Header view drag types */
1724 const GtkTargetEntry header_view_drag_types[] = {
1725 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1729 enable_drag_and_drop (GtkWidget *self)
1731 #ifdef MODEST_TOOLKIT_HILDON2
1734 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1735 header_view_drag_types,
1736 G_N_ELEMENTS (header_view_drag_types),
1737 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1741 disable_drag_and_drop (GtkWidget *self)
1743 #ifdef MODEST_TOOLKIT_HILDON2
1746 gtk_drag_source_unset (self);
1750 setup_drag_and_drop (GtkWidget *self)
1752 #ifdef MODEST_TOOLKIT_HILDON2
1755 enable_drag_and_drop(self);
1756 g_signal_connect(G_OBJECT (self), "drag_data_get",
1757 G_CALLBACK(drag_data_get_cb), NULL);
1759 g_signal_connect(G_OBJECT (self), "drag_begin",
1760 G_CALLBACK(drag_begin_cb), NULL);
1762 g_signal_connect(G_OBJECT (self), "drag_end",
1763 G_CALLBACK(drag_end_cb), NULL);
1766 static GtkTreePath *
1767 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1769 GtkTreePath *path = NULL;
1770 GtkTreeSelection *sel = NULL;
1773 sel = gtk_tree_view_get_selection(self);
1774 rows = gtk_tree_selection_get_selected_rows (sel, model);
1776 if ((rows == NULL) || (g_list_length(rows) != 1))
1779 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1784 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1790 #ifndef MODEST_TOOLKIT_HILDON2
1792 * This function moves the tree view scroll to the current selected
1793 * row when the widget grabs the focus
1796 on_focus_in (GtkWidget *self,
1797 GdkEventFocus *event,
1800 GtkTreeSelection *selection;
1801 GtkTreeModel *model;
1802 GList *selected = NULL;
1803 GtkTreePath *selected_path = NULL;
1805 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1809 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1810 /* If none selected yet, pick the first one */
1811 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1815 /* Return if the model is empty */
1816 if (!gtk_tree_model_get_iter_first (model, &iter))
1819 path = gtk_tree_model_get_path (model, &iter);
1820 gtk_tree_selection_select_path (selection, path);
1821 gtk_tree_path_free (path);
1824 /* Need to get the all the rows because is selection multiple */
1825 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1826 if (selected == NULL) return FALSE;
1827 selected_path = (GtkTreePath *) selected->data;
1830 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1831 g_list_free (selected);
1837 on_focus_out (GtkWidget *self,
1838 GdkEventFocus *event,
1842 if (!gtk_widget_is_focus (self)) {
1843 GtkTreeSelection *selection = NULL;
1844 GList *selected_rows = NULL;
1845 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1846 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1847 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1848 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1849 gtk_tree_selection_unselect_all (selection);
1850 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1851 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1852 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1853 g_list_free (selected_rows);
1861 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1863 enable_drag_and_drop(self);
1868 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1870 GtkTreeSelection *selection = NULL;
1871 GtkTreePath *path = NULL;
1872 gboolean already_selected = FALSE, already_opened = FALSE;
1873 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1875 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1877 GtkTreeModel *model;
1879 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1880 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1882 /* Get header from model */
1883 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1884 if (gtk_tree_model_get_iter (model, &iter, path)) {
1885 GValue value = {0,};
1888 gtk_tree_model_get_value (model, &iter,
1889 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1891 header = (TnyHeader *) g_value_get_object (&value);
1892 if (TNY_IS_HEADER (header)) {
1893 status = modest_tny_all_send_queues_get_msg_status (header);
1894 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1897 g_value_unset (&value);
1901 /* Enable drag and drop only if the user clicks on a row that
1902 it's already selected. If not, let him select items using
1903 the pointer. If the message is in an OUTBOX and in sending
1904 status disable drag and drop as well */
1905 if (!already_selected ||
1906 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1908 disable_drag_and_drop(self);
1911 gtk_tree_path_free(path);
1913 /* If it's already opened then do not let the button-press
1914 event go on because it'll perform a message open because
1915 we're clicking on to an already selected header */
1920 folder_monitor_update (TnyFolderObserver *self,
1921 TnyFolderChange *change)
1923 ModestHeaderViewPrivate *priv = NULL;
1924 TnyFolderChangeChanged changed;
1925 TnyFolder *folder = NULL;
1927 changed = tny_folder_change_get_changed (change);
1929 /* Do not notify the observers if the folder of the header
1930 view has changed before this call to the observer
1932 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1933 folder = tny_folder_change_get_folder (change);
1934 if (folder != priv->folder)
1937 MODEST_DEBUG_BLOCK (
1938 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1939 g_print ("ADDED %d/%d (r/t) \n",
1940 tny_folder_change_get_new_unread_count (change),
1941 tny_folder_change_get_new_all_count (change));
1942 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1943 g_print ("ALL COUNT %d\n",
1944 tny_folder_change_get_new_all_count (change));
1945 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1946 g_print ("UNREAD COUNT %d\n",
1947 tny_folder_change_get_new_unread_count (change));
1948 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1949 g_print ("EXPUNGED %d/%d (r/t) \n",
1950 tny_folder_change_get_new_unread_count (change),
1951 tny_folder_change_get_new_all_count (change));
1952 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1953 g_print ("FOLDER RENAME\n");
1954 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1955 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1956 tny_folder_change_get_new_unread_count (change),
1957 tny_folder_change_get_new_all_count (change));
1958 g_print ("---------------------------------------------------\n");
1961 /* Check folder count */
1962 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1963 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1965 g_mutex_lock (priv->observers_lock);
1967 /* Emit signal to evaluate how headers changes affects
1968 to the window view */
1969 g_signal_emit (G_OBJECT(self),
1970 signals[MSG_COUNT_CHANGED_SIGNAL],
1973 /* Added or removed headers, so data stored on cliboard are invalid */
1974 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1975 modest_email_clipboard_clear (priv->clipboard);
1977 g_mutex_unlock (priv->observers_lock);
1983 g_object_unref (folder);
1987 modest_header_view_is_empty (ModestHeaderView *self)
1989 ModestHeaderViewPrivate *priv;
1991 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1993 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1995 return priv->status == HEADER_VIEW_EMPTY;
1999 modest_header_view_clear (ModestHeaderView *self)
2001 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2003 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2007 modest_header_view_copy_selection (ModestHeaderView *header_view)
2009 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2011 /* Copy selection */
2012 _clipboard_set_selected_data (header_view, FALSE);
2016 modest_header_view_cut_selection (ModestHeaderView *header_view)
2018 ModestHeaderViewPrivate *priv = NULL;
2019 const gchar **hidding = NULL;
2020 guint i, n_selected;
2022 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2024 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2026 /* Copy selection */
2027 _clipboard_set_selected_data (header_view, TRUE);
2029 /* Get hidding ids */
2030 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2032 /* Clear hidding array created by previous cut operation */
2033 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2035 /* Copy hidding array */
2036 priv->n_selected = n_selected;
2037 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2038 for (i=0; i < n_selected; i++)
2039 priv->hidding_ids[i] = g_strdup(hidding[i]);
2041 /* Hide cut headers */
2042 modest_header_view_refilter (header_view);
2049 _clipboard_set_selected_data (ModestHeaderView *header_view,
2052 ModestHeaderViewPrivate *priv = NULL;
2053 TnyList *headers = NULL;
2055 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2056 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2058 /* Set selected data on clipboard */
2059 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2060 headers = modest_header_view_get_selected_headers (header_view);
2061 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2064 g_object_unref (headers);
2068 ModestHeaderView *self;
2073 notify_filter_change (gpointer data)
2075 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2077 g_signal_emit (info->self,
2078 signals[MSG_COUNT_CHANGED_SIGNAL],
2079 0, info->folder, NULL);
2085 notify_filter_change_destroy (gpointer data)
2087 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2088 ModestHeaderViewPrivate *priv;
2090 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2091 priv->status_timeout = 0;
2093 g_object_unref (info->self);
2094 g_object_unref (info->folder);
2095 g_slice_free (NotifyFilterInfo, info);
2099 filter_row (GtkTreeModel *model,
2103 ModestHeaderViewPrivate *priv = NULL;
2104 TnyHeaderFlags flags;
2105 TnyHeader *header = NULL;
2108 gboolean visible = TRUE;
2109 gboolean found = FALSE;
2110 GValue value = {0,};
2111 HeaderViewStatus old_status;
2113 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2114 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2116 /* Get header from model */
2117 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2118 flags = (TnyHeaderFlags) g_value_get_int (&value);
2119 g_value_unset (&value);
2120 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2121 header = (TnyHeader *) g_value_get_object (&value);
2122 g_value_unset (&value);
2124 /* Hide deleted and mark as deleted heders */
2125 if (flags & TNY_HEADER_FLAG_DELETED ||
2126 flags & TNY_HEADER_FLAG_EXPUNGED) {
2131 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2132 if (priv->is_outbox &&
2133 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2139 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2140 if (priv->is_outbox &&
2141 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2147 /* If no data on clipboard, return always TRUE */
2148 if (modest_email_clipboard_cleared(priv->clipboard)) {
2153 /* Get message id from header (ensure is a valid id) */
2160 if (priv->hidding_ids != NULL) {
2161 id = tny_header_dup_message_id (header);
2162 for (i=0; i < priv->n_selected && !found; i++)
2163 if (priv->hidding_ids[i] != NULL && id != NULL)
2164 found = (!strcmp (priv->hidding_ids[i], id));
2171 old_status = priv->status;
2172 priv->status = ((gboolean) priv->status) && !visible;
2173 if ((priv->notify_status) && (priv->status != old_status)) {
2174 NotifyFilterInfo *info;
2176 if (priv->status_timeout)
2177 g_source_remove (priv->status_timeout);
2179 info = g_slice_new0 (NotifyFilterInfo);
2180 info->self = g_object_ref (G_OBJECT (user_data));
2181 info->folder = tny_header_get_folder (header);
2182 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2183 notify_filter_change,
2185 notify_filter_change_destroy);
2192 _clear_hidding_filter (ModestHeaderView *header_view)
2194 ModestHeaderViewPrivate *priv = NULL;
2197 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2198 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2200 if (priv->hidding_ids != NULL) {
2201 for (i=0; i < priv->n_selected; i++)
2202 g_free (priv->hidding_ids[i]);
2203 g_free(priv->hidding_ids);
2208 modest_header_view_refilter (ModestHeaderView *header_view)
2210 GtkTreeModel *model = NULL;
2211 ModestHeaderViewPrivate *priv = NULL;
2213 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2214 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2216 /* Hide cut headers */
2217 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2218 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2219 priv->status = HEADER_VIEW_INIT;
2220 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2225 * Called when an account is removed. If I'm showing a folder of the
2226 * account that has been removed then clear the view
2229 on_account_removed (TnyAccountStore *self,
2230 TnyAccount *account,
2233 ModestHeaderViewPrivate *priv = NULL;
2235 /* Ignore changes in transport accounts */
2236 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2239 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2242 TnyAccount *my_account;
2244 my_account = tny_folder_get_account (priv->folder);
2245 if (my_account == account)
2246 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2247 g_object_unref (my_account);
2252 modest_header_view_add_observer(ModestHeaderView *header_view,
2253 ModestHeaderViewObserver *observer)
2255 ModestHeaderViewPrivate *priv;
2257 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2258 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2260 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2262 g_mutex_lock(priv->observer_list_lock);
2263 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2264 g_mutex_unlock(priv->observer_list_lock);
2268 modest_header_view_remove_observer(ModestHeaderView *header_view,
2269 ModestHeaderViewObserver *observer)
2271 ModestHeaderViewPrivate *priv;
2273 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2274 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2276 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2278 g_mutex_lock(priv->observer_list_lock);
2279 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2280 g_mutex_unlock(priv->observer_list_lock);
2284 modest_header_view_notify_observers(ModestHeaderView *header_view,
2285 GtkTreeModel *model,
2286 const gchar *tny_folder_id)
2288 ModestHeaderViewPrivate *priv = NULL;
2290 ModestHeaderViewObserver *observer;
2293 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2295 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2297 g_mutex_lock(priv->observer_list_lock);
2298 iter = priv->observer_list;
2299 while(iter != NULL){
2300 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2301 modest_header_view_observer_update(observer, model,
2303 iter = g_slist_next(iter);
2305 g_mutex_unlock(priv->observer_list_lock);
2309 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2311 ModestHeaderViewPrivate *priv = NULL;
2313 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2314 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2318 modest_header_view_set_filter (ModestHeaderView *self,
2319 ModestHeaderViewFilter filter)
2321 ModestHeaderViewPrivate *priv;
2322 GtkTreeModel *filter_model;
2324 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2325 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2327 priv->filter |= filter;
2329 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2330 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2331 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2336 modest_header_view_unset_filter (ModestHeaderView *self,
2337 ModestHeaderViewFilter filter)
2339 ModestHeaderViewPrivate *priv;
2340 GtkTreeModel *filter_model;
2342 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2343 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2345 priv->filter &= ~filter;
2347 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2348 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2349 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2354 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2356 if (strcmp ("style", spec->name) == 0) {
2357 update_style (MODEST_HEADER_VIEW (obj));
2358 gtk_widget_queue_draw (GTK_WIDGET (obj));
2363 update_style (ModestHeaderView *self)
2365 ModestHeaderViewPrivate *priv;
2366 GdkColor style_color;
2367 PangoAttrList *attr_list;
2369 PangoAttribute *attr;
2371 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2372 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2376 attr_list = pango_attr_list_new ();
2377 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2378 gdk_color_parse ("grey", &style_color);
2380 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2381 pango_attr_list_insert (attr_list, attr);
2384 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2386 "SmallSystemFont", NULL,
2389 attr = pango_attr_font_desc_new (pango_font_description_copy
2390 (style->font_desc));
2391 pango_attr_list_insert (attr_list, attr);
2393 g_object_set (G_OBJECT (priv->renderer_address),
2394 "foreground-gdk", &style_color,
2395 "foreground-set", TRUE,
2396 "attributes", attr_list,
2398 g_object_set (G_OBJECT (priv->renderer_date_status),
2399 "foreground-gdk", &style_color,
2400 "foreground-set", TRUE,
2401 "attributes", attr_list,
2403 pango_attr_list_unref (attr_list);