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_subject;
171 GtkCellRenderer *renderer_address;
172 GtkCellRenderer *renderer_date_status;
175 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
176 struct _HeadersCountChangedHelper {
177 ModestHeaderView *self;
178 TnyFolderChange *change;
182 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
183 MODEST_TYPE_HEADER_VIEW, \
184 ModestHeaderViewPrivate))
188 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
191 HEADER_SELECTED_SIGNAL,
192 HEADER_ACTIVATED_SIGNAL,
193 ITEM_NOT_FOUND_SIGNAL,
194 MSG_COUNT_CHANGED_SIGNAL,
195 UPDATING_MSG_LIST_SIGNAL,
200 static GObjectClass *parent_class = NULL;
202 /* uncomment the following if you have defined any signals */
203 static guint signals[LAST_SIGNAL] = {0};
206 modest_header_view_get_type (void)
208 static GType my_type = 0;
210 static const GTypeInfo my_info = {
211 sizeof(ModestHeaderViewClass),
212 NULL, /* base init */
213 NULL, /* base finalize */
214 (GClassInitFunc) modest_header_view_class_init,
215 NULL, /* class finalize */
216 NULL, /* class data */
217 sizeof(ModestHeaderView),
219 (GInstanceInitFunc) modest_header_view_init,
223 static const GInterfaceInfo tny_folder_observer_info =
225 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
226 NULL, /* interface_finalize */
227 NULL /* interface_data */
229 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
233 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
234 &tny_folder_observer_info);
242 modest_header_view_class_init (ModestHeaderViewClass *klass)
244 GObjectClass *gobject_class;
245 gobject_class = (GObjectClass*) klass;
247 parent_class = g_type_class_peek_parent (klass);
248 gobject_class->finalize = modest_header_view_finalize;
249 gobject_class->dispose = modest_header_view_dispose;
251 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
253 signals[HEADER_SELECTED_SIGNAL] =
254 g_signal_new ("header_selected",
255 G_TYPE_FROM_CLASS (gobject_class),
257 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
259 g_cclosure_marshal_VOID__POINTER,
260 G_TYPE_NONE, 1, G_TYPE_POINTER);
262 signals[HEADER_ACTIVATED_SIGNAL] =
263 g_signal_new ("header_activated",
264 G_TYPE_FROM_CLASS (gobject_class),
266 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
268 gtk_marshal_VOID__POINTER_POINTER,
269 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
272 signals[ITEM_NOT_FOUND_SIGNAL] =
273 g_signal_new ("item_not_found",
274 G_TYPE_FROM_CLASS (gobject_class),
276 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
278 g_cclosure_marshal_VOID__INT,
279 G_TYPE_NONE, 1, G_TYPE_INT);
281 signals[MSG_COUNT_CHANGED_SIGNAL] =
282 g_signal_new ("msg_count_changed",
283 G_TYPE_FROM_CLASS (gobject_class),
285 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
287 modest_marshal_VOID__POINTER_POINTER,
288 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
290 signals[UPDATING_MSG_LIST_SIGNAL] =
291 g_signal_new ("updating-msg-list",
292 G_TYPE_FROM_CLASS (gobject_class),
294 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
296 g_cclosure_marshal_VOID__BOOLEAN,
297 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
299 #ifdef MODEST_TOOLKIT_HILDON2
300 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
306 tny_folder_observer_init (TnyFolderObserverIface *klass)
308 klass->update = folder_monitor_update;
311 static GtkTreeViewColumn*
312 get_new_column (const gchar *name, GtkCellRenderer *renderer,
313 gboolean resizable, gint sort_col_id, gboolean show_as_text,
314 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
316 GtkTreeViewColumn *column;
318 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
319 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
321 gtk_tree_view_column_set_resizable (column, resizable);
323 gtk_tree_view_column_set_expand (column, TRUE);
326 gtk_tree_view_column_add_attribute (column, renderer, "text",
328 if (sort_col_id >= 0)
329 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
331 gtk_tree_view_column_set_sort_indicator (column, FALSE);
332 gtk_tree_view_column_set_reorderable (column, TRUE);
335 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
342 remove_all_columns (ModestHeaderView *obj)
344 GList *columns, *cursor;
346 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
348 for (cursor = columns; cursor; cursor = cursor->next)
349 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
350 GTK_TREE_VIEW_COLUMN(cursor->data));
351 g_list_free (columns);
355 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
357 GtkTreeModel *sortable;
358 GtkTreeViewColumn *column=NULL;
359 GtkTreeSelection *selection = NULL;
360 GtkCellRenderer *renderer_header,
361 *renderer_attach, *renderer_compact_date_or_status;
362 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
363 *renderer_subject_box, *renderer_recpt,
365 ModestHeaderViewPrivate *priv;
366 GtkTreeViewColumn *compact_column = NULL;
369 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
370 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
372 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
374 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
376 /* TODO: check whether these renderers need to be freed */
377 renderer_attach = gtk_cell_renderer_pixbuf_new ();
378 renderer_priority = gtk_cell_renderer_pixbuf_new ();
379 renderer_header = gtk_cell_renderer_text_new ();
381 renderer_compact_header = modest_vbox_cell_renderer_new ();
382 renderer_recpt_box = modest_hbox_cell_renderer_new ();
383 renderer_subject_box = modest_hbox_cell_renderer_new ();
384 renderer_recpt = gtk_cell_renderer_text_new ();
385 priv->renderer_address = renderer_recpt;
386 priv->renderer_subject = gtk_cell_renderer_text_new ();
387 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
388 priv->renderer_date_status = renderer_compact_date_or_status;
390 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
391 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
392 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
393 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
394 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
395 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
396 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
397 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
398 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
399 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
400 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
401 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
402 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
403 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
405 #ifdef MODEST_TOOLKIT_HILDON2
406 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
408 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
409 #ifndef MODEST_TOOLKIT_GTK
410 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
411 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
413 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
414 g_object_set(G_OBJECT(renderer_header),
415 "ellipsize", PANGO_ELLIPSIZE_END,
417 g_object_set (G_OBJECT (priv->renderer_subject),
418 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
420 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
421 g_object_set (G_OBJECT (renderer_recpt),
422 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
424 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
425 g_object_set(G_OBJECT(renderer_compact_date_or_status),
426 "xalign", 1.0, "yalign", 0.1,
428 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
429 #ifdef MODEST_TOOLKIT_HILDON2
430 g_object_set (G_OBJECT (renderer_priority),
432 "xalign", 0.0, NULL);
433 g_object_set (G_OBJECT (renderer_attach),
435 "xalign", 0.0, NULL);
437 g_object_set (G_OBJECT (renderer_priority),
438 "yalign", 0.5, NULL);
439 g_object_set (G_OBJECT (renderer_attach),
440 "yalign", 0.0, NULL);
443 #ifdef MODEST_TOOLKIT_HILDON1
444 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
445 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
446 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
447 #elif MODEST_TOOLKIT_HILDON2
448 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
449 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
450 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
452 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
453 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
454 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
457 remove_all_columns (self);
459 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
460 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
461 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
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 TnyList *header_list = NULL;
810 GList *list, *tmp = NULL;
811 GtkTreeModel *tree_model = NULL;
814 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
817 /* Get selected rows */
818 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
819 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
822 header_list = tny_simple_list_new();
824 list = g_list_reverse (list);
827 /* get header from selection */
828 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
829 gtk_tree_model_get (tree_model, &iter,
830 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
832 /* Prepend to list */
833 tny_list_prepend (header_list, G_OBJECT (header));
834 g_object_unref (G_OBJECT (header));
836 tmp = g_list_next (tmp);
839 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
846 /* scroll our list view so the selected item is visible */
848 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
850 #ifdef MODEST_TOOLKIT_GTK
852 GtkTreePath *selected_path;
853 GtkTreePath *start, *end;
857 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
858 selected_path = gtk_tree_model_get_path (model, iter);
860 start = gtk_tree_path_new ();
861 end = gtk_tree_path_new ();
863 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
865 if (gtk_tree_path_compare (selected_path, start) < 0 ||
866 gtk_tree_path_compare (end, selected_path) < 0)
867 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
868 selected_path, NULL, TRUE,
871 gtk_tree_path_free (selected_path);
872 gtk_tree_path_free (start);
873 gtk_tree_path_free (end);
875 #endif /* MODEST_TOOLKIT_GTK */
880 modest_header_view_select_next (ModestHeaderView *self)
882 GtkTreeSelection *sel;
887 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
889 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
890 path = get_selected_row (GTK_TREE_VIEW(self), &model);
891 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
892 /* Unselect previous path */
893 gtk_tree_selection_unselect_path (sel, path);
895 /* Move path down and selects new one */
896 if (gtk_tree_model_iter_next (model, &iter)) {
897 gtk_tree_selection_select_iter (sel, &iter);
898 scroll_to_selected (self, &iter, FALSE);
900 gtk_tree_path_free(path);
906 modest_header_view_select_prev (ModestHeaderView *self)
908 GtkTreeSelection *sel;
913 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
915 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
916 path = get_selected_row (GTK_TREE_VIEW(self), &model);
917 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
918 /* Unselect previous path */
919 gtk_tree_selection_unselect_path (sel, path);
922 if (gtk_tree_path_prev (path)) {
923 gtk_tree_model_get_iter (model, &iter, path);
925 /* Select the new one */
926 gtk_tree_selection_select_iter (sel, &iter);
927 scroll_to_selected (self, &iter, TRUE);
930 gtk_tree_path_free (path);
935 modest_header_view_get_columns (ModestHeaderView *self)
937 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
939 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
945 modest_header_view_set_style (ModestHeaderView *self,
946 ModestHeaderViewStyle style)
948 ModestHeaderViewPrivate *priv;
949 gboolean show_col_headers = FALSE;
950 ModestHeaderViewStyle old_style;
952 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
953 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
956 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
957 if (priv->style == style)
958 return TRUE; /* nothing to do */
961 case MODEST_HEADER_VIEW_STYLE_DETAILS:
962 show_col_headers = TRUE;
964 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
967 g_return_val_if_reached (FALSE);
969 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
970 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
972 old_style = priv->style;
979 ModestHeaderViewStyle
980 modest_header_view_get_style (ModestHeaderView *self)
982 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
984 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
987 /* This is used to automatically select the first header if the user
988 * has not selected any header yet.
991 modest_header_view_on_expose_event(GtkTreeView *header_view,
992 GdkEventExpose *event,
995 GtkTreeSelection *sel;
997 GtkTreeIter tree_iter;
998 ModestHeaderViewPrivate *priv;
1000 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1001 model = gtk_tree_view_get_model(header_view);
1006 #ifdef MODEST_TOOLKIT_HILDON2
1009 sel = gtk_tree_view_get_selection(header_view);
1010 if(!gtk_tree_selection_count_selected_rows(sel)) {
1011 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1012 GtkTreePath *tree_iter_path;
1013 /* Prevent the widget from getting the focus
1014 when selecting the first item */
1015 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1016 g_object_set(header_view, "can-focus", FALSE, NULL);
1017 gtk_tree_selection_select_iter(sel, &tree_iter);
1018 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1019 g_object_set(header_view, "can-focus", TRUE, NULL);
1020 if (priv->autoselect_reference) {
1021 gtk_tree_row_reference_free (priv->autoselect_reference);
1023 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1024 gtk_tree_path_free (tree_iter_path);
1027 if (priv->autoselect_reference != NULL) {
1028 gboolean moved_selection = FALSE;
1029 GtkTreePath * last_path;
1030 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1031 moved_selection = TRUE;
1035 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1036 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1037 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1038 moved_selection = TRUE;
1039 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1041 gtk_tree_path_free (last_path);
1043 if (moved_selection) {
1044 gtk_tree_row_reference_free (priv->autoselect_reference);
1045 priv->autoselect_reference = NULL;
1048 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1049 GtkTreePath *current_path;
1050 current_path = gtk_tree_model_get_path (model, &tree_iter);
1051 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1052 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1053 g_object_set(header_view, "can-focus", FALSE, NULL);
1054 gtk_tree_selection_unselect_all (sel);
1055 gtk_tree_selection_select_iter(sel, &tree_iter);
1056 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1057 g_object_set(header_view, "can-focus", TRUE, NULL);
1058 gtk_tree_row_reference_free (priv->autoselect_reference);
1059 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1061 gtk_tree_path_free (current_path);
1062 gtk_tree_path_free (last_path);
1072 modest_header_view_get_folder (ModestHeaderView *self)
1074 ModestHeaderViewPrivate *priv;
1076 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1078 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1081 g_object_ref (priv->folder);
1083 return priv->folder;
1087 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1093 ModestHeaderView *self;
1094 ModestHeaderViewPrivate *priv;
1096 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1098 self = MODEST_HEADER_VIEW (user_data);
1099 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1101 if (cancelled || err)
1104 /* Add IDLE observer (monitor) and another folder observer for
1105 new messages (self) */
1106 g_mutex_lock (priv->observers_lock);
1107 if (priv->monitor) {
1108 tny_folder_monitor_stop (priv->monitor);
1109 g_object_unref (G_OBJECT (priv->monitor));
1111 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1112 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1113 tny_folder_monitor_start (priv->monitor);
1114 g_mutex_unlock (priv->observers_lock);
1118 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1122 ModestHeaderViewPrivate *priv;
1123 GList *cols, *cursor;
1124 GtkTreeModel *filter_model, *sortable;
1126 GtkSortType sort_type;
1128 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1130 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1132 /* Start the monitor in the callback of the
1133 tny_gtk_header_list_model_set_folder call. It's crucial to
1134 do it there and not just after the call because we want the
1135 monitor to observe only the headers returned by the
1136 tny_folder_get_headers_async call that it's inside the
1137 tny_gtk_header_list_model_set_folder call. This way the
1138 monitor infrastructure could successfully cope with
1139 duplicates. For example if a tny_folder_add_msg_async is
1140 happening while tny_gtk_header_list_model_set_folder is
1141 invoked, then the first call could add a header that will
1142 be added again by tny_gtk_header_list_model_set_folder, so
1143 we'd end up with duplicate headers. sergio */
1144 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1146 set_folder_intern_get_headers_async_cb,
1149 /* Create a tree model filter to hide and show rows for cut operations */
1150 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1151 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1152 filter_row, self, NULL);
1153 g_object_unref (headers);
1155 /* Init filter_row function to examine empty status */
1156 priv->status = HEADER_VIEW_INIT;
1158 /* Create sortable model */
1159 sortable = gtk_tree_model_sort_new_with_model (filter_model);
1161 /* install our special sorting functions */
1162 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1164 /* Restore sort column id */
1166 type = modest_tny_folder_guess_folder_type (folder);
1167 if (type == TNY_FOLDER_TYPE_INVALID)
1168 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1170 sort_colid = modest_header_view_get_sort_column_id (self, type);
1171 sort_type = modest_header_view_get_sort_type (self, type);
1172 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1175 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1176 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1177 (GtkTreeIterCompareFunc) cmp_rows,
1179 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1180 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1181 (GtkTreeIterCompareFunc) cmp_subject_rows,
1186 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1187 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1188 g_object_unref (sortable);
1195 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1197 GtkSortType sort_type)
1199 ModestHeaderViewPrivate *priv = NULL;
1200 GtkTreeModel *sortable = NULL;
1203 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1204 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1206 /* Get model and private data */
1207 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1208 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1210 /* Sort tree model */
1211 type = modest_tny_folder_guess_folder_type (priv->folder);
1212 if (type == TNY_FOLDER_TYPE_INVALID)
1213 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1215 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1218 /* Store new sort parameters */
1219 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1224 modest_header_view_set_sort_params (ModestHeaderView *self,
1226 GtkSortType sort_type,
1229 ModestHeaderViewPrivate *priv;
1230 ModestHeaderViewStyle style;
1232 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1233 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1234 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1236 style = modest_header_view_get_style (self);
1237 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1239 priv->sort_colid[style][type] = sort_colid;
1240 priv->sort_type[style][type] = sort_type;
1244 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1247 ModestHeaderViewPrivate *priv;
1248 ModestHeaderViewStyle style;
1250 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1251 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1253 style = modest_header_view_get_style (self);
1254 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1256 return priv->sort_colid[style][type];
1260 modest_header_view_get_sort_type (ModestHeaderView *self,
1263 ModestHeaderViewPrivate *priv;
1264 ModestHeaderViewStyle style;
1266 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1267 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1269 style = modest_header_view_get_style (self);
1270 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1272 return priv->sort_type[style][type];
1276 ModestHeaderView *header_view;
1277 RefreshAsyncUserCallback cb;
1282 folder_refreshed_cb (ModestMailOperation *mail_op,
1286 ModestHeaderViewPrivate *priv;
1287 SetFolderHelper *info;
1289 info = (SetFolderHelper*) user_data;
1291 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1295 info->cb (mail_op, folder, info->user_data);
1297 /* Start the folder count changes observer. We do not need it
1298 before the refresh. Note that the monitor could still be
1299 called for this refresh but now we know that the callback
1300 was previously called */
1301 g_mutex_lock (priv->observers_lock);
1302 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1303 g_mutex_unlock (priv->observers_lock);
1305 /* Notify the observers that the update is over */
1306 g_signal_emit (G_OBJECT (info->header_view),
1307 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1309 /* Allow filtering notifications from now on if the current
1310 folder is still the same (if not then the user has selected
1311 another one to refresh, we should wait until that refresh
1313 if (priv->folder == folder)
1314 priv->notify_status = TRUE;
1317 g_object_unref (info->header_view);
1322 refresh_folder_error_handler (ModestMailOperation *mail_op,
1325 const GError *error = modest_mail_operation_get_error (mail_op);
1327 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1328 error->code == TNY_IO_ERROR_WRITE ||
1329 error->code == TNY_IO_ERROR_READ) {
1330 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1331 /* If the mail op has been cancelled then it's not an error: don't show any message */
1332 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1333 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1334 modest_platform_information_banner (NULL, NULL, msg);
1341 modest_header_view_set_folder (ModestHeaderView *self,
1344 ModestWindow *progress_window,
1345 RefreshAsyncUserCallback callback,
1348 ModestHeaderViewPrivate *priv;
1350 g_return_if_fail (self);
1352 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1355 if (priv->status_timeout) {
1356 g_source_remove (priv->status_timeout);
1357 priv->status_timeout = 0;
1360 g_mutex_lock (priv->observers_lock);
1361 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1362 g_object_unref (priv->folder);
1363 priv->folder = NULL;
1364 g_mutex_unlock (priv->observers_lock);
1368 GtkTreeSelection *selection;
1369 SetFolderHelper *info;
1370 ModestMailOperation *mail_op = NULL;
1372 /* Set folder in the model */
1373 modest_header_view_set_folder_intern (self, folder);
1375 /* Pick my reference. Nothing to do with the mail operation */
1376 priv->folder = g_object_ref (folder);
1378 /* Do not notify about filterings until the refresh finishes */
1379 priv->notify_status = FALSE;
1381 /* Clear the selection if exists */
1382 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1383 gtk_tree_selection_unselect_all(selection);
1384 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1386 /* Notify the observers that the update begins */
1387 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1390 /* create the helper */
1391 info = g_malloc0 (sizeof (SetFolderHelper));
1392 info->header_view = g_object_ref (self);
1393 info->cb = callback;
1394 info->user_data = user_data;
1396 /* Create the mail operation (source will be the parent widget) */
1397 if (progress_window)
1398 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1399 refresh_folder_error_handler,
1402 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1405 /* Refresh the folder asynchronously */
1406 modest_mail_operation_refresh_folder (mail_op,
1408 folder_refreshed_cb,
1411 folder_refreshed_cb (mail_op, folder, info);
1415 g_object_unref (mail_op);
1417 g_mutex_lock (priv->observers_lock);
1419 if (priv->monitor) {
1420 tny_folder_monitor_stop (priv->monitor);
1421 g_object_unref (G_OBJECT (priv->monitor));
1422 priv->monitor = NULL;
1425 if (priv->autoselect_reference) {
1426 gtk_tree_row_reference_free (priv->autoselect_reference);
1427 priv->autoselect_reference = NULL;
1430 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1432 modest_header_view_notify_observers(self, NULL, NULL);
1434 g_mutex_unlock (priv->observers_lock);
1436 /* Notify the observers that the update is over */
1437 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1443 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1444 GtkTreeViewColumn *column, gpointer userdata)
1446 ModestHeaderView *self = NULL;
1448 GtkTreeModel *model = NULL;
1449 TnyHeader *header = NULL;
1450 TnyHeaderFlags flags;
1452 self = MODEST_HEADER_VIEW (treeview);
1454 model = gtk_tree_view_get_model (treeview);
1455 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1458 /* get the first selected item */
1459 gtk_tree_model_get (model, &iter,
1460 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1461 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1464 /* Dont open DELETED messages */
1465 if (flags & TNY_HEADER_FLAG_DELETED) {
1468 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1469 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1470 modest_platform_information_banner (NULL, NULL, msg);
1476 g_signal_emit (G_OBJECT(self),
1477 signals[HEADER_ACTIVATED_SIGNAL],
1483 g_object_unref (G_OBJECT (header));
1488 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1490 GtkTreeModel *model;
1491 TnyHeader *header = NULL;
1492 GtkTreePath *path = NULL;
1494 ModestHeaderView *self;
1495 GList *selected = NULL;
1497 g_return_if_fail (sel);
1498 g_return_if_fail (user_data);
1500 self = MODEST_HEADER_VIEW (user_data);
1502 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1503 if (selected != NULL)
1504 path = (GtkTreePath *) selected->data;
1505 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1506 return; /* msg was _un_selected */
1508 gtk_tree_model_get (model, &iter,
1509 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1513 g_signal_emit (G_OBJECT(self),
1514 signals[HEADER_SELECTED_SIGNAL],
1517 g_object_unref (G_OBJECT (header));
1519 /* free all items in 'selected' */
1520 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1521 g_list_free (selected);
1525 /* PROTECTED method. It's useful when we want to force a given
1526 selection to reload a msg. For example if we have selected a header
1527 in offline mode, when Modest become online, we want to reload the
1528 message automatically without an user click over the header */
1530 _modest_header_view_change_selection (GtkTreeSelection *selection,
1533 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1534 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1536 on_selection_changed (selection, user_data);
1540 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1547 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1551 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1555 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1563 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1571 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1572 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1576 case TNY_HEADER_FLAG_ATTACHMENTS:
1578 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1579 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1580 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1581 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1583 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1584 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1586 return cmp ? cmp : t1 - t2;
1588 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1589 TnyHeader *header1 = NULL, *header2 = NULL;
1591 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
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_INSTANCE_COLUMN, &header2,
1594 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1596 /* This is for making priority values respect the intuitive sort relationship
1597 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1599 if (header1 && header2) {
1600 cmp = compare_priorities (tny_header_get_priority (header1),
1601 tny_header_get_priority (header2));
1602 g_object_unref (header1);
1603 g_object_unref (header2);
1605 return cmp ? cmp : t1 - t2;
1611 return &iter1 - &iter2; /* oughhhh */
1616 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1622 /* static int counter = 0; */
1624 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1626 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1627 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1628 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1629 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1631 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1632 val2 + modest_text_utils_get_subject_prefix_len(val2),
1639 /* Drag and drop stuff */
1641 drag_data_get_cb (GtkWidget *widget,
1642 GdkDragContext *context,
1643 GtkSelectionData *selection_data,
1648 ModestHeaderView *self = NULL;
1649 ModestHeaderViewPrivate *priv = NULL;
1651 self = MODEST_HEADER_VIEW (widget);
1652 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1654 /* Set the data. Do not use the current selection because it
1655 could be different than the selection at the beginning of
1657 modest_dnd_selection_data_set_paths (selection_data,
1658 priv->drag_begin_cached_selected_rows);
1662 * We're caching the selected rows at the beginning because the
1663 * selection could change between drag-begin and drag-data-get, for
1664 * example if we have a set of rows already selected, and then we
1665 * click in one of them (without SHIFT key pressed) and begin a drag,
1666 * the selection at that moment contains all the selected lines, but
1667 * after dropping the selection, the release event provokes that only
1668 * the row used to begin the drag is selected, so at the end the
1669 * drag&drop affects only one rows instead of all the selected ones.
1673 drag_begin_cb (GtkWidget *widget,
1674 GdkDragContext *context,
1677 ModestHeaderView *self = NULL;
1678 ModestHeaderViewPrivate *priv = NULL;
1679 GtkTreeSelection *selection;
1681 self = MODEST_HEADER_VIEW (widget);
1682 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1684 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1685 priv->drag_begin_cached_selected_rows =
1686 gtk_tree_selection_get_selected_rows (selection, NULL);
1690 * We use the drag-end signal to clear the cached selection, we use
1691 * this because this allways happens, whether or not the d&d was a
1695 drag_end_cb (GtkWidget *widget,
1699 ModestHeaderView *self = NULL;
1700 ModestHeaderViewPrivate *priv = NULL;
1702 self = MODEST_HEADER_VIEW (widget);
1703 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1705 /* Free cached data */
1706 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1707 g_list_free (priv->drag_begin_cached_selected_rows);
1708 priv->drag_begin_cached_selected_rows = NULL;
1711 /* Header view drag types */
1712 const GtkTargetEntry header_view_drag_types[] = {
1713 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1717 enable_drag_and_drop (GtkWidget *self)
1719 #ifdef MODEST_TOOLKIT_HILDON2
1722 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1723 header_view_drag_types,
1724 G_N_ELEMENTS (header_view_drag_types),
1725 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1729 disable_drag_and_drop (GtkWidget *self)
1731 #ifdef MODEST_TOOLKIT_HILDON2
1734 gtk_drag_source_unset (self);
1738 setup_drag_and_drop (GtkWidget *self)
1740 #ifdef MODEST_TOOLKIT_HILDON2
1743 enable_drag_and_drop(self);
1744 g_signal_connect(G_OBJECT (self), "drag_data_get",
1745 G_CALLBACK(drag_data_get_cb), NULL);
1747 g_signal_connect(G_OBJECT (self), "drag_begin",
1748 G_CALLBACK(drag_begin_cb), NULL);
1750 g_signal_connect(G_OBJECT (self), "drag_end",
1751 G_CALLBACK(drag_end_cb), NULL);
1754 static GtkTreePath *
1755 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1757 GtkTreePath *path = NULL;
1758 GtkTreeSelection *sel = NULL;
1761 sel = gtk_tree_view_get_selection(self);
1762 rows = gtk_tree_selection_get_selected_rows (sel, model);
1764 if ((rows == NULL) || (g_list_length(rows) != 1))
1767 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1772 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1778 #ifndef MODEST_TOOLKIT_HILDON2
1780 * This function moves the tree view scroll to the current selected
1781 * row when the widget grabs the focus
1784 on_focus_in (GtkWidget *self,
1785 GdkEventFocus *event,
1788 GtkTreeSelection *selection;
1789 GtkTreeModel *model;
1790 GList *selected = NULL;
1791 GtkTreePath *selected_path = NULL;
1793 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1797 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1798 /* If none selected yet, pick the first one */
1799 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1803 /* Return if the model is empty */
1804 if (!gtk_tree_model_get_iter_first (model, &iter))
1807 path = gtk_tree_model_get_path (model, &iter);
1808 gtk_tree_selection_select_path (selection, path);
1809 gtk_tree_path_free (path);
1812 /* Need to get the all the rows because is selection multiple */
1813 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1814 if (selected == NULL) return FALSE;
1815 selected_path = (GtkTreePath *) selected->data;
1818 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1819 g_list_free (selected);
1825 on_focus_out (GtkWidget *self,
1826 GdkEventFocus *event,
1830 if (!gtk_widget_is_focus (self)) {
1831 GtkTreeSelection *selection = NULL;
1832 GList *selected_rows = NULL;
1833 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1834 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1835 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1836 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1837 gtk_tree_selection_unselect_all (selection);
1838 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1839 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1840 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1841 g_list_free (selected_rows);
1849 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1851 enable_drag_and_drop(self);
1856 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1858 GtkTreeSelection *selection = NULL;
1859 GtkTreePath *path = NULL;
1860 gboolean already_selected = FALSE, already_opened = FALSE;
1861 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1863 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1865 GtkTreeModel *model;
1867 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1868 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1870 /* Get header from model */
1871 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1872 if (gtk_tree_model_get_iter (model, &iter, path)) {
1873 GValue value = {0,};
1876 gtk_tree_model_get_value (model, &iter,
1877 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1879 header = (TnyHeader *) g_value_get_object (&value);
1880 if (TNY_IS_HEADER (header)) {
1881 status = modest_tny_all_send_queues_get_msg_status (header);
1882 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1885 g_value_unset (&value);
1889 /* Enable drag and drop only if the user clicks on a row that
1890 it's already selected. If not, let him select items using
1891 the pointer. If the message is in an OUTBOX and in sending
1892 status disable drag and drop as well */
1893 if (!already_selected ||
1894 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1896 disable_drag_and_drop(self);
1899 gtk_tree_path_free(path);
1901 /* If it's already opened then do not let the button-press
1902 event go on because it'll perform a message open because
1903 we're clicking on to an already selected header */
1908 folder_monitor_update (TnyFolderObserver *self,
1909 TnyFolderChange *change)
1911 ModestHeaderViewPrivate *priv = NULL;
1912 TnyFolderChangeChanged changed;
1913 TnyFolder *folder = NULL;
1915 changed = tny_folder_change_get_changed (change);
1917 /* Do not notify the observers if the folder of the header
1918 view has changed before this call to the observer
1920 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1921 folder = tny_folder_change_get_folder (change);
1922 if (folder != priv->folder)
1925 MODEST_DEBUG_BLOCK (
1926 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1927 g_print ("ADDED %d/%d (r/t) \n",
1928 tny_folder_change_get_new_unread_count (change),
1929 tny_folder_change_get_new_all_count (change));
1930 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1931 g_print ("ALL COUNT %d\n",
1932 tny_folder_change_get_new_all_count (change));
1933 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1934 g_print ("UNREAD COUNT %d\n",
1935 tny_folder_change_get_new_unread_count (change));
1936 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1937 g_print ("EXPUNGED %d/%d (r/t) \n",
1938 tny_folder_change_get_new_unread_count (change),
1939 tny_folder_change_get_new_all_count (change));
1940 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1941 g_print ("FOLDER RENAME\n");
1942 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1943 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1944 tny_folder_change_get_new_unread_count (change),
1945 tny_folder_change_get_new_all_count (change));
1946 g_print ("---------------------------------------------------\n");
1949 /* Check folder count */
1950 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1951 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1953 g_mutex_lock (priv->observers_lock);
1955 /* Emit signal to evaluate how headers changes affects
1956 to the window view */
1957 g_signal_emit (G_OBJECT(self),
1958 signals[MSG_COUNT_CHANGED_SIGNAL],
1961 /* Added or removed headers, so data stored on cliboard are invalid */
1962 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1963 modest_email_clipboard_clear (priv->clipboard);
1965 g_mutex_unlock (priv->observers_lock);
1971 g_object_unref (folder);
1975 modest_header_view_is_empty (ModestHeaderView *self)
1977 ModestHeaderViewPrivate *priv;
1979 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1981 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1983 return priv->status == HEADER_VIEW_EMPTY;
1987 modest_header_view_clear (ModestHeaderView *self)
1989 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1991 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
1995 modest_header_view_copy_selection (ModestHeaderView *header_view)
1997 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1999 /* Copy selection */
2000 _clipboard_set_selected_data (header_view, FALSE);
2004 modest_header_view_cut_selection (ModestHeaderView *header_view)
2006 ModestHeaderViewPrivate *priv = NULL;
2007 const gchar **hidding = NULL;
2008 guint i, n_selected;
2010 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2012 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2014 /* Copy selection */
2015 _clipboard_set_selected_data (header_view, TRUE);
2017 /* Get hidding ids */
2018 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2020 /* Clear hidding array created by previous cut operation */
2021 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2023 /* Copy hidding array */
2024 priv->n_selected = n_selected;
2025 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2026 for (i=0; i < n_selected; i++)
2027 priv->hidding_ids[i] = g_strdup(hidding[i]);
2029 /* Hide cut headers */
2030 modest_header_view_refilter (header_view);
2037 _clipboard_set_selected_data (ModestHeaderView *header_view,
2040 ModestHeaderViewPrivate *priv = NULL;
2041 TnyList *headers = NULL;
2043 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2044 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2046 /* Set selected data on clipboard */
2047 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2048 headers = modest_header_view_get_selected_headers (header_view);
2049 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2052 g_object_unref (headers);
2056 ModestHeaderView *self;
2061 notify_filter_change (gpointer data)
2063 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2065 g_signal_emit (info->self,
2066 signals[MSG_COUNT_CHANGED_SIGNAL],
2067 0, info->folder, NULL);
2073 notify_filter_change_destroy (gpointer data)
2075 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2076 ModestHeaderViewPrivate *priv;
2078 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2079 priv->status_timeout = 0;
2081 g_object_unref (info->self);
2082 g_object_unref (info->folder);
2083 g_slice_free (NotifyFilterInfo, info);
2087 filter_row (GtkTreeModel *model,
2091 ModestHeaderViewPrivate *priv = NULL;
2092 TnyHeaderFlags flags;
2093 TnyHeader *header = NULL;
2096 gboolean visible = TRUE;
2097 gboolean found = FALSE;
2098 GValue value = {0,};
2099 HeaderViewStatus old_status;
2101 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2102 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2104 /* Get header from model */
2105 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2106 flags = (TnyHeaderFlags) g_value_get_int (&value);
2107 g_value_unset (&value);
2108 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2109 header = (TnyHeader *) g_value_get_object (&value);
2110 g_value_unset (&value);
2112 /* Get message id from header (ensure is a valid id) */
2118 /* Hide deleted and mark as deleted heders */
2119 if (flags & TNY_HEADER_FLAG_DELETED ||
2120 flags & TNY_HEADER_FLAG_EXPUNGED) {
2125 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2126 if (priv->is_outbox &&
2127 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2133 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2134 if (priv->is_outbox &&
2135 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2141 /* If no data on clipboard, return always TRUE */
2142 if (modest_email_clipboard_cleared(priv->clipboard)) {
2148 if (priv->hidding_ids != NULL) {
2149 id = tny_header_dup_message_id (header);
2150 for (i=0; i < priv->n_selected && !found; i++)
2151 if (priv->hidding_ids[i] != NULL && id != NULL)
2152 found = (!strcmp (priv->hidding_ids[i], id));
2159 old_status = priv->status;
2160 priv->status = ((gboolean) priv->status) && !visible;
2161 if ((priv->notify_status) && (priv->status != old_status)) {
2162 if (priv->status_timeout)
2163 g_source_remove (priv->status_timeout);
2166 NotifyFilterInfo *info;
2168 info = g_slice_new0 (NotifyFilterInfo);
2169 info->self = g_object_ref (G_OBJECT (user_data));
2171 info->folder = tny_header_get_folder (header);
2172 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2173 notify_filter_change,
2175 notify_filter_change_destroy);
2183 _clear_hidding_filter (ModestHeaderView *header_view)
2185 ModestHeaderViewPrivate *priv = NULL;
2188 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2189 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2191 if (priv->hidding_ids != NULL) {
2192 for (i=0; i < priv->n_selected; i++)
2193 g_free (priv->hidding_ids[i]);
2194 g_free(priv->hidding_ids);
2199 modest_header_view_refilter (ModestHeaderView *header_view)
2201 GtkTreeModel *model = NULL;
2202 ModestHeaderViewPrivate *priv = NULL;
2204 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2205 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2207 /* Hide cut headers */
2208 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2209 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2210 priv->status = HEADER_VIEW_INIT;
2211 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2216 * Called when an account is removed. If I'm showing a folder of the
2217 * account that has been removed then clear the view
2220 on_account_removed (TnyAccountStore *self,
2221 TnyAccount *account,
2224 ModestHeaderViewPrivate *priv = NULL;
2226 /* Ignore changes in transport accounts */
2227 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2230 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2233 TnyAccount *my_account;
2235 my_account = tny_folder_get_account (priv->folder);
2236 if (my_account == account)
2237 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2238 g_object_unref (my_account);
2243 modest_header_view_add_observer(ModestHeaderView *header_view,
2244 ModestHeaderViewObserver *observer)
2246 ModestHeaderViewPrivate *priv;
2248 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2249 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2251 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2253 g_mutex_lock(priv->observer_list_lock);
2254 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2255 g_mutex_unlock(priv->observer_list_lock);
2259 modest_header_view_remove_observer(ModestHeaderView *header_view,
2260 ModestHeaderViewObserver *observer)
2262 ModestHeaderViewPrivate *priv;
2264 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2265 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2267 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2269 g_mutex_lock(priv->observer_list_lock);
2270 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2271 g_mutex_unlock(priv->observer_list_lock);
2275 modest_header_view_notify_observers(ModestHeaderView *header_view,
2276 GtkTreeModel *model,
2277 const gchar *tny_folder_id)
2279 ModestHeaderViewPrivate *priv = NULL;
2281 ModestHeaderViewObserver *observer;
2284 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2286 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2288 g_mutex_lock(priv->observer_list_lock);
2289 iter = priv->observer_list;
2290 while(iter != NULL){
2291 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2292 modest_header_view_observer_update(observer, model,
2294 iter = g_slist_next(iter);
2296 g_mutex_unlock(priv->observer_list_lock);
2300 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2302 ModestHeaderViewPrivate *priv = NULL;
2304 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2305 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2309 modest_header_view_set_filter (ModestHeaderView *self,
2310 ModestHeaderViewFilter filter)
2312 ModestHeaderViewPrivate *priv;
2313 GtkTreeModel *filter_model;
2315 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2316 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2318 priv->filter |= filter;
2320 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2321 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2322 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2327 modest_header_view_unset_filter (ModestHeaderView *self,
2328 ModestHeaderViewFilter filter)
2330 ModestHeaderViewPrivate *priv;
2331 GtkTreeModel *filter_model;
2333 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2334 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2336 priv->filter &= ~filter;
2338 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2339 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2340 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2345 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2347 if (strcmp ("style", spec->name) == 0) {
2348 update_style (MODEST_HEADER_VIEW (obj));
2349 gtk_widget_queue_draw (GTK_WIDGET (obj));
2354 update_style (ModestHeaderView *self)
2356 ModestHeaderViewPrivate *priv;
2357 GdkColor style_color;
2358 GdkColor style_active_color;
2359 PangoAttrList *attr_list;
2361 PangoAttribute *attr;
2362 GdkColor *new_color;
2364 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2365 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2369 attr_list = pango_attr_list_new ();
2370 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2371 gdk_color_parse ("grey", &style_color);
2373 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2374 pango_attr_list_insert (attr_list, attr);
2377 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2379 "SmallSystemFont", NULL,
2382 attr = pango_attr_font_desc_new (pango_font_description_copy
2383 (style->font_desc));
2384 pango_attr_list_insert (attr_list, attr);
2386 g_object_set (G_OBJECT (priv->renderer_address),
2387 "foreground-gdk", &style_color,
2388 "foreground-set", TRUE,
2389 "attributes", attr_list,
2391 g_object_set (G_OBJECT (priv->renderer_date_status),
2392 "foreground-gdk", &style_color,
2393 "foreground-set", TRUE,
2394 "attributes", attr_list,
2396 pango_attr_list_unref (attr_list);
2398 g_object_set (G_OBJECT (priv->renderer_address),
2399 "foreground-gdk", &style_color,
2400 "foreground-set", TRUE,
2401 "scale", PANGO_SCALE_SMALL,
2404 g_object_set (G_OBJECT (priv->renderer_date_status),
2405 "foreground-gdk", &style_color,
2406 "foreground-set", TRUE,
2407 "scale", PANGO_SCALE_SMALL,
2412 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2413 new_color = gdk_color_copy (&style_active_color);
2417 #ifdef MODEST_TOOLKIT_HILDON2
2418 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (new_color != NULL));
2419 g_object_set_data_full (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, new_color, (GDestroyNotify) gdk_color_free);