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;
176 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
177 struct _HeadersCountChangedHelper {
178 ModestHeaderView *self;
179 TnyFolderChange *change;
183 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
184 MODEST_TYPE_HEADER_VIEW, \
185 ModestHeaderViewPrivate))
189 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
192 HEADER_SELECTED_SIGNAL,
193 HEADER_ACTIVATED_SIGNAL,
194 ITEM_NOT_FOUND_SIGNAL,
195 MSG_COUNT_CHANGED_SIGNAL,
196 UPDATING_MSG_LIST_SIGNAL,
201 static GObjectClass *parent_class = NULL;
203 /* uncomment the following if you have defined any signals */
204 static guint signals[LAST_SIGNAL] = {0};
207 modest_header_view_get_type (void)
209 static GType my_type = 0;
211 static const GTypeInfo my_info = {
212 sizeof(ModestHeaderViewClass),
213 NULL, /* base init */
214 NULL, /* base finalize */
215 (GClassInitFunc) modest_header_view_class_init,
216 NULL, /* class finalize */
217 NULL, /* class data */
218 sizeof(ModestHeaderView),
220 (GInstanceInitFunc) modest_header_view_init,
224 static const GInterfaceInfo tny_folder_observer_info =
226 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
227 NULL, /* interface_finalize */
228 NULL /* interface_data */
230 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
234 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
235 &tny_folder_observer_info);
243 modest_header_view_class_init (ModestHeaderViewClass *klass)
245 GObjectClass *gobject_class;
246 gobject_class = (GObjectClass*) klass;
248 parent_class = g_type_class_peek_parent (klass);
249 gobject_class->finalize = modest_header_view_finalize;
250 gobject_class->dispose = modest_header_view_dispose;
252 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
254 signals[HEADER_SELECTED_SIGNAL] =
255 g_signal_new ("header_selected",
256 G_TYPE_FROM_CLASS (gobject_class),
258 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
260 g_cclosure_marshal_VOID__POINTER,
261 G_TYPE_NONE, 1, G_TYPE_POINTER);
263 signals[HEADER_ACTIVATED_SIGNAL] =
264 g_signal_new ("header_activated",
265 G_TYPE_FROM_CLASS (gobject_class),
267 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
269 gtk_marshal_VOID__POINTER_POINTER,
270 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
273 signals[ITEM_NOT_FOUND_SIGNAL] =
274 g_signal_new ("item_not_found",
275 G_TYPE_FROM_CLASS (gobject_class),
277 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
279 g_cclosure_marshal_VOID__INT,
280 G_TYPE_NONE, 1, G_TYPE_INT);
282 signals[MSG_COUNT_CHANGED_SIGNAL] =
283 g_signal_new ("msg_count_changed",
284 G_TYPE_FROM_CLASS (gobject_class),
286 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
288 modest_marshal_VOID__POINTER_POINTER,
289 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
291 signals[UPDATING_MSG_LIST_SIGNAL] =
292 g_signal_new ("updating-msg-list",
293 G_TYPE_FROM_CLASS (gobject_class),
295 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
297 g_cclosure_marshal_VOID__BOOLEAN,
298 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
300 #ifdef MODEST_TOOLKIT_HILDON2
301 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
307 tny_folder_observer_init (TnyFolderObserverIface *klass)
309 klass->update = folder_monitor_update;
312 static GtkTreeViewColumn*
313 get_new_column (const gchar *name, GtkCellRenderer *renderer,
314 gboolean resizable, gint sort_col_id, gboolean show_as_text,
315 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
317 GtkTreeViewColumn *column;
319 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
320 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
322 gtk_tree_view_column_set_resizable (column, resizable);
324 gtk_tree_view_column_set_expand (column, TRUE);
327 gtk_tree_view_column_add_attribute (column, renderer, "text",
329 if (sort_col_id >= 0)
330 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
332 gtk_tree_view_column_set_sort_indicator (column, FALSE);
333 gtk_tree_view_column_set_reorderable (column, TRUE);
336 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
343 remove_all_columns (ModestHeaderView *obj)
345 GList *columns, *cursor;
347 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
349 for (cursor = columns; cursor; cursor = cursor->next)
350 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
351 GTK_TREE_VIEW_COLUMN(cursor->data));
352 g_list_free (columns);
356 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
358 GtkTreeModel *sortable;
359 GtkTreeViewColumn *column=NULL;
360 GtkTreeSelection *selection = NULL;
361 GtkCellRenderer *renderer_header,
362 *renderer_attach, *renderer_compact_date_or_status;
363 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
364 *renderer_subject_box, *renderer_recpt,
366 ModestHeaderViewPrivate *priv;
367 GtkTreeViewColumn *compact_column = NULL;
370 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
371 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
373 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
375 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
377 /* TODO: check whether these renderers need to be freed */
378 renderer_attach = gtk_cell_renderer_pixbuf_new ();
379 renderer_priority = gtk_cell_renderer_pixbuf_new ();
380 renderer_header = gtk_cell_renderer_text_new ();
382 renderer_compact_header = modest_vbox_cell_renderer_new ();
383 renderer_recpt_box = modest_hbox_cell_renderer_new ();
384 renderer_subject_box = modest_hbox_cell_renderer_new ();
385 renderer_recpt = gtk_cell_renderer_text_new ();
386 priv->renderer_address = renderer_recpt;
387 priv->renderer_subject = gtk_cell_renderer_text_new ();
388 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
389 priv->renderer_date_status = renderer_compact_date_or_status;
391 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
392 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
393 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
394 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
395 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
396 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
397 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
398 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
399 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
400 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
401 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
402 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
403 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
404 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
406 #ifdef MODEST_TOOLKIT_HILDON2
407 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
409 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
410 #ifndef MODEST_TOOLKIT_GTK
411 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
412 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
414 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
415 g_object_set(G_OBJECT(renderer_header),
416 "ellipsize", PANGO_ELLIPSIZE_END,
418 g_object_set (G_OBJECT (priv->renderer_subject),
419 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
421 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
422 g_object_set (G_OBJECT (renderer_recpt),
423 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
425 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
426 g_object_set(G_OBJECT(renderer_compact_date_or_status),
427 "xalign", 1.0, "yalign", 0.1,
429 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
430 #ifdef MODEST_TOOLKIT_HILDON2
431 g_object_set (G_OBJECT (renderer_priority),
433 "xalign", 0.0, NULL);
434 g_object_set (G_OBJECT (renderer_attach),
436 "xalign", 0.0, NULL);
438 g_object_set (G_OBJECT (renderer_priority),
439 "yalign", 0.5, NULL);
440 g_object_set (G_OBJECT (renderer_attach),
441 "yalign", 0.0, NULL);
444 #ifdef MODEST_TOOLKIT_HILDON1
445 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
446 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
447 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
448 #elif MODEST_TOOLKIT_HILDON2
449 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
450 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
451 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
453 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
454 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
455 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
458 remove_all_columns (self);
460 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
461 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
462 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
464 /* Add new columns */
465 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
466 ModestHeaderViewColumn col =
467 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
469 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
470 g_printerr ("modest: invalid column %d in column list\n", col);
476 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
477 column = get_new_column (_("A"), renderer_attach, FALSE,
478 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
480 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
482 gtk_tree_view_column_set_fixed_width (column, 45);
486 case MODEST_HEADER_VIEW_COLUMN_FROM:
487 column = get_new_column (_("From"), renderer_header, TRUE,
488 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
490 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
491 GINT_TO_POINTER(TRUE));
494 case MODEST_HEADER_VIEW_COLUMN_TO:
495 column = get_new_column (_("To"), renderer_header, TRUE,
496 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
498 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
499 GINT_TO_POINTER(FALSE));
502 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
503 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
504 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
506 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
507 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
508 compact_column = column;
511 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
512 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
513 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
515 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
516 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
517 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
518 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
519 compact_column = column;
523 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
524 column = get_new_column (_("Subject"), renderer_header, TRUE,
525 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
527 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
531 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
532 column = get_new_column (_("Received"), renderer_header, TRUE,
533 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
535 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
536 GINT_TO_POINTER(TRUE));
539 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
540 column = get_new_column (_("Sent"), renderer_header, TRUE,
541 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
543 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
544 GINT_TO_POINTER(FALSE));
547 case MODEST_HEADER_VIEW_COLUMN_SIZE:
548 column = get_new_column (_("Size"), renderer_header, TRUE,
549 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
551 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
554 case MODEST_HEADER_VIEW_COLUMN_STATUS:
555 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
556 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
558 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
563 g_return_val_if_reached(FALSE);
566 /* we keep the column id around */
567 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
568 GINT_TO_POINTER(col));
570 /* we need this ptr when sorting the rows */
571 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
573 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
577 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
578 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
579 (GtkTreeIterCompareFunc) cmp_rows,
580 compact_column, NULL);
581 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
582 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
583 (GtkTreeIterCompareFunc) cmp_subject_rows,
584 compact_column, NULL);
588 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
594 datetime_format_changed (ModestDatetimeFormatter *formatter,
595 ModestHeaderView *self)
597 gtk_widget_queue_draw (GTK_WIDGET (self));
601 modest_header_view_init (ModestHeaderView *obj)
603 ModestHeaderViewPrivate *priv;
606 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
609 priv->is_outbox = FALSE;
611 priv->monitor = NULL;
612 priv->observers_lock = g_mutex_new ();
613 priv->autoselect_reference = NULL;
615 priv->status = HEADER_VIEW_INIT;
616 priv->status_timeout = 0;
617 priv->notify_status = TRUE;
619 priv->observer_list_lock = g_mutex_new();
620 priv->observer_list = NULL;
622 priv->clipboard = modest_runtime_get_email_clipboard ();
623 priv->hidding_ids = NULL;
624 priv->n_selected = 0;
625 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
626 priv->selection_changed_handler = 0;
627 priv->acc_removed_handler = 0;
629 /* Sort parameters */
630 for (j=0; j < 2; j++) {
631 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
632 priv->sort_colid[j][i] = -1;
633 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
637 priv->datetime_formatter = modest_datetime_formatter_new ();
638 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
639 G_CALLBACK (datetime_format_changed), (gpointer) obj);
641 setup_drag_and_drop (GTK_WIDGET(obj));
645 modest_header_view_dispose (GObject *obj)
647 ModestHeaderView *self;
648 ModestHeaderViewPrivate *priv;
649 GtkTreeSelection *sel;
651 self = MODEST_HEADER_VIEW(obj);
652 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
654 if (priv->datetime_formatter) {
655 g_object_unref (priv->datetime_formatter);
656 priv->datetime_formatter = NULL;
659 /* Free in the dispose to avoid unref cycles */
661 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
662 g_object_unref (G_OBJECT (priv->folder));
666 /* We need to do this here in the dispose because the
667 selection won't exist when finalizing */
668 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
669 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
670 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
671 priv->selection_changed_handler = 0;
674 G_OBJECT_CLASS(parent_class)->dispose (obj);
678 modest_header_view_finalize (GObject *obj)
680 ModestHeaderView *self;
681 ModestHeaderViewPrivate *priv;
683 self = MODEST_HEADER_VIEW(obj);
684 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
686 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
687 priv->acc_removed_handler)) {
688 g_signal_handler_disconnect (modest_runtime_get_account_store (),
689 priv->acc_removed_handler);
692 /* There is no need to lock because there should not be any
693 * reference to self now. */
694 g_mutex_free(priv->observer_list_lock);
695 g_slist_free(priv->observer_list);
697 g_mutex_lock (priv->observers_lock);
699 tny_folder_monitor_stop (priv->monitor);
700 g_object_unref (G_OBJECT (priv->monitor));
702 g_mutex_unlock (priv->observers_lock);
703 g_mutex_free (priv->observers_lock);
705 /* Clear hidding array created by cut operation */
706 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
708 if (priv->autoselect_reference != NULL) {
709 gtk_tree_row_reference_free (priv->autoselect_reference);
710 priv->autoselect_reference = NULL;
713 G_OBJECT_CLASS(parent_class)->finalize (obj);
718 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
721 GtkTreeSelection *sel;
722 ModestHeaderView *self;
723 ModestHeaderViewPrivate *priv;
725 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
728 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
729 self = MODEST_HEADER_VIEW(obj);
730 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
732 modest_header_view_set_style (self, style);
734 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
735 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
736 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
738 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
739 TRUE); /* alternating row colors */
741 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
742 priv->selection_changed_handler =
743 g_signal_connect_after (sel, "changed",
744 G_CALLBACK(on_selection_changed), self);
746 g_signal_connect (self, "row-activated",
747 G_CALLBACK (on_header_row_activated), NULL);
749 #ifndef MODEST_TOOLKIT_HILDON2
750 g_signal_connect (self, "focus-in-event",
751 G_CALLBACK(on_focus_in), NULL);
752 g_signal_connect (self, "focus-out-event",
753 G_CALLBACK(on_focus_out), NULL);
756 g_signal_connect (self, "button-press-event",
757 G_CALLBACK(on_button_press_event), NULL);
758 g_signal_connect (self, "button-release-event",
759 G_CALLBACK(on_button_release_event), NULL);
761 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
763 G_CALLBACK (on_account_removed),
766 g_signal_connect (self, "expose-event",
767 G_CALLBACK(modest_header_view_on_expose_event),
770 return GTK_WIDGET(self);
775 modest_header_view_count_selected_headers (ModestHeaderView *self)
777 GtkTreeSelection *sel;
780 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
782 /* Get selection object and check selected rows count */
783 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
784 selected_rows = gtk_tree_selection_count_selected_rows (sel);
786 return selected_rows;
790 modest_header_view_has_selected_headers (ModestHeaderView *self)
792 GtkTreeSelection *sel;
795 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
797 /* Get selection object and check selected rows count */
798 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
799 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
806 modest_header_view_get_selected_headers (ModestHeaderView *self)
808 GtkTreeSelection *sel;
809 TnyList *header_list = NULL;
811 GList *list, *tmp = NULL;
812 GtkTreeModel *tree_model = NULL;
815 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
818 /* Get selected rows */
819 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
820 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
823 header_list = tny_simple_list_new();
825 list = g_list_reverse (list);
828 /* get header from selection */
829 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
830 gtk_tree_model_get (tree_model, &iter,
831 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
833 /* Prepend to list */
834 tny_list_prepend (header_list, G_OBJECT (header));
835 g_object_unref (G_OBJECT (header));
837 tmp = g_list_next (tmp);
840 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
847 /* scroll our list view so the selected item is visible */
849 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
851 #ifdef MODEST_TOOLKIT_GTK
853 GtkTreePath *selected_path;
854 GtkTreePath *start, *end;
858 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
859 selected_path = gtk_tree_model_get_path (model, iter);
861 start = gtk_tree_path_new ();
862 end = gtk_tree_path_new ();
864 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
866 if (gtk_tree_path_compare (selected_path, start) < 0 ||
867 gtk_tree_path_compare (end, selected_path) < 0)
868 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
869 selected_path, NULL, TRUE,
872 gtk_tree_path_free (selected_path);
873 gtk_tree_path_free (start);
874 gtk_tree_path_free (end);
876 #endif /* MODEST_TOOLKIT_GTK */
881 modest_header_view_select_next (ModestHeaderView *self)
883 GtkTreeSelection *sel;
888 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
890 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
891 path = get_selected_row (GTK_TREE_VIEW(self), &model);
892 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
893 /* Unselect previous path */
894 gtk_tree_selection_unselect_path (sel, path);
896 /* Move path down and selects new one */
897 if (gtk_tree_model_iter_next (model, &iter)) {
898 gtk_tree_selection_select_iter (sel, &iter);
899 scroll_to_selected (self, &iter, FALSE);
901 gtk_tree_path_free(path);
907 modest_header_view_select_prev (ModestHeaderView *self)
909 GtkTreeSelection *sel;
914 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
916 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
917 path = get_selected_row (GTK_TREE_VIEW(self), &model);
918 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
919 /* Unselect previous path */
920 gtk_tree_selection_unselect_path (sel, path);
923 if (gtk_tree_path_prev (path)) {
924 gtk_tree_model_get_iter (model, &iter, path);
926 /* Select the new one */
927 gtk_tree_selection_select_iter (sel, &iter);
928 scroll_to_selected (self, &iter, TRUE);
931 gtk_tree_path_free (path);
936 modest_header_view_get_columns (ModestHeaderView *self)
938 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
940 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
946 modest_header_view_set_style (ModestHeaderView *self,
947 ModestHeaderViewStyle style)
949 ModestHeaderViewPrivate *priv;
950 gboolean show_col_headers = FALSE;
951 ModestHeaderViewStyle old_style;
953 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
954 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
957 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
958 if (priv->style == style)
959 return TRUE; /* nothing to do */
962 case MODEST_HEADER_VIEW_STYLE_DETAILS:
963 show_col_headers = TRUE;
965 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
968 g_return_val_if_reached (FALSE);
970 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
971 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
973 old_style = priv->style;
980 ModestHeaderViewStyle
981 modest_header_view_get_style (ModestHeaderView *self)
983 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
985 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
988 /* This is used to automatically select the first header if the user
989 * has not selected any header yet.
992 modest_header_view_on_expose_event(GtkTreeView *header_view,
993 GdkEventExpose *event,
996 GtkTreeSelection *sel;
998 GtkTreeIter tree_iter;
999 ModestHeaderViewPrivate *priv;
1001 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1002 model = gtk_tree_view_get_model(header_view);
1007 #ifdef MODEST_TOOLKIT_HILDON2
1010 sel = gtk_tree_view_get_selection(header_view);
1011 if(!gtk_tree_selection_count_selected_rows(sel)) {
1012 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1013 GtkTreePath *tree_iter_path;
1014 /* Prevent the widget from getting the focus
1015 when selecting the first item */
1016 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1017 g_object_set(header_view, "can-focus", FALSE, NULL);
1018 gtk_tree_selection_select_iter(sel, &tree_iter);
1019 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1020 g_object_set(header_view, "can-focus", TRUE, NULL);
1021 if (priv->autoselect_reference) {
1022 gtk_tree_row_reference_free (priv->autoselect_reference);
1024 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1025 gtk_tree_path_free (tree_iter_path);
1028 if (priv->autoselect_reference != NULL) {
1029 gboolean moved_selection = FALSE;
1030 GtkTreePath * last_path;
1031 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1032 moved_selection = TRUE;
1036 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1037 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1038 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1039 moved_selection = TRUE;
1040 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1042 gtk_tree_path_free (last_path);
1044 if (moved_selection) {
1045 gtk_tree_row_reference_free (priv->autoselect_reference);
1046 priv->autoselect_reference = NULL;
1049 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1050 GtkTreePath *current_path;
1051 current_path = gtk_tree_model_get_path (model, &tree_iter);
1052 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1053 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1054 g_object_set(header_view, "can-focus", FALSE, NULL);
1055 gtk_tree_selection_unselect_all (sel);
1056 gtk_tree_selection_select_iter(sel, &tree_iter);
1057 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1058 g_object_set(header_view, "can-focus", TRUE, NULL);
1059 gtk_tree_row_reference_free (priv->autoselect_reference);
1060 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1062 gtk_tree_path_free (current_path);
1063 gtk_tree_path_free (last_path);
1073 modest_header_view_get_folder (ModestHeaderView *self)
1075 ModestHeaderViewPrivate *priv;
1077 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1079 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1082 g_object_ref (priv->folder);
1084 return priv->folder;
1088 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1094 ModestHeaderView *self;
1095 ModestHeaderViewPrivate *priv;
1097 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1099 self = MODEST_HEADER_VIEW (user_data);
1100 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1102 if (cancelled || err)
1105 /* Add IDLE observer (monitor) and another folder observer for
1106 new messages (self) */
1107 g_mutex_lock (priv->observers_lock);
1108 if (priv->monitor) {
1109 tny_folder_monitor_stop (priv->monitor);
1110 g_object_unref (G_OBJECT (priv->monitor));
1112 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1113 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1114 tny_folder_monitor_start (priv->monitor);
1115 g_mutex_unlock (priv->observers_lock);
1119 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1123 ModestHeaderViewPrivate *priv;
1124 GList *cols, *cursor;
1125 GtkTreeModel *filter_model, *sortable;
1127 GtkSortType sort_type;
1129 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1131 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1133 /* Start the monitor in the callback of the
1134 tny_gtk_header_list_model_set_folder call. It's crucial to
1135 do it there and not just after the call because we want the
1136 monitor to observe only the headers returned by the
1137 tny_folder_get_headers_async call that it's inside the
1138 tny_gtk_header_list_model_set_folder call. This way the
1139 monitor infrastructure could successfully cope with
1140 duplicates. For example if a tny_folder_add_msg_async is
1141 happening while tny_gtk_header_list_model_set_folder is
1142 invoked, then the first call could add a header that will
1143 be added again by tny_gtk_header_list_model_set_folder, so
1144 we'd end up with duplicate headers. sergio */
1145 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1147 set_folder_intern_get_headers_async_cb,
1150 /* Create a tree model filter to hide and show rows for cut operations */
1151 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1152 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1153 filter_row, self, NULL);
1154 g_object_unref (headers);
1156 /* Init filter_row function to examine empty status */
1157 priv->status = HEADER_VIEW_INIT;
1159 /* Create sortable model */
1160 sortable = gtk_tree_model_sort_new_with_model (filter_model);
1161 g_object_unref (filter_model);
1163 /* install our special sorting functions */
1164 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1166 /* Restore sort column id */
1168 type = modest_tny_folder_guess_folder_type (folder);
1169 if (type == TNY_FOLDER_TYPE_INVALID)
1170 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1172 sort_colid = modest_header_view_get_sort_column_id (self, type);
1173 sort_type = modest_header_view_get_sort_type (self, type);
1174 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1177 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1178 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1179 (GtkTreeIterCompareFunc) cmp_rows,
1181 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1182 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1183 (GtkTreeIterCompareFunc) cmp_subject_rows,
1188 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1189 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1190 g_object_unref (sortable);
1197 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1199 GtkSortType sort_type)
1201 ModestHeaderViewPrivate *priv = NULL;
1202 GtkTreeModel *sortable = NULL;
1205 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1206 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1208 /* Get model and private data */
1209 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1210 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1212 /* Sort tree model */
1213 type = modest_tny_folder_guess_folder_type (priv->folder);
1214 if (type == TNY_FOLDER_TYPE_INVALID)
1215 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1217 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1220 /* Store new sort parameters */
1221 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1226 modest_header_view_set_sort_params (ModestHeaderView *self,
1228 GtkSortType sort_type,
1231 ModestHeaderViewPrivate *priv;
1232 ModestHeaderViewStyle style;
1234 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1235 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1236 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1238 style = modest_header_view_get_style (self);
1239 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1241 priv->sort_colid[style][type] = sort_colid;
1242 priv->sort_type[style][type] = sort_type;
1246 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1249 ModestHeaderViewPrivate *priv;
1250 ModestHeaderViewStyle style;
1252 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1253 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1255 style = modest_header_view_get_style (self);
1256 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1258 return priv->sort_colid[style][type];
1262 modest_header_view_get_sort_type (ModestHeaderView *self,
1265 ModestHeaderViewPrivate *priv;
1266 ModestHeaderViewStyle style;
1268 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1269 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1271 style = modest_header_view_get_style (self);
1272 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1274 return priv->sort_type[style][type];
1278 ModestHeaderView *header_view;
1279 RefreshAsyncUserCallback cb;
1284 folder_refreshed_cb (ModestMailOperation *mail_op,
1288 ModestHeaderViewPrivate *priv;
1289 SetFolderHelper *info;
1291 info = (SetFolderHelper*) user_data;
1293 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1297 info->cb (mail_op, folder, info->user_data);
1299 /* Start the folder count changes observer. We do not need it
1300 before the refresh. Note that the monitor could still be
1301 called for this refresh but now we know that the callback
1302 was previously called */
1303 g_mutex_lock (priv->observers_lock);
1304 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1305 g_mutex_unlock (priv->observers_lock);
1307 /* Notify the observers that the update is over */
1308 g_signal_emit (G_OBJECT (info->header_view),
1309 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1311 /* Allow filtering notifications from now on if the current
1312 folder is still the same (if not then the user has selected
1313 another one to refresh, we should wait until that refresh
1315 if (priv->folder == folder)
1316 priv->notify_status = TRUE;
1319 g_object_unref (info->header_view);
1324 refresh_folder_error_handler (ModestMailOperation *mail_op,
1327 const GError *error = modest_mail_operation_get_error (mail_op);
1329 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1330 error->code == TNY_IO_ERROR_WRITE ||
1331 error->code == TNY_IO_ERROR_READ) {
1332 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1333 /* If the mail op has been cancelled then it's not an error: don't show any message */
1334 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1335 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1336 modest_platform_information_banner (NULL, NULL, msg);
1343 modest_header_view_set_folder (ModestHeaderView *self,
1346 ModestWindow *progress_window,
1347 RefreshAsyncUserCallback callback,
1350 ModestHeaderViewPrivate *priv;
1352 g_return_if_fail (self);
1354 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1357 if (priv->status_timeout) {
1358 g_source_remove (priv->status_timeout);
1359 priv->status_timeout = 0;
1362 g_mutex_lock (priv->observers_lock);
1363 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1364 g_object_unref (priv->folder);
1365 priv->folder = NULL;
1366 g_mutex_unlock (priv->observers_lock);
1370 GtkTreeSelection *selection;
1371 SetFolderHelper *info;
1372 ModestMailOperation *mail_op = NULL;
1374 /* Set folder in the model */
1375 modest_header_view_set_folder_intern (self, folder);
1377 /* Pick my reference. Nothing to do with the mail operation */
1378 priv->folder = g_object_ref (folder);
1380 /* Do not notify about filterings until the refresh finishes */
1381 priv->notify_status = FALSE;
1383 /* Clear the selection if exists */
1384 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1385 gtk_tree_selection_unselect_all(selection);
1386 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1388 /* Notify the observers that the update begins */
1389 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1392 /* create the helper */
1393 info = g_malloc0 (sizeof (SetFolderHelper));
1394 info->header_view = g_object_ref (self);
1395 info->cb = callback;
1396 info->user_data = user_data;
1398 /* Create the mail operation (source will be the parent widget) */
1399 if (progress_window)
1400 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1401 refresh_folder_error_handler,
1404 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1407 /* Refresh the folder asynchronously */
1408 modest_mail_operation_refresh_folder (mail_op,
1410 folder_refreshed_cb,
1413 folder_refreshed_cb (mail_op, folder, info);
1417 g_object_unref (mail_op);
1419 g_mutex_lock (priv->observers_lock);
1421 if (priv->monitor) {
1422 tny_folder_monitor_stop (priv->monitor);
1423 g_object_unref (G_OBJECT (priv->monitor));
1424 priv->monitor = NULL;
1427 if (priv->autoselect_reference) {
1428 gtk_tree_row_reference_free (priv->autoselect_reference);
1429 priv->autoselect_reference = NULL;
1432 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1434 modest_header_view_notify_observers(self, NULL, NULL);
1436 g_mutex_unlock (priv->observers_lock);
1438 /* Notify the observers that the update is over */
1439 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1445 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1446 GtkTreeViewColumn *column, gpointer userdata)
1448 ModestHeaderView *self = NULL;
1450 GtkTreeModel *model = NULL;
1451 TnyHeader *header = NULL;
1452 TnyHeaderFlags flags;
1454 self = MODEST_HEADER_VIEW (treeview);
1456 model = gtk_tree_view_get_model (treeview);
1457 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1460 /* get the first selected item */
1461 gtk_tree_model_get (model, &iter,
1462 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1463 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1466 /* Dont open DELETED messages */
1467 if (flags & TNY_HEADER_FLAG_DELETED) {
1470 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1471 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1472 modest_platform_information_banner (NULL, NULL, msg);
1478 g_signal_emit (G_OBJECT(self),
1479 signals[HEADER_ACTIVATED_SIGNAL],
1485 g_object_unref (G_OBJECT (header));
1490 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1492 GtkTreeModel *model;
1493 TnyHeader *header = NULL;
1494 GtkTreePath *path = NULL;
1496 ModestHeaderView *self;
1497 GList *selected = NULL;
1499 g_return_if_fail (sel);
1500 g_return_if_fail (user_data);
1502 self = MODEST_HEADER_VIEW (user_data);
1504 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1505 if (selected != NULL)
1506 path = (GtkTreePath *) selected->data;
1507 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1508 return; /* msg was _un_selected */
1510 gtk_tree_model_get (model, &iter,
1511 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1515 g_signal_emit (G_OBJECT(self),
1516 signals[HEADER_SELECTED_SIGNAL],
1519 g_object_unref (G_OBJECT (header));
1521 /* free all items in 'selected' */
1522 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1523 g_list_free (selected);
1527 /* PROTECTED method. It's useful when we want to force a given
1528 selection to reload a msg. For example if we have selected a header
1529 in offline mode, when Modest become online, we want to reload the
1530 message automatically without an user click over the header */
1532 _modest_header_view_change_selection (GtkTreeSelection *selection,
1535 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1536 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1538 on_selection_changed (selection, user_data);
1542 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1549 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1553 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1557 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1565 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1573 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1574 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1578 case TNY_HEADER_FLAG_ATTACHMENTS:
1580 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1581 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1582 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1583 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1585 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1586 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1588 return cmp ? cmp : t1 - t2;
1590 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1591 TnyHeader *header1 = NULL, *header2 = NULL;
1593 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1594 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1595 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1596 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1598 /* This is for making priority values respect the intuitive sort relationship
1599 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1601 if (header1 && header2) {
1602 cmp = compare_priorities (tny_header_get_priority (header1),
1603 tny_header_get_priority (header2));
1604 g_object_unref (header1);
1605 g_object_unref (header2);
1607 return cmp ? cmp : t1 - t2;
1613 return &iter1 - &iter2; /* oughhhh */
1618 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1624 /* static int counter = 0; */
1626 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1628 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1629 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1630 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1631 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1633 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1634 val2 + modest_text_utils_get_subject_prefix_len(val2),
1641 /* Drag and drop stuff */
1643 drag_data_get_cb (GtkWidget *widget,
1644 GdkDragContext *context,
1645 GtkSelectionData *selection_data,
1650 ModestHeaderView *self = NULL;
1651 ModestHeaderViewPrivate *priv = NULL;
1653 self = MODEST_HEADER_VIEW (widget);
1654 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1656 /* Set the data. Do not use the current selection because it
1657 could be different than the selection at the beginning of
1659 modest_dnd_selection_data_set_paths (selection_data,
1660 priv->drag_begin_cached_selected_rows);
1664 * We're caching the selected rows at the beginning because the
1665 * selection could change between drag-begin and drag-data-get, for
1666 * example if we have a set of rows already selected, and then we
1667 * click in one of them (without SHIFT key pressed) and begin a drag,
1668 * the selection at that moment contains all the selected lines, but
1669 * after dropping the selection, the release event provokes that only
1670 * the row used to begin the drag is selected, so at the end the
1671 * drag&drop affects only one rows instead of all the selected ones.
1675 drag_begin_cb (GtkWidget *widget,
1676 GdkDragContext *context,
1679 ModestHeaderView *self = NULL;
1680 ModestHeaderViewPrivate *priv = NULL;
1681 GtkTreeSelection *selection;
1683 self = MODEST_HEADER_VIEW (widget);
1684 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1686 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1687 priv->drag_begin_cached_selected_rows =
1688 gtk_tree_selection_get_selected_rows (selection, NULL);
1692 * We use the drag-end signal to clear the cached selection, we use
1693 * this because this allways happens, whether or not the d&d was a
1697 drag_end_cb (GtkWidget *widget,
1701 ModestHeaderView *self = NULL;
1702 ModestHeaderViewPrivate *priv = NULL;
1704 self = MODEST_HEADER_VIEW (widget);
1705 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1707 /* Free cached data */
1708 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1709 g_list_free (priv->drag_begin_cached_selected_rows);
1710 priv->drag_begin_cached_selected_rows = NULL;
1713 /* Header view drag types */
1714 const GtkTargetEntry header_view_drag_types[] = {
1715 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1719 enable_drag_and_drop (GtkWidget *self)
1721 #ifdef MODEST_TOOLKIT_HILDON2
1724 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1725 header_view_drag_types,
1726 G_N_ELEMENTS (header_view_drag_types),
1727 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1731 disable_drag_and_drop (GtkWidget *self)
1733 #ifdef MODEST_TOOLKIT_HILDON2
1736 gtk_drag_source_unset (self);
1740 setup_drag_and_drop (GtkWidget *self)
1742 #ifdef MODEST_TOOLKIT_HILDON2
1745 enable_drag_and_drop(self);
1746 g_signal_connect(G_OBJECT (self), "drag_data_get",
1747 G_CALLBACK(drag_data_get_cb), NULL);
1749 g_signal_connect(G_OBJECT (self), "drag_begin",
1750 G_CALLBACK(drag_begin_cb), NULL);
1752 g_signal_connect(G_OBJECT (self), "drag_end",
1753 G_CALLBACK(drag_end_cb), NULL);
1756 static GtkTreePath *
1757 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1759 GtkTreePath *path = NULL;
1760 GtkTreeSelection *sel = NULL;
1763 sel = gtk_tree_view_get_selection(self);
1764 rows = gtk_tree_selection_get_selected_rows (sel, model);
1766 if ((rows == NULL) || (g_list_length(rows) != 1))
1769 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1774 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1780 #ifndef MODEST_TOOLKIT_HILDON2
1782 * This function moves the tree view scroll to the current selected
1783 * row when the widget grabs the focus
1786 on_focus_in (GtkWidget *self,
1787 GdkEventFocus *event,
1790 GtkTreeSelection *selection;
1791 GtkTreeModel *model;
1792 GList *selected = NULL;
1793 GtkTreePath *selected_path = NULL;
1795 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1799 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1800 /* If none selected yet, pick the first one */
1801 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1805 /* Return if the model is empty */
1806 if (!gtk_tree_model_get_iter_first (model, &iter))
1809 path = gtk_tree_model_get_path (model, &iter);
1810 gtk_tree_selection_select_path (selection, path);
1811 gtk_tree_path_free (path);
1814 /* Need to get the all the rows because is selection multiple */
1815 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1816 if (selected == NULL) return FALSE;
1817 selected_path = (GtkTreePath *) selected->data;
1820 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1821 g_list_free (selected);
1827 on_focus_out (GtkWidget *self,
1828 GdkEventFocus *event,
1832 if (!gtk_widget_is_focus (self)) {
1833 GtkTreeSelection *selection = NULL;
1834 GList *selected_rows = NULL;
1835 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1836 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1837 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1838 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1839 gtk_tree_selection_unselect_all (selection);
1840 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1841 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1842 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1843 g_list_free (selected_rows);
1851 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1853 enable_drag_and_drop(self);
1858 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1860 GtkTreeSelection *selection = NULL;
1861 GtkTreePath *path = NULL;
1862 gboolean already_selected = FALSE, already_opened = FALSE;
1863 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1865 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1867 GtkTreeModel *model;
1869 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1870 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1872 /* Get header from model */
1873 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1874 if (gtk_tree_model_get_iter (model, &iter, path)) {
1875 GValue value = {0,};
1878 gtk_tree_model_get_value (model, &iter,
1879 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1881 header = (TnyHeader *) g_value_get_object (&value);
1882 if (TNY_IS_HEADER (header)) {
1883 status = modest_tny_all_send_queues_get_msg_status (header);
1884 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1887 g_value_unset (&value);
1891 /* Enable drag and drop only if the user clicks on a row that
1892 it's already selected. If not, let him select items using
1893 the pointer. If the message is in an OUTBOX and in sending
1894 status disable drag and drop as well */
1895 if (!already_selected ||
1896 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1898 disable_drag_and_drop(self);
1901 gtk_tree_path_free(path);
1903 /* If it's already opened then do not let the button-press
1904 event go on because it'll perform a message open because
1905 we're clicking on to an already selected header */
1910 folder_monitor_update (TnyFolderObserver *self,
1911 TnyFolderChange *change)
1913 ModestHeaderViewPrivate *priv = NULL;
1914 TnyFolderChangeChanged changed;
1915 TnyFolder *folder = NULL;
1917 changed = tny_folder_change_get_changed (change);
1919 /* Do not notify the observers if the folder of the header
1920 view has changed before this call to the observer
1922 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1923 folder = tny_folder_change_get_folder (change);
1924 if (folder != priv->folder)
1927 MODEST_DEBUG_BLOCK (
1928 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1929 g_print ("ADDED %d/%d (r/t) \n",
1930 tny_folder_change_get_new_unread_count (change),
1931 tny_folder_change_get_new_all_count (change));
1932 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1933 g_print ("ALL COUNT %d\n",
1934 tny_folder_change_get_new_all_count (change));
1935 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1936 g_print ("UNREAD COUNT %d\n",
1937 tny_folder_change_get_new_unread_count (change));
1938 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1939 g_print ("EXPUNGED %d/%d (r/t) \n",
1940 tny_folder_change_get_new_unread_count (change),
1941 tny_folder_change_get_new_all_count (change));
1942 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1943 g_print ("FOLDER RENAME\n");
1944 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1945 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1946 tny_folder_change_get_new_unread_count (change),
1947 tny_folder_change_get_new_all_count (change));
1948 g_print ("---------------------------------------------------\n");
1951 /* Check folder count */
1952 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1953 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1955 g_mutex_lock (priv->observers_lock);
1957 /* Emit signal to evaluate how headers changes affects
1958 to the window view */
1959 g_signal_emit (G_OBJECT(self),
1960 signals[MSG_COUNT_CHANGED_SIGNAL],
1963 /* Added or removed headers, so data stored on cliboard are invalid */
1964 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1965 modest_email_clipboard_clear (priv->clipboard);
1967 g_mutex_unlock (priv->observers_lock);
1973 g_object_unref (folder);
1977 modest_header_view_is_empty (ModestHeaderView *self)
1979 ModestHeaderViewPrivate *priv;
1981 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1983 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1985 return priv->status == HEADER_VIEW_EMPTY;
1989 modest_header_view_clear (ModestHeaderView *self)
1991 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1993 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
1997 modest_header_view_copy_selection (ModestHeaderView *header_view)
1999 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2001 /* Copy selection */
2002 _clipboard_set_selected_data (header_view, FALSE);
2006 modest_header_view_cut_selection (ModestHeaderView *header_view)
2008 ModestHeaderViewPrivate *priv = NULL;
2009 const gchar **hidding = NULL;
2010 guint i, n_selected;
2012 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2014 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2016 /* Copy selection */
2017 _clipboard_set_selected_data (header_view, TRUE);
2019 /* Get hidding ids */
2020 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2022 /* Clear hidding array created by previous cut operation */
2023 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2025 /* Copy hidding array */
2026 priv->n_selected = n_selected;
2027 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2028 for (i=0; i < n_selected; i++)
2029 priv->hidding_ids[i] = g_strdup(hidding[i]);
2031 /* Hide cut headers */
2032 modest_header_view_refilter (header_view);
2039 _clipboard_set_selected_data (ModestHeaderView *header_view,
2042 ModestHeaderViewPrivate *priv = NULL;
2043 TnyList *headers = NULL;
2045 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2046 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2048 /* Set selected data on clipboard */
2049 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2050 headers = modest_header_view_get_selected_headers (header_view);
2051 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2054 g_object_unref (headers);
2058 ModestHeaderView *self;
2063 notify_filter_change (gpointer data)
2065 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2067 g_signal_emit (info->self,
2068 signals[MSG_COUNT_CHANGED_SIGNAL],
2069 0, info->folder, NULL);
2075 notify_filter_change_destroy (gpointer data)
2077 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2078 ModestHeaderViewPrivate *priv;
2080 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2081 priv->status_timeout = 0;
2083 g_object_unref (info->self);
2084 g_object_unref (info->folder);
2085 g_slice_free (NotifyFilterInfo, info);
2089 filter_row (GtkTreeModel *model,
2093 ModestHeaderViewPrivate *priv = NULL;
2094 TnyHeaderFlags flags;
2095 TnyHeader *header = NULL;
2098 gboolean visible = TRUE;
2099 gboolean found = FALSE;
2100 GValue value = {0,};
2101 HeaderViewStatus old_status;
2103 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2104 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2106 /* Get header from model */
2107 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2108 flags = (TnyHeaderFlags) g_value_get_int (&value);
2109 g_value_unset (&value);
2110 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2111 header = (TnyHeader *) g_value_get_object (&value);
2112 g_value_unset (&value);
2114 /* Get message id from header (ensure is a valid id) */
2120 /* Hide deleted and mark as deleted heders */
2121 if (flags & TNY_HEADER_FLAG_DELETED ||
2122 flags & TNY_HEADER_FLAG_EXPUNGED) {
2127 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2128 if (priv->is_outbox &&
2129 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2135 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2136 if (priv->is_outbox &&
2137 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2143 /* If no data on clipboard, return always TRUE */
2144 if (modest_email_clipboard_cleared(priv->clipboard)) {
2150 if (priv->hidding_ids != NULL) {
2151 id = tny_header_dup_message_id (header);
2152 for (i=0; i < priv->n_selected && !found; i++)
2153 if (priv->hidding_ids[i] != NULL && id != NULL)
2154 found = (!strcmp (priv->hidding_ids[i], id));
2161 old_status = priv->status;
2162 priv->status = ((gboolean) priv->status) && !visible;
2163 if ((priv->notify_status) && (priv->status != old_status)) {
2164 if (priv->status_timeout)
2165 g_source_remove (priv->status_timeout);
2168 NotifyFilterInfo *info;
2170 info = g_slice_new0 (NotifyFilterInfo);
2171 info->self = g_object_ref (G_OBJECT (user_data));
2173 info->folder = tny_header_get_folder (header);
2174 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2175 notify_filter_change,
2177 notify_filter_change_destroy);
2185 _clear_hidding_filter (ModestHeaderView *header_view)
2187 ModestHeaderViewPrivate *priv = NULL;
2190 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2191 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2193 if (priv->hidding_ids != NULL) {
2194 for (i=0; i < priv->n_selected; i++)
2195 g_free (priv->hidding_ids[i]);
2196 g_free(priv->hidding_ids);
2201 modest_header_view_refilter (ModestHeaderView *header_view)
2203 GtkTreeModel *model = NULL;
2204 ModestHeaderViewPrivate *priv = NULL;
2206 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2207 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2209 /* Hide cut headers */
2210 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2211 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2212 priv->status = HEADER_VIEW_INIT;
2213 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2218 * Called when an account is removed. If I'm showing a folder of the
2219 * account that has been removed then clear the view
2222 on_account_removed (TnyAccountStore *self,
2223 TnyAccount *account,
2226 ModestHeaderViewPrivate *priv = NULL;
2228 /* Ignore changes in transport accounts */
2229 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2232 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2235 TnyAccount *my_account;
2237 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2238 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2239 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2240 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2242 my_account = tny_folder_get_account (priv->folder);
2246 if (my_account == account)
2247 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2248 g_object_unref (my_account);
2254 modest_header_view_add_observer(ModestHeaderView *header_view,
2255 ModestHeaderViewObserver *observer)
2257 ModestHeaderViewPrivate *priv;
2259 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2260 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2262 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2264 g_mutex_lock(priv->observer_list_lock);
2265 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2266 g_mutex_unlock(priv->observer_list_lock);
2270 modest_header_view_remove_observer(ModestHeaderView *header_view,
2271 ModestHeaderViewObserver *observer)
2273 ModestHeaderViewPrivate *priv;
2275 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2276 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2278 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2280 g_mutex_lock(priv->observer_list_lock);
2281 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2282 g_mutex_unlock(priv->observer_list_lock);
2286 modest_header_view_notify_observers(ModestHeaderView *header_view,
2287 GtkTreeModel *model,
2288 const gchar *tny_folder_id)
2290 ModestHeaderViewPrivate *priv = NULL;
2292 ModestHeaderViewObserver *observer;
2295 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2297 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2299 g_mutex_lock(priv->observer_list_lock);
2300 iter = priv->observer_list;
2301 while(iter != NULL){
2302 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2303 modest_header_view_observer_update(observer, model,
2305 iter = g_slist_next(iter);
2307 g_mutex_unlock(priv->observer_list_lock);
2311 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2313 ModestHeaderViewPrivate *priv = NULL;
2315 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2316 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2320 modest_header_view_set_filter (ModestHeaderView *self,
2321 ModestHeaderViewFilter filter)
2323 ModestHeaderViewPrivate *priv;
2324 GtkTreeModel *filter_model;
2326 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2327 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2329 priv->filter |= filter;
2331 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2332 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2333 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2338 modest_header_view_unset_filter (ModestHeaderView *self,
2339 ModestHeaderViewFilter filter)
2341 ModestHeaderViewPrivate *priv;
2342 GtkTreeModel *filter_model;
2344 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2345 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2347 priv->filter &= ~filter;
2349 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2350 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2351 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2356 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2358 if (strcmp ("style", spec->name) == 0) {
2359 update_style (MODEST_HEADER_VIEW (obj));
2360 gtk_widget_queue_draw (GTK_WIDGET (obj));
2365 update_style (ModestHeaderView *self)
2367 ModestHeaderViewPrivate *priv;
2368 GdkColor style_color;
2369 GdkColor style_active_color;
2370 PangoAttrList *attr_list;
2372 PangoAttribute *attr;
2373 GdkColor *new_color;
2375 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2376 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2380 attr_list = pango_attr_list_new ();
2381 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2382 gdk_color_parse ("grey", &style_color);
2384 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2385 pango_attr_list_insert (attr_list, attr);
2388 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2390 "SmallSystemFont", NULL,
2393 attr = pango_attr_font_desc_new (pango_font_description_copy
2394 (style->font_desc));
2395 pango_attr_list_insert (attr_list, attr);
2397 g_object_set (G_OBJECT (priv->renderer_address),
2398 "foreground-gdk", &style_color,
2399 "foreground-set", TRUE,
2400 "attributes", attr_list,
2402 g_object_set (G_OBJECT (priv->renderer_date_status),
2403 "foreground-gdk", &style_color,
2404 "foreground-set", TRUE,
2405 "attributes", attr_list,
2407 pango_attr_list_unref (attr_list);
2409 g_object_set (G_OBJECT (priv->renderer_address),
2410 "foreground-gdk", &style_color,
2411 "foreground-set", TRUE,
2412 "scale", PANGO_SCALE_SMALL,
2415 g_object_set (G_OBJECT (priv->renderer_date_status),
2416 "foreground-gdk", &style_color,
2417 "foreground-set", TRUE,
2418 "scale", PANGO_SCALE_SMALL,
2423 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2424 new_color = gdk_color_copy (&style_active_color);
2428 #ifdef MODEST_TOOLKIT_HILDON2
2429 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (new_color != NULL));
2430 g_object_set_data_full (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, new_color, (GDestroyNotify) gdk_color_free);
2435 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2440 GtkTreeModel *tree_model;
2445 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2452 g_debug ("located path: %s", gtk_tree_path_to_string (path));
2455 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2456 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2460 gtk_tree_model_get (tree_model, &iter,
2461 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,