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);
1160 g_object_unref (filter_model);
1162 /* install our special sorting functions */
1163 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1165 /* Restore sort column id */
1167 type = modest_tny_folder_guess_folder_type (folder);
1168 if (type == TNY_FOLDER_TYPE_INVALID)
1169 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1171 sort_colid = modest_header_view_get_sort_column_id (self, type);
1172 sort_type = modest_header_view_get_sort_type (self, type);
1173 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1176 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1177 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1178 (GtkTreeIterCompareFunc) cmp_rows,
1180 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1181 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1182 (GtkTreeIterCompareFunc) cmp_subject_rows,
1187 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1188 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1189 g_object_unref (sortable);
1196 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1198 GtkSortType sort_type)
1200 ModestHeaderViewPrivate *priv = NULL;
1201 GtkTreeModel *sortable = NULL;
1204 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1205 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1207 /* Get model and private data */
1208 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1209 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1211 /* Sort tree model */
1212 type = modest_tny_folder_guess_folder_type (priv->folder);
1213 if (type == TNY_FOLDER_TYPE_INVALID)
1214 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1216 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1219 /* Store new sort parameters */
1220 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1225 modest_header_view_set_sort_params (ModestHeaderView *self,
1227 GtkSortType sort_type,
1230 ModestHeaderViewPrivate *priv;
1231 ModestHeaderViewStyle style;
1233 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1234 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1235 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1237 style = modest_header_view_get_style (self);
1238 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1240 priv->sort_colid[style][type] = sort_colid;
1241 priv->sort_type[style][type] = sort_type;
1245 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1248 ModestHeaderViewPrivate *priv;
1249 ModestHeaderViewStyle style;
1251 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1252 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1254 style = modest_header_view_get_style (self);
1255 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1257 return priv->sort_colid[style][type];
1261 modest_header_view_get_sort_type (ModestHeaderView *self,
1264 ModestHeaderViewPrivate *priv;
1265 ModestHeaderViewStyle style;
1267 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1268 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1270 style = modest_header_view_get_style (self);
1271 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1273 return priv->sort_type[style][type];
1277 ModestHeaderView *header_view;
1278 RefreshAsyncUserCallback cb;
1283 folder_refreshed_cb (ModestMailOperation *mail_op,
1287 ModestHeaderViewPrivate *priv;
1288 SetFolderHelper *info;
1290 info = (SetFolderHelper*) user_data;
1292 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1296 info->cb (mail_op, folder, info->user_data);
1298 /* Start the folder count changes observer. We do not need it
1299 before the refresh. Note that the monitor could still be
1300 called for this refresh but now we know that the callback
1301 was previously called */
1302 g_mutex_lock (priv->observers_lock);
1303 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1304 g_mutex_unlock (priv->observers_lock);
1306 /* Notify the observers that the update is over */
1307 g_signal_emit (G_OBJECT (info->header_view),
1308 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1310 /* Allow filtering notifications from now on if the current
1311 folder is still the same (if not then the user has selected
1312 another one to refresh, we should wait until that refresh
1314 if (priv->folder == folder)
1315 priv->notify_status = TRUE;
1318 g_object_unref (info->header_view);
1323 refresh_folder_error_handler (ModestMailOperation *mail_op,
1326 const GError *error = modest_mail_operation_get_error (mail_op);
1328 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1329 error->code == TNY_IO_ERROR_WRITE ||
1330 error->code == TNY_IO_ERROR_READ) {
1331 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1332 /* If the mail op has been cancelled then it's not an error: don't show any message */
1333 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1334 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1335 modest_platform_information_banner (NULL, NULL, msg);
1342 modest_header_view_set_folder (ModestHeaderView *self,
1345 ModestWindow *progress_window,
1346 RefreshAsyncUserCallback callback,
1349 ModestHeaderViewPrivate *priv;
1351 g_return_if_fail (self);
1353 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1356 if (priv->status_timeout) {
1357 g_source_remove (priv->status_timeout);
1358 priv->status_timeout = 0;
1361 g_mutex_lock (priv->observers_lock);
1362 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1363 g_object_unref (priv->folder);
1364 priv->folder = NULL;
1365 g_mutex_unlock (priv->observers_lock);
1369 GtkTreeSelection *selection;
1370 SetFolderHelper *info;
1371 ModestMailOperation *mail_op = NULL;
1373 /* Set folder in the model */
1374 modest_header_view_set_folder_intern (self, folder);
1376 /* Pick my reference. Nothing to do with the mail operation */
1377 priv->folder = g_object_ref (folder);
1379 /* Do not notify about filterings until the refresh finishes */
1380 priv->notify_status = FALSE;
1382 /* Clear the selection if exists */
1383 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1384 gtk_tree_selection_unselect_all(selection);
1385 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1387 /* Notify the observers that the update begins */
1388 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1391 /* create the helper */
1392 info = g_malloc0 (sizeof (SetFolderHelper));
1393 info->header_view = g_object_ref (self);
1394 info->cb = callback;
1395 info->user_data = user_data;
1397 /* Create the mail operation (source will be the parent widget) */
1398 if (progress_window)
1399 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1400 refresh_folder_error_handler,
1403 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1406 /* Refresh the folder asynchronously */
1407 modest_mail_operation_refresh_folder (mail_op,
1409 folder_refreshed_cb,
1412 folder_refreshed_cb (mail_op, folder, info);
1416 g_object_unref (mail_op);
1418 g_mutex_lock (priv->observers_lock);
1420 if (priv->monitor) {
1421 tny_folder_monitor_stop (priv->monitor);
1422 g_object_unref (G_OBJECT (priv->monitor));
1423 priv->monitor = NULL;
1426 if (priv->autoselect_reference) {
1427 gtk_tree_row_reference_free (priv->autoselect_reference);
1428 priv->autoselect_reference = NULL;
1431 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1433 modest_header_view_notify_observers(self, NULL, NULL);
1435 g_mutex_unlock (priv->observers_lock);
1437 /* Notify the observers that the update is over */
1438 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1444 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1445 GtkTreeViewColumn *column, gpointer userdata)
1447 ModestHeaderView *self = NULL;
1449 GtkTreeModel *model = NULL;
1450 TnyHeader *header = NULL;
1451 TnyHeaderFlags flags;
1453 self = MODEST_HEADER_VIEW (treeview);
1455 model = gtk_tree_view_get_model (treeview);
1456 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1459 /* get the first selected item */
1460 gtk_tree_model_get (model, &iter,
1461 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1462 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1465 /* Dont open DELETED messages */
1466 if (flags & TNY_HEADER_FLAG_DELETED) {
1469 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1470 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1471 modest_platform_information_banner (NULL, NULL, msg);
1477 g_signal_emit (G_OBJECT(self),
1478 signals[HEADER_ACTIVATED_SIGNAL],
1484 g_object_unref (G_OBJECT (header));
1489 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1491 GtkTreeModel *model;
1492 TnyHeader *header = NULL;
1493 GtkTreePath *path = NULL;
1495 ModestHeaderView *self;
1496 GList *selected = NULL;
1498 g_return_if_fail (sel);
1499 g_return_if_fail (user_data);
1501 self = MODEST_HEADER_VIEW (user_data);
1503 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1504 if (selected != NULL)
1505 path = (GtkTreePath *) selected->data;
1506 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1507 return; /* msg was _un_selected */
1509 gtk_tree_model_get (model, &iter,
1510 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1514 g_signal_emit (G_OBJECT(self),
1515 signals[HEADER_SELECTED_SIGNAL],
1518 g_object_unref (G_OBJECT (header));
1520 /* free all items in 'selected' */
1521 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1522 g_list_free (selected);
1526 /* PROTECTED method. It's useful when we want to force a given
1527 selection to reload a msg. For example if we have selected a header
1528 in offline mode, when Modest become online, we want to reload the
1529 message automatically without an user click over the header */
1531 _modest_header_view_change_selection (GtkTreeSelection *selection,
1534 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1535 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1537 on_selection_changed (selection, user_data);
1541 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1548 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1552 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1556 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1564 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1572 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1573 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1577 case TNY_HEADER_FLAG_ATTACHMENTS:
1579 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1580 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1581 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1582 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1584 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1585 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1587 return cmp ? cmp : t1 - t2;
1589 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1590 TnyHeader *header1 = NULL, *header2 = NULL;
1592 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1593 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1594 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1595 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1597 /* This is for making priority values respect the intuitive sort relationship
1598 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1600 if (header1 && header2) {
1601 cmp = compare_priorities (tny_header_get_priority (header1),
1602 tny_header_get_priority (header2));
1603 g_object_unref (header1);
1604 g_object_unref (header2);
1606 return cmp ? cmp : t1 - t2;
1612 return &iter1 - &iter2; /* oughhhh */
1617 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1623 /* static int counter = 0; */
1625 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1627 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1628 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1629 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1630 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1632 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1633 val2 + modest_text_utils_get_subject_prefix_len(val2),
1640 /* Drag and drop stuff */
1642 drag_data_get_cb (GtkWidget *widget,
1643 GdkDragContext *context,
1644 GtkSelectionData *selection_data,
1649 ModestHeaderView *self = NULL;
1650 ModestHeaderViewPrivate *priv = NULL;
1652 self = MODEST_HEADER_VIEW (widget);
1653 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1655 /* Set the data. Do not use the current selection because it
1656 could be different than the selection at the beginning of
1658 modest_dnd_selection_data_set_paths (selection_data,
1659 priv->drag_begin_cached_selected_rows);
1663 * We're caching the selected rows at the beginning because the
1664 * selection could change between drag-begin and drag-data-get, for
1665 * example if we have a set of rows already selected, and then we
1666 * click in one of them (without SHIFT key pressed) and begin a drag,
1667 * the selection at that moment contains all the selected lines, but
1668 * after dropping the selection, the release event provokes that only
1669 * the row used to begin the drag is selected, so at the end the
1670 * drag&drop affects only one rows instead of all the selected ones.
1674 drag_begin_cb (GtkWidget *widget,
1675 GdkDragContext *context,
1678 ModestHeaderView *self = NULL;
1679 ModestHeaderViewPrivate *priv = NULL;
1680 GtkTreeSelection *selection;
1682 self = MODEST_HEADER_VIEW (widget);
1683 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1685 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1686 priv->drag_begin_cached_selected_rows =
1687 gtk_tree_selection_get_selected_rows (selection, NULL);
1691 * We use the drag-end signal to clear the cached selection, we use
1692 * this because this allways happens, whether or not the d&d was a
1696 drag_end_cb (GtkWidget *widget,
1700 ModestHeaderView *self = NULL;
1701 ModestHeaderViewPrivate *priv = NULL;
1703 self = MODEST_HEADER_VIEW (widget);
1704 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1706 /* Free cached data */
1707 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1708 g_list_free (priv->drag_begin_cached_selected_rows);
1709 priv->drag_begin_cached_selected_rows = NULL;
1712 /* Header view drag types */
1713 const GtkTargetEntry header_view_drag_types[] = {
1714 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1718 enable_drag_and_drop (GtkWidget *self)
1720 #ifdef MODEST_TOOLKIT_HILDON2
1723 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1724 header_view_drag_types,
1725 G_N_ELEMENTS (header_view_drag_types),
1726 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1730 disable_drag_and_drop (GtkWidget *self)
1732 #ifdef MODEST_TOOLKIT_HILDON2
1735 gtk_drag_source_unset (self);
1739 setup_drag_and_drop (GtkWidget *self)
1741 #ifdef MODEST_TOOLKIT_HILDON2
1744 enable_drag_and_drop(self);
1745 g_signal_connect(G_OBJECT (self), "drag_data_get",
1746 G_CALLBACK(drag_data_get_cb), NULL);
1748 g_signal_connect(G_OBJECT (self), "drag_begin",
1749 G_CALLBACK(drag_begin_cb), NULL);
1751 g_signal_connect(G_OBJECT (self), "drag_end",
1752 G_CALLBACK(drag_end_cb), NULL);
1755 static GtkTreePath *
1756 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1758 GtkTreePath *path = NULL;
1759 GtkTreeSelection *sel = NULL;
1762 sel = gtk_tree_view_get_selection(self);
1763 rows = gtk_tree_selection_get_selected_rows (sel, model);
1765 if ((rows == NULL) || (g_list_length(rows) != 1))
1768 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1773 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1779 #ifndef MODEST_TOOLKIT_HILDON2
1781 * This function moves the tree view scroll to the current selected
1782 * row when the widget grabs the focus
1785 on_focus_in (GtkWidget *self,
1786 GdkEventFocus *event,
1789 GtkTreeSelection *selection;
1790 GtkTreeModel *model;
1791 GList *selected = NULL;
1792 GtkTreePath *selected_path = NULL;
1794 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1798 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1799 /* If none selected yet, pick the first one */
1800 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1804 /* Return if the model is empty */
1805 if (!gtk_tree_model_get_iter_first (model, &iter))
1808 path = gtk_tree_model_get_path (model, &iter);
1809 gtk_tree_selection_select_path (selection, path);
1810 gtk_tree_path_free (path);
1813 /* Need to get the all the rows because is selection multiple */
1814 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1815 if (selected == NULL) return FALSE;
1816 selected_path = (GtkTreePath *) selected->data;
1819 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1820 g_list_free (selected);
1826 on_focus_out (GtkWidget *self,
1827 GdkEventFocus *event,
1831 if (!gtk_widget_is_focus (self)) {
1832 GtkTreeSelection *selection = NULL;
1833 GList *selected_rows = NULL;
1834 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1835 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1836 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1837 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1838 gtk_tree_selection_unselect_all (selection);
1839 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1840 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1841 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1842 g_list_free (selected_rows);
1850 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1852 enable_drag_and_drop(self);
1857 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1859 GtkTreeSelection *selection = NULL;
1860 GtkTreePath *path = NULL;
1861 gboolean already_selected = FALSE, already_opened = FALSE;
1862 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1864 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1866 GtkTreeModel *model;
1868 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1869 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1871 /* Get header from model */
1872 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1873 if (gtk_tree_model_get_iter (model, &iter, path)) {
1874 GValue value = {0,};
1877 gtk_tree_model_get_value (model, &iter,
1878 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1880 header = (TnyHeader *) g_value_get_object (&value);
1881 if (TNY_IS_HEADER (header)) {
1882 status = modest_tny_all_send_queues_get_msg_status (header);
1883 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1886 g_value_unset (&value);
1890 /* Enable drag and drop only if the user clicks on a row that
1891 it's already selected. If not, let him select items using
1892 the pointer. If the message is in an OUTBOX and in sending
1893 status disable drag and drop as well */
1894 if (!already_selected ||
1895 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1897 disable_drag_and_drop(self);
1900 gtk_tree_path_free(path);
1902 /* If it's already opened then do not let the button-press
1903 event go on because it'll perform a message open because
1904 we're clicking on to an already selected header */
1909 folder_monitor_update (TnyFolderObserver *self,
1910 TnyFolderChange *change)
1912 ModestHeaderViewPrivate *priv = NULL;
1913 TnyFolderChangeChanged changed;
1914 TnyFolder *folder = NULL;
1916 changed = tny_folder_change_get_changed (change);
1918 /* Do not notify the observers if the folder of the header
1919 view has changed before this call to the observer
1921 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1922 folder = tny_folder_change_get_folder (change);
1923 if (folder != priv->folder)
1926 MODEST_DEBUG_BLOCK (
1927 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1928 g_print ("ADDED %d/%d (r/t) \n",
1929 tny_folder_change_get_new_unread_count (change),
1930 tny_folder_change_get_new_all_count (change));
1931 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1932 g_print ("ALL COUNT %d\n",
1933 tny_folder_change_get_new_all_count (change));
1934 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1935 g_print ("UNREAD COUNT %d\n",
1936 tny_folder_change_get_new_unread_count (change));
1937 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1938 g_print ("EXPUNGED %d/%d (r/t) \n",
1939 tny_folder_change_get_new_unread_count (change),
1940 tny_folder_change_get_new_all_count (change));
1941 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1942 g_print ("FOLDER RENAME\n");
1943 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1944 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1945 tny_folder_change_get_new_unread_count (change),
1946 tny_folder_change_get_new_all_count (change));
1947 g_print ("---------------------------------------------------\n");
1950 /* Check folder count */
1951 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1952 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1954 g_mutex_lock (priv->observers_lock);
1956 /* Emit signal to evaluate how headers changes affects
1957 to the window view */
1958 g_signal_emit (G_OBJECT(self),
1959 signals[MSG_COUNT_CHANGED_SIGNAL],
1962 /* Added or removed headers, so data stored on cliboard are invalid */
1963 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1964 modest_email_clipboard_clear (priv->clipboard);
1966 g_mutex_unlock (priv->observers_lock);
1972 g_object_unref (folder);
1976 modest_header_view_is_empty (ModestHeaderView *self)
1978 ModestHeaderViewPrivate *priv;
1980 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1982 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1984 return priv->status == HEADER_VIEW_EMPTY;
1988 modest_header_view_clear (ModestHeaderView *self)
1990 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1992 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
1996 modest_header_view_copy_selection (ModestHeaderView *header_view)
1998 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2000 /* Copy selection */
2001 _clipboard_set_selected_data (header_view, FALSE);
2005 modest_header_view_cut_selection (ModestHeaderView *header_view)
2007 ModestHeaderViewPrivate *priv = NULL;
2008 const gchar **hidding = NULL;
2009 guint i, n_selected;
2011 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2013 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2015 /* Copy selection */
2016 _clipboard_set_selected_data (header_view, TRUE);
2018 /* Get hidding ids */
2019 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2021 /* Clear hidding array created by previous cut operation */
2022 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2024 /* Copy hidding array */
2025 priv->n_selected = n_selected;
2026 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2027 for (i=0; i < n_selected; i++)
2028 priv->hidding_ids[i] = g_strdup(hidding[i]);
2030 /* Hide cut headers */
2031 modest_header_view_refilter (header_view);
2038 _clipboard_set_selected_data (ModestHeaderView *header_view,
2041 ModestHeaderViewPrivate *priv = NULL;
2042 TnyList *headers = NULL;
2044 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2045 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2047 /* Set selected data on clipboard */
2048 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2049 headers = modest_header_view_get_selected_headers (header_view);
2050 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2053 g_object_unref (headers);
2057 ModestHeaderView *self;
2062 notify_filter_change (gpointer data)
2064 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2066 g_signal_emit (info->self,
2067 signals[MSG_COUNT_CHANGED_SIGNAL],
2068 0, info->folder, NULL);
2074 notify_filter_change_destroy (gpointer data)
2076 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2077 ModestHeaderViewPrivate *priv;
2079 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2080 priv->status_timeout = 0;
2082 g_object_unref (info->self);
2083 g_object_unref (info->folder);
2084 g_slice_free (NotifyFilterInfo, info);
2088 filter_row (GtkTreeModel *model,
2092 ModestHeaderViewPrivate *priv = NULL;
2093 TnyHeaderFlags flags;
2094 TnyHeader *header = NULL;
2097 gboolean visible = TRUE;
2098 gboolean found = FALSE;
2099 GValue value = {0,};
2100 HeaderViewStatus old_status;
2102 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2103 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2105 /* Get header from model */
2106 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2107 flags = (TnyHeaderFlags) g_value_get_int (&value);
2108 g_value_unset (&value);
2109 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2110 header = (TnyHeader *) g_value_get_object (&value);
2111 g_value_unset (&value);
2113 /* Get message id from header (ensure is a valid id) */
2119 /* Hide deleted and mark as deleted heders */
2120 if (flags & TNY_HEADER_FLAG_DELETED ||
2121 flags & TNY_HEADER_FLAG_EXPUNGED) {
2126 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2127 if (priv->is_outbox &&
2128 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2134 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2135 if (priv->is_outbox &&
2136 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2142 /* If no data on clipboard, return always TRUE */
2143 if (modest_email_clipboard_cleared(priv->clipboard)) {
2149 if (priv->hidding_ids != NULL) {
2150 id = tny_header_dup_message_id (header);
2151 for (i=0; i < priv->n_selected && !found; i++)
2152 if (priv->hidding_ids[i] != NULL && id != NULL)
2153 found = (!strcmp (priv->hidding_ids[i], id));
2160 old_status = priv->status;
2161 priv->status = ((gboolean) priv->status) && !visible;
2162 if ((priv->notify_status) && (priv->status != old_status)) {
2163 if (priv->status_timeout)
2164 g_source_remove (priv->status_timeout);
2167 NotifyFilterInfo *info;
2169 info = g_slice_new0 (NotifyFilterInfo);
2170 info->self = g_object_ref (G_OBJECT (user_data));
2172 info->folder = tny_header_get_folder (header);
2173 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2174 notify_filter_change,
2176 notify_filter_change_destroy);
2184 _clear_hidding_filter (ModestHeaderView *header_view)
2186 ModestHeaderViewPrivate *priv = NULL;
2189 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2190 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2192 if (priv->hidding_ids != NULL) {
2193 for (i=0; i < priv->n_selected; i++)
2194 g_free (priv->hidding_ids[i]);
2195 g_free(priv->hidding_ids);
2200 modest_header_view_refilter (ModestHeaderView *header_view)
2202 GtkTreeModel *model = NULL;
2203 ModestHeaderViewPrivate *priv = NULL;
2205 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2206 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2208 /* Hide cut headers */
2209 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2210 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2211 priv->status = HEADER_VIEW_INIT;
2212 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2217 * Called when an account is removed. If I'm showing a folder of the
2218 * account that has been removed then clear the view
2221 on_account_removed (TnyAccountStore *self,
2222 TnyAccount *account,
2225 ModestHeaderViewPrivate *priv = NULL;
2227 /* Ignore changes in transport accounts */
2228 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2231 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2234 TnyAccount *my_account;
2236 my_account = tny_folder_get_account (priv->folder);
2237 if (my_account == account)
2238 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2239 g_object_unref (my_account);
2244 modest_header_view_add_observer(ModestHeaderView *header_view,
2245 ModestHeaderViewObserver *observer)
2247 ModestHeaderViewPrivate *priv;
2249 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2250 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2252 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2254 g_mutex_lock(priv->observer_list_lock);
2255 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2256 g_mutex_unlock(priv->observer_list_lock);
2260 modest_header_view_remove_observer(ModestHeaderView *header_view,
2261 ModestHeaderViewObserver *observer)
2263 ModestHeaderViewPrivate *priv;
2265 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2266 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2268 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2270 g_mutex_lock(priv->observer_list_lock);
2271 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2272 g_mutex_unlock(priv->observer_list_lock);
2276 modest_header_view_notify_observers(ModestHeaderView *header_view,
2277 GtkTreeModel *model,
2278 const gchar *tny_folder_id)
2280 ModestHeaderViewPrivate *priv = NULL;
2282 ModestHeaderViewObserver *observer;
2285 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2287 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2289 g_mutex_lock(priv->observer_list_lock);
2290 iter = priv->observer_list;
2291 while(iter != NULL){
2292 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2293 modest_header_view_observer_update(observer, model,
2295 iter = g_slist_next(iter);
2297 g_mutex_unlock(priv->observer_list_lock);
2301 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2303 ModestHeaderViewPrivate *priv = NULL;
2305 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2306 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2310 modest_header_view_set_filter (ModestHeaderView *self,
2311 ModestHeaderViewFilter filter)
2313 ModestHeaderViewPrivate *priv;
2314 GtkTreeModel *filter_model;
2316 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2317 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2319 priv->filter |= filter;
2321 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2322 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2323 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2328 modest_header_view_unset_filter (ModestHeaderView *self,
2329 ModestHeaderViewFilter filter)
2331 ModestHeaderViewPrivate *priv;
2332 GtkTreeModel *filter_model;
2334 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2335 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2337 priv->filter &= ~filter;
2339 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2340 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2341 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2346 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2348 if (strcmp ("style", spec->name) == 0) {
2349 update_style (MODEST_HEADER_VIEW (obj));
2350 gtk_widget_queue_draw (GTK_WIDGET (obj));
2355 update_style (ModestHeaderView *self)
2357 ModestHeaderViewPrivate *priv;
2358 GdkColor style_color;
2359 GdkColor style_active_color;
2360 PangoAttrList *attr_list;
2362 PangoAttribute *attr;
2363 GdkColor *new_color;
2365 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2366 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2370 attr_list = pango_attr_list_new ();
2371 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2372 gdk_color_parse ("grey", &style_color);
2374 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2375 pango_attr_list_insert (attr_list, attr);
2378 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2380 "SmallSystemFont", NULL,
2383 attr = pango_attr_font_desc_new (pango_font_description_copy
2384 (style->font_desc));
2385 pango_attr_list_insert (attr_list, attr);
2387 g_object_set (G_OBJECT (priv->renderer_address),
2388 "foreground-gdk", &style_color,
2389 "foreground-set", TRUE,
2390 "attributes", attr_list,
2392 g_object_set (G_OBJECT (priv->renderer_date_status),
2393 "foreground-gdk", &style_color,
2394 "foreground-set", TRUE,
2395 "attributes", attr_list,
2397 pango_attr_list_unref (attr_list);
2399 g_object_set (G_OBJECT (priv->renderer_address),
2400 "foreground-gdk", &style_color,
2401 "foreground-set", TRUE,
2402 "scale", PANGO_SCALE_SMALL,
2405 g_object_set (G_OBJECT (priv->renderer_date_status),
2406 "foreground-gdk", &style_color,
2407 "foreground-set", TRUE,
2408 "scale", PANGO_SCALE_SMALL,
2413 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2414 new_color = gdk_color_copy (&style_active_color);
2418 #ifdef MODEST_TOOLKIT_HILDON2
2419 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (new_color != NULL));
2420 g_object_set_data_full (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, new_color, (GDestroyNotify) gdk_color_free);
2425 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2430 GtkTreeModel *tree_model;
2435 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2442 g_debug ("located path: %s", gtk_tree_path_to_string (path));
2445 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2446 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2450 gtk_tree_model_get (tree_model, &iter,
2451 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,