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>
36 #include <tny-merge-folder.h>
39 #include <modest-header-view.h>
40 #include <modest-header-view-priv.h>
41 #include <modest-dnd.h>
42 #include <modest-tny-folder.h>
43 #include <modest-debug.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;
174 GdkColor active_color;
175 GdkColor secondary_color;
178 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
179 struct _HeadersCountChangedHelper {
180 ModestHeaderView *self;
181 TnyFolderChange *change;
185 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
186 MODEST_TYPE_HEADER_VIEW, \
187 ModestHeaderViewPrivate))
191 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
194 HEADER_SELECTED_SIGNAL,
195 HEADER_ACTIVATED_SIGNAL,
196 ITEM_NOT_FOUND_SIGNAL,
197 MSG_COUNT_CHANGED_SIGNAL,
198 UPDATING_MSG_LIST_SIGNAL,
203 static GObjectClass *parent_class = NULL;
205 /* uncomment the following if you have defined any signals */
206 static guint signals[LAST_SIGNAL] = {0};
209 modest_header_view_get_type (void)
211 static GType my_type = 0;
213 static const GTypeInfo my_info = {
214 sizeof(ModestHeaderViewClass),
215 NULL, /* base init */
216 NULL, /* base finalize */
217 (GClassInitFunc) modest_header_view_class_init,
218 NULL, /* class finalize */
219 NULL, /* class data */
220 sizeof(ModestHeaderView),
222 (GInstanceInitFunc) modest_header_view_init,
226 static const GInterfaceInfo tny_folder_observer_info =
228 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
229 NULL, /* interface_finalize */
230 NULL /* interface_data */
232 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
236 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
237 &tny_folder_observer_info);
245 modest_header_view_class_init (ModestHeaderViewClass *klass)
247 GObjectClass *gobject_class;
248 gobject_class = (GObjectClass*) klass;
250 parent_class = g_type_class_peek_parent (klass);
251 gobject_class->finalize = modest_header_view_finalize;
252 gobject_class->dispose = modest_header_view_dispose;
254 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
256 signals[HEADER_SELECTED_SIGNAL] =
257 g_signal_new ("header_selected",
258 G_TYPE_FROM_CLASS (gobject_class),
260 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
262 g_cclosure_marshal_VOID__POINTER,
263 G_TYPE_NONE, 1, G_TYPE_POINTER);
265 signals[HEADER_ACTIVATED_SIGNAL] =
266 g_signal_new ("header_activated",
267 G_TYPE_FROM_CLASS (gobject_class),
269 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
271 gtk_marshal_VOID__POINTER_POINTER,
272 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
275 signals[ITEM_NOT_FOUND_SIGNAL] =
276 g_signal_new ("item_not_found",
277 G_TYPE_FROM_CLASS (gobject_class),
279 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
281 g_cclosure_marshal_VOID__INT,
282 G_TYPE_NONE, 1, G_TYPE_INT);
284 signals[MSG_COUNT_CHANGED_SIGNAL] =
285 g_signal_new ("msg_count_changed",
286 G_TYPE_FROM_CLASS (gobject_class),
288 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
290 modest_marshal_VOID__POINTER_POINTER,
291 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
293 signals[UPDATING_MSG_LIST_SIGNAL] =
294 g_signal_new ("updating-msg-list",
295 G_TYPE_FROM_CLASS (gobject_class),
297 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
299 g_cclosure_marshal_VOID__BOOLEAN,
300 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
302 #ifdef MODEST_TOOLKIT_HILDON2
303 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
309 tny_folder_observer_init (TnyFolderObserverIface *klass)
311 klass->update = folder_monitor_update;
314 static GtkTreeViewColumn*
315 get_new_column (const gchar *name, GtkCellRenderer *renderer,
316 gboolean resizable, gint sort_col_id, gboolean show_as_text,
317 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
319 GtkTreeViewColumn *column;
321 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
322 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
324 gtk_tree_view_column_set_resizable (column, resizable);
326 gtk_tree_view_column_set_expand (column, TRUE);
329 gtk_tree_view_column_add_attribute (column, renderer, "text",
331 if (sort_col_id >= 0)
332 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
334 gtk_tree_view_column_set_sort_indicator (column, FALSE);
335 gtk_tree_view_column_set_reorderable (column, TRUE);
338 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
345 remove_all_columns (ModestHeaderView *obj)
347 GList *columns, *cursor;
349 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
351 for (cursor = columns; cursor; cursor = cursor->next)
352 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
353 GTK_TREE_VIEW_COLUMN(cursor->data));
354 g_list_free (columns);
358 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
360 GtkTreeModel *sortable;
361 GtkTreeViewColumn *column=NULL;
362 GtkTreeSelection *selection = NULL;
363 GtkCellRenderer *renderer_header,
364 *renderer_attach, *renderer_compact_date_or_status;
365 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
366 *renderer_subject_box, *renderer_recpt,
368 ModestHeaderViewPrivate *priv;
369 GtkTreeViewColumn *compact_column = NULL;
372 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
373 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
375 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
377 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
379 /* TODO: check whether these renderers need to be freed */
380 renderer_attach = gtk_cell_renderer_pixbuf_new ();
381 renderer_priority = gtk_cell_renderer_pixbuf_new ();
382 renderer_header = gtk_cell_renderer_text_new ();
384 renderer_compact_header = modest_vbox_cell_renderer_new ();
385 renderer_recpt_box = modest_hbox_cell_renderer_new ();
386 renderer_subject_box = modest_hbox_cell_renderer_new ();
387 renderer_recpt = gtk_cell_renderer_text_new ();
388 priv->renderer_address = renderer_recpt;
389 priv->renderer_subject = gtk_cell_renderer_text_new ();
390 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
391 priv->renderer_date_status = renderer_compact_date_or_status;
393 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
394 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
395 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
396 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
397 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
398 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
399 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
400 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
401 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
402 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
403 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
404 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
405 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
406 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
408 #ifdef MODEST_TOOLKIT_HILDON2
409 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
411 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
412 #ifndef MODEST_TOOLKIT_GTK
413 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
414 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
416 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
417 g_object_set(G_OBJECT(renderer_header),
418 "ellipsize", PANGO_ELLIPSIZE_END,
420 g_object_set (G_OBJECT (priv->renderer_subject),
421 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
423 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
424 g_object_set (G_OBJECT (renderer_recpt),
425 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
427 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
428 g_object_set(G_OBJECT(renderer_compact_date_or_status),
429 "xalign", 1.0, "yalign", 0.1,
431 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
432 #ifdef MODEST_TOOLKIT_HILDON2
433 g_object_set (G_OBJECT (renderer_priority),
435 "xalign", 0.0, NULL);
436 g_object_set (G_OBJECT (renderer_attach),
438 "xalign", 0.0, NULL);
440 g_object_set (G_OBJECT (renderer_priority),
441 "yalign", 0.5, NULL);
442 g_object_set (G_OBJECT (renderer_attach),
443 "yalign", 0.0, NULL);
446 #ifdef MODEST_TOOLKIT_HILDON1
447 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
448 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
449 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
450 #elif MODEST_TOOLKIT_HILDON2
451 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
452 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
453 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
455 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
456 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
457 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
460 remove_all_columns (self);
462 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
463 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
464 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
466 /* Add new columns */
467 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
468 ModestHeaderViewColumn col =
469 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
471 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
472 g_printerr ("modest: invalid column %d in column list\n", col);
478 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
479 column = get_new_column (_("A"), renderer_attach, FALSE,
480 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
482 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
484 gtk_tree_view_column_set_fixed_width (column, 45);
488 case MODEST_HEADER_VIEW_COLUMN_FROM:
489 column = get_new_column (_("From"), renderer_header, TRUE,
490 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
492 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
493 GINT_TO_POINTER(TRUE));
496 case MODEST_HEADER_VIEW_COLUMN_TO:
497 column = get_new_column (_("To"), renderer_header, TRUE,
498 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
500 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
501 GINT_TO_POINTER(FALSE));
504 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
505 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
506 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
508 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
509 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
510 compact_column = column;
513 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
514 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
515 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
517 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
518 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
519 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
520 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
521 compact_column = column;
525 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
526 column = get_new_column (_("Subject"), renderer_header, TRUE,
527 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
529 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
533 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
534 column = get_new_column (_("Received"), renderer_header, TRUE,
535 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
537 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
538 GINT_TO_POINTER(TRUE));
541 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
542 column = get_new_column (_("Sent"), renderer_header, TRUE,
543 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
545 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
546 GINT_TO_POINTER(FALSE));
549 case MODEST_HEADER_VIEW_COLUMN_SIZE:
550 column = get_new_column (_("Size"), renderer_header, TRUE,
551 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
553 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
556 case MODEST_HEADER_VIEW_COLUMN_STATUS:
557 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
558 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
560 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
565 g_return_val_if_reached(FALSE);
568 /* we keep the column id around */
569 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
570 GINT_TO_POINTER(col));
572 /* we need this ptr when sorting the rows */
573 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
575 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
579 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
580 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
581 (GtkTreeIterCompareFunc) cmp_rows,
582 compact_column, NULL);
583 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
584 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
585 (GtkTreeIterCompareFunc) cmp_subject_rows,
586 compact_column, NULL);
590 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
596 datetime_format_changed (ModestDatetimeFormatter *formatter,
597 ModestHeaderView *self)
599 gtk_widget_queue_draw (GTK_WIDGET (self));
603 modest_header_view_init (ModestHeaderView *obj)
605 ModestHeaderViewPrivate *priv;
608 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
611 priv->is_outbox = FALSE;
613 priv->monitor = NULL;
614 priv->observers_lock = g_mutex_new ();
615 priv->autoselect_reference = NULL;
617 priv->status = HEADER_VIEW_INIT;
618 priv->status_timeout = 0;
619 priv->notify_status = TRUE;
621 priv->observer_list_lock = g_mutex_new();
622 priv->observer_list = NULL;
624 priv->clipboard = modest_runtime_get_email_clipboard ();
625 priv->hidding_ids = NULL;
626 priv->n_selected = 0;
627 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
628 priv->selection_changed_handler = 0;
629 priv->acc_removed_handler = 0;
631 /* Sort parameters */
632 for (j=0; j < 2; j++) {
633 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
634 priv->sort_colid[j][i] = -1;
635 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
639 priv->datetime_formatter = modest_datetime_formatter_new ();
640 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
641 G_CALLBACK (datetime_format_changed), (gpointer) obj);
643 setup_drag_and_drop (GTK_WIDGET(obj));
647 modest_header_view_dispose (GObject *obj)
649 ModestHeaderView *self;
650 ModestHeaderViewPrivate *priv;
651 GtkTreeSelection *sel;
653 self = MODEST_HEADER_VIEW(obj);
654 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
656 if (priv->datetime_formatter) {
657 g_object_unref (priv->datetime_formatter);
658 priv->datetime_formatter = NULL;
661 /* Free in the dispose to avoid unref cycles */
663 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
664 g_object_unref (G_OBJECT (priv->folder));
668 /* We need to do this here in the dispose because the
669 selection won't exist when finalizing */
670 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
671 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
672 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
673 priv->selection_changed_handler = 0;
676 G_OBJECT_CLASS(parent_class)->dispose (obj);
680 modest_header_view_finalize (GObject *obj)
682 ModestHeaderView *self;
683 ModestHeaderViewPrivate *priv;
685 self = MODEST_HEADER_VIEW(obj);
686 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
688 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
689 priv->acc_removed_handler)) {
690 g_signal_handler_disconnect (modest_runtime_get_account_store (),
691 priv->acc_removed_handler);
694 /* There is no need to lock because there should not be any
695 * reference to self now. */
696 g_mutex_free(priv->observer_list_lock);
697 g_slist_free(priv->observer_list);
699 g_mutex_lock (priv->observers_lock);
701 tny_folder_monitor_stop (priv->monitor);
702 g_object_unref (G_OBJECT (priv->monitor));
704 g_mutex_unlock (priv->observers_lock);
705 g_mutex_free (priv->observers_lock);
707 /* Clear hidding array created by cut operation */
708 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
710 if (priv->autoselect_reference != NULL) {
711 gtk_tree_row_reference_free (priv->autoselect_reference);
712 priv->autoselect_reference = NULL;
715 G_OBJECT_CLASS(parent_class)->finalize (obj);
720 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
723 GtkTreeSelection *sel;
724 ModestHeaderView *self;
725 ModestHeaderViewPrivate *priv;
727 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
730 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
731 self = MODEST_HEADER_VIEW(obj);
732 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
734 modest_header_view_set_style (self, style);
736 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
737 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
738 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
740 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
741 TRUE); /* alternating row colors */
743 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
744 priv->selection_changed_handler =
745 g_signal_connect_after (sel, "changed",
746 G_CALLBACK(on_selection_changed), self);
748 g_signal_connect (self, "row-activated",
749 G_CALLBACK (on_header_row_activated), NULL);
751 #ifndef MODEST_TOOLKIT_HILDON2
752 g_signal_connect (self, "focus-in-event",
753 G_CALLBACK(on_focus_in), NULL);
754 g_signal_connect (self, "focus-out-event",
755 G_CALLBACK(on_focus_out), NULL);
758 g_signal_connect (self, "button-press-event",
759 G_CALLBACK(on_button_press_event), NULL);
760 g_signal_connect (self, "button-release-event",
761 G_CALLBACK(on_button_release_event), NULL);
763 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
765 G_CALLBACK (on_account_removed),
768 g_signal_connect (self, "expose-event",
769 G_CALLBACK(modest_header_view_on_expose_event),
772 return GTK_WIDGET(self);
777 modest_header_view_count_selected_headers (ModestHeaderView *self)
779 GtkTreeSelection *sel;
782 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
784 /* Get selection object and check selected rows count */
785 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
786 selected_rows = gtk_tree_selection_count_selected_rows (sel);
788 return selected_rows;
792 modest_header_view_has_selected_headers (ModestHeaderView *self)
794 GtkTreeSelection *sel;
797 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
799 /* Get selection object and check selected rows count */
800 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
801 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
808 modest_header_view_get_selected_headers (ModestHeaderView *self)
810 GtkTreeSelection *sel;
811 TnyList *header_list = NULL;
813 GList *list, *tmp = NULL;
814 GtkTreeModel *tree_model = NULL;
817 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
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 && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
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,
1127 ModestHeaderViewPrivate *priv;
1128 GList *cols, *cursor;
1129 GtkTreeModel *filter_model, *sortable;
1131 GtkSortType sort_type;
1133 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1135 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1137 /* Start the monitor in the callback of the
1138 tny_gtk_header_list_model_set_folder call. It's crucial to
1139 do it there and not just after the call because we want the
1140 monitor to observe only the headers returned by the
1141 tny_folder_get_headers_async call that it's inside the
1142 tny_gtk_header_list_model_set_folder call. This way the
1143 monitor infrastructure could successfully cope with
1144 duplicates. For example if a tny_folder_add_msg_async is
1145 happening while tny_gtk_header_list_model_set_folder is
1146 invoked, then the first call could add a header that will
1147 be added again by tny_gtk_header_list_model_set_folder, so
1148 we'd end up with duplicate headers. sergio */
1149 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1151 set_folder_intern_get_headers_async_cb,
1154 /* Create a tree model filter to hide and show rows for cut operations */
1155 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1156 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1157 filter_row, self, NULL);
1158 g_object_unref (headers);
1160 /* Init filter_row function to examine empty status */
1161 priv->status = HEADER_VIEW_INIT;
1163 /* Create sortable model */
1164 sortable = gtk_tree_model_sort_new_with_model (filter_model);
1165 g_object_unref (filter_model);
1167 /* install our special sorting functions */
1168 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1170 /* Restore sort column id */
1172 type = modest_tny_folder_guess_folder_type (folder);
1173 if (type == TNY_FOLDER_TYPE_INVALID)
1174 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1176 sort_colid = modest_header_view_get_sort_column_id (self, type);
1177 sort_type = modest_header_view_get_sort_type (self, type);
1178 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1181 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1182 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1183 (GtkTreeIterCompareFunc) cmp_rows,
1185 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1186 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1187 (GtkTreeIterCompareFunc) cmp_subject_rows,
1192 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1193 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1194 g_object_unref (sortable);
1201 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1203 GtkSortType sort_type)
1205 ModestHeaderViewPrivate *priv = NULL;
1206 GtkTreeModel *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 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, refresh);
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,
1577 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1578 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1582 case TNY_HEADER_FLAG_ATTACHMENTS:
1584 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1585 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1586 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1587 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1589 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1590 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1592 return cmp ? cmp : t1 - t2;
1594 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1595 TnyHeader *header1 = NULL, *header2 = NULL;
1597 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1598 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1599 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1600 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1602 /* This is for making priority values respect the intuitive sort relationship
1603 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1605 if (header1 && header2) {
1606 cmp = compare_priorities (tny_header_get_priority (header1),
1607 tny_header_get_priority (header2));
1608 g_object_unref (header1);
1609 g_object_unref (header2);
1611 return cmp ? cmp : t1 - t2;
1617 return &iter1 - &iter2; /* oughhhh */
1622 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1629 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1631 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1632 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1633 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1634 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1636 /* Do not use the prefixes for sorting. Consume all the blank
1637 spaces for sorting */
1638 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1639 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1642 /* If they're equal based on subject without prefix then just
1643 sort them by length. This will show messages like this.
1650 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1657 /* Drag and drop stuff */
1659 drag_data_get_cb (GtkWidget *widget,
1660 GdkDragContext *context,
1661 GtkSelectionData *selection_data,
1666 ModestHeaderView *self = NULL;
1667 ModestHeaderViewPrivate *priv = NULL;
1669 self = MODEST_HEADER_VIEW (widget);
1670 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1672 /* Set the data. Do not use the current selection because it
1673 could be different than the selection at the beginning of
1675 modest_dnd_selection_data_set_paths (selection_data,
1676 priv->drag_begin_cached_selected_rows);
1680 * We're caching the selected rows at the beginning because the
1681 * selection could change between drag-begin and drag-data-get, for
1682 * example if we have a set of rows already selected, and then we
1683 * click in one of them (without SHIFT key pressed) and begin a drag,
1684 * the selection at that moment contains all the selected lines, but
1685 * after dropping the selection, the release event provokes that only
1686 * the row used to begin the drag is selected, so at the end the
1687 * drag&drop affects only one rows instead of all the selected ones.
1691 drag_begin_cb (GtkWidget *widget,
1692 GdkDragContext *context,
1695 ModestHeaderView *self = NULL;
1696 ModestHeaderViewPrivate *priv = NULL;
1697 GtkTreeSelection *selection;
1699 self = MODEST_HEADER_VIEW (widget);
1700 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1702 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1703 priv->drag_begin_cached_selected_rows =
1704 gtk_tree_selection_get_selected_rows (selection, NULL);
1708 * We use the drag-end signal to clear the cached selection, we use
1709 * this because this allways happens, whether or not the d&d was a
1713 drag_end_cb (GtkWidget *widget,
1717 ModestHeaderView *self = NULL;
1718 ModestHeaderViewPrivate *priv = NULL;
1720 self = MODEST_HEADER_VIEW (widget);
1721 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1723 /* Free cached data */
1724 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1725 g_list_free (priv->drag_begin_cached_selected_rows);
1726 priv->drag_begin_cached_selected_rows = NULL;
1729 /* Header view drag types */
1730 const GtkTargetEntry header_view_drag_types[] = {
1731 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1735 enable_drag_and_drop (GtkWidget *self)
1737 #ifdef MODEST_TOOLKIT_HILDON2
1740 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1741 header_view_drag_types,
1742 G_N_ELEMENTS (header_view_drag_types),
1743 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1747 disable_drag_and_drop (GtkWidget *self)
1749 #ifdef MODEST_TOOLKIT_HILDON2
1752 gtk_drag_source_unset (self);
1756 setup_drag_and_drop (GtkWidget *self)
1758 #ifdef MODEST_TOOLKIT_HILDON2
1761 enable_drag_and_drop(self);
1762 g_signal_connect(G_OBJECT (self), "drag_data_get",
1763 G_CALLBACK(drag_data_get_cb), NULL);
1765 g_signal_connect(G_OBJECT (self), "drag_begin",
1766 G_CALLBACK(drag_begin_cb), NULL);
1768 g_signal_connect(G_OBJECT (self), "drag_end",
1769 G_CALLBACK(drag_end_cb), NULL);
1772 static GtkTreePath *
1773 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1775 GtkTreePath *path = NULL;
1776 GtkTreeSelection *sel = NULL;
1779 sel = gtk_tree_view_get_selection(self);
1780 rows = gtk_tree_selection_get_selected_rows (sel, model);
1782 if ((rows == NULL) || (g_list_length(rows) != 1))
1785 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1790 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1796 #ifndef MODEST_TOOLKIT_HILDON2
1798 * This function moves the tree view scroll to the current selected
1799 * row when the widget grabs the focus
1802 on_focus_in (GtkWidget *self,
1803 GdkEventFocus *event,
1806 GtkTreeSelection *selection;
1807 GtkTreeModel *model;
1808 GList *selected = NULL;
1809 GtkTreePath *selected_path = NULL;
1811 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1815 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1816 /* If none selected yet, pick the first one */
1817 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1821 /* Return if the model is empty */
1822 if (!gtk_tree_model_get_iter_first (model, &iter))
1825 path = gtk_tree_model_get_path (model, &iter);
1826 gtk_tree_selection_select_path (selection, path);
1827 gtk_tree_path_free (path);
1830 /* Need to get the all the rows because is selection multiple */
1831 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1832 if (selected == NULL) return FALSE;
1833 selected_path = (GtkTreePath *) selected->data;
1836 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1837 g_list_free (selected);
1843 on_focus_out (GtkWidget *self,
1844 GdkEventFocus *event,
1848 if (!gtk_widget_is_focus (self)) {
1849 GtkTreeSelection *selection = NULL;
1850 GList *selected_rows = NULL;
1851 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1852 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1853 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1854 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1855 gtk_tree_selection_unselect_all (selection);
1856 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1857 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1858 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1859 g_list_free (selected_rows);
1867 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1869 enable_drag_and_drop(self);
1874 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1876 GtkTreeSelection *selection = NULL;
1877 GtkTreePath *path = NULL;
1878 gboolean already_selected = FALSE, already_opened = FALSE;
1879 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1881 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1883 GtkTreeModel *model;
1885 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1886 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1888 /* Get header from model */
1889 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1890 if (gtk_tree_model_get_iter (model, &iter, path)) {
1891 GValue value = {0,};
1894 gtk_tree_model_get_value (model, &iter,
1895 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1897 header = (TnyHeader *) g_value_get_object (&value);
1898 if (TNY_IS_HEADER (header)) {
1899 status = modest_tny_all_send_queues_get_msg_status (header);
1900 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1903 g_value_unset (&value);
1907 /* Enable drag and drop only if the user clicks on a row that
1908 it's already selected. If not, let him select items using
1909 the pointer. If the message is in an OUTBOX and in sending
1910 status disable drag and drop as well */
1911 if (!already_selected ||
1912 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1914 disable_drag_and_drop(self);
1917 gtk_tree_path_free(path);
1919 /* If it's already opened then do not let the button-press
1920 event go on because it'll perform a message open because
1921 we're clicking on to an already selected header */
1926 folder_monitor_update (TnyFolderObserver *self,
1927 TnyFolderChange *change)
1929 ModestHeaderViewPrivate *priv = NULL;
1930 TnyFolderChangeChanged changed;
1931 TnyFolder *folder = NULL;
1933 changed = tny_folder_change_get_changed (change);
1935 /* Do not notify the observers if the folder of the header
1936 view has changed before this call to the observer
1938 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1939 folder = tny_folder_change_get_folder (change);
1940 if (folder != priv->folder)
1943 MODEST_DEBUG_BLOCK (
1944 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1945 g_print ("ADDED %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_ALL_COUNT)
1949 g_print ("ALL COUNT %d\n",
1950 tny_folder_change_get_new_all_count (change));
1951 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1952 g_print ("UNREAD COUNT %d\n",
1953 tny_folder_change_get_new_unread_count (change));
1954 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1955 g_print ("EXPUNGED %d/%d (r/t) \n",
1956 tny_folder_change_get_new_unread_count (change),
1957 tny_folder_change_get_new_all_count (change));
1958 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1959 g_print ("FOLDER RENAME\n");
1960 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1961 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1962 tny_folder_change_get_new_unread_count (change),
1963 tny_folder_change_get_new_all_count (change));
1964 g_print ("---------------------------------------------------\n");
1967 /* Check folder count */
1968 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1969 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1971 g_mutex_lock (priv->observers_lock);
1973 /* Emit signal to evaluate how headers changes affects
1974 to the window view */
1975 g_signal_emit (G_OBJECT(self),
1976 signals[MSG_COUNT_CHANGED_SIGNAL],
1979 /* Added or removed headers, so data stored on cliboard are invalid */
1980 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1981 modest_email_clipboard_clear (priv->clipboard);
1983 g_mutex_unlock (priv->observers_lock);
1989 g_object_unref (folder);
1993 modest_header_view_is_empty (ModestHeaderView *self)
1995 ModestHeaderViewPrivate *priv;
1997 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1999 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2001 return priv->status == HEADER_VIEW_EMPTY;
2005 modest_header_view_clear (ModestHeaderView *self)
2007 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2009 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2013 modest_header_view_copy_selection (ModestHeaderView *header_view)
2015 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2017 /* Copy selection */
2018 _clipboard_set_selected_data (header_view, FALSE);
2022 modest_header_view_cut_selection (ModestHeaderView *header_view)
2024 ModestHeaderViewPrivate *priv = NULL;
2025 const gchar **hidding = NULL;
2026 guint i, n_selected;
2028 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2030 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2032 /* Copy selection */
2033 _clipboard_set_selected_data (header_view, TRUE);
2035 /* Get hidding ids */
2036 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2038 /* Clear hidding array created by previous cut operation */
2039 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2041 /* Copy hidding array */
2042 priv->n_selected = n_selected;
2043 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2044 for (i=0; i < n_selected; i++)
2045 priv->hidding_ids[i] = g_strdup(hidding[i]);
2047 /* Hide cut headers */
2048 modest_header_view_refilter (header_view);
2055 _clipboard_set_selected_data (ModestHeaderView *header_view,
2058 ModestHeaderViewPrivate *priv = NULL;
2059 TnyList *headers = NULL;
2061 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2062 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2064 /* Set selected data on clipboard */
2065 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2066 headers = modest_header_view_get_selected_headers (header_view);
2067 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2070 g_object_unref (headers);
2074 ModestHeaderView *self;
2079 notify_filter_change (gpointer data)
2081 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2083 g_signal_emit (info->self,
2084 signals[MSG_COUNT_CHANGED_SIGNAL],
2085 0, info->folder, NULL);
2091 notify_filter_change_destroy (gpointer data)
2093 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2094 ModestHeaderViewPrivate *priv;
2096 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2097 priv->status_timeout = 0;
2099 g_object_unref (info->self);
2100 g_object_unref (info->folder);
2101 g_slice_free (NotifyFilterInfo, info);
2105 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2107 /* For the moment we only need to filter outbox */
2108 return priv->is_outbox;
2112 filter_row (GtkTreeModel *model,
2116 ModestHeaderViewPrivate *priv = NULL;
2117 TnyHeaderFlags flags;
2118 TnyHeader *header = NULL;
2121 gboolean visible = TRUE;
2122 gboolean found = FALSE;
2123 GValue value = {0,};
2124 HeaderViewStatus old_status;
2126 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2127 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2129 /* Get header from model */
2130 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2131 flags = (TnyHeaderFlags) g_value_get_int (&value);
2132 g_value_unset (&value);
2133 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2134 header = (TnyHeader *) g_value_get_object (&value);
2135 g_value_unset (&value);
2137 /* Get message id from header (ensure is a valid id) */
2143 /* Hide deleted and mark as deleted heders */
2144 if (flags & TNY_HEADER_FLAG_DELETED ||
2145 flags & TNY_HEADER_FLAG_EXPUNGED) {
2150 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2151 if (current_folder_needs_filtering (priv) &&
2152 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2158 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2159 if (current_folder_needs_filtering (priv) &&
2160 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2166 /* If no data on clipboard, return always TRUE */
2167 if (modest_email_clipboard_cleared(priv->clipboard)) {
2173 if (priv->hidding_ids != NULL) {
2174 id = tny_header_dup_message_id (header);
2175 for (i=0; i < priv->n_selected && !found; i++)
2176 if (priv->hidding_ids[i] != NULL && id != NULL)
2177 found = (!strcmp (priv->hidding_ids[i], id));
2184 old_status = priv->status;
2185 priv->status = ((gboolean) priv->status) && !visible;
2186 if ((priv->notify_status) && (priv->status != old_status)) {
2187 if (priv->status_timeout)
2188 g_source_remove (priv->status_timeout);
2191 NotifyFilterInfo *info;
2193 info = g_slice_new0 (NotifyFilterInfo);
2194 info->self = g_object_ref (G_OBJECT (user_data));
2196 info->folder = tny_header_get_folder (header);
2197 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2198 notify_filter_change,
2200 notify_filter_change_destroy);
2208 _clear_hidding_filter (ModestHeaderView *header_view)
2210 ModestHeaderViewPrivate *priv = NULL;
2213 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2214 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2216 if (priv->hidding_ids != NULL) {
2217 for (i=0; i < priv->n_selected; i++)
2218 g_free (priv->hidding_ids[i]);
2219 g_free(priv->hidding_ids);
2224 modest_header_view_refilter (ModestHeaderView *header_view)
2226 GtkTreeModel *model, *sortable = NULL;
2227 ModestHeaderViewPrivate *priv = NULL;
2229 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2230 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2232 /* Hide cut headers */
2233 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2234 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2235 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2236 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2237 priv->status = HEADER_VIEW_INIT;
2238 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2244 * Called when an account is removed. If I'm showing a folder of the
2245 * account that has been removed then clear the view
2248 on_account_removed (TnyAccountStore *self,
2249 TnyAccount *account,
2252 ModestHeaderViewPrivate *priv = NULL;
2254 /* Ignore changes in transport accounts */
2255 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2258 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2261 TnyAccount *my_account;
2263 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2264 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2265 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2266 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2268 my_account = tny_folder_get_account (priv->folder);
2272 if (my_account == account)
2273 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2274 g_object_unref (my_account);
2280 modest_header_view_add_observer(ModestHeaderView *header_view,
2281 ModestHeaderViewObserver *observer)
2283 ModestHeaderViewPrivate *priv;
2285 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2286 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2288 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2290 g_mutex_lock(priv->observer_list_lock);
2291 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2292 g_mutex_unlock(priv->observer_list_lock);
2296 modest_header_view_remove_observer(ModestHeaderView *header_view,
2297 ModestHeaderViewObserver *observer)
2299 ModestHeaderViewPrivate *priv;
2301 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2302 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2304 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2306 g_mutex_lock(priv->observer_list_lock);
2307 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2308 g_mutex_unlock(priv->observer_list_lock);
2312 modest_header_view_notify_observers(ModestHeaderView *header_view,
2313 GtkTreeModel *model,
2314 const gchar *tny_folder_id)
2316 ModestHeaderViewPrivate *priv = NULL;
2318 ModestHeaderViewObserver *observer;
2321 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2323 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2325 g_mutex_lock(priv->observer_list_lock);
2326 iter = priv->observer_list;
2327 while(iter != NULL){
2328 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2329 modest_header_view_observer_update(observer, model,
2331 iter = g_slist_next(iter);
2333 g_mutex_unlock(priv->observer_list_lock);
2337 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2339 ModestHeaderViewPrivate *priv = NULL;
2341 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2342 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2346 modest_header_view_set_filter (ModestHeaderView *self,
2347 ModestHeaderViewFilter filter)
2349 ModestHeaderViewPrivate *priv;
2351 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2352 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2354 priv->filter |= filter;
2356 if (current_folder_needs_filtering (priv))
2357 modest_header_view_refilter (self);
2361 modest_header_view_unset_filter (ModestHeaderView *self,
2362 ModestHeaderViewFilter filter)
2364 ModestHeaderViewPrivate *priv;
2366 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2367 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2369 priv->filter &= ~filter;
2371 if (current_folder_needs_filtering (priv))
2372 modest_header_view_refilter (self);
2376 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2378 if (strcmp ("style", spec->name) == 0) {
2379 update_style (MODEST_HEADER_VIEW (obj));
2380 gtk_widget_queue_draw (GTK_WIDGET (obj));
2385 update_style (ModestHeaderView *self)
2387 ModestHeaderViewPrivate *priv;
2388 GdkColor style_color;
2389 GdkColor style_active_color;
2390 PangoAttrList *attr_list;
2392 PangoAttribute *attr;
2394 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2395 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2399 attr_list = pango_attr_list_new ();
2400 if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
2401 gdk_color_parse (MODEST_SECONDARY_COLOR, &style_color);
2403 priv->secondary_color = style_color;
2404 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2405 pango_attr_list_insert (attr_list, attr);
2408 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2410 "SmallSystemFont", NULL,
2413 attr = pango_attr_font_desc_new (pango_font_description_copy
2414 (style->font_desc));
2415 pango_attr_list_insert (attr_list, attr);
2417 g_object_set (G_OBJECT (priv->renderer_address),
2418 "foreground-gdk", &(priv->secondary_color),
2419 "foreground-set", TRUE,
2420 "attributes", attr_list,
2422 g_object_set (G_OBJECT (priv->renderer_date_status),
2423 "foreground-gdk", &(priv->secondary_color),
2424 "foreground-set", TRUE,
2425 "attributes", attr_list,
2427 pango_attr_list_unref (attr_list);
2429 g_object_set (G_OBJECT (priv->renderer_address),
2430 "foreground-gdk", &(priv->secondary_color),
2431 "foreground-set", TRUE,
2432 "scale", PANGO_SCALE_SMALL,
2435 g_object_set (G_OBJECT (priv->renderer_date_status),
2436 "foreground-gdk", &(priv->secondary_color),
2437 "foreground-set", TRUE,
2438 "scale", PANGO_SCALE_SMALL,
2443 if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
2444 priv->active_color = style_active_color;
2445 #ifdef MODEST_TOOLKIT_HILDON2
2446 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2447 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2450 #ifdef MODEST_TOOLKIT_HILDON2
2451 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2457 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2462 GtkTreeModel *tree_model;
2467 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2475 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2476 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2480 gtk_tree_model_get (tree_model, &iter,
2481 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,