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-main-window.h>
45 #include <modest-ui-actions.h>
46 #include <modest-marshal.h>
47 #include <modest-text-utils.h>
48 #include <modest-icon-names.h>
49 #include <modest-runtime.h>
50 #include "modest-platform.h"
51 #include <modest-hbox-cell-renderer.h>
52 #include <modest-vbox-cell-renderer.h>
53 #include <modest-datetime-formatter.h>
54 #include <modest-ui-constants.h>
56 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
57 static void modest_header_view_init (ModestHeaderView *obj);
58 static void modest_header_view_finalize (GObject *obj);
59 static void modest_header_view_dispose (GObject *obj);
61 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
62 GtkTreeViewColumn *column, gpointer userdata);
64 static gint cmp_rows (GtkTreeModel *tree_model,
69 static gint cmp_subject_rows (GtkTreeModel *tree_model,
74 static gboolean filter_row (GtkTreeModel *model,
78 static void on_account_removed (TnyAccountStore *self,
82 static void on_selection_changed (GtkTreeSelection *sel,
85 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
88 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
91 static void setup_drag_and_drop (GtkWidget *self);
93 static void enable_drag_and_drop (GtkWidget *self);
95 static void disable_drag_and_drop (GtkWidget *self);
97 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
99 #ifndef MODEST_TOOLKIT_HILDON2
100 static gboolean on_focus_in (GtkWidget *sef,
101 GdkEventFocus *event,
104 static gboolean on_focus_out (GtkWidget *self,
105 GdkEventFocus *event,
109 static void folder_monitor_update (TnyFolderObserver *self,
110 TnyFolderChange *change);
112 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
114 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
116 static void _clear_hidding_filter (ModestHeaderView *header_view);
118 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
120 const gchar *tny_folder_id);
122 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
123 GdkEventExpose *event,
126 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
127 static void update_style (ModestHeaderView *self);
130 HEADER_VIEW_NON_EMPTY,
135 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
136 struct _ModestHeaderViewPrivate {
138 ModestHeaderViewStyle style;
141 TnyFolderMonitor *monitor;
142 GMutex *observers_lock;
144 /*header-view-observer observer*/
145 GMutex *observer_list_lock;
146 GSList *observer_list;
148 /* not unref this object, its a singlenton */
149 ModestEmailClipboard *clipboard;
151 /* Filter tree model */
154 GtkTreeRowReference *autoselect_reference;
155 ModestHeaderViewFilter filter;
157 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
158 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
160 gulong selection_changed_handler;
161 gulong acc_removed_handler;
163 GList *drag_begin_cached_selected_rows;
165 HeaderViewStatus status;
166 guint status_timeout;
167 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
169 ModestDatetimeFormatter *datetime_formatter;
171 GtkCellRenderer *renderer_subject;
172 GtkCellRenderer *renderer_address;
173 GtkCellRenderer *renderer_date_status;
175 GdkColor active_color;
176 GdkColor secondary_color;
181 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
182 struct _HeadersCountChangedHelper {
183 ModestHeaderView *self;
184 TnyFolderChange *change;
188 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
189 MODEST_TYPE_HEADER_VIEW, \
190 ModestHeaderViewPrivate))
194 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
197 HEADER_SELECTED_SIGNAL,
198 HEADER_ACTIVATED_SIGNAL,
199 ITEM_NOT_FOUND_SIGNAL,
200 MSG_COUNT_CHANGED_SIGNAL,
201 UPDATING_MSG_LIST_SIGNAL,
206 static GObjectClass *parent_class = NULL;
208 /* uncomment the following if you have defined any signals */
209 static guint signals[LAST_SIGNAL] = {0};
212 modest_header_view_get_type (void)
214 static GType my_type = 0;
216 static const GTypeInfo my_info = {
217 sizeof(ModestHeaderViewClass),
218 NULL, /* base init */
219 NULL, /* base finalize */
220 (GClassInitFunc) modest_header_view_class_init,
221 NULL, /* class finalize */
222 NULL, /* class data */
223 sizeof(ModestHeaderView),
225 (GInstanceInitFunc) modest_header_view_init,
229 static const GInterfaceInfo tny_folder_observer_info =
231 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
232 NULL, /* interface_finalize */
233 NULL /* interface_data */
235 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
239 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
240 &tny_folder_observer_info);
248 modest_header_view_class_init (ModestHeaderViewClass *klass)
250 GObjectClass *gobject_class;
251 gobject_class = (GObjectClass*) klass;
253 parent_class = g_type_class_peek_parent (klass);
254 gobject_class->finalize = modest_header_view_finalize;
255 gobject_class->dispose = modest_header_view_dispose;
257 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
259 signals[HEADER_SELECTED_SIGNAL] =
260 g_signal_new ("header_selected",
261 G_TYPE_FROM_CLASS (gobject_class),
263 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
265 g_cclosure_marshal_VOID__POINTER,
266 G_TYPE_NONE, 1, G_TYPE_POINTER);
268 signals[HEADER_ACTIVATED_SIGNAL] =
269 g_signal_new ("header_activated",
270 G_TYPE_FROM_CLASS (gobject_class),
272 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
274 gtk_marshal_VOID__POINTER_POINTER,
275 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
278 signals[ITEM_NOT_FOUND_SIGNAL] =
279 g_signal_new ("item_not_found",
280 G_TYPE_FROM_CLASS (gobject_class),
282 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
284 g_cclosure_marshal_VOID__INT,
285 G_TYPE_NONE, 1, G_TYPE_INT);
287 signals[MSG_COUNT_CHANGED_SIGNAL] =
288 g_signal_new ("msg_count_changed",
289 G_TYPE_FROM_CLASS (gobject_class),
291 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
293 modest_marshal_VOID__POINTER_POINTER,
294 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
296 signals[UPDATING_MSG_LIST_SIGNAL] =
297 g_signal_new ("updating-msg-list",
298 G_TYPE_FROM_CLASS (gobject_class),
300 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
302 g_cclosure_marshal_VOID__BOOLEAN,
303 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
305 #ifdef MODEST_TOOLKIT_HILDON2
306 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
312 tny_folder_observer_init (TnyFolderObserverIface *klass)
314 klass->update = folder_monitor_update;
317 static GtkTreeViewColumn*
318 get_new_column (const gchar *name, GtkCellRenderer *renderer,
319 gboolean resizable, gint sort_col_id, gboolean show_as_text,
320 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
322 GtkTreeViewColumn *column;
324 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
325 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
327 gtk_tree_view_column_set_resizable (column, resizable);
329 gtk_tree_view_column_set_expand (column, TRUE);
332 gtk_tree_view_column_add_attribute (column, renderer, "text",
334 if (sort_col_id >= 0)
335 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
337 gtk_tree_view_column_set_sort_indicator (column, FALSE);
338 gtk_tree_view_column_set_reorderable (column, TRUE);
341 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
348 remove_all_columns (ModestHeaderView *obj)
350 GList *columns, *cursor;
352 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
354 for (cursor = columns; cursor; cursor = cursor->next)
355 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
356 GTK_TREE_VIEW_COLUMN(cursor->data));
357 g_list_free (columns);
361 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
363 GtkTreeModel *sortable;
364 GtkTreeViewColumn *column=NULL;
365 GtkTreeSelection *selection = NULL;
366 GtkCellRenderer *renderer_header,
367 *renderer_attach, *renderer_compact_date_or_status;
368 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
369 *renderer_subject_box, *renderer_recpt,
371 ModestHeaderViewPrivate *priv;
372 GtkTreeViewColumn *compact_column = NULL;
375 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
376 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
378 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
380 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
382 /* TODO: check whether these renderers need to be freed */
383 renderer_attach = gtk_cell_renderer_pixbuf_new ();
384 renderer_priority = gtk_cell_renderer_pixbuf_new ();
385 renderer_header = gtk_cell_renderer_text_new ();
387 renderer_compact_header = modest_vbox_cell_renderer_new ();
388 renderer_recpt_box = modest_hbox_cell_renderer_new ();
389 renderer_subject_box = modest_hbox_cell_renderer_new ();
390 renderer_recpt = gtk_cell_renderer_text_new ();
391 priv->renderer_address = renderer_recpt;
392 priv->renderer_subject = gtk_cell_renderer_text_new ();
393 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
394 priv->renderer_date_status = renderer_compact_date_or_status;
396 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
397 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
398 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
399 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
400 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
401 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
402 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
403 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
404 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
405 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
406 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
407 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
408 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
409 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
411 #ifdef MODEST_TOOLKIT_HILDON2
412 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
414 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
415 #ifndef MODEST_TOOLKIT_GTK
416 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
417 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
419 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
420 g_object_set(G_OBJECT(renderer_header),
421 "ellipsize", PANGO_ELLIPSIZE_END,
423 g_object_set (G_OBJECT (priv->renderer_subject),
424 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
426 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
427 g_object_set (G_OBJECT (renderer_recpt),
428 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
430 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
431 g_object_set(G_OBJECT(renderer_compact_date_or_status),
432 "xalign", 1.0, "yalign", 0.1,
434 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
435 #ifdef MODEST_TOOLKIT_HILDON2
436 g_object_set (G_OBJECT (renderer_priority),
438 "xalign", 0.0, NULL);
439 g_object_set (G_OBJECT (renderer_attach),
441 "xalign", 0.0, NULL);
443 g_object_set (G_OBJECT (renderer_priority),
444 "yalign", 0.5, NULL);
445 g_object_set (G_OBJECT (renderer_attach),
446 "yalign", 0.0, NULL);
449 #ifdef MODEST_TOOLKIT_HILDON1
450 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
451 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
452 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
453 #elif MODEST_TOOLKIT_HILDON2
454 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
455 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
456 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
458 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
459 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
460 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
463 remove_all_columns (self);
465 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
466 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
467 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
469 /* Add new columns */
470 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
471 ModestHeaderViewColumn col =
472 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
474 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
475 g_printerr ("modest: invalid column %d in column list\n", col);
481 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
482 column = get_new_column (_("A"), renderer_attach, FALSE,
483 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
485 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
487 gtk_tree_view_column_set_fixed_width (column, 45);
491 case MODEST_HEADER_VIEW_COLUMN_FROM:
492 column = get_new_column (_("From"), renderer_header, TRUE,
493 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
495 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
496 GINT_TO_POINTER(TRUE));
499 case MODEST_HEADER_VIEW_COLUMN_TO:
500 column = get_new_column (_("To"), renderer_header, TRUE,
501 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
503 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
504 GINT_TO_POINTER(FALSE));
507 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
508 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
509 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
511 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
512 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
513 compact_column = column;
516 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
517 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
518 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
520 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
521 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
522 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
523 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
524 compact_column = column;
528 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
529 column = get_new_column (_("Subject"), renderer_header, TRUE,
530 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
532 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
536 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
537 column = get_new_column (_("Received"), renderer_header, TRUE,
538 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
540 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
541 GINT_TO_POINTER(TRUE));
544 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
545 column = get_new_column (_("Sent"), renderer_header, TRUE,
546 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
548 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
549 GINT_TO_POINTER(FALSE));
552 case MODEST_HEADER_VIEW_COLUMN_SIZE:
553 column = get_new_column (_("Size"), renderer_header, TRUE,
554 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
556 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
559 case MODEST_HEADER_VIEW_COLUMN_STATUS:
560 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
561 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
563 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
568 g_return_val_if_reached(FALSE);
571 /* we keep the column id around */
572 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
573 GINT_TO_POINTER(col));
575 /* we need this ptr when sorting the rows */
576 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
578 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
582 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
583 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
584 (GtkTreeIterCompareFunc) cmp_rows,
585 compact_column, NULL);
586 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
587 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
588 (GtkTreeIterCompareFunc) cmp_subject_rows,
589 compact_column, NULL);
593 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
599 datetime_format_changed (ModestDatetimeFormatter *formatter,
600 ModestHeaderView *self)
602 gtk_widget_queue_draw (GTK_WIDGET (self));
606 modest_header_view_init (ModestHeaderView *obj)
608 ModestHeaderViewPrivate *priv;
611 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
613 priv->show_latest = 0;
616 priv->is_outbox = FALSE;
618 priv->monitor = NULL;
619 priv->observers_lock = g_mutex_new ();
620 priv->autoselect_reference = NULL;
622 priv->status = HEADER_VIEW_INIT;
623 priv->status_timeout = 0;
624 priv->notify_status = TRUE;
626 priv->observer_list_lock = g_mutex_new();
627 priv->observer_list = NULL;
629 priv->clipboard = modest_runtime_get_email_clipboard ();
630 priv->hidding_ids = NULL;
631 priv->n_selected = 0;
632 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
633 priv->selection_changed_handler = 0;
634 priv->acc_removed_handler = 0;
636 /* Sort parameters */
637 for (j=0; j < 2; j++) {
638 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
639 priv->sort_colid[j][i] = -1;
640 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
644 priv->datetime_formatter = modest_datetime_formatter_new ();
645 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
646 G_CALLBACK (datetime_format_changed), (gpointer) obj);
648 setup_drag_and_drop (GTK_WIDGET(obj));
652 modest_header_view_dispose (GObject *obj)
654 ModestHeaderView *self;
655 ModestHeaderViewPrivate *priv;
656 GtkTreeSelection *sel;
658 self = MODEST_HEADER_VIEW(obj);
659 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
661 if (priv->datetime_formatter) {
662 g_object_unref (priv->datetime_formatter);
663 priv->datetime_formatter = NULL;
666 /* Free in the dispose to avoid unref cycles */
668 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
669 g_object_unref (G_OBJECT (priv->folder));
673 /* We need to do this here in the dispose because the
674 selection won't exist when finalizing */
675 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
676 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
677 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
678 priv->selection_changed_handler = 0;
681 G_OBJECT_CLASS(parent_class)->dispose (obj);
685 modest_header_view_finalize (GObject *obj)
687 ModestHeaderView *self;
688 ModestHeaderViewPrivate *priv;
690 self = MODEST_HEADER_VIEW(obj);
691 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
693 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
694 priv->acc_removed_handler)) {
695 g_signal_handler_disconnect (modest_runtime_get_account_store (),
696 priv->acc_removed_handler);
699 /* There is no need to lock because there should not be any
700 * reference to self now. */
701 g_mutex_free(priv->observer_list_lock);
702 g_slist_free(priv->observer_list);
704 g_mutex_lock (priv->observers_lock);
706 tny_folder_monitor_stop (priv->monitor);
707 g_object_unref (G_OBJECT (priv->monitor));
709 g_mutex_unlock (priv->observers_lock);
710 g_mutex_free (priv->observers_lock);
712 /* Clear hidding array created by cut operation */
713 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
715 if (priv->autoselect_reference != NULL) {
716 gtk_tree_row_reference_free (priv->autoselect_reference);
717 priv->autoselect_reference = NULL;
720 G_OBJECT_CLASS(parent_class)->finalize (obj);
725 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
728 GtkTreeSelection *sel;
729 ModestHeaderView *self;
730 ModestHeaderViewPrivate *priv;
732 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
735 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
736 self = MODEST_HEADER_VIEW(obj);
737 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
739 modest_header_view_set_style (self, style);
741 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
742 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
743 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
745 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
746 TRUE); /* alternating row colors */
748 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
749 priv->selection_changed_handler =
750 g_signal_connect_after (sel, "changed",
751 G_CALLBACK(on_selection_changed), self);
753 g_signal_connect (self, "row-activated",
754 G_CALLBACK (on_header_row_activated), NULL);
756 #ifndef MODEST_TOOLKIT_HILDON2
757 g_signal_connect (self, "focus-in-event",
758 G_CALLBACK(on_focus_in), NULL);
759 g_signal_connect (self, "focus-out-event",
760 G_CALLBACK(on_focus_out), NULL);
763 g_signal_connect (self, "button-press-event",
764 G_CALLBACK(on_button_press_event), NULL);
765 g_signal_connect (self, "button-release-event",
766 G_CALLBACK(on_button_release_event), NULL);
768 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
770 G_CALLBACK (on_account_removed),
773 g_signal_connect (self, "expose-event",
774 G_CALLBACK(modest_header_view_on_expose_event),
777 return GTK_WIDGET(self);
782 modest_header_view_count_selected_headers (ModestHeaderView *self)
784 GtkTreeSelection *sel;
787 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
789 /* Get selection object and check selected rows count */
790 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
791 selected_rows = gtk_tree_selection_count_selected_rows (sel);
793 return selected_rows;
797 modest_header_view_has_selected_headers (ModestHeaderView *self)
799 GtkTreeSelection *sel;
802 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
804 /* Get selection object and check selected rows count */
805 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
806 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
813 modest_header_view_get_selected_headers (ModestHeaderView *self)
815 GtkTreeSelection *sel;
816 TnyList *header_list = NULL;
818 GList *list, *tmp = NULL;
819 GtkTreeModel *tree_model = NULL;
822 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
825 /* Get selected rows */
826 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
827 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
830 header_list = tny_simple_list_new();
832 list = g_list_reverse (list);
835 /* get header from selection */
836 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
837 gtk_tree_model_get (tree_model, &iter,
838 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
840 /* Prepend to list */
841 tny_list_prepend (header_list, G_OBJECT (header));
842 g_object_unref (G_OBJECT (header));
844 tmp = g_list_next (tmp);
847 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
854 /* scroll our list view so the selected item is visible */
856 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
858 #ifdef MODEST_TOOLKIT_GTK
860 GtkTreePath *selected_path;
861 GtkTreePath *start, *end;
865 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
866 selected_path = gtk_tree_model_get_path (model, iter);
868 start = gtk_tree_path_new ();
869 end = gtk_tree_path_new ();
871 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
873 if (gtk_tree_path_compare (selected_path, start) < 0 ||
874 gtk_tree_path_compare (end, selected_path) < 0)
875 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
876 selected_path, NULL, TRUE,
879 gtk_tree_path_free (selected_path);
880 gtk_tree_path_free (start);
881 gtk_tree_path_free (end);
883 #endif /* MODEST_TOOLKIT_GTK */
888 modest_header_view_select_next (ModestHeaderView *self)
890 GtkTreeSelection *sel;
895 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
897 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
898 path = get_selected_row (GTK_TREE_VIEW(self), &model);
899 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
900 /* Unselect previous path */
901 gtk_tree_selection_unselect_path (sel, path);
903 /* Move path down and selects new one */
904 if (gtk_tree_model_iter_next (model, &iter)) {
905 gtk_tree_selection_select_iter (sel, &iter);
906 scroll_to_selected (self, &iter, FALSE);
908 gtk_tree_path_free(path);
914 modest_header_view_select_prev (ModestHeaderView *self)
916 GtkTreeSelection *sel;
921 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
923 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
924 path = get_selected_row (GTK_TREE_VIEW(self), &model);
925 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
926 /* Unselect previous path */
927 gtk_tree_selection_unselect_path (sel, path);
930 if (gtk_tree_path_prev (path)) {
931 gtk_tree_model_get_iter (model, &iter, path);
933 /* Select the new one */
934 gtk_tree_selection_select_iter (sel, &iter);
935 scroll_to_selected (self, &iter, TRUE);
938 gtk_tree_path_free (path);
943 modest_header_view_get_columns (ModestHeaderView *self)
945 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
947 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
953 modest_header_view_set_style (ModestHeaderView *self,
954 ModestHeaderViewStyle style)
956 ModestHeaderViewPrivate *priv;
957 gboolean show_col_headers = FALSE;
958 ModestHeaderViewStyle old_style;
960 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
961 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
964 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
965 if (priv->style == style)
966 return TRUE; /* nothing to do */
969 case MODEST_HEADER_VIEW_STYLE_DETAILS:
970 show_col_headers = TRUE;
972 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
975 g_return_val_if_reached (FALSE);
977 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
978 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
980 old_style = priv->style;
987 ModestHeaderViewStyle
988 modest_header_view_get_style (ModestHeaderView *self)
990 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
992 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
995 /* This is used to automatically select the first header if the user
996 * has not selected any header yet.
999 modest_header_view_on_expose_event(GtkTreeView *header_view,
1000 GdkEventExpose *event,
1003 GtkTreeSelection *sel;
1004 GtkTreeModel *model;
1005 GtkTreeIter tree_iter;
1006 ModestHeaderViewPrivate *priv;
1008 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1009 model = gtk_tree_view_get_model(header_view);
1014 #ifdef MODEST_TOOLKIT_HILDON2
1017 sel = gtk_tree_view_get_selection(header_view);
1018 if(!gtk_tree_selection_count_selected_rows(sel)) {
1019 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1020 GtkTreePath *tree_iter_path;
1021 /* Prevent the widget from getting the focus
1022 when selecting the first item */
1023 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1024 g_object_set(header_view, "can-focus", FALSE, NULL);
1025 gtk_tree_selection_select_iter(sel, &tree_iter);
1026 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1027 g_object_set(header_view, "can-focus", TRUE, NULL);
1028 if (priv->autoselect_reference) {
1029 gtk_tree_row_reference_free (priv->autoselect_reference);
1031 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1032 gtk_tree_path_free (tree_iter_path);
1035 if (priv->autoselect_reference != NULL && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
1036 gboolean moved_selection = FALSE;
1037 GtkTreePath * last_path;
1038 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1039 moved_selection = TRUE;
1043 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1044 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1045 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1046 moved_selection = TRUE;
1047 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1049 gtk_tree_path_free (last_path);
1051 if (moved_selection) {
1052 gtk_tree_row_reference_free (priv->autoselect_reference);
1053 priv->autoselect_reference = NULL;
1056 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1057 GtkTreePath *current_path;
1058 current_path = gtk_tree_model_get_path (model, &tree_iter);
1059 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1060 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1061 g_object_set(header_view, "can-focus", FALSE, NULL);
1062 gtk_tree_selection_unselect_all (sel);
1063 gtk_tree_selection_select_iter(sel, &tree_iter);
1064 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1065 g_object_set(header_view, "can-focus", TRUE, NULL);
1066 gtk_tree_row_reference_free (priv->autoselect_reference);
1067 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1069 gtk_tree_path_free (current_path);
1070 gtk_tree_path_free (last_path);
1080 modest_header_view_get_folder (ModestHeaderView *self)
1082 ModestHeaderViewPrivate *priv;
1084 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1086 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1089 g_object_ref (priv->folder);
1091 return priv->folder;
1095 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1101 ModestHeaderView *self;
1102 ModestHeaderViewPrivate *priv;
1104 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1106 self = MODEST_HEADER_VIEW (user_data);
1107 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1109 if (cancelled || err)
1112 /* Add IDLE observer (monitor) and another folder observer for
1113 new messages (self) */
1114 g_mutex_lock (priv->observers_lock);
1115 if (priv->monitor) {
1116 tny_folder_monitor_stop (priv->monitor);
1117 g_object_unref (G_OBJECT (priv->monitor));
1119 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1120 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1121 tny_folder_monitor_start (priv->monitor);
1122 g_mutex_unlock (priv->observers_lock);
1126 modest_header_view_set_folder_intern (ModestHeaderView *self,
1132 ModestHeaderViewPrivate *priv;
1133 GList *cols, *cursor;
1134 GtkTreeModel *filter_model, *sortable;
1136 GtkSortType sort_type;
1138 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1140 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1141 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (headers), priv->show_latest);
1143 /* Start the monitor in the callback of the
1144 tny_gtk_header_list_model_set_folder call. It's crucial to
1145 do it there and not just after the call because we want the
1146 monitor to observe only the headers returned by the
1147 tny_folder_get_headers_async call that it's inside the
1148 tny_gtk_header_list_model_set_folder call. This way the
1149 monitor infrastructure could successfully cope with
1150 duplicates. For example if a tny_folder_add_msg_async is
1151 happening while tny_gtk_header_list_model_set_folder is
1152 invoked, then the first call could add a header that will
1153 be added again by tny_gtk_header_list_model_set_folder, so
1154 we'd end up with duplicate headers. sergio */
1155 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1157 set_folder_intern_get_headers_async_cb,
1160 /* Create a tree model filter to hide and show rows for cut operations */
1161 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1162 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1163 filter_row, self, NULL);
1164 g_object_unref (headers);
1166 /* Init filter_row function to examine empty status */
1167 priv->status = HEADER_VIEW_INIT;
1169 /* Create sortable model */
1170 sortable = gtk_tree_model_sort_new_with_model (filter_model);
1171 g_object_unref (filter_model);
1173 /* install our special sorting functions */
1174 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1176 /* Restore sort column id */
1178 type = modest_tny_folder_guess_folder_type (folder);
1179 if (type == TNY_FOLDER_TYPE_INVALID)
1180 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1182 sort_colid = modest_header_view_get_sort_column_id (self, type);
1183 sort_type = modest_header_view_get_sort_type (self, type);
1184 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1187 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1188 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1189 (GtkTreeIterCompareFunc) cmp_rows,
1191 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1192 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1193 (GtkTreeIterCompareFunc) cmp_subject_rows,
1198 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1199 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1200 g_object_unref (sortable);
1207 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1209 GtkSortType sort_type)
1211 ModestHeaderViewPrivate *priv = NULL;
1212 GtkTreeModel *sortable = NULL;
1215 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1216 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1218 /* Get model and private data */
1219 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1220 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1222 /* Sort tree model */
1223 type = modest_tny_folder_guess_folder_type (priv->folder);
1224 if (type == TNY_FOLDER_TYPE_INVALID)
1225 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1227 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1230 /* Store new sort parameters */
1231 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1236 modest_header_view_set_sort_params (ModestHeaderView *self,
1238 GtkSortType sort_type,
1241 ModestHeaderViewPrivate *priv;
1242 ModestHeaderViewStyle style;
1244 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1245 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1246 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1248 style = modest_header_view_get_style (self);
1249 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1251 priv->sort_colid[style][type] = sort_colid;
1252 priv->sort_type[style][type] = sort_type;
1256 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1259 ModestHeaderViewPrivate *priv;
1260 ModestHeaderViewStyle style;
1262 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1263 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1265 style = modest_header_view_get_style (self);
1266 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1268 return priv->sort_colid[style][type];
1272 modest_header_view_get_sort_type (ModestHeaderView *self,
1275 ModestHeaderViewPrivate *priv;
1276 ModestHeaderViewStyle style;
1278 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1279 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1281 style = modest_header_view_get_style (self);
1282 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1284 return priv->sort_type[style][type];
1288 ModestHeaderView *header_view;
1289 RefreshAsyncUserCallback cb;
1294 folder_refreshed_cb (ModestMailOperation *mail_op,
1298 ModestHeaderViewPrivate *priv;
1299 SetFolderHelper *info;
1301 info = (SetFolderHelper*) user_data;
1303 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1307 info->cb (mail_op, folder, info->user_data);
1309 /* Start the folder count changes observer. We do not need it
1310 before the refresh. Note that the monitor could still be
1311 called for this refresh but now we know that the callback
1312 was previously called */
1313 g_mutex_lock (priv->observers_lock);
1314 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1315 g_mutex_unlock (priv->observers_lock);
1317 /* Notify the observers that the update is over */
1318 g_signal_emit (G_OBJECT (info->header_view),
1319 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1321 /* Allow filtering notifications from now on if the current
1322 folder is still the same (if not then the user has selected
1323 another one to refresh, we should wait until that refresh
1325 if (priv->folder == folder)
1326 priv->notify_status = TRUE;
1329 g_object_unref (info->header_view);
1334 refresh_folder_error_handler (ModestMailOperation *mail_op,
1337 const GError *error = modest_mail_operation_get_error (mail_op);
1339 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1340 error->code == TNY_IO_ERROR_WRITE ||
1341 error->code == TNY_IO_ERROR_READ) {
1342 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1343 /* If the mail op has been cancelled then it's not an error: don't show any message */
1344 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1345 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1346 modest_platform_information_banner (NULL, NULL, msg);
1353 modest_header_view_set_folder (ModestHeaderView *self,
1356 ModestWindow *progress_window,
1357 RefreshAsyncUserCallback callback,
1360 ModestHeaderViewPrivate *priv;
1362 g_return_if_fail (self);
1364 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1367 if (priv->status_timeout) {
1368 g_source_remove (priv->status_timeout);
1369 priv->status_timeout = 0;
1372 g_mutex_lock (priv->observers_lock);
1373 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1374 g_object_unref (priv->folder);
1375 priv->folder = NULL;
1376 g_mutex_unlock (priv->observers_lock);
1380 GtkTreeSelection *selection;
1381 SetFolderHelper *info;
1382 ModestMailOperation *mail_op = NULL;
1384 /* Set folder in the model */
1385 modest_header_view_set_folder_intern (self, folder, refresh);
1387 /* Pick my reference. Nothing to do with the mail operation */
1388 priv->folder = g_object_ref (folder);
1390 /* Do not notify about filterings until the refresh finishes */
1391 priv->notify_status = FALSE;
1393 /* Clear the selection if exists */
1394 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1395 gtk_tree_selection_unselect_all(selection);
1396 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1398 /* Notify the observers that the update begins */
1399 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1402 /* create the helper */
1403 info = g_malloc0 (sizeof (SetFolderHelper));
1404 info->header_view = g_object_ref (self);
1405 info->cb = callback;
1406 info->user_data = user_data;
1408 /* Create the mail operation (source will be the parent widget) */
1409 if (progress_window)
1410 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1411 refresh_folder_error_handler,
1414 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1417 /* Refresh the folder asynchronously */
1418 modest_mail_operation_refresh_folder (mail_op,
1420 folder_refreshed_cb,
1423 folder_refreshed_cb (mail_op, folder, info);
1427 g_object_unref (mail_op);
1429 g_mutex_lock (priv->observers_lock);
1431 if (priv->monitor) {
1432 tny_folder_monitor_stop (priv->monitor);
1433 g_object_unref (G_OBJECT (priv->monitor));
1434 priv->monitor = NULL;
1437 if (priv->autoselect_reference) {
1438 gtk_tree_row_reference_free (priv->autoselect_reference);
1439 priv->autoselect_reference = NULL;
1442 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1444 modest_header_view_notify_observers(self, NULL, NULL);
1446 g_mutex_unlock (priv->observers_lock);
1448 /* Notify the observers that the update is over */
1449 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1455 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1456 GtkTreeViewColumn *column, gpointer userdata)
1458 ModestHeaderView *self = NULL;
1460 GtkTreeModel *model = NULL;
1461 TnyHeader *header = NULL;
1462 TnyHeaderFlags flags;
1464 self = MODEST_HEADER_VIEW (treeview);
1466 model = gtk_tree_view_get_model (treeview);
1467 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1470 /* get the first selected item */
1471 gtk_tree_model_get (model, &iter,
1472 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1473 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1476 /* Dont open DELETED messages */
1477 if (flags & TNY_HEADER_FLAG_DELETED) {
1480 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1481 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1482 modest_platform_information_banner (NULL, NULL, msg);
1488 g_signal_emit (G_OBJECT(self),
1489 signals[HEADER_ACTIVATED_SIGNAL],
1495 g_object_unref (G_OBJECT (header));
1500 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1502 GtkTreeModel *model;
1503 TnyHeader *header = NULL;
1504 GtkTreePath *path = NULL;
1506 ModestHeaderView *self;
1507 GList *selected = NULL;
1509 g_return_if_fail (sel);
1510 g_return_if_fail (user_data);
1512 self = MODEST_HEADER_VIEW (user_data);
1514 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1515 if (selected != NULL)
1516 path = (GtkTreePath *) selected->data;
1517 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1518 return; /* msg was _un_selected */
1520 gtk_tree_model_get (model, &iter,
1521 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1525 g_signal_emit (G_OBJECT(self),
1526 signals[HEADER_SELECTED_SIGNAL],
1529 g_object_unref (G_OBJECT (header));
1531 /* free all items in 'selected' */
1532 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1533 g_list_free (selected);
1537 /* PROTECTED method. It's useful when we want to force a given
1538 selection to reload a msg. For example if we have selected a header
1539 in offline mode, when Modest become online, we want to reload the
1540 message automatically without an user click over the header */
1542 _modest_header_view_change_selection (GtkTreeSelection *selection,
1545 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1546 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1548 on_selection_changed (selection, user_data);
1552 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1559 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1563 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1567 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1575 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1583 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1584 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1588 case TNY_HEADER_FLAG_ATTACHMENTS:
1590 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1591 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1592 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1593 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1595 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1596 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1598 return cmp ? cmp : t1 - t2;
1600 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1601 TnyHeader *header1 = NULL, *header2 = NULL;
1603 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1604 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1605 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1606 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1608 /* This is for making priority values respect the intuitive sort relationship
1609 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1611 if (header1 && header2) {
1612 cmp = compare_priorities (tny_header_get_priority (header1),
1613 tny_header_get_priority (header2));
1614 g_object_unref (header1);
1615 g_object_unref (header2);
1617 return cmp ? cmp : t1 - t2;
1623 return &iter1 - &iter2; /* oughhhh */
1628 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1635 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1637 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1638 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1639 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1640 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1642 /* Do not use the prefixes for sorting. Consume all the blank
1643 spaces for sorting */
1644 cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
1645 g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
1648 /* If they're equal based on subject without prefix then just
1649 sort them by length. This will show messages like this.
1656 cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
1663 /* Drag and drop stuff */
1665 drag_data_get_cb (GtkWidget *widget,
1666 GdkDragContext *context,
1667 GtkSelectionData *selection_data,
1672 ModestHeaderView *self = NULL;
1673 ModestHeaderViewPrivate *priv = NULL;
1675 self = MODEST_HEADER_VIEW (widget);
1676 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1678 /* Set the data. Do not use the current selection because it
1679 could be different than the selection at the beginning of
1681 modest_dnd_selection_data_set_paths (selection_data,
1682 priv->drag_begin_cached_selected_rows);
1686 * We're caching the selected rows at the beginning because the
1687 * selection could change between drag-begin and drag-data-get, for
1688 * example if we have a set of rows already selected, and then we
1689 * click in one of them (without SHIFT key pressed) and begin a drag,
1690 * the selection at that moment contains all the selected lines, but
1691 * after dropping the selection, the release event provokes that only
1692 * the row used to begin the drag is selected, so at the end the
1693 * drag&drop affects only one rows instead of all the selected ones.
1697 drag_begin_cb (GtkWidget *widget,
1698 GdkDragContext *context,
1701 ModestHeaderView *self = NULL;
1702 ModestHeaderViewPrivate *priv = NULL;
1703 GtkTreeSelection *selection;
1705 self = MODEST_HEADER_VIEW (widget);
1706 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1708 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1709 priv->drag_begin_cached_selected_rows =
1710 gtk_tree_selection_get_selected_rows (selection, NULL);
1714 * We use the drag-end signal to clear the cached selection, we use
1715 * this because this allways happens, whether or not the d&d was a
1719 drag_end_cb (GtkWidget *widget,
1723 ModestHeaderView *self = NULL;
1724 ModestHeaderViewPrivate *priv = NULL;
1726 self = MODEST_HEADER_VIEW (widget);
1727 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1729 /* Free cached data */
1730 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1731 g_list_free (priv->drag_begin_cached_selected_rows);
1732 priv->drag_begin_cached_selected_rows = NULL;
1735 /* Header view drag types */
1736 const GtkTargetEntry header_view_drag_types[] = {
1737 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1741 enable_drag_and_drop (GtkWidget *self)
1743 #ifdef MODEST_TOOLKIT_HILDON2
1746 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1747 header_view_drag_types,
1748 G_N_ELEMENTS (header_view_drag_types),
1749 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1753 disable_drag_and_drop (GtkWidget *self)
1755 #ifdef MODEST_TOOLKIT_HILDON2
1758 gtk_drag_source_unset (self);
1762 setup_drag_and_drop (GtkWidget *self)
1764 #ifdef MODEST_TOOLKIT_HILDON2
1767 enable_drag_and_drop(self);
1768 g_signal_connect(G_OBJECT (self), "drag_data_get",
1769 G_CALLBACK(drag_data_get_cb), NULL);
1771 g_signal_connect(G_OBJECT (self), "drag_begin",
1772 G_CALLBACK(drag_begin_cb), NULL);
1774 g_signal_connect(G_OBJECT (self), "drag_end",
1775 G_CALLBACK(drag_end_cb), NULL);
1778 static GtkTreePath *
1779 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1781 GtkTreePath *path = NULL;
1782 GtkTreeSelection *sel = NULL;
1785 sel = gtk_tree_view_get_selection(self);
1786 rows = gtk_tree_selection_get_selected_rows (sel, model);
1788 if ((rows == NULL) || (g_list_length(rows) != 1))
1791 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1796 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1802 #ifndef MODEST_TOOLKIT_HILDON2
1804 * This function moves the tree view scroll to the current selected
1805 * row when the widget grabs the focus
1808 on_focus_in (GtkWidget *self,
1809 GdkEventFocus *event,
1812 GtkTreeSelection *selection;
1813 GtkTreeModel *model;
1814 GList *selected = NULL;
1815 GtkTreePath *selected_path = NULL;
1817 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1821 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1822 /* If none selected yet, pick the first one */
1823 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1827 /* Return if the model is empty */
1828 if (!gtk_tree_model_get_iter_first (model, &iter))
1831 path = gtk_tree_model_get_path (model, &iter);
1832 gtk_tree_selection_select_path (selection, path);
1833 gtk_tree_path_free (path);
1836 /* Need to get the all the rows because is selection multiple */
1837 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1838 if (selected == NULL) return FALSE;
1839 selected_path = (GtkTreePath *) selected->data;
1842 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1843 g_list_free (selected);
1849 on_focus_out (GtkWidget *self,
1850 GdkEventFocus *event,
1854 if (!gtk_widget_is_focus (self)) {
1855 GtkTreeSelection *selection = NULL;
1856 GList *selected_rows = NULL;
1857 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1858 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1859 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1860 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1861 gtk_tree_selection_unselect_all (selection);
1862 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1863 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1864 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1865 g_list_free (selected_rows);
1873 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1875 enable_drag_and_drop(self);
1880 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1882 GtkTreeSelection *selection = NULL;
1883 GtkTreePath *path = NULL;
1884 gboolean already_selected = FALSE, already_opened = FALSE;
1885 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1887 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1889 GtkTreeModel *model;
1891 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1892 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1894 /* Get header from model */
1895 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1896 if (gtk_tree_model_get_iter (model, &iter, path)) {
1897 GValue value = {0,};
1900 gtk_tree_model_get_value (model, &iter,
1901 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1903 header = (TnyHeader *) g_value_get_object (&value);
1904 if (TNY_IS_HEADER (header)) {
1905 status = modest_tny_all_send_queues_get_msg_status (header);
1906 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1909 g_value_unset (&value);
1913 /* Enable drag and drop only if the user clicks on a row that
1914 it's already selected. If not, let him select items using
1915 the pointer. If the message is in an OUTBOX and in sending
1916 status disable drag and drop as well */
1917 if (!already_selected ||
1918 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1920 disable_drag_and_drop(self);
1923 gtk_tree_path_free(path);
1925 /* If it's already opened then do not let the button-press
1926 event go on because it'll perform a message open because
1927 we're clicking on to an already selected header */
1932 folder_monitor_update (TnyFolderObserver *self,
1933 TnyFolderChange *change)
1935 ModestHeaderViewPrivate *priv = NULL;
1936 TnyFolderChangeChanged changed;
1937 TnyFolder *folder = NULL;
1939 changed = tny_folder_change_get_changed (change);
1941 /* Do not notify the observers if the folder of the header
1942 view has changed before this call to the observer
1944 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1945 folder = tny_folder_change_get_folder (change);
1946 if (folder != priv->folder)
1949 MODEST_DEBUG_BLOCK (
1950 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1951 g_print ("ADDED %d/%d (r/t) \n",
1952 tny_folder_change_get_new_unread_count (change),
1953 tny_folder_change_get_new_all_count (change));
1954 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1955 g_print ("ALL COUNT %d\n",
1956 tny_folder_change_get_new_all_count (change));
1957 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1958 g_print ("UNREAD COUNT %d\n",
1959 tny_folder_change_get_new_unread_count (change));
1960 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1961 g_print ("EXPUNGED %d/%d (r/t) \n",
1962 tny_folder_change_get_new_unread_count (change),
1963 tny_folder_change_get_new_all_count (change));
1964 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1965 g_print ("FOLDER RENAME\n");
1966 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1967 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1968 tny_folder_change_get_new_unread_count (change),
1969 tny_folder_change_get_new_all_count (change));
1970 g_print ("---------------------------------------------------\n");
1973 /* Check folder count */
1974 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1975 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1977 g_mutex_lock (priv->observers_lock);
1979 /* Emit signal to evaluate how headers changes affects
1980 to the window view */
1981 g_signal_emit (G_OBJECT(self),
1982 signals[MSG_COUNT_CHANGED_SIGNAL],
1985 /* Added or removed headers, so data stored on cliboard are invalid */
1986 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1987 modest_email_clipboard_clear (priv->clipboard);
1989 g_mutex_unlock (priv->observers_lock);
1995 g_object_unref (folder);
1999 modest_header_view_is_empty (ModestHeaderView *self)
2001 ModestHeaderViewPrivate *priv;
2003 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
2005 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
2007 return priv->status == HEADER_VIEW_EMPTY;
2011 modest_header_view_clear (ModestHeaderView *self)
2013 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
2015 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2019 modest_header_view_copy_selection (ModestHeaderView *header_view)
2021 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2023 /* Copy selection */
2024 _clipboard_set_selected_data (header_view, FALSE);
2028 modest_header_view_cut_selection (ModestHeaderView *header_view)
2030 ModestHeaderViewPrivate *priv = NULL;
2031 const gchar **hidding = NULL;
2032 guint i, n_selected;
2034 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2036 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2038 /* Copy selection */
2039 _clipboard_set_selected_data (header_view, TRUE);
2041 /* Get hidding ids */
2042 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2044 /* Clear hidding array created by previous cut operation */
2045 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2047 /* Copy hidding array */
2048 priv->n_selected = n_selected;
2049 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2050 for (i=0; i < n_selected; i++)
2051 priv->hidding_ids[i] = g_strdup(hidding[i]);
2053 /* Hide cut headers */
2054 modest_header_view_refilter (header_view);
2061 _clipboard_set_selected_data (ModestHeaderView *header_view,
2064 ModestHeaderViewPrivate *priv = NULL;
2065 TnyList *headers = NULL;
2067 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2068 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2070 /* Set selected data on clipboard */
2071 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2072 headers = modest_header_view_get_selected_headers (header_view);
2073 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2076 g_object_unref (headers);
2080 ModestHeaderView *self;
2085 notify_filter_change (gpointer data)
2087 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2089 g_signal_emit (info->self,
2090 signals[MSG_COUNT_CHANGED_SIGNAL],
2091 0, info->folder, NULL);
2097 notify_filter_change_destroy (gpointer data)
2099 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2100 ModestHeaderViewPrivate *priv;
2102 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2103 priv->status_timeout = 0;
2105 g_object_unref (info->self);
2106 g_object_unref (info->folder);
2107 g_slice_free (NotifyFilterInfo, info);
2111 current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
2113 /* For the moment we only need to filter outbox */
2114 return priv->is_outbox;
2118 filter_row (GtkTreeModel *model,
2122 ModestHeaderViewPrivate *priv = NULL;
2123 TnyHeaderFlags flags;
2124 TnyHeader *header = NULL;
2127 gboolean visible = TRUE;
2128 gboolean found = FALSE;
2129 GValue value = {0,};
2130 HeaderViewStatus old_status;
2132 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2133 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2135 /* Get header from model */
2136 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2137 flags = (TnyHeaderFlags) g_value_get_int (&value);
2138 g_value_unset (&value);
2139 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2140 header = (TnyHeader *) g_value_get_object (&value);
2141 g_value_unset (&value);
2143 /* Get message id from header (ensure is a valid id) */
2149 /* Hide deleted and mark as deleted heders */
2150 if (flags & TNY_HEADER_FLAG_DELETED ||
2151 flags & TNY_HEADER_FLAG_EXPUNGED) {
2156 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2157 if (current_folder_needs_filtering (priv) &&
2158 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2164 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2165 if (current_folder_needs_filtering (priv) &&
2166 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2172 /* If no data on clipboard, return always TRUE */
2173 if (modest_email_clipboard_cleared(priv->clipboard)) {
2179 if (priv->hidding_ids != NULL) {
2180 id = tny_header_dup_message_id (header);
2181 for (i=0; i < priv->n_selected && !found; i++)
2182 if (priv->hidding_ids[i] != NULL && id != NULL)
2183 found = (!strcmp (priv->hidding_ids[i], id));
2190 old_status = priv->status;
2191 priv->status = ((gboolean) priv->status) && !visible;
2192 if ((priv->notify_status) && (priv->status != old_status)) {
2193 if (priv->status_timeout)
2194 g_source_remove (priv->status_timeout);
2197 NotifyFilterInfo *info;
2199 info = g_slice_new0 (NotifyFilterInfo);
2200 info->self = g_object_ref (G_OBJECT (user_data));
2202 info->folder = tny_header_get_folder (header);
2203 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2204 notify_filter_change,
2206 notify_filter_change_destroy);
2214 _clear_hidding_filter (ModestHeaderView *header_view)
2216 ModestHeaderViewPrivate *priv = NULL;
2219 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2220 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2222 if (priv->hidding_ids != NULL) {
2223 for (i=0; i < priv->n_selected; i++)
2224 g_free (priv->hidding_ids[i]);
2225 g_free(priv->hidding_ids);
2230 modest_header_view_refilter (ModestHeaderView *header_view)
2232 GtkTreeModel *model, *sortable = NULL;
2233 ModestHeaderViewPrivate *priv = NULL;
2235 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2236 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2238 /* Hide cut headers */
2239 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2240 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2241 model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2242 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2243 priv->status = HEADER_VIEW_INIT;
2244 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2250 * Called when an account is removed. If I'm showing a folder of the
2251 * account that has been removed then clear the view
2254 on_account_removed (TnyAccountStore *self,
2255 TnyAccount *account,
2258 ModestHeaderViewPrivate *priv = NULL;
2260 /* Ignore changes in transport accounts */
2261 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2264 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2267 TnyAccount *my_account;
2269 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2270 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2271 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2272 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2274 my_account = tny_folder_get_account (priv->folder);
2278 if (my_account == account)
2279 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2280 g_object_unref (my_account);
2286 modest_header_view_add_observer(ModestHeaderView *header_view,
2287 ModestHeaderViewObserver *observer)
2289 ModestHeaderViewPrivate *priv;
2291 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2292 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2294 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2296 g_mutex_lock(priv->observer_list_lock);
2297 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2298 g_mutex_unlock(priv->observer_list_lock);
2302 modest_header_view_remove_observer(ModestHeaderView *header_view,
2303 ModestHeaderViewObserver *observer)
2305 ModestHeaderViewPrivate *priv;
2307 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2308 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2310 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2312 g_mutex_lock(priv->observer_list_lock);
2313 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2314 g_mutex_unlock(priv->observer_list_lock);
2318 modest_header_view_notify_observers(ModestHeaderView *header_view,
2319 GtkTreeModel *model,
2320 const gchar *tny_folder_id)
2322 ModestHeaderViewPrivate *priv = NULL;
2324 ModestHeaderViewObserver *observer;
2327 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2329 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2331 g_mutex_lock(priv->observer_list_lock);
2332 iter = priv->observer_list;
2333 while(iter != NULL){
2334 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2335 modest_header_view_observer_update(observer, model,
2337 iter = g_slist_next(iter);
2339 g_mutex_unlock(priv->observer_list_lock);
2343 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2345 ModestHeaderViewPrivate *priv = NULL;
2347 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2348 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2352 modest_header_view_set_filter (ModestHeaderView *self,
2353 ModestHeaderViewFilter filter)
2355 ModestHeaderViewPrivate *priv;
2357 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2358 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2360 priv->filter |= filter;
2362 if (current_folder_needs_filtering (priv))
2363 modest_header_view_refilter (self);
2367 modest_header_view_unset_filter (ModestHeaderView *self,
2368 ModestHeaderViewFilter filter)
2370 ModestHeaderViewPrivate *priv;
2372 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2373 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2375 priv->filter &= ~filter;
2377 if (current_folder_needs_filtering (priv))
2378 modest_header_view_refilter (self);
2382 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2384 if (strcmp ("style", spec->name) == 0) {
2385 update_style (MODEST_HEADER_VIEW (obj));
2386 gtk_widget_queue_draw (GTK_WIDGET (obj));
2391 update_style (ModestHeaderView *self)
2393 ModestHeaderViewPrivate *priv;
2394 GdkColor style_color;
2395 GdkColor style_active_color;
2396 PangoAttrList *attr_list;
2398 PangoAttribute *attr;
2400 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2401 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2405 attr_list = pango_attr_list_new ();
2406 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2407 gdk_color_parse ("grey", &style_color);
2409 priv->secondary_color = style_color;
2410 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2411 pango_attr_list_insert (attr_list, attr);
2414 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2416 "SmallSystemFont", NULL,
2419 attr = pango_attr_font_desc_new (pango_font_description_copy
2420 (style->font_desc));
2421 pango_attr_list_insert (attr_list, attr);
2423 g_object_set (G_OBJECT (priv->renderer_address),
2424 "foreground-gdk", &(priv->secondary_color),
2425 "foreground-set", TRUE,
2426 "attributes", attr_list,
2428 g_object_set (G_OBJECT (priv->renderer_date_status),
2429 "foreground-gdk", &(priv->secondary_color),
2430 "foreground-set", TRUE,
2431 "attributes", attr_list,
2433 pango_attr_list_unref (attr_list);
2435 g_object_set (G_OBJECT (priv->renderer_address),
2436 "foreground-gdk", &(priv->secondary_color),
2437 "foreground-set", TRUE,
2438 "scale", PANGO_SCALE_SMALL,
2441 g_object_set (G_OBJECT (priv->renderer_date_status),
2442 "foreground-gdk", &(priv->secondary_color),
2443 "foreground-set", TRUE,
2444 "scale", PANGO_SCALE_SMALL,
2449 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2450 priv->active_color = style_active_color;
2451 #ifdef MODEST_TOOLKIT_HILDON2
2452 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
2453 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2456 #ifdef MODEST_TOOLKIT_HILDON2
2457 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2463 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2468 GtkTreeModel *tree_model;
2473 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2481 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2482 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2486 gtk_tree_model_get (tree_model, &iter,
2487 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2494 modest_header_view_set_show_latest (ModestHeaderView *header_view,
2497 ModestHeaderViewPrivate *priv;
2498 GtkTreeModel *sortable, *filter, *model;
2500 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2501 priv->show_latest = show_latest;
2503 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2504 if (GTK_IS_TREE_MODEL_SORT (sortable)) {
2505 filter = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
2506 if (GTK_IS_TREE_MODEL_FILTER (filter)) {
2507 model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
2509 tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (model), priv->show_latest);
2516 modest_header_view_get_show_latest (ModestHeaderView *header_view)
2518 ModestHeaderViewPrivate *priv;
2520 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2521 return priv->show_latest;