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 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1341 modest_platform_information_banner (NULL, NULL, msg);
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);
1422 g_object_unref (mail_op);
1424 g_mutex_lock (priv->observers_lock);
1426 if (priv->monitor) {
1427 tny_folder_monitor_stop (priv->monitor);
1428 g_object_unref (G_OBJECT (priv->monitor));
1429 priv->monitor = NULL;
1432 if (priv->autoselect_reference) {
1433 gtk_tree_row_reference_free (priv->autoselect_reference);
1434 priv->autoselect_reference = NULL;
1437 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1439 modest_header_view_notify_observers(self, NULL, NULL);
1441 g_mutex_unlock (priv->observers_lock);
1443 /* Notify the observers that the update is over */
1444 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1450 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1451 GtkTreeViewColumn *column, gpointer userdata)
1453 ModestHeaderView *self = NULL;
1454 ModestHeaderViewPrivate *priv = NULL;
1456 GtkTreeModel *model = NULL;
1457 TnyHeader *header = NULL;
1458 TnyHeaderFlags flags;
1460 self = MODEST_HEADER_VIEW (treeview);
1461 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1463 model = gtk_tree_view_get_model (treeview);
1464 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1467 /* get the first selected item */
1468 gtk_tree_model_get (model, &iter,
1469 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1470 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1473 /* Dont open DELETED messages */
1474 if (flags & TNY_HEADER_FLAG_DELETED) {
1477 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1478 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1479 modest_platform_information_banner (NULL, NULL, msg);
1485 g_signal_emit (G_OBJECT(self),
1486 signals[HEADER_ACTIVATED_SIGNAL],
1492 g_object_unref (G_OBJECT (header));
1497 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1499 GtkTreeModel *model;
1500 TnyHeader *header = NULL;
1501 GtkTreePath *path = NULL;
1503 ModestHeaderView *self;
1504 ModestHeaderViewPrivate *priv;
1505 GList *selected = NULL;
1507 g_return_if_fail (sel);
1508 g_return_if_fail (user_data);
1510 self = MODEST_HEADER_VIEW (user_data);
1511 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1513 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1514 if (selected != NULL)
1515 path = (GtkTreePath *) selected->data;
1516 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1517 return; /* msg was _un_selected */
1519 gtk_tree_model_get (model, &iter,
1520 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1524 g_signal_emit (G_OBJECT(self),
1525 signals[HEADER_SELECTED_SIGNAL],
1528 g_object_unref (G_OBJECT (header));
1530 /* free all items in 'selected' */
1531 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1532 g_list_free (selected);
1536 /* PROTECTED method. It's useful when we want to force a given
1537 selection to reload a msg. For example if we have selected a header
1538 in offline mode, when Modest become online, we want to reload the
1539 message automatically without an user click over the header */
1541 _modest_header_view_change_selection (GtkTreeSelection *selection,
1544 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1545 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1547 on_selection_changed (selection, user_data);
1551 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1558 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1562 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1566 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1574 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1581 /* static int counter = 0; */
1583 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1584 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1585 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1589 case TNY_HEADER_FLAG_ATTACHMENTS:
1591 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1592 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1593 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1594 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1596 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1597 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1599 return cmp ? cmp : t1 - t2;
1601 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1602 TnyHeader *header1 = NULL, *header2 = NULL;
1604 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1605 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1606 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1607 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1609 /* This is for making priority values respect the intuitive sort relationship
1610 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1612 if (header1 && header2) {
1613 cmp = compare_priorities (tny_header_get_priority (header1),
1614 tny_header_get_priority (header2));
1615 g_object_unref (header1);
1616 g_object_unref (header2);
1618 return cmp ? cmp : t1 - t2;
1624 return &iter1 - &iter2; /* oughhhh */
1629 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1635 /* static int counter = 0; */
1637 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1639 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1640 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1641 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1642 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1644 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1645 val2 + modest_text_utils_get_subject_prefix_len(val2),
1652 /* Drag and drop stuff */
1654 drag_data_get_cb (GtkWidget *widget,
1655 GdkDragContext *context,
1656 GtkSelectionData *selection_data,
1661 ModestHeaderView *self = NULL;
1662 ModestHeaderViewPrivate *priv = NULL;
1664 self = MODEST_HEADER_VIEW (widget);
1665 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1667 /* Set the data. Do not use the current selection because it
1668 could be different than the selection at the beginning of
1670 modest_dnd_selection_data_set_paths (selection_data,
1671 priv->drag_begin_cached_selected_rows);
1675 * We're caching the selected rows at the beginning because the
1676 * selection could change between drag-begin and drag-data-get, for
1677 * example if we have a set of rows already selected, and then we
1678 * click in one of them (without SHIFT key pressed) and begin a drag,
1679 * the selection at that moment contains all the selected lines, but
1680 * after dropping the selection, the release event provokes that only
1681 * the row used to begin the drag is selected, so at the end the
1682 * drag&drop affects only one rows instead of all the selected ones.
1686 drag_begin_cb (GtkWidget *widget,
1687 GdkDragContext *context,
1690 ModestHeaderView *self = NULL;
1691 ModestHeaderViewPrivate *priv = NULL;
1692 GtkTreeSelection *selection;
1694 self = MODEST_HEADER_VIEW (widget);
1695 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1697 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1698 priv->drag_begin_cached_selected_rows =
1699 gtk_tree_selection_get_selected_rows (selection, NULL);
1703 * We use the drag-end signal to clear the cached selection, we use
1704 * this because this allways happens, whether or not the d&d was a
1708 drag_end_cb (GtkWidget *widget,
1712 ModestHeaderView *self = NULL;
1713 ModestHeaderViewPrivate *priv = NULL;
1715 self = MODEST_HEADER_VIEW (widget);
1716 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1718 /* Free cached data */
1719 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1720 g_list_free (priv->drag_begin_cached_selected_rows);
1721 priv->drag_begin_cached_selected_rows = NULL;
1724 /* Header view drag types */
1725 const GtkTargetEntry header_view_drag_types[] = {
1726 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1730 enable_drag_and_drop (GtkWidget *self)
1732 #ifdef MODEST_TOOLKIT_HILDON2
1735 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1736 header_view_drag_types,
1737 G_N_ELEMENTS (header_view_drag_types),
1738 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1742 disable_drag_and_drop (GtkWidget *self)
1744 #ifdef MODEST_TOOLKIT_HILDON2
1747 gtk_drag_source_unset (self);
1751 setup_drag_and_drop (GtkWidget *self)
1753 #ifdef MODEST_TOOLKIT_HILDON2
1756 enable_drag_and_drop(self);
1757 g_signal_connect(G_OBJECT (self), "drag_data_get",
1758 G_CALLBACK(drag_data_get_cb), NULL);
1760 g_signal_connect(G_OBJECT (self), "drag_begin",
1761 G_CALLBACK(drag_begin_cb), NULL);
1763 g_signal_connect(G_OBJECT (self), "drag_end",
1764 G_CALLBACK(drag_end_cb), NULL);
1767 static GtkTreePath *
1768 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1770 GtkTreePath *path = NULL;
1771 GtkTreeSelection *sel = NULL;
1774 sel = gtk_tree_view_get_selection(self);
1775 rows = gtk_tree_selection_get_selected_rows (sel, model);
1777 if ((rows == NULL) || (g_list_length(rows) != 1))
1780 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1785 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1791 #ifndef MODEST_TOOLKIT_HILDON2
1793 * This function moves the tree view scroll to the current selected
1794 * row when the widget grabs the focus
1797 on_focus_in (GtkWidget *self,
1798 GdkEventFocus *event,
1801 GtkTreeSelection *selection;
1802 GtkTreeModel *model;
1803 GList *selected = NULL;
1804 GtkTreePath *selected_path = NULL;
1806 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1810 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1811 /* If none selected yet, pick the first one */
1812 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1816 /* Return if the model is empty */
1817 if (!gtk_tree_model_get_iter_first (model, &iter))
1820 path = gtk_tree_model_get_path (model, &iter);
1821 gtk_tree_selection_select_path (selection, path);
1822 gtk_tree_path_free (path);
1825 /* Need to get the all the rows because is selection multiple */
1826 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1827 if (selected == NULL) return FALSE;
1828 selected_path = (GtkTreePath *) selected->data;
1831 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1832 g_list_free (selected);
1838 on_focus_out (GtkWidget *self,
1839 GdkEventFocus *event,
1843 if (!gtk_widget_is_focus (self)) {
1844 GtkTreeSelection *selection = NULL;
1845 GList *selected_rows = NULL;
1846 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1847 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1848 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1849 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1850 gtk_tree_selection_unselect_all (selection);
1851 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1852 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1853 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1854 g_list_free (selected_rows);
1862 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1864 enable_drag_and_drop(self);
1869 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1871 GtkTreeSelection *selection = NULL;
1872 GtkTreePath *path = NULL;
1873 gboolean already_selected = FALSE, already_opened = FALSE;
1874 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1876 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1878 GtkTreeModel *model;
1880 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1881 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1883 /* Get header from model */
1884 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1885 if (gtk_tree_model_get_iter (model, &iter, path)) {
1886 GValue value = {0,};
1889 gtk_tree_model_get_value (model, &iter,
1890 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1892 header = (TnyHeader *) g_value_get_object (&value);
1893 if (TNY_IS_HEADER (header)) {
1894 status = modest_tny_all_send_queues_get_msg_status (header);
1895 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1898 g_value_unset (&value);
1902 /* Enable drag and drop only if the user clicks on a row that
1903 it's already selected. If not, let him select items using
1904 the pointer. If the message is in an OUTBOX and in sending
1905 status disable drag and drop as well */
1906 if (!already_selected ||
1907 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1909 disable_drag_and_drop(self);
1912 gtk_tree_path_free(path);
1914 /* If it's already opened then do not let the button-press
1915 event go on because it'll perform a message open because
1916 we're clicking on to an already selected header */
1921 folder_monitor_update (TnyFolderObserver *self,
1922 TnyFolderChange *change)
1924 ModestHeaderViewPrivate *priv = NULL;
1925 TnyFolderChangeChanged changed;
1926 TnyFolder *folder = NULL;
1928 changed = tny_folder_change_get_changed (change);
1930 /* Do not notify the observers if the folder of the header
1931 view has changed before this call to the observer
1933 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1934 folder = tny_folder_change_get_folder (change);
1935 if (folder != priv->folder)
1938 MODEST_DEBUG_BLOCK (
1939 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1940 g_print ("ADDED %d/%d (r/t) \n",
1941 tny_folder_change_get_new_unread_count (change),
1942 tny_folder_change_get_new_all_count (change));
1943 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1944 g_print ("ALL COUNT %d\n",
1945 tny_folder_change_get_new_all_count (change));
1946 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1947 g_print ("UNREAD COUNT %d\n",
1948 tny_folder_change_get_new_unread_count (change));
1949 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1950 g_print ("EXPUNGED %d/%d (r/t) \n",
1951 tny_folder_change_get_new_unread_count (change),
1952 tny_folder_change_get_new_all_count (change));
1953 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1954 g_print ("FOLDER RENAME\n");
1955 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1956 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1957 tny_folder_change_get_new_unread_count (change),
1958 tny_folder_change_get_new_all_count (change));
1959 g_print ("---------------------------------------------------\n");
1962 /* Check folder count */
1963 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1964 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1966 g_mutex_lock (priv->observers_lock);
1968 /* Emit signal to evaluate how headers changes affects
1969 to the window view */
1970 g_signal_emit (G_OBJECT(self),
1971 signals[MSG_COUNT_CHANGED_SIGNAL],
1974 /* Added or removed headers, so data stored on cliboard are invalid */
1975 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1976 modest_email_clipboard_clear (priv->clipboard);
1978 g_mutex_unlock (priv->observers_lock);
1984 g_object_unref (folder);
1988 modest_header_view_is_empty (ModestHeaderView *self)
1990 ModestHeaderViewPrivate *priv;
1992 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1994 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1996 return priv->status == HEADER_VIEW_EMPTY;
2000 modest_header_view_clear (ModestHeaderView *self)
2002 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2004 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2008 modest_header_view_copy_selection (ModestHeaderView *header_view)
2010 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2012 /* Copy selection */
2013 _clipboard_set_selected_data (header_view, FALSE);
2017 modest_header_view_cut_selection (ModestHeaderView *header_view)
2019 ModestHeaderViewPrivate *priv = NULL;
2020 const gchar **hidding = NULL;
2021 guint i, n_selected;
2023 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2025 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2027 /* Copy selection */
2028 _clipboard_set_selected_data (header_view, TRUE);
2030 /* Get hidding ids */
2031 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2033 /* Clear hidding array created by previous cut operation */
2034 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2036 /* Copy hidding array */
2037 priv->n_selected = n_selected;
2038 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2039 for (i=0; i < n_selected; i++)
2040 priv->hidding_ids[i] = g_strdup(hidding[i]);
2042 /* Hide cut headers */
2043 modest_header_view_refilter (header_view);
2050 _clipboard_set_selected_data (ModestHeaderView *header_view,
2053 ModestHeaderViewPrivate *priv = NULL;
2054 TnyList *headers = NULL;
2056 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2057 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2059 /* Set selected data on clipboard */
2060 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2061 headers = modest_header_view_get_selected_headers (header_view);
2062 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2065 g_object_unref (headers);
2069 ModestHeaderView *self;
2074 notify_filter_change (gpointer data)
2076 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2078 g_signal_emit (info->self,
2079 signals[MSG_COUNT_CHANGED_SIGNAL],
2080 0, info->folder, NULL);
2086 notify_filter_change_destroy (gpointer data)
2088 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2089 ModestHeaderViewPrivate *priv;
2091 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2092 priv->status_timeout = 0;
2094 g_object_unref (info->self);
2095 g_object_unref (info->folder);
2096 g_slice_free (NotifyFilterInfo, info);
2100 filter_row (GtkTreeModel *model,
2104 ModestHeaderViewPrivate *priv = NULL;
2105 TnyHeaderFlags flags;
2106 TnyHeader *header = NULL;
2109 gboolean visible = TRUE;
2110 gboolean found = FALSE;
2111 GValue value = {0,};
2112 HeaderViewStatus old_status;
2114 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2115 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2117 /* Get header from model */
2118 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2119 flags = (TnyHeaderFlags) g_value_get_int (&value);
2120 g_value_unset (&value);
2121 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2122 header = (TnyHeader *) g_value_get_object (&value);
2123 g_value_unset (&value);
2125 /* Hide deleted and mark as deleted heders */
2126 if (flags & TNY_HEADER_FLAG_DELETED ||
2127 flags & TNY_HEADER_FLAG_EXPUNGED) {
2132 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2133 if (priv->is_outbox &&
2134 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2140 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2141 if (priv->is_outbox &&
2142 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2148 /* If no data on clipboard, return always TRUE */
2149 if (modest_email_clipboard_cleared(priv->clipboard)) {
2154 /* Get message id from header (ensure is a valid id) */
2161 if (priv->hidding_ids != NULL) {
2162 id = tny_header_dup_message_id (header);
2163 for (i=0; i < priv->n_selected && !found; i++)
2164 if (priv->hidding_ids[i] != NULL && id != NULL)
2165 found = (!strcmp (priv->hidding_ids[i], id));
2172 old_status = priv->status;
2173 priv->status = ((gboolean) priv->status) && !visible;
2174 if ((priv->notify_status) && (priv->status != old_status)) {
2175 NotifyFilterInfo *info;
2177 if (priv->status_timeout)
2178 g_source_remove (priv->status_timeout);
2180 info = g_slice_new0 (NotifyFilterInfo);
2181 info->self = g_object_ref (G_OBJECT (user_data));
2182 info->folder = tny_header_get_folder (header);
2183 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2184 notify_filter_change,
2186 notify_filter_change_destroy);
2193 _clear_hidding_filter (ModestHeaderView *header_view)
2195 ModestHeaderViewPrivate *priv = NULL;
2198 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2199 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2201 if (priv->hidding_ids != NULL) {
2202 for (i=0; i < priv->n_selected; i++)
2203 g_free (priv->hidding_ids[i]);
2204 g_free(priv->hidding_ids);
2209 modest_header_view_refilter (ModestHeaderView *header_view)
2211 GtkTreeModel *model = NULL;
2212 ModestHeaderViewPrivate *priv = NULL;
2214 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2215 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2217 /* Hide cut headers */
2218 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2219 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2220 priv->status = HEADER_VIEW_INIT;
2221 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2226 * Called when an account is removed. If I'm showing a folder of the
2227 * account that has been removed then clear the view
2230 on_account_removed (TnyAccountStore *self,
2231 TnyAccount *account,
2234 ModestHeaderViewPrivate *priv = NULL;
2236 /* Ignore changes in transport accounts */
2237 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2240 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2243 TnyAccount *my_account;
2245 my_account = tny_folder_get_account (priv->folder);
2246 if (my_account == account)
2247 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2248 g_object_unref (my_account);
2253 modest_header_view_add_observer(ModestHeaderView *header_view,
2254 ModestHeaderViewObserver *observer)
2256 ModestHeaderViewPrivate *priv;
2258 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2259 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2261 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2263 g_mutex_lock(priv->observer_list_lock);
2264 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2265 g_mutex_unlock(priv->observer_list_lock);
2269 modest_header_view_remove_observer(ModestHeaderView *header_view,
2270 ModestHeaderViewObserver *observer)
2272 ModestHeaderViewPrivate *priv;
2274 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2275 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2277 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2279 g_mutex_lock(priv->observer_list_lock);
2280 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2281 g_mutex_unlock(priv->observer_list_lock);
2285 modest_header_view_notify_observers(ModestHeaderView *header_view,
2286 GtkTreeModel *model,
2287 const gchar *tny_folder_id)
2289 ModestHeaderViewPrivate *priv = NULL;
2291 ModestHeaderViewObserver *observer;
2294 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2296 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2298 g_mutex_lock(priv->observer_list_lock);
2299 iter = priv->observer_list;
2300 while(iter != NULL){
2301 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2302 modest_header_view_observer_update(observer, model,
2304 iter = g_slist_next(iter);
2306 g_mutex_unlock(priv->observer_list_lock);
2310 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2312 ModestHeaderViewPrivate *priv = NULL;
2314 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2315 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2319 modest_header_view_set_filter (ModestHeaderView *self,
2320 ModestHeaderViewFilter filter)
2322 ModestHeaderViewPrivate *priv;
2323 GtkTreeModel *filter_model;
2325 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2326 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2328 priv->filter |= filter;
2330 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2331 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2332 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2337 modest_header_view_unset_filter (ModestHeaderView *self,
2338 ModestHeaderViewFilter filter)
2340 ModestHeaderViewPrivate *priv;
2341 GtkTreeModel *filter_model;
2343 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2344 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2346 priv->filter &= ~filter;
2348 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2349 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2350 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2355 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2357 if (strcmp ("style", spec->name) == 0) {
2358 update_style (MODEST_HEADER_VIEW (obj));
2359 gtk_widget_queue_draw (GTK_WIDGET (obj));
2364 update_style (ModestHeaderView *self)
2366 ModestHeaderViewPrivate *priv;
2367 GdkColor style_color;
2368 PangoAttrList *attr_list;
2370 PangoAttribute *attr;
2372 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2373 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2377 attr_list = pango_attr_list_new ();
2378 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2379 gdk_color_parse ("grey", &style_color);
2381 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2382 pango_attr_list_insert (attr_list, attr);
2385 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2387 "SmallSystemFont", NULL,
2390 attr = pango_attr_font_desc_new (pango_font_description_copy
2391 (style->font_desc));
2392 pango_attr_list_insert (attr_list, attr);
2394 g_object_set (G_OBJECT (priv->renderer_address),
2395 "foreground-gdk", &style_color,
2396 "foreground-set", TRUE,
2397 "attributes", attr_list,
2399 g_object_set (G_OBJECT (priv->renderer_date_status),
2400 "foreground-gdk", &style_color,
2401 "foreground-set", TRUE,
2402 "attributes", attr_list,
2404 pango_attr_list_unref (attr_list);
2406 g_object_set (G_OBJECT (priv->renderer_address),
2407 "foreground-gdk", &style_color,
2408 "foreground-set", TRUE,
2409 "scale", PANGO_SCALE_SMALL,
2412 g_object_set (G_OBJECT (priv->renderer_date_status),
2413 "foreground-gdk", &style_color,
2414 "foreground-set", TRUE,
2415 "scale", PANGO_SCALE_SMALL,