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 *tree_filter, *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", MODEST_MARGIN_DOUBLE, 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, 26);
449 gtk_cell_renderer_set_fixed_size (renderer_priority, 24, 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 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
462 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
464 /* Add new columns */
465 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
466 ModestHeaderViewColumn col =
467 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
469 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
470 g_printerr ("modest: invalid column %d in column list\n", col);
476 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
477 column = get_new_column (_("A"), renderer_attach, FALSE,
478 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
480 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
482 gtk_tree_view_column_set_fixed_width (column, 45);
486 case MODEST_HEADER_VIEW_COLUMN_FROM:
487 column = get_new_column (_("From"), renderer_header, TRUE,
488 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
490 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
491 GINT_TO_POINTER(TRUE));
494 case MODEST_HEADER_VIEW_COLUMN_TO:
495 column = get_new_column (_("To"), renderer_header, TRUE,
496 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
498 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
499 GINT_TO_POINTER(FALSE));
502 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
503 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
504 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
506 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
507 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
508 compact_column = column;
511 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
512 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
513 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
515 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
516 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
517 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
518 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
519 compact_column = column;
523 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
524 column = get_new_column (_("Subject"), renderer_header, TRUE,
525 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
527 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
531 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
532 column = get_new_column (_("Received"), renderer_header, TRUE,
533 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
535 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
536 GINT_TO_POINTER(TRUE));
539 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
540 column = get_new_column (_("Sent"), renderer_header, TRUE,
541 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
543 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
544 GINT_TO_POINTER(FALSE));
547 case MODEST_HEADER_VIEW_COLUMN_SIZE:
548 column = get_new_column (_("Size"), renderer_header, TRUE,
549 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
551 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
554 case MODEST_HEADER_VIEW_COLUMN_STATUS:
555 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
556 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
558 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
563 g_return_val_if_reached(FALSE);
566 /* we keep the column id around */
567 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
568 GINT_TO_POINTER(col));
570 /* we need this ptr when sorting the rows */
571 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
573 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
577 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
578 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
579 (GtkTreeIterCompareFunc) cmp_rows,
580 compact_column, NULL);
581 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
582 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
583 (GtkTreeIterCompareFunc) cmp_subject_rows,
584 compact_column, NULL);
588 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
594 datetime_format_changed (ModestDatetimeFormatter *formatter,
595 ModestHeaderView *self)
597 gtk_widget_queue_draw (GTK_WIDGET (self));
601 modest_header_view_init (ModestHeaderView *obj)
603 ModestHeaderViewPrivate *priv;
606 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
609 priv->is_outbox = FALSE;
611 priv->monitor = NULL;
612 priv->observers_lock = g_mutex_new ();
613 priv->autoselect_reference = NULL;
615 priv->status = HEADER_VIEW_INIT;
616 priv->status_timeout = 0;
617 priv->notify_status = TRUE;
619 priv->observer_list_lock = g_mutex_new();
620 priv->observer_list = NULL;
622 priv->clipboard = modest_runtime_get_email_clipboard ();
623 priv->hidding_ids = NULL;
624 priv->n_selected = 0;
625 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
626 priv->selection_changed_handler = 0;
627 priv->acc_removed_handler = 0;
629 /* Sort parameters */
630 for (j=0; j < 2; j++) {
631 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
632 priv->sort_colid[j][i] = -1;
633 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
637 priv->datetime_formatter = modest_datetime_formatter_new ();
638 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
639 G_CALLBACK (datetime_format_changed), (gpointer) obj);
641 setup_drag_and_drop (GTK_WIDGET(obj));
645 modest_header_view_dispose (GObject *obj)
647 ModestHeaderView *self;
648 ModestHeaderViewPrivate *priv;
649 GtkTreeSelection *sel;
651 self = MODEST_HEADER_VIEW(obj);
652 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
654 if (priv->datetime_formatter) {
655 g_object_unref (priv->datetime_formatter);
656 priv->datetime_formatter = NULL;
659 /* Free in the dispose to avoid unref cycles */
661 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
662 g_object_unref (G_OBJECT (priv->folder));
666 /* We need to do this here in the dispose because the
667 selection won't exist when finalizing */
668 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
669 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
670 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
671 priv->selection_changed_handler = 0;
674 G_OBJECT_CLASS(parent_class)->dispose (obj);
678 modest_header_view_finalize (GObject *obj)
680 ModestHeaderView *self;
681 ModestHeaderViewPrivate *priv;
683 self = MODEST_HEADER_VIEW(obj);
684 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
686 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
687 priv->acc_removed_handler)) {
688 g_signal_handler_disconnect (modest_runtime_get_account_store (),
689 priv->acc_removed_handler);
692 /* There is no need to lock because there should not be any
693 * reference to self now. */
694 g_mutex_free(priv->observer_list_lock);
695 g_slist_free(priv->observer_list);
697 g_mutex_lock (priv->observers_lock);
699 tny_folder_monitor_stop (priv->monitor);
700 g_object_unref (G_OBJECT (priv->monitor));
702 g_mutex_unlock (priv->observers_lock);
703 g_mutex_free (priv->observers_lock);
705 /* Clear hidding array created by cut operation */
706 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
708 if (priv->autoselect_reference != NULL) {
709 gtk_tree_row_reference_free (priv->autoselect_reference);
710 priv->autoselect_reference = NULL;
713 G_OBJECT_CLASS(parent_class)->finalize (obj);
718 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
721 GtkTreeSelection *sel;
722 ModestHeaderView *self;
723 ModestHeaderViewPrivate *priv;
725 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
728 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
729 self = MODEST_HEADER_VIEW(obj);
730 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
732 modest_header_view_set_style (self, style);
734 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
735 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
736 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
738 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
739 TRUE); /* alternating row colors */
741 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
742 priv->selection_changed_handler =
743 g_signal_connect_after (sel, "changed",
744 G_CALLBACK(on_selection_changed), self);
746 g_signal_connect (self, "row-activated",
747 G_CALLBACK (on_header_row_activated), NULL);
749 #ifndef MODEST_TOOLKIT_HILDON2
750 g_signal_connect (self, "focus-in-event",
751 G_CALLBACK(on_focus_in), NULL);
752 g_signal_connect (self, "focus-out-event",
753 G_CALLBACK(on_focus_out), NULL);
756 g_signal_connect (self, "button-press-event",
757 G_CALLBACK(on_button_press_event), NULL);
758 g_signal_connect (self, "button-release-event",
759 G_CALLBACK(on_button_release_event), NULL);
761 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
763 G_CALLBACK (on_account_removed),
766 g_signal_connect (self, "expose-event",
767 G_CALLBACK(modest_header_view_on_expose_event),
770 return GTK_WIDGET(self);
775 modest_header_view_count_selected_headers (ModestHeaderView *self)
777 GtkTreeSelection *sel;
780 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
782 /* Get selection object and check selected rows count */
783 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
784 selected_rows = gtk_tree_selection_count_selected_rows (sel);
786 return selected_rows;
790 modest_header_view_has_selected_headers (ModestHeaderView *self)
792 GtkTreeSelection *sel;
795 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
797 /* Get selection object and check selected rows count */
798 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
799 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
806 modest_header_view_get_selected_headers (ModestHeaderView *self)
808 GtkTreeSelection *sel;
809 ModestHeaderViewPrivate *priv;
810 TnyList *header_list = NULL;
812 GList *list, *tmp = NULL;
813 GtkTreeModel *tree_model = NULL;
816 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
818 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
820 /* Get selected rows */
821 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
822 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
825 header_list = tny_simple_list_new();
827 list = g_list_reverse (list);
830 /* get header from selection */
831 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
832 gtk_tree_model_get (tree_model, &iter,
833 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
835 /* Prepend to list */
836 tny_list_prepend (header_list, G_OBJECT (header));
837 g_object_unref (G_OBJECT (header));
839 tmp = g_list_next (tmp);
842 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
849 /* scroll our list view so the selected item is visible */
851 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
853 #ifdef MODEST_TOOLKIT_GTK
855 GtkTreePath *selected_path;
856 GtkTreePath *start, *end;
860 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
861 selected_path = gtk_tree_model_get_path (model, iter);
863 start = gtk_tree_path_new ();
864 end = gtk_tree_path_new ();
866 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
868 if (gtk_tree_path_compare (selected_path, start) < 0 ||
869 gtk_tree_path_compare (end, selected_path) < 0)
870 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
871 selected_path, NULL, TRUE,
874 gtk_tree_path_free (selected_path);
875 gtk_tree_path_free (start);
876 gtk_tree_path_free (end);
878 #endif /* MODEST_TOOLKIT_GTK */
883 modest_header_view_select_next (ModestHeaderView *self)
885 GtkTreeSelection *sel;
890 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
892 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
893 path = get_selected_row (GTK_TREE_VIEW(self), &model);
894 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
895 /* Unselect previous path */
896 gtk_tree_selection_unselect_path (sel, path);
898 /* Move path down and selects new one */
899 if (gtk_tree_model_iter_next (model, &iter)) {
900 gtk_tree_selection_select_iter (sel, &iter);
901 scroll_to_selected (self, &iter, FALSE);
903 gtk_tree_path_free(path);
909 modest_header_view_select_prev (ModestHeaderView *self)
911 GtkTreeSelection *sel;
916 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
918 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
919 path = get_selected_row (GTK_TREE_VIEW(self), &model);
920 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
921 /* Unselect previous path */
922 gtk_tree_selection_unselect_path (sel, path);
925 if (gtk_tree_path_prev (path)) {
926 gtk_tree_model_get_iter (model, &iter, path);
928 /* Select the new one */
929 gtk_tree_selection_select_iter (sel, &iter);
930 scroll_to_selected (self, &iter, TRUE);
933 gtk_tree_path_free (path);
938 modest_header_view_get_columns (ModestHeaderView *self)
940 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
942 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
948 modest_header_view_set_style (ModestHeaderView *self,
949 ModestHeaderViewStyle style)
951 ModestHeaderViewPrivate *priv;
952 gboolean show_col_headers = FALSE;
953 ModestHeaderViewStyle old_style;
955 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
956 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
959 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
960 if (priv->style == style)
961 return TRUE; /* nothing to do */
964 case MODEST_HEADER_VIEW_STYLE_DETAILS:
965 show_col_headers = TRUE;
967 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
970 g_return_val_if_reached (FALSE);
972 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
973 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
975 old_style = priv->style;
982 ModestHeaderViewStyle
983 modest_header_view_get_style (ModestHeaderView *self)
985 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
987 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
990 /* This is used to automatically select the first header if the user
991 * has not selected any header yet.
994 modest_header_view_on_expose_event(GtkTreeView *header_view,
995 GdkEventExpose *event,
998 GtkTreeSelection *sel;
1000 GtkTreeIter tree_iter;
1001 ModestHeaderViewPrivate *priv;
1003 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1004 model = gtk_tree_view_get_model(header_view);
1009 #ifdef MODEST_TOOLKIT_HILDON2
1012 sel = gtk_tree_view_get_selection(header_view);
1013 if(!gtk_tree_selection_count_selected_rows(sel)) {
1014 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1015 GtkTreePath *tree_iter_path;
1016 /* Prevent the widget from getting the focus
1017 when selecting the first item */
1018 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1019 g_object_set(header_view, "can-focus", FALSE, NULL);
1020 gtk_tree_selection_select_iter(sel, &tree_iter);
1021 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1022 g_object_set(header_view, "can-focus", TRUE, NULL);
1023 if (priv->autoselect_reference) {
1024 gtk_tree_row_reference_free (priv->autoselect_reference);
1026 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1027 gtk_tree_path_free (tree_iter_path);
1030 if (priv->autoselect_reference != NULL) {
1031 gboolean moved_selection = FALSE;
1032 GtkTreePath * last_path;
1033 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1034 moved_selection = TRUE;
1038 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1039 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1040 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1041 moved_selection = TRUE;
1042 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1044 gtk_tree_path_free (last_path);
1046 if (moved_selection) {
1047 gtk_tree_row_reference_free (priv->autoselect_reference);
1048 priv->autoselect_reference = NULL;
1051 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1052 GtkTreePath *current_path;
1053 current_path = gtk_tree_model_get_path (model, &tree_iter);
1054 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1055 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1056 g_object_set(header_view, "can-focus", FALSE, NULL);
1057 gtk_tree_selection_unselect_all (sel);
1058 gtk_tree_selection_select_iter(sel, &tree_iter);
1059 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1060 g_object_set(header_view, "can-focus", TRUE, NULL);
1061 gtk_tree_row_reference_free (priv->autoselect_reference);
1062 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1064 gtk_tree_path_free (current_path);
1065 gtk_tree_path_free (last_path);
1075 modest_header_view_get_folder (ModestHeaderView *self)
1077 ModestHeaderViewPrivate *priv;
1079 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1081 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1084 g_object_ref (priv->folder);
1086 return priv->folder;
1090 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1096 ModestHeaderView *self;
1097 ModestHeaderViewPrivate *priv;
1099 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1101 self = MODEST_HEADER_VIEW (user_data);
1102 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1104 if (cancelled || err)
1107 /* Add IDLE observer (monitor) and another folder observer for
1108 new messages (self) */
1109 g_mutex_lock (priv->observers_lock);
1110 if (priv->monitor) {
1111 tny_folder_monitor_stop (priv->monitor);
1112 g_object_unref (G_OBJECT (priv->monitor));
1114 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1115 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1116 tny_folder_monitor_start (priv->monitor);
1117 g_mutex_unlock (priv->observers_lock);
1121 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1125 ModestHeaderViewPrivate *priv;
1126 GList *cols, *cursor;
1127 GtkTreeModel *filter_model, *sortable;
1129 GtkSortType sort_type;
1131 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1133 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1135 /* Start the monitor in the callback of the
1136 tny_gtk_header_list_model_set_folder call. It's crucial to
1137 do it there and not just after the call because we want the
1138 monitor to observe only the headers returned by the
1139 tny_folder_get_headers_async call that it's inside the
1140 tny_gtk_header_list_model_set_folder call. This way the
1141 monitor infrastructure could successfully cope with
1142 duplicates. For example if a tny_folder_add_msg_async is
1143 happening while tny_gtk_header_list_model_set_folder is
1144 invoked, then the first call could add a header that will
1145 be added again by tny_gtk_header_list_model_set_folder, so
1146 we'd end up with duplicate headers. sergio */
1147 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1149 set_folder_intern_get_headers_async_cb,
1152 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1153 g_object_unref (G_OBJECT (headers));
1155 /* Init filter_row function to examine empty status */
1156 priv->status = HEADER_VIEW_INIT;
1158 /* Create a tree model filter to hide and show rows for cut operations */
1159 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1160 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1164 g_object_unref (G_OBJECT (sortable));
1166 /* install our special sorting functions */
1167 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1169 /* Restore sort column id */
1171 type = modest_tny_folder_guess_folder_type (folder);
1172 if (type == TNY_FOLDER_TYPE_INVALID)
1173 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1175 sort_colid = modest_header_view_get_sort_column_id (self, type);
1176 sort_type = modest_header_view_get_sort_type (self, type);
1177 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1180 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1181 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1182 (GtkTreeIterCompareFunc) cmp_rows,
1184 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1185 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1186 (GtkTreeIterCompareFunc) cmp_subject_rows,
1191 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1192 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1193 tny_folder_get_id(folder));
1194 g_object_unref (G_OBJECT (filter_model));
1201 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1203 GtkSortType sort_type)
1205 ModestHeaderViewPrivate *priv = NULL;
1206 GtkTreeModel *tree_filter, *sortable = NULL;
1209 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1210 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1212 /* Get model and private data */
1213 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1214 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1215 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1216 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1218 /* Sort tree model */
1219 type = modest_tny_folder_guess_folder_type (priv->folder);
1220 if (type == TNY_FOLDER_TYPE_INVALID)
1221 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1223 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1226 /* Store new sort parameters */
1227 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1232 modest_header_view_set_sort_params (ModestHeaderView *self,
1234 GtkSortType sort_type,
1237 ModestHeaderViewPrivate *priv;
1238 ModestHeaderViewStyle style;
1240 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1241 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1242 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1244 style = modest_header_view_get_style (self);
1245 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1247 priv->sort_colid[style][type] = sort_colid;
1248 priv->sort_type[style][type] = sort_type;
1252 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1255 ModestHeaderViewPrivate *priv;
1256 ModestHeaderViewStyle style;
1258 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1259 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1261 style = modest_header_view_get_style (self);
1262 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1264 return priv->sort_colid[style][type];
1268 modest_header_view_get_sort_type (ModestHeaderView *self,
1271 ModestHeaderViewPrivate *priv;
1272 ModestHeaderViewStyle style;
1274 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1275 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1277 style = modest_header_view_get_style (self);
1278 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1280 return priv->sort_type[style][type];
1284 ModestHeaderView *header_view;
1285 RefreshAsyncUserCallback cb;
1290 folder_refreshed_cb (ModestMailOperation *mail_op,
1294 ModestHeaderViewPrivate *priv;
1295 SetFolderHelper *info;
1297 info = (SetFolderHelper*) user_data;
1299 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1303 info->cb (mail_op, folder, info->user_data);
1305 /* Start the folder count changes observer. We do not need it
1306 before the refresh. Note that the monitor could still be
1307 called for this refresh but now we know that the callback
1308 was previously called */
1309 g_mutex_lock (priv->observers_lock);
1310 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1311 g_mutex_unlock (priv->observers_lock);
1313 /* Notify the observers that the update is over */
1314 g_signal_emit (G_OBJECT (info->header_view),
1315 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1317 /* Allow filtering notifications from now on if the current
1318 folder is still the same (if not then the user has selected
1319 another one to refresh, we should wait until that refresh
1321 if (priv->folder == folder)
1322 priv->notify_status = TRUE;
1325 g_object_unref (info->header_view);
1330 refresh_folder_error_handler (ModestMailOperation *mail_op,
1333 const GError *error = modest_mail_operation_get_error (mail_op);
1335 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1336 error->code == TNY_IO_ERROR_WRITE ||
1337 error->code == TNY_IO_ERROR_READ) {
1338 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1339 /* If the mail op has been cancelled then it's not an error: don't show any message */
1340 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1341 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1342 modest_platform_information_banner (NULL, NULL, msg);
1349 modest_header_view_set_folder (ModestHeaderView *self,
1352 ModestWindow *progress_window,
1353 RefreshAsyncUserCallback callback,
1356 ModestHeaderViewPrivate *priv;
1358 g_return_if_fail (self);
1360 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1363 if (priv->status_timeout) {
1364 g_source_remove (priv->status_timeout);
1365 priv->status_timeout = 0;
1368 g_mutex_lock (priv->observers_lock);
1369 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1370 g_object_unref (priv->folder);
1371 priv->folder = NULL;
1372 g_mutex_unlock (priv->observers_lock);
1376 GtkTreeSelection *selection;
1377 SetFolderHelper *info;
1378 ModestMailOperation *mail_op = NULL;
1380 /* Set folder in the model */
1381 modest_header_view_set_folder_intern (self, folder);
1383 /* Pick my reference. Nothing to do with the mail operation */
1384 priv->folder = g_object_ref (folder);
1386 /* Do not notify about filterings until the refresh finishes */
1387 priv->notify_status = FALSE;
1389 /* Clear the selection if exists */
1390 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1391 gtk_tree_selection_unselect_all(selection);
1392 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1394 /* Notify the observers that the update begins */
1395 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1398 /* create the helper */
1399 info = g_malloc0 (sizeof (SetFolderHelper));
1400 info->header_view = g_object_ref (self);
1401 info->cb = callback;
1402 info->user_data = user_data;
1404 /* Create the mail operation (source will be the parent widget) */
1405 if (progress_window)
1406 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1407 refresh_folder_error_handler,
1410 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1413 /* Refresh the folder asynchronously */
1414 modest_mail_operation_refresh_folder (mail_op,
1416 folder_refreshed_cb,
1419 folder_refreshed_cb (mail_op, folder, info);
1423 g_object_unref (mail_op);
1425 g_mutex_lock (priv->observers_lock);
1427 if (priv->monitor) {
1428 tny_folder_monitor_stop (priv->monitor);
1429 g_object_unref (G_OBJECT (priv->monitor));
1430 priv->monitor = NULL;
1433 if (priv->autoselect_reference) {
1434 gtk_tree_row_reference_free (priv->autoselect_reference);
1435 priv->autoselect_reference = NULL;
1438 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1440 modest_header_view_notify_observers(self, NULL, NULL);
1442 g_mutex_unlock (priv->observers_lock);
1444 /* Notify the observers that the update is over */
1445 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1451 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1452 GtkTreeViewColumn *column, gpointer userdata)
1454 ModestHeaderView *self = NULL;
1455 ModestHeaderViewPrivate *priv = NULL;
1457 GtkTreeModel *model = NULL;
1458 TnyHeader *header = NULL;
1459 TnyHeaderFlags flags;
1461 self = MODEST_HEADER_VIEW (treeview);
1462 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1464 model = gtk_tree_view_get_model (treeview);
1465 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1468 /* get the first selected item */
1469 gtk_tree_model_get (model, &iter,
1470 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1471 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1474 /* Dont open DELETED messages */
1475 if (flags & TNY_HEADER_FLAG_DELETED) {
1478 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1479 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1480 modest_platform_information_banner (NULL, NULL, msg);
1486 g_signal_emit (G_OBJECT(self),
1487 signals[HEADER_ACTIVATED_SIGNAL],
1493 g_object_unref (G_OBJECT (header));
1498 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1500 GtkTreeModel *model;
1501 TnyHeader *header = NULL;
1502 GtkTreePath *path = NULL;
1504 ModestHeaderView *self;
1505 ModestHeaderViewPrivate *priv;
1506 GList *selected = NULL;
1508 g_return_if_fail (sel);
1509 g_return_if_fail (user_data);
1511 self = MODEST_HEADER_VIEW (user_data);
1512 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1514 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1515 if (selected != NULL)
1516 path = (GtkTreePath *) selected->data;
1517 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1518 return; /* msg was _un_selected */
1520 gtk_tree_model_get (model, &iter,
1521 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1525 g_signal_emit (G_OBJECT(self),
1526 signals[HEADER_SELECTED_SIGNAL],
1529 g_object_unref (G_OBJECT (header));
1531 /* free all items in 'selected' */
1532 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1533 g_list_free (selected);
1537 /* PROTECTED method. It's useful when we want to force a given
1538 selection to reload a msg. For example if we have selected a header
1539 in offline mode, when Modest become online, we want to reload the
1540 message automatically without an user click over the header */
1542 _modest_header_view_change_selection (GtkTreeSelection *selection,
1545 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1546 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1548 on_selection_changed (selection, user_data);
1552 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1559 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1563 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1567 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1575 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1582 /* static int counter = 0; */
1584 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1585 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1586 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1590 case TNY_HEADER_FLAG_ATTACHMENTS:
1592 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
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_FLAGS_COLUMN, &val2,
1595 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1597 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1598 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1600 return cmp ? cmp : t1 - t2;
1602 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1603 TnyHeader *header1 = NULL, *header2 = NULL;
1605 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1606 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1607 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1608 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1610 /* This is for making priority values respect the intuitive sort relationship
1611 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1613 if (header1 && header2) {
1614 cmp = compare_priorities (tny_header_get_priority (header1),
1615 tny_header_get_priority (header2));
1616 g_object_unref (header1);
1617 g_object_unref (header2);
1619 return cmp ? cmp : t1 - t2;
1625 return &iter1 - &iter2; /* oughhhh */
1630 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1636 /* static int counter = 0; */
1638 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1640 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1641 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1642 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1643 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1645 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1646 val2 + modest_text_utils_get_subject_prefix_len(val2),
1653 /* Drag and drop stuff */
1655 drag_data_get_cb (GtkWidget *widget,
1656 GdkDragContext *context,
1657 GtkSelectionData *selection_data,
1662 ModestHeaderView *self = NULL;
1663 ModestHeaderViewPrivate *priv = NULL;
1665 self = MODEST_HEADER_VIEW (widget);
1666 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1668 /* Set the data. Do not use the current selection because it
1669 could be different than the selection at the beginning of
1671 modest_dnd_selection_data_set_paths (selection_data,
1672 priv->drag_begin_cached_selected_rows);
1676 * We're caching the selected rows at the beginning because the
1677 * selection could change between drag-begin and drag-data-get, for
1678 * example if we have a set of rows already selected, and then we
1679 * click in one of them (without SHIFT key pressed) and begin a drag,
1680 * the selection at that moment contains all the selected lines, but
1681 * after dropping the selection, the release event provokes that only
1682 * the row used to begin the drag is selected, so at the end the
1683 * drag&drop affects only one rows instead of all the selected ones.
1687 drag_begin_cb (GtkWidget *widget,
1688 GdkDragContext *context,
1691 ModestHeaderView *self = NULL;
1692 ModestHeaderViewPrivate *priv = NULL;
1693 GtkTreeSelection *selection;
1695 self = MODEST_HEADER_VIEW (widget);
1696 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1698 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1699 priv->drag_begin_cached_selected_rows =
1700 gtk_tree_selection_get_selected_rows (selection, NULL);
1704 * We use the drag-end signal to clear the cached selection, we use
1705 * this because this allways happens, whether or not the d&d was a
1709 drag_end_cb (GtkWidget *widget,
1713 ModestHeaderView *self = NULL;
1714 ModestHeaderViewPrivate *priv = NULL;
1716 self = MODEST_HEADER_VIEW (widget);
1717 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1719 /* Free cached data */
1720 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1721 g_list_free (priv->drag_begin_cached_selected_rows);
1722 priv->drag_begin_cached_selected_rows = NULL;
1725 /* Header view drag types */
1726 const GtkTargetEntry header_view_drag_types[] = {
1727 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1731 enable_drag_and_drop (GtkWidget *self)
1733 #ifdef MODEST_TOOLKIT_HILDON2
1736 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1737 header_view_drag_types,
1738 G_N_ELEMENTS (header_view_drag_types),
1739 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1743 disable_drag_and_drop (GtkWidget *self)
1745 #ifdef MODEST_TOOLKIT_HILDON2
1748 gtk_drag_source_unset (self);
1752 setup_drag_and_drop (GtkWidget *self)
1754 #ifdef MODEST_TOOLKIT_HILDON2
1757 enable_drag_and_drop(self);
1758 g_signal_connect(G_OBJECT (self), "drag_data_get",
1759 G_CALLBACK(drag_data_get_cb), NULL);
1761 g_signal_connect(G_OBJECT (self), "drag_begin",
1762 G_CALLBACK(drag_begin_cb), NULL);
1764 g_signal_connect(G_OBJECT (self), "drag_end",
1765 G_CALLBACK(drag_end_cb), NULL);
1768 static GtkTreePath *
1769 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1771 GtkTreePath *path = NULL;
1772 GtkTreeSelection *sel = NULL;
1775 sel = gtk_tree_view_get_selection(self);
1776 rows = gtk_tree_selection_get_selected_rows (sel, model);
1778 if ((rows == NULL) || (g_list_length(rows) != 1))
1781 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1786 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1792 #ifndef MODEST_TOOLKIT_HILDON2
1794 * This function moves the tree view scroll to the current selected
1795 * row when the widget grabs the focus
1798 on_focus_in (GtkWidget *self,
1799 GdkEventFocus *event,
1802 GtkTreeSelection *selection;
1803 GtkTreeModel *model;
1804 GList *selected = NULL;
1805 GtkTreePath *selected_path = NULL;
1807 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1811 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1812 /* If none selected yet, pick the first one */
1813 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1817 /* Return if the model is empty */
1818 if (!gtk_tree_model_get_iter_first (model, &iter))
1821 path = gtk_tree_model_get_path (model, &iter);
1822 gtk_tree_selection_select_path (selection, path);
1823 gtk_tree_path_free (path);
1826 /* Need to get the all the rows because is selection multiple */
1827 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1828 if (selected == NULL) return FALSE;
1829 selected_path = (GtkTreePath *) selected->data;
1832 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1833 g_list_free (selected);
1839 on_focus_out (GtkWidget *self,
1840 GdkEventFocus *event,
1844 if (!gtk_widget_is_focus (self)) {
1845 GtkTreeSelection *selection = NULL;
1846 GList *selected_rows = NULL;
1847 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1848 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1849 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1850 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1851 gtk_tree_selection_unselect_all (selection);
1852 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1853 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1854 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1855 g_list_free (selected_rows);
1863 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1865 enable_drag_and_drop(self);
1870 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1872 GtkTreeSelection *selection = NULL;
1873 GtkTreePath *path = NULL;
1874 gboolean already_selected = FALSE, already_opened = FALSE;
1875 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1877 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1879 GtkTreeModel *model;
1881 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1882 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1884 /* Get header from model */
1885 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1886 if (gtk_tree_model_get_iter (model, &iter, path)) {
1887 GValue value = {0,};
1890 gtk_tree_model_get_value (model, &iter,
1891 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1893 header = (TnyHeader *) g_value_get_object (&value);
1894 if (TNY_IS_HEADER (header)) {
1895 status = modest_tny_all_send_queues_get_msg_status (header);
1896 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1899 g_value_unset (&value);
1903 /* Enable drag and drop only if the user clicks on a row that
1904 it's already selected. If not, let him select items using
1905 the pointer. If the message is in an OUTBOX and in sending
1906 status disable drag and drop as well */
1907 if (!already_selected ||
1908 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1910 disable_drag_and_drop(self);
1913 gtk_tree_path_free(path);
1915 /* If it's already opened then do not let the button-press
1916 event go on because it'll perform a message open because
1917 we're clicking on to an already selected header */
1922 folder_monitor_update (TnyFolderObserver *self,
1923 TnyFolderChange *change)
1925 ModestHeaderViewPrivate *priv = NULL;
1926 TnyFolderChangeChanged changed;
1927 TnyFolder *folder = NULL;
1929 changed = tny_folder_change_get_changed (change);
1931 /* Do not notify the observers if the folder of the header
1932 view has changed before this call to the observer
1934 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1935 folder = tny_folder_change_get_folder (change);
1936 if (folder != priv->folder)
1939 MODEST_DEBUG_BLOCK (
1940 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1941 g_print ("ADDED %d/%d (r/t) \n",
1942 tny_folder_change_get_new_unread_count (change),
1943 tny_folder_change_get_new_all_count (change));
1944 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1945 g_print ("ALL COUNT %d\n",
1946 tny_folder_change_get_new_all_count (change));
1947 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1948 g_print ("UNREAD COUNT %d\n",
1949 tny_folder_change_get_new_unread_count (change));
1950 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1951 g_print ("EXPUNGED %d/%d (r/t) \n",
1952 tny_folder_change_get_new_unread_count (change),
1953 tny_folder_change_get_new_all_count (change));
1954 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1955 g_print ("FOLDER RENAME\n");
1956 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1957 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1958 tny_folder_change_get_new_unread_count (change),
1959 tny_folder_change_get_new_all_count (change));
1960 g_print ("---------------------------------------------------\n");
1963 /* Check folder count */
1964 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1965 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1967 g_mutex_lock (priv->observers_lock);
1969 /* Emit signal to evaluate how headers changes affects
1970 to the window view */
1971 g_signal_emit (G_OBJECT(self),
1972 signals[MSG_COUNT_CHANGED_SIGNAL],
1975 /* Added or removed headers, so data stored on cliboard are invalid */
1976 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1977 modest_email_clipboard_clear (priv->clipboard);
1979 g_mutex_unlock (priv->observers_lock);
1985 g_object_unref (folder);
1989 modest_header_view_is_empty (ModestHeaderView *self)
1991 ModestHeaderViewPrivate *priv;
1993 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1995 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1997 return priv->status == HEADER_VIEW_EMPTY;
2001 modest_header_view_clear (ModestHeaderView *self)
2003 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2005 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2009 modest_header_view_copy_selection (ModestHeaderView *header_view)
2011 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2013 /* Copy selection */
2014 _clipboard_set_selected_data (header_view, FALSE);
2018 modest_header_view_cut_selection (ModestHeaderView *header_view)
2020 ModestHeaderViewPrivate *priv = NULL;
2021 const gchar **hidding = NULL;
2022 guint i, n_selected;
2024 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2026 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2028 /* Copy selection */
2029 _clipboard_set_selected_data (header_view, TRUE);
2031 /* Get hidding ids */
2032 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2034 /* Clear hidding array created by previous cut operation */
2035 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2037 /* Copy hidding array */
2038 priv->n_selected = n_selected;
2039 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2040 for (i=0; i < n_selected; i++)
2041 priv->hidding_ids[i] = g_strdup(hidding[i]);
2043 /* Hide cut headers */
2044 modest_header_view_refilter (header_view);
2051 _clipboard_set_selected_data (ModestHeaderView *header_view,
2054 ModestHeaderViewPrivate *priv = NULL;
2055 TnyList *headers = NULL;
2057 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2058 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2060 /* Set selected data on clipboard */
2061 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2062 headers = modest_header_view_get_selected_headers (header_view);
2063 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2066 g_object_unref (headers);
2070 ModestHeaderView *self;
2075 notify_filter_change (gpointer data)
2077 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2079 g_signal_emit (info->self,
2080 signals[MSG_COUNT_CHANGED_SIGNAL],
2081 0, info->folder, NULL);
2087 notify_filter_change_destroy (gpointer data)
2089 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2090 ModestHeaderViewPrivate *priv;
2092 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2093 priv->status_timeout = 0;
2095 g_object_unref (info->self);
2096 g_object_unref (info->folder);
2097 g_slice_free (NotifyFilterInfo, info);
2101 filter_row (GtkTreeModel *model,
2105 ModestHeaderViewPrivate *priv = NULL;
2106 TnyHeaderFlags flags;
2107 TnyHeader *header = NULL;
2110 gboolean visible = TRUE;
2111 gboolean found = FALSE;
2112 GValue value = {0,};
2113 HeaderViewStatus old_status;
2115 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2116 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2118 /* Get header from model */
2119 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2120 flags = (TnyHeaderFlags) g_value_get_int (&value);
2121 g_value_unset (&value);
2122 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2123 header = (TnyHeader *) g_value_get_object (&value);
2124 g_value_unset (&value);
2126 /* Hide deleted and mark as deleted heders */
2127 if (flags & TNY_HEADER_FLAG_DELETED ||
2128 flags & TNY_HEADER_FLAG_EXPUNGED) {
2133 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2134 if (priv->is_outbox &&
2135 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2141 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2142 if (priv->is_outbox &&
2143 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2149 /* If no data on clipboard, return always TRUE */
2150 if (modest_email_clipboard_cleared(priv->clipboard)) {
2155 /* Get message id from header (ensure is a valid id) */
2162 if (priv->hidding_ids != NULL) {
2163 id = tny_header_dup_message_id (header);
2164 for (i=0; i < priv->n_selected && !found; i++)
2165 if (priv->hidding_ids[i] != NULL && id != NULL)
2166 found = (!strcmp (priv->hidding_ids[i], id));
2173 old_status = priv->status;
2174 priv->status = ((gboolean) priv->status) && !visible;
2175 if ((priv->notify_status) && (priv->status != old_status)) {
2176 NotifyFilterInfo *info;
2178 if (priv->status_timeout)
2179 g_source_remove (priv->status_timeout);
2181 info = g_slice_new0 (NotifyFilterInfo);
2182 info->self = g_object_ref (G_OBJECT (user_data));
2183 info->folder = tny_header_get_folder (header);
2184 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2185 notify_filter_change,
2187 notify_filter_change_destroy);
2194 _clear_hidding_filter (ModestHeaderView *header_view)
2196 ModestHeaderViewPrivate *priv = NULL;
2199 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2200 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2202 if (priv->hidding_ids != NULL) {
2203 for (i=0; i < priv->n_selected; i++)
2204 g_free (priv->hidding_ids[i]);
2205 g_free(priv->hidding_ids);
2210 modest_header_view_refilter (ModestHeaderView *header_view)
2212 GtkTreeModel *model = NULL;
2213 ModestHeaderViewPrivate *priv = NULL;
2215 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2216 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2218 /* Hide cut headers */
2219 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2220 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2221 priv->status = HEADER_VIEW_INIT;
2222 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2227 * Called when an account is removed. If I'm showing a folder of the
2228 * account that has been removed then clear the view
2231 on_account_removed (TnyAccountStore *self,
2232 TnyAccount *account,
2235 ModestHeaderViewPrivate *priv = NULL;
2237 /* Ignore changes in transport accounts */
2238 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2241 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2244 TnyAccount *my_account;
2246 my_account = tny_folder_get_account (priv->folder);
2247 if (my_account == account)
2248 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2249 g_object_unref (my_account);
2254 modest_header_view_add_observer(ModestHeaderView *header_view,
2255 ModestHeaderViewObserver *observer)
2257 ModestHeaderViewPrivate *priv;
2259 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2260 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2262 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2264 g_mutex_lock(priv->observer_list_lock);
2265 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2266 g_mutex_unlock(priv->observer_list_lock);
2270 modest_header_view_remove_observer(ModestHeaderView *header_view,
2271 ModestHeaderViewObserver *observer)
2273 ModestHeaderViewPrivate *priv;
2275 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2276 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2278 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2280 g_mutex_lock(priv->observer_list_lock);
2281 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2282 g_mutex_unlock(priv->observer_list_lock);
2286 modest_header_view_notify_observers(ModestHeaderView *header_view,
2287 GtkTreeModel *model,
2288 const gchar *tny_folder_id)
2290 ModestHeaderViewPrivate *priv = NULL;
2292 ModestHeaderViewObserver *observer;
2295 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2297 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2299 g_mutex_lock(priv->observer_list_lock);
2300 iter = priv->observer_list;
2301 while(iter != NULL){
2302 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2303 modest_header_view_observer_update(observer, model,
2305 iter = g_slist_next(iter);
2307 g_mutex_unlock(priv->observer_list_lock);
2311 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2313 ModestHeaderViewPrivate *priv = NULL;
2315 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2316 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2320 modest_header_view_set_filter (ModestHeaderView *self,
2321 ModestHeaderViewFilter filter)
2323 ModestHeaderViewPrivate *priv;
2324 GtkTreeModel *filter_model;
2326 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2327 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2329 priv->filter |= filter;
2331 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2332 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2333 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2338 modest_header_view_unset_filter (ModestHeaderView *self,
2339 ModestHeaderViewFilter filter)
2341 ModestHeaderViewPrivate *priv;
2342 GtkTreeModel *filter_model;
2344 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2345 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2347 priv->filter &= ~filter;
2349 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2350 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2351 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2356 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2358 if (strcmp ("style", spec->name) == 0) {
2359 update_style (MODEST_HEADER_VIEW (obj));
2360 gtk_widget_queue_draw (GTK_WIDGET (obj));
2365 update_style (ModestHeaderView *self)
2367 ModestHeaderViewPrivate *priv;
2368 GdkColor style_color;
2369 GdkColor style_active_color;
2370 PangoAttrList *attr_list;
2372 PangoAttribute *attr;
2373 GdkColor *new_color;
2375 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2376 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2380 attr_list = pango_attr_list_new ();
2381 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2382 gdk_color_parse ("grey", &style_color);
2384 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2385 pango_attr_list_insert (attr_list, attr);
2388 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2390 "SmallSystemFont", NULL,
2393 attr = pango_attr_font_desc_new (pango_font_description_copy
2394 (style->font_desc));
2395 pango_attr_list_insert (attr_list, attr);
2397 g_object_set (G_OBJECT (priv->renderer_address),
2398 "foreground-gdk", &style_color,
2399 "foreground-set", TRUE,
2400 "attributes", attr_list,
2402 g_object_set (G_OBJECT (priv->renderer_date_status),
2403 "foreground-gdk", &style_color,
2404 "foreground-set", TRUE,
2405 "attributes", attr_list,
2407 pango_attr_list_unref (attr_list);
2409 g_object_set (G_OBJECT (priv->renderer_address),
2410 "foreground-gdk", &style_color,
2411 "foreground-set", TRUE,
2412 "scale", PANGO_SCALE_SMALL,
2415 g_object_set (G_OBJECT (priv->renderer_date_status),
2416 "foreground-gdk", &style_color,
2417 "foreground-set", TRUE,
2418 "scale", PANGO_SCALE_SMALL,
2423 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2424 new_color = gdk_color_copy (&style_active_color);
2428 #ifdef MODEST_TOOLKIT_HILDON2
2429 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (new_color != NULL));
2430 g_object_set_data_full (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, new_color, (GDestroyNotify) gdk_color_free);