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", 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 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 TnyList *header_list = NULL;
811 GList *list, *tmp = NULL;
812 GtkTreeModel *tree_model = NULL;
815 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
818 /* Get selected rows */
819 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
820 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
823 header_list = tny_simple_list_new();
825 list = g_list_reverse (list);
828 /* get header from selection */
829 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
830 gtk_tree_model_get (tree_model, &iter,
831 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
833 /* Prepend to list */
834 tny_list_prepend (header_list, G_OBJECT (header));
835 g_object_unref (G_OBJECT (header));
837 tmp = g_list_next (tmp);
840 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
847 /* scroll our list view so the selected item is visible */
849 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
851 #ifdef MODEST_TOOLKIT_GTK
853 GtkTreePath *selected_path;
854 GtkTreePath *start, *end;
858 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
859 selected_path = gtk_tree_model_get_path (model, iter);
861 start = gtk_tree_path_new ();
862 end = gtk_tree_path_new ();
864 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
866 if (gtk_tree_path_compare (selected_path, start) < 0 ||
867 gtk_tree_path_compare (end, selected_path) < 0)
868 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
869 selected_path, NULL, TRUE,
872 gtk_tree_path_free (selected_path);
873 gtk_tree_path_free (start);
874 gtk_tree_path_free (end);
876 #endif /* MODEST_TOOLKIT_GTK */
881 modest_header_view_select_next (ModestHeaderView *self)
883 GtkTreeSelection *sel;
888 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
890 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
891 path = get_selected_row (GTK_TREE_VIEW(self), &model);
892 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
893 /* Unselect previous path */
894 gtk_tree_selection_unselect_path (sel, path);
896 /* Move path down and selects new one */
897 if (gtk_tree_model_iter_next (model, &iter)) {
898 gtk_tree_selection_select_iter (sel, &iter);
899 scroll_to_selected (self, &iter, FALSE);
901 gtk_tree_path_free(path);
907 modest_header_view_select_prev (ModestHeaderView *self)
909 GtkTreeSelection *sel;
914 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
916 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
917 path = get_selected_row (GTK_TREE_VIEW(self), &model);
918 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
919 /* Unselect previous path */
920 gtk_tree_selection_unselect_path (sel, path);
923 if (gtk_tree_path_prev (path)) {
924 gtk_tree_model_get_iter (model, &iter, path);
926 /* Select the new one */
927 gtk_tree_selection_select_iter (sel, &iter);
928 scroll_to_selected (self, &iter, TRUE);
931 gtk_tree_path_free (path);
936 modest_header_view_get_columns (ModestHeaderView *self)
938 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
940 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
946 modest_header_view_set_style (ModestHeaderView *self,
947 ModestHeaderViewStyle style)
949 ModestHeaderViewPrivate *priv;
950 gboolean show_col_headers = FALSE;
951 ModestHeaderViewStyle old_style;
953 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
954 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
957 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
958 if (priv->style == style)
959 return TRUE; /* nothing to do */
962 case MODEST_HEADER_VIEW_STYLE_DETAILS:
963 show_col_headers = TRUE;
965 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
968 g_return_val_if_reached (FALSE);
970 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
971 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
973 old_style = priv->style;
980 ModestHeaderViewStyle
981 modest_header_view_get_style (ModestHeaderView *self)
983 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
985 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
988 /* This is used to automatically select the first header if the user
989 * has not selected any header yet.
992 modest_header_view_on_expose_event(GtkTreeView *header_view,
993 GdkEventExpose *event,
996 GtkTreeSelection *sel;
998 GtkTreeIter tree_iter;
999 ModestHeaderViewPrivate *priv;
1001 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1002 model = gtk_tree_view_get_model(header_view);
1007 #ifdef MODEST_TOOLKIT_HILDON2
1010 sel = gtk_tree_view_get_selection(header_view);
1011 if(!gtk_tree_selection_count_selected_rows(sel)) {
1012 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1013 GtkTreePath *tree_iter_path;
1014 /* Prevent the widget from getting the focus
1015 when selecting the first item */
1016 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1017 g_object_set(header_view, "can-focus", FALSE, NULL);
1018 gtk_tree_selection_select_iter(sel, &tree_iter);
1019 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1020 g_object_set(header_view, "can-focus", TRUE, NULL);
1021 if (priv->autoselect_reference) {
1022 gtk_tree_row_reference_free (priv->autoselect_reference);
1024 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1025 gtk_tree_path_free (tree_iter_path);
1028 if (priv->autoselect_reference != NULL) {
1029 gboolean moved_selection = FALSE;
1030 GtkTreePath * last_path;
1031 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1032 moved_selection = TRUE;
1036 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1037 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1038 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1039 moved_selection = TRUE;
1040 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1042 gtk_tree_path_free (last_path);
1044 if (moved_selection) {
1045 gtk_tree_row_reference_free (priv->autoselect_reference);
1046 priv->autoselect_reference = NULL;
1049 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1050 GtkTreePath *current_path;
1051 current_path = gtk_tree_model_get_path (model, &tree_iter);
1052 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1053 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1054 g_object_set(header_view, "can-focus", FALSE, NULL);
1055 gtk_tree_selection_unselect_all (sel);
1056 gtk_tree_selection_select_iter(sel, &tree_iter);
1057 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1058 g_object_set(header_view, "can-focus", TRUE, NULL);
1059 gtk_tree_row_reference_free (priv->autoselect_reference);
1060 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1062 gtk_tree_path_free (current_path);
1063 gtk_tree_path_free (last_path);
1073 modest_header_view_get_folder (ModestHeaderView *self)
1075 ModestHeaderViewPrivate *priv;
1077 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1079 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1082 g_object_ref (priv->folder);
1084 return priv->folder;
1088 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1094 ModestHeaderView *self;
1095 ModestHeaderViewPrivate *priv;
1097 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1099 self = MODEST_HEADER_VIEW (user_data);
1100 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1102 if (cancelled || err)
1105 /* Add IDLE observer (monitor) and another folder observer for
1106 new messages (self) */
1107 g_mutex_lock (priv->observers_lock);
1108 if (priv->monitor) {
1109 tny_folder_monitor_stop (priv->monitor);
1110 g_object_unref (G_OBJECT (priv->monitor));
1112 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1113 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1114 tny_folder_monitor_start (priv->monitor);
1115 g_mutex_unlock (priv->observers_lock);
1119 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1123 ModestHeaderViewPrivate *priv;
1124 GList *cols, *cursor;
1125 GtkTreeModel *filter_model, *sortable;
1127 GtkSortType sort_type;
1129 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1131 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1133 /* Start the monitor in the callback of the
1134 tny_gtk_header_list_model_set_folder call. It's crucial to
1135 do it there and not just after the call because we want the
1136 monitor to observe only the headers returned by the
1137 tny_folder_get_headers_async call that it's inside the
1138 tny_gtk_header_list_model_set_folder call. This way the
1139 monitor infrastructure could successfully cope with
1140 duplicates. For example if a tny_folder_add_msg_async is
1141 happening while tny_gtk_header_list_model_set_folder is
1142 invoked, then the first call could add a header that will
1143 be added again by tny_gtk_header_list_model_set_folder, so
1144 we'd end up with duplicate headers. sergio */
1145 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1147 set_folder_intern_get_headers_async_cb,
1150 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1151 g_object_unref (G_OBJECT (headers));
1153 /* Init filter_row function to examine empty status */
1154 priv->status = HEADER_VIEW_INIT;
1156 /* Create a tree model filter to hide and show rows for cut operations */
1157 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1158 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1162 g_object_unref (G_OBJECT (sortable));
1164 /* install our special sorting functions */
1165 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1167 /* Restore sort column id */
1169 type = modest_tny_folder_guess_folder_type (folder);
1170 if (type == TNY_FOLDER_TYPE_INVALID)
1171 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1173 sort_colid = modest_header_view_get_sort_column_id (self, type);
1174 sort_type = modest_header_view_get_sort_type (self, type);
1175 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1178 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1179 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1180 (GtkTreeIterCompareFunc) cmp_rows,
1182 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1183 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1184 (GtkTreeIterCompareFunc) cmp_subject_rows,
1189 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1190 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1191 tny_folder_get_id(folder));
1192 g_object_unref (G_OBJECT (filter_model));
1199 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1201 GtkSortType sort_type)
1203 ModestHeaderViewPrivate *priv = NULL;
1204 GtkTreeModel *tree_filter, *sortable = NULL;
1207 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1208 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1210 /* Get model and private data */
1211 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1212 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1213 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1214 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1216 /* Sort tree model */
1217 type = modest_tny_folder_guess_folder_type (priv->folder);
1218 if (type == TNY_FOLDER_TYPE_INVALID)
1219 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1221 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1224 /* Store new sort parameters */
1225 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1230 modest_header_view_set_sort_params (ModestHeaderView *self,
1232 GtkSortType sort_type,
1235 ModestHeaderViewPrivate *priv;
1236 ModestHeaderViewStyle style;
1238 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1239 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1240 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1242 style = modest_header_view_get_style (self);
1243 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1245 priv->sort_colid[style][type] = sort_colid;
1246 priv->sort_type[style][type] = sort_type;
1250 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1253 ModestHeaderViewPrivate *priv;
1254 ModestHeaderViewStyle style;
1256 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1257 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1259 style = modest_header_view_get_style (self);
1260 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1262 return priv->sort_colid[style][type];
1266 modest_header_view_get_sort_type (ModestHeaderView *self,
1269 ModestHeaderViewPrivate *priv;
1270 ModestHeaderViewStyle style;
1272 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1273 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1275 style = modest_header_view_get_style (self);
1276 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1278 return priv->sort_type[style][type];
1282 ModestHeaderView *header_view;
1283 RefreshAsyncUserCallback cb;
1288 folder_refreshed_cb (ModestMailOperation *mail_op,
1292 ModestHeaderViewPrivate *priv;
1293 SetFolderHelper *info;
1295 info = (SetFolderHelper*) user_data;
1297 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1301 info->cb (mail_op, folder, info->user_data);
1303 /* Start the folder count changes observer. We do not need it
1304 before the refresh. Note that the monitor could still be
1305 called for this refresh but now we know that the callback
1306 was previously called */
1307 g_mutex_lock (priv->observers_lock);
1308 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1309 g_mutex_unlock (priv->observers_lock);
1311 /* Notify the observers that the update is over */
1312 g_signal_emit (G_OBJECT (info->header_view),
1313 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1315 /* Allow filtering notifications from now on if the current
1316 folder is still the same (if not then the user has selected
1317 another one to refresh, we should wait until that refresh
1319 if (priv->folder == folder)
1320 priv->notify_status = TRUE;
1323 g_object_unref (info->header_view);
1328 refresh_folder_error_handler (ModestMailOperation *mail_op,
1331 const GError *error = modest_mail_operation_get_error (mail_op);
1333 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1334 error->code == TNY_IO_ERROR_WRITE ||
1335 error->code == TNY_IO_ERROR_READ) {
1336 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1337 /* If the mail op has been cancelled then it's not an error: don't show any message */
1338 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1339 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1340 modest_platform_information_banner (NULL, NULL, msg);
1347 modest_header_view_set_folder (ModestHeaderView *self,
1350 ModestWindow *progress_window,
1351 RefreshAsyncUserCallback callback,
1354 ModestHeaderViewPrivate *priv;
1356 g_return_if_fail (self);
1358 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1361 if (priv->status_timeout) {
1362 g_source_remove (priv->status_timeout);
1363 priv->status_timeout = 0;
1366 g_mutex_lock (priv->observers_lock);
1367 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1368 g_object_unref (priv->folder);
1369 priv->folder = NULL;
1370 g_mutex_unlock (priv->observers_lock);
1374 GtkTreeSelection *selection;
1375 SetFolderHelper *info;
1376 ModestMailOperation *mail_op = NULL;
1378 /* Set folder in the model */
1379 modest_header_view_set_folder_intern (self, folder);
1381 /* Pick my reference. Nothing to do with the mail operation */
1382 priv->folder = g_object_ref (folder);
1384 /* Do not notify about filterings until the refresh finishes */
1385 priv->notify_status = FALSE;
1387 /* Clear the selection if exists */
1388 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1389 gtk_tree_selection_unselect_all(selection);
1390 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1392 /* Notify the observers that the update begins */
1393 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1396 /* create the helper */
1397 info = g_malloc0 (sizeof (SetFolderHelper));
1398 info->header_view = g_object_ref (self);
1399 info->cb = callback;
1400 info->user_data = user_data;
1402 /* Create the mail operation (source will be the parent widget) */
1403 if (progress_window)
1404 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1405 refresh_folder_error_handler,
1408 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1411 /* Refresh the folder asynchronously */
1412 modest_mail_operation_refresh_folder (mail_op,
1414 folder_refreshed_cb,
1417 folder_refreshed_cb (mail_op, folder, info);
1421 g_object_unref (mail_op);
1423 g_mutex_lock (priv->observers_lock);
1425 if (priv->monitor) {
1426 tny_folder_monitor_stop (priv->monitor);
1427 g_object_unref (G_OBJECT (priv->monitor));
1428 priv->monitor = NULL;
1431 if (priv->autoselect_reference) {
1432 gtk_tree_row_reference_free (priv->autoselect_reference);
1433 priv->autoselect_reference = NULL;
1436 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1438 modest_header_view_notify_observers(self, NULL, NULL);
1440 g_mutex_unlock (priv->observers_lock);
1442 /* Notify the observers that the update is over */
1443 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1449 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1450 GtkTreeViewColumn *column, gpointer userdata)
1452 ModestHeaderView *self = NULL;
1454 GtkTreeModel *model = NULL;
1455 TnyHeader *header = NULL;
1456 TnyHeaderFlags flags;
1458 self = MODEST_HEADER_VIEW (treeview);
1460 model = gtk_tree_view_get_model (treeview);
1461 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1464 /* get the first selected item */
1465 gtk_tree_model_get (model, &iter,
1466 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1467 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1470 /* Dont open DELETED messages */
1471 if (flags & TNY_HEADER_FLAG_DELETED) {
1474 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1475 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1476 modest_platform_information_banner (NULL, NULL, msg);
1482 g_signal_emit (G_OBJECT(self),
1483 signals[HEADER_ACTIVATED_SIGNAL],
1489 g_object_unref (G_OBJECT (header));
1494 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1496 GtkTreeModel *model;
1497 TnyHeader *header = NULL;
1498 GtkTreePath *path = NULL;
1500 ModestHeaderView *self;
1501 GList *selected = NULL;
1503 g_return_if_fail (sel);
1504 g_return_if_fail (user_data);
1506 self = MODEST_HEADER_VIEW (user_data);
1508 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1509 if (selected != NULL)
1510 path = (GtkTreePath *) selected->data;
1511 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1512 return; /* msg was _un_selected */
1514 gtk_tree_model_get (model, &iter,
1515 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1519 g_signal_emit (G_OBJECT(self),
1520 signals[HEADER_SELECTED_SIGNAL],
1523 g_object_unref (G_OBJECT (header));
1525 /* free all items in 'selected' */
1526 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1527 g_list_free (selected);
1531 /* PROTECTED method. It's useful when we want to force a given
1532 selection to reload a msg. For example if we have selected a header
1533 in offline mode, when Modest become online, we want to reload the
1534 message automatically without an user click over the header */
1536 _modest_header_view_change_selection (GtkTreeSelection *selection,
1539 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1540 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1542 on_selection_changed (selection, user_data);
1546 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1553 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1557 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1561 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1569 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1576 /* static int counter = 0; */
1578 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1579 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1580 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1584 case TNY_HEADER_FLAG_ATTACHMENTS:
1586 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1587 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1588 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1589 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1591 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1592 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1594 return cmp ? cmp : t1 - t2;
1596 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1597 TnyHeader *header1 = NULL, *header2 = NULL;
1599 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1600 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1601 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1602 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1604 /* This is for making priority values respect the intuitive sort relationship
1605 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1607 if (header1 && header2) {
1608 cmp = compare_priorities (tny_header_get_priority (header1),
1609 tny_header_get_priority (header2));
1610 g_object_unref (header1);
1611 g_object_unref (header2);
1613 return cmp ? cmp : t1 - t2;
1619 return &iter1 - &iter2; /* oughhhh */
1624 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1630 /* static int counter = 0; */
1632 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1634 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1635 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1636 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1637 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1639 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1640 val2 + modest_text_utils_get_subject_prefix_len(val2),
1647 /* Drag and drop stuff */
1649 drag_data_get_cb (GtkWidget *widget,
1650 GdkDragContext *context,
1651 GtkSelectionData *selection_data,
1656 ModestHeaderView *self = NULL;
1657 ModestHeaderViewPrivate *priv = NULL;
1659 self = MODEST_HEADER_VIEW (widget);
1660 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1662 /* Set the data. Do not use the current selection because it
1663 could be different than the selection at the beginning of
1665 modest_dnd_selection_data_set_paths (selection_data,
1666 priv->drag_begin_cached_selected_rows);
1670 * We're caching the selected rows at the beginning because the
1671 * selection could change between drag-begin and drag-data-get, for
1672 * example if we have a set of rows already selected, and then we
1673 * click in one of them (without SHIFT key pressed) and begin a drag,
1674 * the selection at that moment contains all the selected lines, but
1675 * after dropping the selection, the release event provokes that only
1676 * the row used to begin the drag is selected, so at the end the
1677 * drag&drop affects only one rows instead of all the selected ones.
1681 drag_begin_cb (GtkWidget *widget,
1682 GdkDragContext *context,
1685 ModestHeaderView *self = NULL;
1686 ModestHeaderViewPrivate *priv = NULL;
1687 GtkTreeSelection *selection;
1689 self = MODEST_HEADER_VIEW (widget);
1690 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1692 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1693 priv->drag_begin_cached_selected_rows =
1694 gtk_tree_selection_get_selected_rows (selection, NULL);
1698 * We use the drag-end signal to clear the cached selection, we use
1699 * this because this allways happens, whether or not the d&d was a
1703 drag_end_cb (GtkWidget *widget,
1707 ModestHeaderView *self = NULL;
1708 ModestHeaderViewPrivate *priv = NULL;
1710 self = MODEST_HEADER_VIEW (widget);
1711 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1713 /* Free cached data */
1714 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1715 g_list_free (priv->drag_begin_cached_selected_rows);
1716 priv->drag_begin_cached_selected_rows = NULL;
1719 /* Header view drag types */
1720 const GtkTargetEntry header_view_drag_types[] = {
1721 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1725 enable_drag_and_drop (GtkWidget *self)
1727 #ifdef MODEST_TOOLKIT_HILDON2
1730 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1731 header_view_drag_types,
1732 G_N_ELEMENTS (header_view_drag_types),
1733 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1737 disable_drag_and_drop (GtkWidget *self)
1739 #ifdef MODEST_TOOLKIT_HILDON2
1742 gtk_drag_source_unset (self);
1746 setup_drag_and_drop (GtkWidget *self)
1748 #ifdef MODEST_TOOLKIT_HILDON2
1751 enable_drag_and_drop(self);
1752 g_signal_connect(G_OBJECT (self), "drag_data_get",
1753 G_CALLBACK(drag_data_get_cb), NULL);
1755 g_signal_connect(G_OBJECT (self), "drag_begin",
1756 G_CALLBACK(drag_begin_cb), NULL);
1758 g_signal_connect(G_OBJECT (self), "drag_end",
1759 G_CALLBACK(drag_end_cb), NULL);
1762 static GtkTreePath *
1763 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1765 GtkTreePath *path = NULL;
1766 GtkTreeSelection *sel = NULL;
1769 sel = gtk_tree_view_get_selection(self);
1770 rows = gtk_tree_selection_get_selected_rows (sel, model);
1772 if ((rows == NULL) || (g_list_length(rows) != 1))
1775 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1780 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1786 #ifndef MODEST_TOOLKIT_HILDON2
1788 * This function moves the tree view scroll to the current selected
1789 * row when the widget grabs the focus
1792 on_focus_in (GtkWidget *self,
1793 GdkEventFocus *event,
1796 GtkTreeSelection *selection;
1797 GtkTreeModel *model;
1798 GList *selected = NULL;
1799 GtkTreePath *selected_path = NULL;
1801 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1805 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1806 /* If none selected yet, pick the first one */
1807 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1811 /* Return if the model is empty */
1812 if (!gtk_tree_model_get_iter_first (model, &iter))
1815 path = gtk_tree_model_get_path (model, &iter);
1816 gtk_tree_selection_select_path (selection, path);
1817 gtk_tree_path_free (path);
1820 /* Need to get the all the rows because is selection multiple */
1821 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1822 if (selected == NULL) return FALSE;
1823 selected_path = (GtkTreePath *) selected->data;
1826 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1827 g_list_free (selected);
1833 on_focus_out (GtkWidget *self,
1834 GdkEventFocus *event,
1838 if (!gtk_widget_is_focus (self)) {
1839 GtkTreeSelection *selection = NULL;
1840 GList *selected_rows = NULL;
1841 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1842 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1843 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1844 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1845 gtk_tree_selection_unselect_all (selection);
1846 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1847 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1848 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1849 g_list_free (selected_rows);
1857 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1859 enable_drag_and_drop(self);
1864 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1866 GtkTreeSelection *selection = NULL;
1867 GtkTreePath *path = NULL;
1868 gboolean already_selected = FALSE, already_opened = FALSE;
1869 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1871 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1873 GtkTreeModel *model;
1875 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1876 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1878 /* Get header from model */
1879 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1880 if (gtk_tree_model_get_iter (model, &iter, path)) {
1881 GValue value = {0,};
1884 gtk_tree_model_get_value (model, &iter,
1885 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1887 header = (TnyHeader *) g_value_get_object (&value);
1888 if (TNY_IS_HEADER (header)) {
1889 status = modest_tny_all_send_queues_get_msg_status (header);
1890 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1893 g_value_unset (&value);
1897 /* Enable drag and drop only if the user clicks on a row that
1898 it's already selected. If not, let him select items using
1899 the pointer. If the message is in an OUTBOX and in sending
1900 status disable drag and drop as well */
1901 if (!already_selected ||
1902 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1904 disable_drag_and_drop(self);
1907 gtk_tree_path_free(path);
1909 /* If it's already opened then do not let the button-press
1910 event go on because it'll perform a message open because
1911 we're clicking on to an already selected header */
1916 folder_monitor_update (TnyFolderObserver *self,
1917 TnyFolderChange *change)
1919 ModestHeaderViewPrivate *priv = NULL;
1920 TnyFolderChangeChanged changed;
1921 TnyFolder *folder = NULL;
1923 changed = tny_folder_change_get_changed (change);
1925 /* Do not notify the observers if the folder of the header
1926 view has changed before this call to the observer
1928 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1929 folder = tny_folder_change_get_folder (change);
1930 if (folder != priv->folder)
1933 MODEST_DEBUG_BLOCK (
1934 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1935 g_print ("ADDED %d/%d (r/t) \n",
1936 tny_folder_change_get_new_unread_count (change),
1937 tny_folder_change_get_new_all_count (change));
1938 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1939 g_print ("ALL COUNT %d\n",
1940 tny_folder_change_get_new_all_count (change));
1941 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1942 g_print ("UNREAD COUNT %d\n",
1943 tny_folder_change_get_new_unread_count (change));
1944 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1945 g_print ("EXPUNGED %d/%d (r/t) \n",
1946 tny_folder_change_get_new_unread_count (change),
1947 tny_folder_change_get_new_all_count (change));
1948 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1949 g_print ("FOLDER RENAME\n");
1950 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1951 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1952 tny_folder_change_get_new_unread_count (change),
1953 tny_folder_change_get_new_all_count (change));
1954 g_print ("---------------------------------------------------\n");
1957 /* Check folder count */
1958 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1959 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1961 g_mutex_lock (priv->observers_lock);
1963 /* Emit signal to evaluate how headers changes affects
1964 to the window view */
1965 g_signal_emit (G_OBJECT(self),
1966 signals[MSG_COUNT_CHANGED_SIGNAL],
1969 /* Added or removed headers, so data stored on cliboard are invalid */
1970 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1971 modest_email_clipboard_clear (priv->clipboard);
1973 g_mutex_unlock (priv->observers_lock);
1979 g_object_unref (folder);
1983 modest_header_view_is_empty (ModestHeaderView *self)
1985 ModestHeaderViewPrivate *priv;
1987 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1989 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1991 return priv->status == HEADER_VIEW_EMPTY;
1995 modest_header_view_clear (ModestHeaderView *self)
1997 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1999 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2003 modest_header_view_copy_selection (ModestHeaderView *header_view)
2005 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2007 /* Copy selection */
2008 _clipboard_set_selected_data (header_view, FALSE);
2012 modest_header_view_cut_selection (ModestHeaderView *header_view)
2014 ModestHeaderViewPrivate *priv = NULL;
2015 const gchar **hidding = NULL;
2016 guint i, n_selected;
2018 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2020 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2022 /* Copy selection */
2023 _clipboard_set_selected_data (header_view, TRUE);
2025 /* Get hidding ids */
2026 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2028 /* Clear hidding array created by previous cut operation */
2029 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2031 /* Copy hidding array */
2032 priv->n_selected = n_selected;
2033 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2034 for (i=0; i < n_selected; i++)
2035 priv->hidding_ids[i] = g_strdup(hidding[i]);
2037 /* Hide cut headers */
2038 modest_header_view_refilter (header_view);
2045 _clipboard_set_selected_data (ModestHeaderView *header_view,
2048 ModestHeaderViewPrivate *priv = NULL;
2049 TnyList *headers = NULL;
2051 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2052 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2054 /* Set selected data on clipboard */
2055 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2056 headers = modest_header_view_get_selected_headers (header_view);
2057 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2060 g_object_unref (headers);
2064 ModestHeaderView *self;
2069 notify_filter_change (gpointer data)
2071 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2073 g_signal_emit (info->self,
2074 signals[MSG_COUNT_CHANGED_SIGNAL],
2075 0, info->folder, NULL);
2081 notify_filter_change_destroy (gpointer data)
2083 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2084 ModestHeaderViewPrivate *priv;
2086 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2087 priv->status_timeout = 0;
2089 g_object_unref (info->self);
2090 g_object_unref (info->folder);
2091 g_slice_free (NotifyFilterInfo, info);
2095 filter_row (GtkTreeModel *model,
2099 ModestHeaderViewPrivate *priv = NULL;
2100 TnyHeaderFlags flags;
2101 TnyHeader *header = NULL;
2104 gboolean visible = TRUE;
2105 gboolean found = FALSE;
2106 GValue value = {0,};
2107 HeaderViewStatus old_status;
2109 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2110 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2112 /* Get header from model */
2113 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2114 flags = (TnyHeaderFlags) g_value_get_int (&value);
2115 g_value_unset (&value);
2116 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2117 header = (TnyHeader *) g_value_get_object (&value);
2118 g_value_unset (&value);
2120 /* Get message id from header (ensure is a valid id) */
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)) {
2156 if (priv->hidding_ids != NULL) {
2157 id = tny_header_dup_message_id (header);
2158 for (i=0; i < priv->n_selected && !found; i++)
2159 if (priv->hidding_ids[i] != NULL && id != NULL)
2160 found = (!strcmp (priv->hidding_ids[i], id));
2167 old_status = priv->status;
2168 priv->status = ((gboolean) priv->status) && !visible;
2169 if ((priv->notify_status) && (priv->status != old_status)) {
2170 if (priv->status_timeout)
2171 g_source_remove (priv->status_timeout);
2174 NotifyFilterInfo *info;
2176 info = g_slice_new0 (NotifyFilterInfo);
2177 info->self = g_object_ref (G_OBJECT (user_data));
2179 info->folder = tny_header_get_folder (header);
2180 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2181 notify_filter_change,
2183 notify_filter_change_destroy);
2191 _clear_hidding_filter (ModestHeaderView *header_view)
2193 ModestHeaderViewPrivate *priv = NULL;
2196 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2197 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2199 if (priv->hidding_ids != NULL) {
2200 for (i=0; i < priv->n_selected; i++)
2201 g_free (priv->hidding_ids[i]);
2202 g_free(priv->hidding_ids);
2207 modest_header_view_refilter (ModestHeaderView *header_view)
2209 GtkTreeModel *model = NULL;
2210 ModestHeaderViewPrivate *priv = NULL;
2212 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2213 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2215 /* Hide cut headers */
2216 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2217 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2218 priv->status = HEADER_VIEW_INIT;
2219 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2224 * Called when an account is removed. If I'm showing a folder of the
2225 * account that has been removed then clear the view
2228 on_account_removed (TnyAccountStore *self,
2229 TnyAccount *account,
2232 ModestHeaderViewPrivate *priv = NULL;
2234 /* Ignore changes in transport accounts */
2235 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2238 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2241 TnyAccount *my_account;
2243 my_account = tny_folder_get_account (priv->folder);
2244 if (my_account == account)
2245 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2246 g_object_unref (my_account);
2251 modest_header_view_add_observer(ModestHeaderView *header_view,
2252 ModestHeaderViewObserver *observer)
2254 ModestHeaderViewPrivate *priv;
2256 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2257 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2259 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2261 g_mutex_lock(priv->observer_list_lock);
2262 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2263 g_mutex_unlock(priv->observer_list_lock);
2267 modest_header_view_remove_observer(ModestHeaderView *header_view,
2268 ModestHeaderViewObserver *observer)
2270 ModestHeaderViewPrivate *priv;
2272 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2273 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2275 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2277 g_mutex_lock(priv->observer_list_lock);
2278 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2279 g_mutex_unlock(priv->observer_list_lock);
2283 modest_header_view_notify_observers(ModestHeaderView *header_view,
2284 GtkTreeModel *model,
2285 const gchar *tny_folder_id)
2287 ModestHeaderViewPrivate *priv = NULL;
2289 ModestHeaderViewObserver *observer;
2292 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2294 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2296 g_mutex_lock(priv->observer_list_lock);
2297 iter = priv->observer_list;
2298 while(iter != NULL){
2299 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2300 modest_header_view_observer_update(observer, model,
2302 iter = g_slist_next(iter);
2304 g_mutex_unlock(priv->observer_list_lock);
2308 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2310 ModestHeaderViewPrivate *priv = NULL;
2312 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2313 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2317 modest_header_view_set_filter (ModestHeaderView *self,
2318 ModestHeaderViewFilter filter)
2320 ModestHeaderViewPrivate *priv;
2321 GtkTreeModel *filter_model;
2323 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2324 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2326 priv->filter |= filter;
2328 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2329 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2330 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2335 modest_header_view_unset_filter (ModestHeaderView *self,
2336 ModestHeaderViewFilter filter)
2338 ModestHeaderViewPrivate *priv;
2339 GtkTreeModel *filter_model;
2341 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2342 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2344 priv->filter &= ~filter;
2346 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2347 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2348 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2353 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2355 if (strcmp ("style", spec->name) == 0) {
2356 update_style (MODEST_HEADER_VIEW (obj));
2357 gtk_widget_queue_draw (GTK_WIDGET (obj));
2362 update_style (ModestHeaderView *self)
2364 ModestHeaderViewPrivate *priv;
2365 GdkColor style_color;
2366 GdkColor style_active_color;
2367 PangoAttrList *attr_list;
2369 PangoAttribute *attr;
2370 GdkColor *new_color;
2372 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2373 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2377 attr_list = pango_attr_list_new ();
2378 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2379 gdk_color_parse ("grey", &style_color);
2381 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2382 pango_attr_list_insert (attr_list, attr);
2385 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2387 "SmallSystemFont", NULL,
2390 attr = pango_attr_font_desc_new (pango_font_description_copy
2391 (style->font_desc));
2392 pango_attr_list_insert (attr_list, attr);
2394 g_object_set (G_OBJECT (priv->renderer_address),
2395 "foreground-gdk", &style_color,
2396 "foreground-set", TRUE,
2397 "attributes", attr_list,
2399 g_object_set (G_OBJECT (priv->renderer_date_status),
2400 "foreground-gdk", &style_color,
2401 "foreground-set", TRUE,
2402 "attributes", attr_list,
2404 pango_attr_list_unref (attr_list);
2406 g_object_set (G_OBJECT (priv->renderer_address),
2407 "foreground-gdk", &style_color,
2408 "foreground-set", TRUE,
2409 "scale", PANGO_SCALE_SMALL,
2412 g_object_set (G_OBJECT (priv->renderer_date_status),
2413 "foreground-gdk", &style_color,
2414 "foreground-set", TRUE,
2415 "scale", PANGO_SCALE_SMALL,
2420 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2421 new_color = gdk_color_copy (&style_active_color);
2425 #ifdef MODEST_TOOLKIT_HILDON2
2426 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (new_color != NULL));
2427 g_object_set_data_full (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, new_color, (GDestroyNotify) gdk_color_free);