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;
179 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
180 struct _HeadersCountChangedHelper {
181 ModestHeaderView *self;
182 TnyFolderChange *change;
186 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
187 MODEST_TYPE_HEADER_VIEW, \
188 ModestHeaderViewPrivate))
192 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
195 HEADER_SELECTED_SIGNAL,
196 HEADER_ACTIVATED_SIGNAL,
197 ITEM_NOT_FOUND_SIGNAL,
198 MSG_COUNT_CHANGED_SIGNAL,
199 UPDATING_MSG_LIST_SIGNAL,
204 static GObjectClass *parent_class = NULL;
206 /* uncomment the following if you have defined any signals */
207 static guint signals[LAST_SIGNAL] = {0};
210 modest_header_view_get_type (void)
212 static GType my_type = 0;
214 static const GTypeInfo my_info = {
215 sizeof(ModestHeaderViewClass),
216 NULL, /* base init */
217 NULL, /* base finalize */
218 (GClassInitFunc) modest_header_view_class_init,
219 NULL, /* class finalize */
220 NULL, /* class data */
221 sizeof(ModestHeaderView),
223 (GInstanceInitFunc) modest_header_view_init,
227 static const GInterfaceInfo tny_folder_observer_info =
229 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
230 NULL, /* interface_finalize */
231 NULL /* interface_data */
233 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
237 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
238 &tny_folder_observer_info);
246 modest_header_view_class_init (ModestHeaderViewClass *klass)
248 GObjectClass *gobject_class;
249 gobject_class = (GObjectClass*) klass;
251 parent_class = g_type_class_peek_parent (klass);
252 gobject_class->finalize = modest_header_view_finalize;
253 gobject_class->dispose = modest_header_view_dispose;
255 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
257 signals[HEADER_SELECTED_SIGNAL] =
258 g_signal_new ("header_selected",
259 G_TYPE_FROM_CLASS (gobject_class),
261 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
263 g_cclosure_marshal_VOID__POINTER,
264 G_TYPE_NONE, 1, G_TYPE_POINTER);
266 signals[HEADER_ACTIVATED_SIGNAL] =
267 g_signal_new ("header_activated",
268 G_TYPE_FROM_CLASS (gobject_class),
270 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
272 gtk_marshal_VOID__POINTER_POINTER,
273 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
276 signals[ITEM_NOT_FOUND_SIGNAL] =
277 g_signal_new ("item_not_found",
278 G_TYPE_FROM_CLASS (gobject_class),
280 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
282 g_cclosure_marshal_VOID__INT,
283 G_TYPE_NONE, 1, G_TYPE_INT);
285 signals[MSG_COUNT_CHANGED_SIGNAL] =
286 g_signal_new ("msg_count_changed",
287 G_TYPE_FROM_CLASS (gobject_class),
289 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
291 modest_marshal_VOID__POINTER_POINTER,
292 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
294 signals[UPDATING_MSG_LIST_SIGNAL] =
295 g_signal_new ("updating-msg-list",
296 G_TYPE_FROM_CLASS (gobject_class),
298 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
300 g_cclosure_marshal_VOID__BOOLEAN,
301 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
303 #ifdef MODEST_TOOLKIT_HILDON2
304 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
310 tny_folder_observer_init (TnyFolderObserverIface *klass)
312 klass->update = folder_monitor_update;
315 static GtkTreeViewColumn*
316 get_new_column (const gchar *name, GtkCellRenderer *renderer,
317 gboolean resizable, gint sort_col_id, gboolean show_as_text,
318 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
320 GtkTreeViewColumn *column;
322 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
323 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
325 gtk_tree_view_column_set_resizable (column, resizable);
327 gtk_tree_view_column_set_expand (column, TRUE);
330 gtk_tree_view_column_add_attribute (column, renderer, "text",
332 if (sort_col_id >= 0)
333 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
335 gtk_tree_view_column_set_sort_indicator (column, FALSE);
336 gtk_tree_view_column_set_reorderable (column, TRUE);
339 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
346 remove_all_columns (ModestHeaderView *obj)
348 GList *columns, *cursor;
350 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
352 for (cursor = columns; cursor; cursor = cursor->next)
353 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
354 GTK_TREE_VIEW_COLUMN(cursor->data));
355 g_list_free (columns);
359 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
361 GtkTreeModel *sortable;
362 GtkTreeViewColumn *column=NULL;
363 GtkTreeSelection *selection = NULL;
364 GtkCellRenderer *renderer_header,
365 *renderer_attach, *renderer_compact_date_or_status;
366 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
367 *renderer_subject_box, *renderer_recpt,
369 ModestHeaderViewPrivate *priv;
370 GtkTreeViewColumn *compact_column = NULL;
373 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
374 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
376 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
378 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
380 /* TODO: check whether these renderers need to be freed */
381 renderer_attach = gtk_cell_renderer_pixbuf_new ();
382 renderer_priority = gtk_cell_renderer_pixbuf_new ();
383 renderer_header = gtk_cell_renderer_text_new ();
385 renderer_compact_header = modest_vbox_cell_renderer_new ();
386 renderer_recpt_box = modest_hbox_cell_renderer_new ();
387 renderer_subject_box = modest_hbox_cell_renderer_new ();
388 renderer_recpt = gtk_cell_renderer_text_new ();
389 priv->renderer_address = renderer_recpt;
390 priv->renderer_subject = gtk_cell_renderer_text_new ();
391 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
392 priv->renderer_date_status = renderer_compact_date_or_status;
394 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
395 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
396 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
397 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
398 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
399 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
400 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), priv->renderer_subject, TRUE);
401 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", priv->renderer_subject);
402 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
403 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
404 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
405 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
406 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
407 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
409 #ifdef MODEST_TOOLKIT_HILDON2
410 g_object_set (G_OBJECT (renderer_compact_header), "xpad", 0, NULL);
412 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
413 #ifndef MODEST_TOOLKIT_GTK
414 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
415 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
417 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
418 g_object_set(G_OBJECT(renderer_header),
419 "ellipsize", PANGO_ELLIPSIZE_END,
421 g_object_set (G_OBJECT (priv->renderer_subject),
422 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
424 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (priv->renderer_subject), 1);
425 g_object_set (G_OBJECT (renderer_recpt),
426 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.1,
428 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
429 g_object_set(G_OBJECT(renderer_compact_date_or_status),
430 "xalign", 1.0, "yalign", 0.1,
432 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
433 #ifdef MODEST_TOOLKIT_HILDON2
434 g_object_set (G_OBJECT (renderer_priority),
436 "xalign", 0.0, NULL);
437 g_object_set (G_OBJECT (renderer_attach),
439 "xalign", 0.0, NULL);
441 g_object_set (G_OBJECT (renderer_priority),
442 "yalign", 0.5, NULL);
443 g_object_set (G_OBJECT (renderer_attach),
444 "yalign", 0.0, NULL);
447 #ifdef MODEST_TOOLKIT_HILDON1
448 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
449 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
450 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
451 #elif MODEST_TOOLKIT_HILDON2
452 gtk_cell_renderer_set_fixed_size (renderer_attach, 24 + MODEST_MARGIN_DEFAULT, 26);
453 gtk_cell_renderer_set_fixed_size (renderer_priority, 24 + MODEST_MARGIN_DEFAULT, 26);
454 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
456 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
457 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
458 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
461 remove_all_columns (self);
463 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
464 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
465 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
467 /* Add new columns */
468 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
469 ModestHeaderViewColumn col =
470 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
472 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
473 g_printerr ("modest: invalid column %d in column list\n", col);
479 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
480 column = get_new_column (_("A"), renderer_attach, FALSE,
481 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
483 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
485 gtk_tree_view_column_set_fixed_width (column, 45);
489 case MODEST_HEADER_VIEW_COLUMN_FROM:
490 column = get_new_column (_("From"), renderer_header, TRUE,
491 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
493 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
494 GINT_TO_POINTER(TRUE));
497 case MODEST_HEADER_VIEW_COLUMN_TO:
498 column = get_new_column (_("To"), renderer_header, TRUE,
499 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
501 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
502 GINT_TO_POINTER(FALSE));
505 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
506 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
507 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
509 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
510 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
511 compact_column = column;
514 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
515 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
516 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
518 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
519 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
520 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
521 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
522 compact_column = column;
526 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
527 column = get_new_column (_("Subject"), renderer_header, TRUE,
528 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
530 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
534 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
535 column = get_new_column (_("Received"), renderer_header, TRUE,
536 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
538 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
539 GINT_TO_POINTER(TRUE));
542 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
543 column = get_new_column (_("Sent"), renderer_header, TRUE,
544 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
546 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
547 GINT_TO_POINTER(FALSE));
550 case MODEST_HEADER_VIEW_COLUMN_SIZE:
551 column = get_new_column (_("Size"), renderer_header, TRUE,
552 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
554 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
557 case MODEST_HEADER_VIEW_COLUMN_STATUS:
558 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
559 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
561 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
566 g_return_val_if_reached(FALSE);
569 /* we keep the column id around */
570 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
571 GINT_TO_POINTER(col));
573 /* we need this ptr when sorting the rows */
574 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
576 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
580 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
581 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
582 (GtkTreeIterCompareFunc) cmp_rows,
583 compact_column, NULL);
584 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
585 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
586 (GtkTreeIterCompareFunc) cmp_subject_rows,
587 compact_column, NULL);
591 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
597 datetime_format_changed (ModestDatetimeFormatter *formatter,
598 ModestHeaderView *self)
600 gtk_widget_queue_draw (GTK_WIDGET (self));
604 modest_header_view_init (ModestHeaderView *obj)
606 ModestHeaderViewPrivate *priv;
609 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
612 priv->is_outbox = FALSE;
614 priv->monitor = NULL;
615 priv->observers_lock = g_mutex_new ();
616 priv->autoselect_reference = NULL;
618 priv->status = HEADER_VIEW_INIT;
619 priv->status_timeout = 0;
620 priv->notify_status = TRUE;
622 priv->observer_list_lock = g_mutex_new();
623 priv->observer_list = NULL;
625 priv->clipboard = modest_runtime_get_email_clipboard ();
626 priv->hidding_ids = NULL;
627 priv->n_selected = 0;
628 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
629 priv->selection_changed_handler = 0;
630 priv->acc_removed_handler = 0;
632 /* Sort parameters */
633 for (j=0; j < 2; j++) {
634 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
635 priv->sort_colid[j][i] = -1;
636 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
640 priv->datetime_formatter = modest_datetime_formatter_new ();
641 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
642 G_CALLBACK (datetime_format_changed), (gpointer) obj);
644 setup_drag_and_drop (GTK_WIDGET(obj));
648 modest_header_view_dispose (GObject *obj)
650 ModestHeaderView *self;
651 ModestHeaderViewPrivate *priv;
652 GtkTreeSelection *sel;
654 self = MODEST_HEADER_VIEW(obj);
655 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
657 if (priv->datetime_formatter) {
658 g_object_unref (priv->datetime_formatter);
659 priv->datetime_formatter = NULL;
662 /* Free in the dispose to avoid unref cycles */
664 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
665 g_object_unref (G_OBJECT (priv->folder));
669 /* We need to do this here in the dispose because the
670 selection won't exist when finalizing */
671 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
672 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
673 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
674 priv->selection_changed_handler = 0;
677 G_OBJECT_CLASS(parent_class)->dispose (obj);
681 modest_header_view_finalize (GObject *obj)
683 ModestHeaderView *self;
684 ModestHeaderViewPrivate *priv;
686 self = MODEST_HEADER_VIEW(obj);
687 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
689 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
690 priv->acc_removed_handler)) {
691 g_signal_handler_disconnect (modest_runtime_get_account_store (),
692 priv->acc_removed_handler);
695 /* There is no need to lock because there should not be any
696 * reference to self now. */
697 g_mutex_free(priv->observer_list_lock);
698 g_slist_free(priv->observer_list);
700 g_mutex_lock (priv->observers_lock);
702 tny_folder_monitor_stop (priv->monitor);
703 g_object_unref (G_OBJECT (priv->monitor));
705 g_mutex_unlock (priv->observers_lock);
706 g_mutex_free (priv->observers_lock);
708 /* Clear hidding array created by cut operation */
709 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
711 if (priv->autoselect_reference != NULL) {
712 gtk_tree_row_reference_free (priv->autoselect_reference);
713 priv->autoselect_reference = NULL;
716 G_OBJECT_CLASS(parent_class)->finalize (obj);
721 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
724 GtkTreeSelection *sel;
725 ModestHeaderView *self;
726 ModestHeaderViewPrivate *priv;
728 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
731 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
732 self = MODEST_HEADER_VIEW(obj);
733 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
735 modest_header_view_set_style (self, style);
737 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
738 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
739 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
741 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
742 TRUE); /* alternating row colors */
744 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
745 priv->selection_changed_handler =
746 g_signal_connect_after (sel, "changed",
747 G_CALLBACK(on_selection_changed), self);
749 g_signal_connect (self, "row-activated",
750 G_CALLBACK (on_header_row_activated), NULL);
752 #ifndef MODEST_TOOLKIT_HILDON2
753 g_signal_connect (self, "focus-in-event",
754 G_CALLBACK(on_focus_in), NULL);
755 g_signal_connect (self, "focus-out-event",
756 G_CALLBACK(on_focus_out), NULL);
759 g_signal_connect (self, "button-press-event",
760 G_CALLBACK(on_button_press_event), NULL);
761 g_signal_connect (self, "button-release-event",
762 G_CALLBACK(on_button_release_event), NULL);
764 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
766 G_CALLBACK (on_account_removed),
769 g_signal_connect (self, "expose-event",
770 G_CALLBACK(modest_header_view_on_expose_event),
773 return GTK_WIDGET(self);
778 modest_header_view_count_selected_headers (ModestHeaderView *self)
780 GtkTreeSelection *sel;
783 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
785 /* Get selection object and check selected rows count */
786 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
787 selected_rows = gtk_tree_selection_count_selected_rows (sel);
789 return selected_rows;
793 modest_header_view_has_selected_headers (ModestHeaderView *self)
795 GtkTreeSelection *sel;
798 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
800 /* Get selection object and check selected rows count */
801 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
802 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
809 modest_header_view_get_selected_headers (ModestHeaderView *self)
811 GtkTreeSelection *sel;
812 TnyList *header_list = NULL;
814 GList *list, *tmp = NULL;
815 GtkTreeModel *tree_model = NULL;
818 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
821 /* Get selected rows */
822 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
823 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
826 header_list = tny_simple_list_new();
828 list = g_list_reverse (list);
831 /* get header from selection */
832 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
833 gtk_tree_model_get (tree_model, &iter,
834 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
836 /* Prepend to list */
837 tny_list_prepend (header_list, G_OBJECT (header));
838 g_object_unref (G_OBJECT (header));
840 tmp = g_list_next (tmp);
843 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
850 /* scroll our list view so the selected item is visible */
852 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
854 #ifdef MODEST_TOOLKIT_GTK
856 GtkTreePath *selected_path;
857 GtkTreePath *start, *end;
861 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
862 selected_path = gtk_tree_model_get_path (model, iter);
864 start = gtk_tree_path_new ();
865 end = gtk_tree_path_new ();
867 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
869 if (gtk_tree_path_compare (selected_path, start) < 0 ||
870 gtk_tree_path_compare (end, selected_path) < 0)
871 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
872 selected_path, NULL, TRUE,
875 gtk_tree_path_free (selected_path);
876 gtk_tree_path_free (start);
877 gtk_tree_path_free (end);
879 #endif /* MODEST_TOOLKIT_GTK */
884 modest_header_view_select_next (ModestHeaderView *self)
886 GtkTreeSelection *sel;
891 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
893 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
894 path = get_selected_row (GTK_TREE_VIEW(self), &model);
895 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
896 /* Unselect previous path */
897 gtk_tree_selection_unselect_path (sel, path);
899 /* Move path down and selects new one */
900 if (gtk_tree_model_iter_next (model, &iter)) {
901 gtk_tree_selection_select_iter (sel, &iter);
902 scroll_to_selected (self, &iter, FALSE);
904 gtk_tree_path_free(path);
910 modest_header_view_select_prev (ModestHeaderView *self)
912 GtkTreeSelection *sel;
917 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
919 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
920 path = get_selected_row (GTK_TREE_VIEW(self), &model);
921 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
922 /* Unselect previous path */
923 gtk_tree_selection_unselect_path (sel, path);
926 if (gtk_tree_path_prev (path)) {
927 gtk_tree_model_get_iter (model, &iter, path);
929 /* Select the new one */
930 gtk_tree_selection_select_iter (sel, &iter);
931 scroll_to_selected (self, &iter, TRUE);
934 gtk_tree_path_free (path);
939 modest_header_view_get_columns (ModestHeaderView *self)
941 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
943 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
949 modest_header_view_set_style (ModestHeaderView *self,
950 ModestHeaderViewStyle style)
952 ModestHeaderViewPrivate *priv;
953 gboolean show_col_headers = FALSE;
954 ModestHeaderViewStyle old_style;
956 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
957 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
960 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
961 if (priv->style == style)
962 return TRUE; /* nothing to do */
965 case MODEST_HEADER_VIEW_STYLE_DETAILS:
966 show_col_headers = TRUE;
968 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
971 g_return_val_if_reached (FALSE);
973 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
974 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
976 old_style = priv->style;
983 ModestHeaderViewStyle
984 modest_header_view_get_style (ModestHeaderView *self)
986 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
988 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
991 /* This is used to automatically select the first header if the user
992 * has not selected any header yet.
995 modest_header_view_on_expose_event(GtkTreeView *header_view,
996 GdkEventExpose *event,
999 GtkTreeSelection *sel;
1000 GtkTreeModel *model;
1001 GtkTreeIter tree_iter;
1002 ModestHeaderViewPrivate *priv;
1004 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1005 model = gtk_tree_view_get_model(header_view);
1010 #ifdef MODEST_TOOLKIT_HILDON2
1013 sel = gtk_tree_view_get_selection(header_view);
1014 if(!gtk_tree_selection_count_selected_rows(sel)) {
1015 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
1016 GtkTreePath *tree_iter_path;
1017 /* Prevent the widget from getting the focus
1018 when selecting the first item */
1019 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1020 g_object_set(header_view, "can-focus", FALSE, NULL);
1021 gtk_tree_selection_select_iter(sel, &tree_iter);
1022 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1023 g_object_set(header_view, "can-focus", TRUE, NULL);
1024 if (priv->autoselect_reference) {
1025 gtk_tree_row_reference_free (priv->autoselect_reference);
1027 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1028 gtk_tree_path_free (tree_iter_path);
1031 if (priv->autoselect_reference != NULL) {
1032 gboolean moved_selection = FALSE;
1033 GtkTreePath * last_path;
1034 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1035 moved_selection = TRUE;
1039 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1040 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1041 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1042 moved_selection = TRUE;
1043 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1045 gtk_tree_path_free (last_path);
1047 if (moved_selection) {
1048 gtk_tree_row_reference_free (priv->autoselect_reference);
1049 priv->autoselect_reference = NULL;
1052 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1053 GtkTreePath *current_path;
1054 current_path = gtk_tree_model_get_path (model, &tree_iter);
1055 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1056 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1057 g_object_set(header_view, "can-focus", FALSE, NULL);
1058 gtk_tree_selection_unselect_all (sel);
1059 gtk_tree_selection_select_iter(sel, &tree_iter);
1060 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1061 g_object_set(header_view, "can-focus", TRUE, NULL);
1062 gtk_tree_row_reference_free (priv->autoselect_reference);
1063 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1065 gtk_tree_path_free (current_path);
1066 gtk_tree_path_free (last_path);
1076 modest_header_view_get_folder (ModestHeaderView *self)
1078 ModestHeaderViewPrivate *priv;
1080 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1082 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1085 g_object_ref (priv->folder);
1087 return priv->folder;
1091 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1097 ModestHeaderView *self;
1098 ModestHeaderViewPrivate *priv;
1100 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1102 self = MODEST_HEADER_VIEW (user_data);
1103 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1105 if (cancelled || err)
1108 /* Add IDLE observer (monitor) and another folder observer for
1109 new messages (self) */
1110 g_mutex_lock (priv->observers_lock);
1111 if (priv->monitor) {
1112 tny_folder_monitor_stop (priv->monitor);
1113 g_object_unref (G_OBJECT (priv->monitor));
1115 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1116 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1117 tny_folder_monitor_start (priv->monitor);
1118 g_mutex_unlock (priv->observers_lock);
1122 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1126 ModestHeaderViewPrivate *priv;
1127 GList *cols, *cursor;
1128 GtkTreeModel *filter_model, *sortable;
1130 GtkSortType sort_type;
1132 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1134 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1136 /* Start the monitor in the callback of the
1137 tny_gtk_header_list_model_set_folder call. It's crucial to
1138 do it there and not just after the call because we want the
1139 monitor to observe only the headers returned by the
1140 tny_folder_get_headers_async call that it's inside the
1141 tny_gtk_header_list_model_set_folder call. This way the
1142 monitor infrastructure could successfully cope with
1143 duplicates. For example if a tny_folder_add_msg_async is
1144 happening while tny_gtk_header_list_model_set_folder is
1145 invoked, then the first call could add a header that will
1146 be added again by tny_gtk_header_list_model_set_folder, so
1147 we'd end up with duplicate headers. sergio */
1148 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1150 set_folder_intern_get_headers_async_cb,
1153 /* Create a tree model filter to hide and show rows for cut operations */
1154 filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
1155 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1156 filter_row, self, NULL);
1157 g_object_unref (headers);
1159 /* Init filter_row function to examine empty status */
1160 priv->status = HEADER_VIEW_INIT;
1162 /* Create sortable model */
1163 sortable = gtk_tree_model_sort_new_with_model (filter_model);
1164 g_object_unref (filter_model);
1166 /* install our special sorting functions */
1167 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1169 /* Restore sort column id */
1171 type = modest_tny_folder_guess_folder_type (folder);
1172 if (type == TNY_FOLDER_TYPE_INVALID)
1173 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1175 sort_colid = modest_header_view_get_sort_column_id (self, type);
1176 sort_type = modest_header_view_get_sort_type (self, type);
1177 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1180 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1181 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1182 (GtkTreeIterCompareFunc) cmp_rows,
1184 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
1185 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1186 (GtkTreeIterCompareFunc) cmp_subject_rows,
1191 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
1192 modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
1193 g_object_unref (sortable);
1200 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1202 GtkSortType sort_type)
1204 ModestHeaderViewPrivate *priv = NULL;
1205 GtkTreeModel *sortable = NULL;
1208 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1209 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1211 /* Get model and private data */
1212 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
1213 sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1215 /* Sort tree model */
1216 type = modest_tny_folder_guess_folder_type (priv->folder);
1217 if (type == TNY_FOLDER_TYPE_INVALID)
1218 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1220 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sortable),
1223 /* Store new sort parameters */
1224 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1229 modest_header_view_set_sort_params (ModestHeaderView *self,
1231 GtkSortType sort_type,
1234 ModestHeaderViewPrivate *priv;
1235 ModestHeaderViewStyle style;
1237 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1238 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1239 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1241 style = modest_header_view_get_style (self);
1242 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1244 priv->sort_colid[style][type] = sort_colid;
1245 priv->sort_type[style][type] = sort_type;
1249 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1252 ModestHeaderViewPrivate *priv;
1253 ModestHeaderViewStyle style;
1255 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1256 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1258 style = modest_header_view_get_style (self);
1259 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1261 return priv->sort_colid[style][type];
1265 modest_header_view_get_sort_type (ModestHeaderView *self,
1268 ModestHeaderViewPrivate *priv;
1269 ModestHeaderViewStyle style;
1271 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1272 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1274 style = modest_header_view_get_style (self);
1275 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1277 return priv->sort_type[style][type];
1281 ModestHeaderView *header_view;
1282 RefreshAsyncUserCallback cb;
1287 folder_refreshed_cb (ModestMailOperation *mail_op,
1291 ModestHeaderViewPrivate *priv;
1292 SetFolderHelper *info;
1294 info = (SetFolderHelper*) user_data;
1296 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1300 info->cb (mail_op, folder, info->user_data);
1302 /* Start the folder count changes observer. We do not need it
1303 before the refresh. Note that the monitor could still be
1304 called for this refresh but now we know that the callback
1305 was previously called */
1306 g_mutex_lock (priv->observers_lock);
1307 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1308 g_mutex_unlock (priv->observers_lock);
1310 /* Notify the observers that the update is over */
1311 g_signal_emit (G_OBJECT (info->header_view),
1312 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1314 /* Allow filtering notifications from now on if the current
1315 folder is still the same (if not then the user has selected
1316 another one to refresh, we should wait until that refresh
1318 if (priv->folder == folder)
1319 priv->notify_status = TRUE;
1322 g_object_unref (info->header_view);
1327 refresh_folder_error_handler (ModestMailOperation *mail_op,
1330 const GError *error = modest_mail_operation_get_error (mail_op);
1332 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1333 error->code == TNY_IO_ERROR_WRITE ||
1334 error->code == TNY_IO_ERROR_READ) {
1335 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1336 /* If the mail op has been cancelled then it's not an error: don't show any message */
1337 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1338 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
1339 modest_platform_information_banner (NULL, NULL, msg);
1346 modest_header_view_set_folder (ModestHeaderView *self,
1349 ModestWindow *progress_window,
1350 RefreshAsyncUserCallback callback,
1353 ModestHeaderViewPrivate *priv;
1355 g_return_if_fail (self);
1357 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1360 if (priv->status_timeout) {
1361 g_source_remove (priv->status_timeout);
1362 priv->status_timeout = 0;
1365 g_mutex_lock (priv->observers_lock);
1366 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1367 g_object_unref (priv->folder);
1368 priv->folder = NULL;
1369 g_mutex_unlock (priv->observers_lock);
1373 GtkTreeSelection *selection;
1374 SetFolderHelper *info;
1375 ModestMailOperation *mail_op = NULL;
1377 /* Set folder in the model */
1378 modest_header_view_set_folder_intern (self, folder);
1380 /* Pick my reference. Nothing to do with the mail operation */
1381 priv->folder = g_object_ref (folder);
1383 /* Do not notify about filterings until the refresh finishes */
1384 priv->notify_status = FALSE;
1386 /* Clear the selection if exists */
1387 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1388 gtk_tree_selection_unselect_all(selection);
1389 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1391 /* Notify the observers that the update begins */
1392 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1395 /* create the helper */
1396 info = g_malloc0 (sizeof (SetFolderHelper));
1397 info->header_view = g_object_ref (self);
1398 info->cb = callback;
1399 info->user_data = user_data;
1401 /* Create the mail operation (source will be the parent widget) */
1402 if (progress_window)
1403 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1404 refresh_folder_error_handler,
1407 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1410 /* Refresh the folder asynchronously */
1411 modest_mail_operation_refresh_folder (mail_op,
1413 folder_refreshed_cb,
1416 folder_refreshed_cb (mail_op, folder, info);
1420 g_object_unref (mail_op);
1422 g_mutex_lock (priv->observers_lock);
1424 if (priv->monitor) {
1425 tny_folder_monitor_stop (priv->monitor);
1426 g_object_unref (G_OBJECT (priv->monitor));
1427 priv->monitor = NULL;
1430 if (priv->autoselect_reference) {
1431 gtk_tree_row_reference_free (priv->autoselect_reference);
1432 priv->autoselect_reference = NULL;
1435 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1437 modest_header_view_notify_observers(self, NULL, NULL);
1439 g_mutex_unlock (priv->observers_lock);
1441 /* Notify the observers that the update is over */
1442 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1448 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1449 GtkTreeViewColumn *column, gpointer userdata)
1451 ModestHeaderView *self = NULL;
1453 GtkTreeModel *model = NULL;
1454 TnyHeader *header = NULL;
1455 TnyHeaderFlags flags;
1457 self = MODEST_HEADER_VIEW (treeview);
1459 model = gtk_tree_view_get_model (treeview);
1460 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1463 /* get the first selected item */
1464 gtk_tree_model_get (model, &iter,
1465 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1466 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1469 /* Dont open DELETED messages */
1470 if (flags & TNY_HEADER_FLAG_DELETED) {
1473 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1474 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1475 modest_platform_information_banner (NULL, NULL, msg);
1481 g_signal_emit (G_OBJECT(self),
1482 signals[HEADER_ACTIVATED_SIGNAL],
1488 g_object_unref (G_OBJECT (header));
1493 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1495 GtkTreeModel *model;
1496 TnyHeader *header = NULL;
1497 GtkTreePath *path = NULL;
1499 ModestHeaderView *self;
1500 GList *selected = NULL;
1502 g_return_if_fail (sel);
1503 g_return_if_fail (user_data);
1505 self = MODEST_HEADER_VIEW (user_data);
1507 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1508 if (selected != NULL)
1509 path = (GtkTreePath *) selected->data;
1510 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1511 return; /* msg was _un_selected */
1513 gtk_tree_model_get (model, &iter,
1514 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1518 g_signal_emit (G_OBJECT(self),
1519 signals[HEADER_SELECTED_SIGNAL],
1522 g_object_unref (G_OBJECT (header));
1524 /* free all items in 'selected' */
1525 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1526 g_list_free (selected);
1530 /* PROTECTED method. It's useful when we want to force a given
1531 selection to reload a msg. For example if we have selected a header
1532 in offline mode, when Modest become online, we want to reload the
1533 message automatically without an user click over the header */
1535 _modest_header_view_change_selection (GtkTreeSelection *selection,
1538 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1539 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1541 on_selection_changed (selection, user_data);
1545 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1552 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1556 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1560 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1568 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1576 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1577 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1581 case TNY_HEADER_FLAG_ATTACHMENTS:
1583 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1584 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1585 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1586 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1588 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1589 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1591 return cmp ? cmp : t1 - t2;
1593 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1594 TnyHeader *header1 = NULL, *header2 = NULL;
1596 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1597 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1598 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1599 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1601 /* This is for making priority values respect the intuitive sort relationship
1602 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1604 if (header1 && header2) {
1605 cmp = compare_priorities (tny_header_get_priority (header1),
1606 tny_header_get_priority (header2));
1607 g_object_unref (header1);
1608 g_object_unref (header2);
1610 return cmp ? cmp : t1 - t2;
1616 return &iter1 - &iter2; /* oughhhh */
1621 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1627 /* static int counter = 0; */
1629 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1631 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1632 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1633 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1634 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1636 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1637 val2 + modest_text_utils_get_subject_prefix_len(val2),
1644 /* Drag and drop stuff */
1646 drag_data_get_cb (GtkWidget *widget,
1647 GdkDragContext *context,
1648 GtkSelectionData *selection_data,
1653 ModestHeaderView *self = NULL;
1654 ModestHeaderViewPrivate *priv = NULL;
1656 self = MODEST_HEADER_VIEW (widget);
1657 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1659 /* Set the data. Do not use the current selection because it
1660 could be different than the selection at the beginning of
1662 modest_dnd_selection_data_set_paths (selection_data,
1663 priv->drag_begin_cached_selected_rows);
1667 * We're caching the selected rows at the beginning because the
1668 * selection could change between drag-begin and drag-data-get, for
1669 * example if we have a set of rows already selected, and then we
1670 * click in one of them (without SHIFT key pressed) and begin a drag,
1671 * the selection at that moment contains all the selected lines, but
1672 * after dropping the selection, the release event provokes that only
1673 * the row used to begin the drag is selected, so at the end the
1674 * drag&drop affects only one rows instead of all the selected ones.
1678 drag_begin_cb (GtkWidget *widget,
1679 GdkDragContext *context,
1682 ModestHeaderView *self = NULL;
1683 ModestHeaderViewPrivate *priv = NULL;
1684 GtkTreeSelection *selection;
1686 self = MODEST_HEADER_VIEW (widget);
1687 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1689 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1690 priv->drag_begin_cached_selected_rows =
1691 gtk_tree_selection_get_selected_rows (selection, NULL);
1695 * We use the drag-end signal to clear the cached selection, we use
1696 * this because this allways happens, whether or not the d&d was a
1700 drag_end_cb (GtkWidget *widget,
1704 ModestHeaderView *self = NULL;
1705 ModestHeaderViewPrivate *priv = NULL;
1707 self = MODEST_HEADER_VIEW (widget);
1708 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1710 /* Free cached data */
1711 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1712 g_list_free (priv->drag_begin_cached_selected_rows);
1713 priv->drag_begin_cached_selected_rows = NULL;
1716 /* Header view drag types */
1717 const GtkTargetEntry header_view_drag_types[] = {
1718 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1722 enable_drag_and_drop (GtkWidget *self)
1724 #ifdef MODEST_TOOLKIT_HILDON2
1727 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1728 header_view_drag_types,
1729 G_N_ELEMENTS (header_view_drag_types),
1730 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1734 disable_drag_and_drop (GtkWidget *self)
1736 #ifdef MODEST_TOOLKIT_HILDON2
1739 gtk_drag_source_unset (self);
1743 setup_drag_and_drop (GtkWidget *self)
1745 #ifdef MODEST_TOOLKIT_HILDON2
1748 enable_drag_and_drop(self);
1749 g_signal_connect(G_OBJECT (self), "drag_data_get",
1750 G_CALLBACK(drag_data_get_cb), NULL);
1752 g_signal_connect(G_OBJECT (self), "drag_begin",
1753 G_CALLBACK(drag_begin_cb), NULL);
1755 g_signal_connect(G_OBJECT (self), "drag_end",
1756 G_CALLBACK(drag_end_cb), NULL);
1759 static GtkTreePath *
1760 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1762 GtkTreePath *path = NULL;
1763 GtkTreeSelection *sel = NULL;
1766 sel = gtk_tree_view_get_selection(self);
1767 rows = gtk_tree_selection_get_selected_rows (sel, model);
1769 if ((rows == NULL) || (g_list_length(rows) != 1))
1772 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1777 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1783 #ifndef MODEST_TOOLKIT_HILDON2
1785 * This function moves the tree view scroll to the current selected
1786 * row when the widget grabs the focus
1789 on_focus_in (GtkWidget *self,
1790 GdkEventFocus *event,
1793 GtkTreeSelection *selection;
1794 GtkTreeModel *model;
1795 GList *selected = NULL;
1796 GtkTreePath *selected_path = NULL;
1798 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1802 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1803 /* If none selected yet, pick the first one */
1804 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1808 /* Return if the model is empty */
1809 if (!gtk_tree_model_get_iter_first (model, &iter))
1812 path = gtk_tree_model_get_path (model, &iter);
1813 gtk_tree_selection_select_path (selection, path);
1814 gtk_tree_path_free (path);
1817 /* Need to get the all the rows because is selection multiple */
1818 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1819 if (selected == NULL) return FALSE;
1820 selected_path = (GtkTreePath *) selected->data;
1823 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1824 g_list_free (selected);
1830 on_focus_out (GtkWidget *self,
1831 GdkEventFocus *event,
1835 if (!gtk_widget_is_focus (self)) {
1836 GtkTreeSelection *selection = NULL;
1837 GList *selected_rows = NULL;
1838 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1839 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1840 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1841 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1842 gtk_tree_selection_unselect_all (selection);
1843 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1844 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1845 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1846 g_list_free (selected_rows);
1854 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1856 enable_drag_and_drop(self);
1861 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1863 GtkTreeSelection *selection = NULL;
1864 GtkTreePath *path = NULL;
1865 gboolean already_selected = FALSE, already_opened = FALSE;
1866 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1868 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1870 GtkTreeModel *model;
1872 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1873 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1875 /* Get header from model */
1876 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1877 if (gtk_tree_model_get_iter (model, &iter, path)) {
1878 GValue value = {0,};
1881 gtk_tree_model_get_value (model, &iter,
1882 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1884 header = (TnyHeader *) g_value_get_object (&value);
1885 if (TNY_IS_HEADER (header)) {
1886 status = modest_tny_all_send_queues_get_msg_status (header);
1887 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1890 g_value_unset (&value);
1894 /* Enable drag and drop only if the user clicks on a row that
1895 it's already selected. If not, let him select items using
1896 the pointer. If the message is in an OUTBOX and in sending
1897 status disable drag and drop as well */
1898 if (!already_selected ||
1899 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1901 disable_drag_and_drop(self);
1904 gtk_tree_path_free(path);
1906 /* If it's already opened then do not let the button-press
1907 event go on because it'll perform a message open because
1908 we're clicking on to an already selected header */
1913 folder_monitor_update (TnyFolderObserver *self,
1914 TnyFolderChange *change)
1916 ModestHeaderViewPrivate *priv = NULL;
1917 TnyFolderChangeChanged changed;
1918 TnyFolder *folder = NULL;
1920 changed = tny_folder_change_get_changed (change);
1922 /* Do not notify the observers if the folder of the header
1923 view has changed before this call to the observer
1925 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1926 folder = tny_folder_change_get_folder (change);
1927 if (folder != priv->folder)
1930 MODEST_DEBUG_BLOCK (
1931 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1932 g_print ("ADDED %d/%d (r/t) \n",
1933 tny_folder_change_get_new_unread_count (change),
1934 tny_folder_change_get_new_all_count (change));
1935 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1936 g_print ("ALL COUNT %d\n",
1937 tny_folder_change_get_new_all_count (change));
1938 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1939 g_print ("UNREAD COUNT %d\n",
1940 tny_folder_change_get_new_unread_count (change));
1941 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1942 g_print ("EXPUNGED %d/%d (r/t) \n",
1943 tny_folder_change_get_new_unread_count (change),
1944 tny_folder_change_get_new_all_count (change));
1945 if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
1946 g_print ("FOLDER RENAME\n");
1947 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1948 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1949 tny_folder_change_get_new_unread_count (change),
1950 tny_folder_change_get_new_all_count (change));
1951 g_print ("---------------------------------------------------\n");
1954 /* Check folder count */
1955 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1956 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1958 g_mutex_lock (priv->observers_lock);
1960 /* Emit signal to evaluate how headers changes affects
1961 to the window view */
1962 g_signal_emit (G_OBJECT(self),
1963 signals[MSG_COUNT_CHANGED_SIGNAL],
1966 /* Added or removed headers, so data stored on cliboard are invalid */
1967 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1968 modest_email_clipboard_clear (priv->clipboard);
1970 g_mutex_unlock (priv->observers_lock);
1976 g_object_unref (folder);
1980 modest_header_view_is_empty (ModestHeaderView *self)
1982 ModestHeaderViewPrivate *priv;
1984 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1986 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1988 return priv->status == HEADER_VIEW_EMPTY;
1992 modest_header_view_clear (ModestHeaderView *self)
1994 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1996 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
2000 modest_header_view_copy_selection (ModestHeaderView *header_view)
2002 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2004 /* Copy selection */
2005 _clipboard_set_selected_data (header_view, FALSE);
2009 modest_header_view_cut_selection (ModestHeaderView *header_view)
2011 ModestHeaderViewPrivate *priv = NULL;
2012 const gchar **hidding = NULL;
2013 guint i, n_selected;
2015 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2017 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2019 /* Copy selection */
2020 _clipboard_set_selected_data (header_view, TRUE);
2022 /* Get hidding ids */
2023 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2025 /* Clear hidding array created by previous cut operation */
2026 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2028 /* Copy hidding array */
2029 priv->n_selected = n_selected;
2030 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2031 for (i=0; i < n_selected; i++)
2032 priv->hidding_ids[i] = g_strdup(hidding[i]);
2034 /* Hide cut headers */
2035 modest_header_view_refilter (header_view);
2042 _clipboard_set_selected_data (ModestHeaderView *header_view,
2045 ModestHeaderViewPrivate *priv = NULL;
2046 TnyList *headers = NULL;
2048 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2049 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2051 /* Set selected data on clipboard */
2052 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2053 headers = modest_header_view_get_selected_headers (header_view);
2054 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2057 g_object_unref (headers);
2061 ModestHeaderView *self;
2066 notify_filter_change (gpointer data)
2068 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2070 g_signal_emit (info->self,
2071 signals[MSG_COUNT_CHANGED_SIGNAL],
2072 0, info->folder, NULL);
2078 notify_filter_change_destroy (gpointer data)
2080 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2081 ModestHeaderViewPrivate *priv;
2083 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2084 priv->status_timeout = 0;
2086 g_object_unref (info->self);
2087 g_object_unref (info->folder);
2088 g_slice_free (NotifyFilterInfo, info);
2092 filter_row (GtkTreeModel *model,
2096 ModestHeaderViewPrivate *priv = NULL;
2097 TnyHeaderFlags flags;
2098 TnyHeader *header = NULL;
2101 gboolean visible = TRUE;
2102 gboolean found = FALSE;
2103 GValue value = {0,};
2104 HeaderViewStatus old_status;
2106 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2107 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2109 /* Get header from model */
2110 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2111 flags = (TnyHeaderFlags) g_value_get_int (&value);
2112 g_value_unset (&value);
2113 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2114 header = (TnyHeader *) g_value_get_object (&value);
2115 g_value_unset (&value);
2117 /* Get message id from header (ensure is a valid id) */
2123 /* Hide deleted and mark as deleted heders */
2124 if (flags & TNY_HEADER_FLAG_DELETED ||
2125 flags & TNY_HEADER_FLAG_EXPUNGED) {
2130 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2131 if (priv->is_outbox &&
2132 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2138 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2139 if (priv->is_outbox &&
2140 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2146 /* If no data on clipboard, return always TRUE */
2147 if (modest_email_clipboard_cleared(priv->clipboard)) {
2153 if (priv->hidding_ids != NULL) {
2154 id = tny_header_dup_message_id (header);
2155 for (i=0; i < priv->n_selected && !found; i++)
2156 if (priv->hidding_ids[i] != NULL && id != NULL)
2157 found = (!strcmp (priv->hidding_ids[i], id));
2164 old_status = priv->status;
2165 priv->status = ((gboolean) priv->status) && !visible;
2166 if ((priv->notify_status) && (priv->status != old_status)) {
2167 if (priv->status_timeout)
2168 g_source_remove (priv->status_timeout);
2171 NotifyFilterInfo *info;
2173 info = g_slice_new0 (NotifyFilterInfo);
2174 info->self = g_object_ref (G_OBJECT (user_data));
2176 info->folder = tny_header_get_folder (header);
2177 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2178 notify_filter_change,
2180 notify_filter_change_destroy);
2188 _clear_hidding_filter (ModestHeaderView *header_view)
2190 ModestHeaderViewPrivate *priv = NULL;
2193 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2194 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2196 if (priv->hidding_ids != NULL) {
2197 for (i=0; i < priv->n_selected; i++)
2198 g_free (priv->hidding_ids[i]);
2199 g_free(priv->hidding_ids);
2204 modest_header_view_refilter (ModestHeaderView *header_view)
2206 GtkTreeModel *model = NULL;
2207 ModestHeaderViewPrivate *priv = NULL;
2209 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2210 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2212 /* Hide cut headers */
2213 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2214 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2215 priv->status = HEADER_VIEW_INIT;
2216 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2221 * Called when an account is removed. If I'm showing a folder of the
2222 * account that has been removed then clear the view
2225 on_account_removed (TnyAccountStore *self,
2226 TnyAccount *account,
2229 ModestHeaderViewPrivate *priv = NULL;
2231 /* Ignore changes in transport accounts */
2232 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2235 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2238 TnyAccount *my_account;
2240 if (TNY_IS_MERGE_FOLDER (priv->folder) &&
2241 tny_folder_get_folder_type (priv->folder) == TNY_FOLDER_TYPE_OUTBOX) {
2242 ModestTnyAccountStore *acc_store = modest_runtime_get_account_store ();
2243 my_account = modest_tny_account_store_get_local_folders_account (acc_store);
2245 my_account = tny_folder_get_account (priv->folder);
2249 if (my_account == account)
2250 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2251 g_object_unref (my_account);
2257 modest_header_view_add_observer(ModestHeaderView *header_view,
2258 ModestHeaderViewObserver *observer)
2260 ModestHeaderViewPrivate *priv;
2262 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2263 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2265 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2267 g_mutex_lock(priv->observer_list_lock);
2268 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2269 g_mutex_unlock(priv->observer_list_lock);
2273 modest_header_view_remove_observer(ModestHeaderView *header_view,
2274 ModestHeaderViewObserver *observer)
2276 ModestHeaderViewPrivate *priv;
2278 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2279 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2281 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2283 g_mutex_lock(priv->observer_list_lock);
2284 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2285 g_mutex_unlock(priv->observer_list_lock);
2289 modest_header_view_notify_observers(ModestHeaderView *header_view,
2290 GtkTreeModel *model,
2291 const gchar *tny_folder_id)
2293 ModestHeaderViewPrivate *priv = NULL;
2295 ModestHeaderViewObserver *observer;
2298 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2300 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2302 g_mutex_lock(priv->observer_list_lock);
2303 iter = priv->observer_list;
2304 while(iter != NULL){
2305 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2306 modest_header_view_observer_update(observer, model,
2308 iter = g_slist_next(iter);
2310 g_mutex_unlock(priv->observer_list_lock);
2314 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2316 ModestHeaderViewPrivate *priv = NULL;
2318 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2319 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2323 modest_header_view_set_filter (ModestHeaderView *self,
2324 ModestHeaderViewFilter filter)
2326 ModestHeaderViewPrivate *priv;
2327 GtkTreeModel *filter_model;
2329 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2330 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2332 priv->filter |= filter;
2334 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2335 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2336 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2341 modest_header_view_unset_filter (ModestHeaderView *self,
2342 ModestHeaderViewFilter filter)
2344 ModestHeaderViewPrivate *priv;
2345 GtkTreeModel *filter_model;
2347 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2348 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2350 priv->filter &= ~filter;
2352 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2353 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2354 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2359 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2361 if (strcmp ("style", spec->name) == 0) {
2362 update_style (MODEST_HEADER_VIEW (obj));
2363 gtk_widget_queue_draw (GTK_WIDGET (obj));
2368 update_style (ModestHeaderView *self)
2370 ModestHeaderViewPrivate *priv;
2371 GdkColor style_color;
2372 GdkColor style_active_color;
2373 PangoAttrList *attr_list;
2375 PangoAttribute *attr;
2377 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2378 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2382 attr_list = pango_attr_list_new ();
2383 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2384 gdk_color_parse ("grey", &style_color);
2386 priv->secondary_color = style_color;
2387 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2388 pango_attr_list_insert (attr_list, attr);
2391 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2393 "SmallSystemFont", NULL,
2396 attr = pango_attr_font_desc_new (pango_font_description_copy
2397 (style->font_desc));
2398 pango_attr_list_insert (attr_list, attr);
2400 g_object_set (G_OBJECT (priv->renderer_address),
2401 "foreground-gdk", &(priv->secondary_color),
2402 "foreground-set", TRUE,
2403 "attributes", attr_list,
2405 g_object_set (G_OBJECT (priv->renderer_date_status),
2406 "foreground-gdk", &(priv->secondary_color),
2407 "foreground-set", TRUE,
2408 "attributes", attr_list,
2410 pango_attr_list_unref (attr_list);
2412 g_object_set (G_OBJECT (priv->renderer_address),
2413 "foreground-gdk", &(priv->secondary_color),
2414 "foreground-set", TRUE,
2415 "scale", PANGO_SCALE_SMALL,
2418 g_object_set (G_OBJECT (priv->renderer_date_status),
2419 "foreground-gdk", &(priv->secondary_color),
2420 "foreground-set", TRUE,
2421 "scale", PANGO_SCALE_SMALL,
2426 if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
2427 priv->active_color = 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 (TRUE));
2430 g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
2433 #ifdef MODEST_TOOLKIT_HILDON2
2434 g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (FALSE));
2440 modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
2445 GtkTreeModel *tree_model;
2450 if (!gtk_tree_view_get_dest_row_at_pos ((GtkTreeView *) header_view,
2457 g_debug ("located path: %s", gtk_tree_path_to_string (path));
2460 tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
2461 if (!gtk_tree_model_get_iter (tree_model, &iter, path))
2465 gtk_tree_model_get (tree_model, &iter,
2466 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,