1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
35 #include <tny-error.h>
38 #include <modest-header-view.h>
39 #include <modest-header-view-priv.h>
40 #include <modest-dnd.h>
41 #include <modest-tny-folder.h>
42 #include <modest-debug.h>
43 #include <modest-main-window.h>
44 #include <modest-ui-actions.h>
45 #include <modest-marshal.h>
46 #include <modest-text-utils.h>
47 #include <modest-icon-names.h>
48 #include <modest-runtime.h>
49 #include "modest-platform.h"
50 #include <modest-hbox-cell-renderer.h>
51 #include <modest-vbox-cell-renderer.h>
52 #include <modest-datetime-formatter.h>
54 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
55 static void modest_header_view_init (ModestHeaderView *obj);
56 static void modest_header_view_finalize (GObject *obj);
57 static void modest_header_view_dispose (GObject *obj);
59 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
60 GtkTreeViewColumn *column, gpointer userdata);
62 static gint cmp_rows (GtkTreeModel *tree_model,
67 static gint cmp_subject_rows (GtkTreeModel *tree_model,
72 static gboolean filter_row (GtkTreeModel *model,
76 static void on_account_removed (TnyAccountStore *self,
80 static void on_selection_changed (GtkTreeSelection *sel,
83 static gboolean on_button_press_event (GtkWidget * self, GdkEventButton * event,
86 static gboolean on_button_release_event(GtkWidget * self, GdkEventButton * event,
89 static void setup_drag_and_drop (GtkWidget *self);
91 static void enable_drag_and_drop (GtkWidget *self);
93 static void disable_drag_and_drop (GtkWidget *self);
95 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
97 #ifndef MODEST_TOOLKIT_HILDON2
98 static gboolean on_focus_in (GtkWidget *sef,
102 static gboolean on_focus_out (GtkWidget *self,
103 GdkEventFocus *event,
107 static void folder_monitor_update (TnyFolderObserver *self,
108 TnyFolderChange *change);
110 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
112 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
114 static void _clear_hidding_filter (ModestHeaderView *header_view);
116 static void modest_header_view_notify_observers(ModestHeaderView *header_view,
118 const gchar *tny_folder_id);
120 static gboolean modest_header_view_on_expose_event (GtkTreeView *header_view,
121 GdkEventExpose *event,
124 static void on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
125 static void update_style (ModestHeaderView *self);
128 HEADER_VIEW_NON_EMPTY,
133 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
134 struct _ModestHeaderViewPrivate {
136 ModestHeaderViewStyle style;
139 TnyFolderMonitor *monitor;
140 GMutex *observers_lock;
142 /*header-view-observer observer*/
143 GMutex *observer_list_lock;
144 GSList *observer_list;
146 /* not unref this object, its a singlenton */
147 ModestEmailClipboard *clipboard;
149 /* Filter tree model */
152 GtkTreeRowReference *autoselect_reference;
153 ModestHeaderViewFilter filter;
155 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
156 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
158 gulong selection_changed_handler;
159 gulong acc_removed_handler;
161 GList *drag_begin_cached_selected_rows;
163 HeaderViewStatus status;
164 guint status_timeout;
165 gboolean notify_status; /* whether or not the filter_row should notify about changes in the filtering */
167 ModestDatetimeFormatter *datetime_formatter;
169 GtkCellRenderer *renderer_address;
170 GtkCellRenderer *renderer_date_status;
173 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
174 struct _HeadersCountChangedHelper {
175 ModestHeaderView *self;
176 TnyFolderChange *change;
180 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
181 MODEST_TYPE_HEADER_VIEW, \
182 ModestHeaderViewPrivate))
186 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
189 HEADER_SELECTED_SIGNAL,
190 HEADER_ACTIVATED_SIGNAL,
191 ITEM_NOT_FOUND_SIGNAL,
192 MSG_COUNT_CHANGED_SIGNAL,
193 UPDATING_MSG_LIST_SIGNAL,
198 static GObjectClass *parent_class = NULL;
200 /* uncomment the following if you have defined any signals */
201 static guint signals[LAST_SIGNAL] = {0};
204 modest_header_view_get_type (void)
206 static GType my_type = 0;
208 static const GTypeInfo my_info = {
209 sizeof(ModestHeaderViewClass),
210 NULL, /* base init */
211 NULL, /* base finalize */
212 (GClassInitFunc) modest_header_view_class_init,
213 NULL, /* class finalize */
214 NULL, /* class data */
215 sizeof(ModestHeaderView),
217 (GInstanceInitFunc) modest_header_view_init,
221 static const GInterfaceInfo tny_folder_observer_info =
223 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
224 NULL, /* interface_finalize */
225 NULL /* interface_data */
227 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
231 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
232 &tny_folder_observer_info);
240 modest_header_view_class_init (ModestHeaderViewClass *klass)
242 GObjectClass *gobject_class;
243 gobject_class = (GObjectClass*) klass;
245 parent_class = g_type_class_peek_parent (klass);
246 gobject_class->finalize = modest_header_view_finalize;
247 gobject_class->dispose = modest_header_view_dispose;
249 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
251 signals[HEADER_SELECTED_SIGNAL] =
252 g_signal_new ("header_selected",
253 G_TYPE_FROM_CLASS (gobject_class),
255 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
257 g_cclosure_marshal_VOID__POINTER,
258 G_TYPE_NONE, 1, G_TYPE_POINTER);
260 signals[HEADER_ACTIVATED_SIGNAL] =
261 g_signal_new ("header_activated",
262 G_TYPE_FROM_CLASS (gobject_class),
264 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
266 gtk_marshal_VOID__POINTER_POINTER,
267 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
270 signals[ITEM_NOT_FOUND_SIGNAL] =
271 g_signal_new ("item_not_found",
272 G_TYPE_FROM_CLASS (gobject_class),
274 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
276 g_cclosure_marshal_VOID__INT,
277 G_TYPE_NONE, 1, G_TYPE_INT);
279 signals[MSG_COUNT_CHANGED_SIGNAL] =
280 g_signal_new ("msg_count_changed",
281 G_TYPE_FROM_CLASS (gobject_class),
283 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
285 modest_marshal_VOID__POINTER_POINTER,
286 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
288 signals[UPDATING_MSG_LIST_SIGNAL] =
289 g_signal_new ("updating-msg-list",
290 G_TYPE_FROM_CLASS (gobject_class),
292 G_STRUCT_OFFSET (ModestHeaderViewClass, updating_msg_list),
294 g_cclosure_marshal_VOID__BOOLEAN,
295 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
297 #ifdef MODEST_TOOLKIT_HILDON2
298 gtk_rc_parse_string ("class \"ModestHeaderView\" style \"fremantle-touchlist\"");
304 tny_folder_observer_init (TnyFolderObserverIface *klass)
306 klass->update = folder_monitor_update;
309 static GtkTreeViewColumn*
310 get_new_column (const gchar *name, GtkCellRenderer *renderer,
311 gboolean resizable, gint sort_col_id, gboolean show_as_text,
312 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
314 GtkTreeViewColumn *column;
316 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
317 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
319 gtk_tree_view_column_set_resizable (column, resizable);
321 gtk_tree_view_column_set_expand (column, TRUE);
324 gtk_tree_view_column_add_attribute (column, renderer, "text",
326 if (sort_col_id >= 0)
327 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
329 gtk_tree_view_column_set_sort_indicator (column, FALSE);
330 gtk_tree_view_column_set_reorderable (column, TRUE);
333 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
340 remove_all_columns (ModestHeaderView *obj)
342 GList *columns, *cursor;
344 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
346 for (cursor = columns; cursor; cursor = cursor->next)
347 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
348 GTK_TREE_VIEW_COLUMN(cursor->data));
349 g_list_free (columns);
353 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
355 GtkTreeModel *tree_filter, *sortable;
356 GtkTreeViewColumn *column=NULL;
357 GtkTreeSelection *selection = NULL;
358 GtkCellRenderer *renderer_header,
359 *renderer_attach, *renderer_compact_date_or_status;
360 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
361 *renderer_subject, *renderer_subject_box, *renderer_recpt,
363 ModestHeaderViewPrivate *priv;
364 GtkTreeViewColumn *compact_column = NULL;
367 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
368 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, FALSE);
370 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
372 priv->is_outbox = (type == TNY_FOLDER_TYPE_OUTBOX);
374 /* TODO: check whether these renderers need to be freed */
375 renderer_attach = gtk_cell_renderer_pixbuf_new ();
376 renderer_priority = gtk_cell_renderer_pixbuf_new ();
377 renderer_header = gtk_cell_renderer_text_new ();
379 renderer_compact_header = modest_vbox_cell_renderer_new ();
380 renderer_recpt_box = modest_hbox_cell_renderer_new ();
381 renderer_subject_box = modest_hbox_cell_renderer_new ();
382 renderer_recpt = gtk_cell_renderer_text_new ();
383 priv->renderer_address = renderer_recpt;
384 renderer_subject = gtk_cell_renderer_text_new ();
385 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
386 priv->renderer_date_status = renderer_compact_date_or_status;
388 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
389 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
390 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
391 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
392 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
393 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
394 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
395 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
396 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
397 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
398 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
399 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
400 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
401 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
403 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
404 #ifndef MODEST_TOOLKIT_GTK
405 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
406 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
408 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
409 g_object_set(G_OBJECT(renderer_header),
410 "ellipsize", PANGO_ELLIPSIZE_END,
412 g_object_set (G_OBJECT (renderer_subject),
413 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
415 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_subject), 1);
416 g_object_set (G_OBJECT (renderer_recpt),
417 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
419 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_recpt), 1);
420 g_object_set(G_OBJECT(renderer_compact_date_or_status),
421 "xalign", 1.0, "yalign", 0.0,
423 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer_compact_date_or_status), 1);
424 g_object_set (G_OBJECT (renderer_priority),
425 "yalign", 1.0, NULL);
426 g_object_set (G_OBJECT (renderer_attach),
427 "yalign", 0.0, NULL);
429 #ifndef MODEST_TOOLKIT_GTK
430 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
431 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
432 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
434 gtk_cell_renderer_set_fixed_size (renderer_attach, 16, 16);
435 gtk_cell_renderer_set_fixed_size (renderer_priority, 16, 16);
436 /* gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64); */
439 remove_all_columns (self);
441 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
442 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
443 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
444 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
446 /* Add new columns */
447 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
448 ModestHeaderViewColumn col =
449 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
451 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
452 g_printerr ("modest: invalid column %d in column list\n", col);
458 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
459 column = get_new_column (_("A"), renderer_attach, FALSE,
460 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
462 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
464 gtk_tree_view_column_set_fixed_width (column, 45);
468 case MODEST_HEADER_VIEW_COLUMN_FROM:
469 column = get_new_column (_("From"), renderer_header, TRUE,
470 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
472 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
473 GINT_TO_POINTER(TRUE));
476 case MODEST_HEADER_VIEW_COLUMN_TO:
477 column = get_new_column (_("To"), renderer_header, TRUE,
478 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
480 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
481 GINT_TO_POINTER(FALSE));
484 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
485 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
486 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
488 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
489 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
490 compact_column = column;
493 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
494 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
495 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
497 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
498 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
499 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
500 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
501 compact_column = column;
505 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
506 column = get_new_column (_("Subject"), renderer_header, TRUE,
507 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
509 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
513 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
514 column = get_new_column (_("Received"), renderer_header, TRUE,
515 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
517 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
518 GINT_TO_POINTER(TRUE));
521 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
522 column = get_new_column (_("Sent"), renderer_header, TRUE,
523 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
525 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
526 GINT_TO_POINTER(FALSE));
529 case MODEST_HEADER_VIEW_COLUMN_SIZE:
530 column = get_new_column (_("Size"), renderer_header, TRUE,
531 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
533 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
536 case MODEST_HEADER_VIEW_COLUMN_STATUS:
537 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
538 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
540 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
545 g_return_val_if_reached(FALSE);
548 /* we keep the column id around */
549 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
550 GINT_TO_POINTER(col));
552 /* we need this ptr when sorting the rows */
553 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
555 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
559 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
560 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
561 (GtkTreeIterCompareFunc) cmp_rows,
562 compact_column, NULL);
563 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
564 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
565 (GtkTreeIterCompareFunc) cmp_subject_rows,
566 compact_column, NULL);
570 g_signal_connect (G_OBJECT (self), "notify::style", G_CALLBACK (on_notify_style), (gpointer) self);
576 datetime_format_changed (ModestDatetimeFormatter *formatter,
577 ModestHeaderView *self)
579 gtk_widget_queue_draw (GTK_WIDGET (self));
583 modest_header_view_init (ModestHeaderView *obj)
585 ModestHeaderViewPrivate *priv;
588 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
591 priv->is_outbox = FALSE;
593 priv->monitor = NULL;
594 priv->observers_lock = g_mutex_new ();
595 priv->autoselect_reference = NULL;
597 priv->status = HEADER_VIEW_INIT;
598 priv->status_timeout = 0;
599 priv->notify_status = TRUE;
601 priv->observer_list_lock = g_mutex_new();
602 priv->observer_list = NULL;
604 priv->clipboard = modest_runtime_get_email_clipboard ();
605 priv->hidding_ids = NULL;
606 priv->n_selected = 0;
607 priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
608 priv->selection_changed_handler = 0;
609 priv->acc_removed_handler = 0;
611 /* Sort parameters */
612 for (j=0; j < 2; j++) {
613 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
614 priv->sort_colid[j][i] = -1;
615 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
619 priv->datetime_formatter = modest_datetime_formatter_new ();
620 g_signal_connect (G_OBJECT (priv->datetime_formatter), "format-changed",
621 G_CALLBACK (datetime_format_changed), (gpointer) obj);
623 setup_drag_and_drop (GTK_WIDGET(obj));
627 modest_header_view_dispose (GObject *obj)
629 ModestHeaderView *self;
630 ModestHeaderViewPrivate *priv;
631 GtkTreeSelection *sel;
633 self = MODEST_HEADER_VIEW(obj);
634 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
636 if (priv->datetime_formatter) {
637 g_object_unref (priv->datetime_formatter);
638 priv->datetime_formatter = NULL;
641 /* Free in the dispose to avoid unref cycles */
643 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
644 g_object_unref (G_OBJECT (priv->folder));
648 /* We need to do this here in the dispose because the
649 selection won't exist when finalizing */
650 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
651 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
652 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
653 priv->selection_changed_handler = 0;
656 G_OBJECT_CLASS(parent_class)->dispose (obj);
660 modest_header_view_finalize (GObject *obj)
662 ModestHeaderView *self;
663 ModestHeaderViewPrivate *priv;
665 self = MODEST_HEADER_VIEW(obj);
666 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
668 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
669 priv->acc_removed_handler)) {
670 g_signal_handler_disconnect (modest_runtime_get_account_store (),
671 priv->acc_removed_handler);
674 /* There is no need to lock because there should not be any
675 * reference to self now. */
676 g_mutex_free(priv->observer_list_lock);
677 g_slist_free(priv->observer_list);
679 g_mutex_lock (priv->observers_lock);
681 tny_folder_monitor_stop (priv->monitor);
682 g_object_unref (G_OBJECT (priv->monitor));
684 g_mutex_unlock (priv->observers_lock);
685 g_mutex_free (priv->observers_lock);
687 /* Clear hidding array created by cut operation */
688 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
690 if (priv->autoselect_reference != NULL) {
691 gtk_tree_row_reference_free (priv->autoselect_reference);
692 priv->autoselect_reference = NULL;
695 G_OBJECT_CLASS(parent_class)->finalize (obj);
700 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
703 GtkTreeSelection *sel;
704 ModestHeaderView *self;
705 ModestHeaderViewPrivate *priv;
707 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
710 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
711 self = MODEST_HEADER_VIEW(obj);
712 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
714 modest_header_view_set_style (self, style);
716 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
717 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
718 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
720 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
721 TRUE); /* alternating row colors */
723 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
724 priv->selection_changed_handler =
725 g_signal_connect_after (sel, "changed",
726 G_CALLBACK(on_selection_changed), self);
728 g_signal_connect (self, "row-activated",
729 G_CALLBACK (on_header_row_activated), NULL);
731 #ifndef MODEST_TOOLKIT_HILDON2
732 g_signal_connect (self, "focus-in-event",
733 G_CALLBACK(on_focus_in), NULL);
734 g_signal_connect (self, "focus-out-event",
735 G_CALLBACK(on_focus_out), NULL);
738 g_signal_connect (self, "button-press-event",
739 G_CALLBACK(on_button_press_event), NULL);
740 g_signal_connect (self, "button-release-event",
741 G_CALLBACK(on_button_release_event), NULL);
743 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
745 G_CALLBACK (on_account_removed),
748 g_signal_connect (self, "expose-event",
749 G_CALLBACK(modest_header_view_on_expose_event),
752 return GTK_WIDGET(self);
757 modest_header_view_count_selected_headers (ModestHeaderView *self)
759 GtkTreeSelection *sel;
762 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
764 /* Get selection object and check selected rows count */
765 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
766 selected_rows = gtk_tree_selection_count_selected_rows (sel);
768 return selected_rows;
772 modest_header_view_has_selected_headers (ModestHeaderView *self)
774 GtkTreeSelection *sel;
777 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
779 /* Get selection object and check selected rows count */
780 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
781 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
788 modest_header_view_get_selected_headers (ModestHeaderView *self)
790 GtkTreeSelection *sel;
791 ModestHeaderViewPrivate *priv;
792 TnyList *header_list = NULL;
794 GList *list, *tmp = NULL;
795 GtkTreeModel *tree_model = NULL;
798 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
800 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
802 /* Get selected rows */
803 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
804 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
807 header_list = tny_simple_list_new();
809 list = g_list_reverse (list);
812 /* get header from selection */
813 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
814 gtk_tree_model_get (tree_model, &iter,
815 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
817 /* Prepend to list */
818 tny_list_prepend (header_list, G_OBJECT (header));
819 g_object_unref (G_OBJECT (header));
821 tmp = g_list_next (tmp);
824 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
831 /* scroll our list view so the selected item is visible */
833 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
835 #ifdef MODEST_TOOLKIT_GTK
837 GtkTreePath *selected_path;
838 GtkTreePath *start, *end;
842 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
843 selected_path = gtk_tree_model_get_path (model, iter);
845 start = gtk_tree_path_new ();
846 end = gtk_tree_path_new ();
848 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
850 if (gtk_tree_path_compare (selected_path, start) < 0 ||
851 gtk_tree_path_compare (end, selected_path) < 0)
852 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
853 selected_path, NULL, TRUE,
856 gtk_tree_path_free (selected_path);
857 gtk_tree_path_free (start);
858 gtk_tree_path_free (end);
860 #endif /* MODEST_TOOLKIT_GTK */
865 modest_header_view_select_next (ModestHeaderView *self)
867 GtkTreeSelection *sel;
872 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
874 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
875 path = get_selected_row (GTK_TREE_VIEW(self), &model);
876 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
877 /* Unselect previous path */
878 gtk_tree_selection_unselect_path (sel, path);
880 /* Move path down and selects new one */
881 if (gtk_tree_model_iter_next (model, &iter)) {
882 gtk_tree_selection_select_iter (sel, &iter);
883 scroll_to_selected (self, &iter, FALSE);
885 gtk_tree_path_free(path);
891 modest_header_view_select_prev (ModestHeaderView *self)
893 GtkTreeSelection *sel;
898 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
900 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
901 path = get_selected_row (GTK_TREE_VIEW(self), &model);
902 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
903 /* Unselect previous path */
904 gtk_tree_selection_unselect_path (sel, path);
907 if (gtk_tree_path_prev (path)) {
908 gtk_tree_model_get_iter (model, &iter, path);
910 /* Select the new one */
911 gtk_tree_selection_select_iter (sel, &iter);
912 scroll_to_selected (self, &iter, TRUE);
915 gtk_tree_path_free (path);
920 modest_header_view_get_columns (ModestHeaderView *self)
922 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
924 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
930 modest_header_view_set_style (ModestHeaderView *self,
931 ModestHeaderViewStyle style)
933 ModestHeaderViewPrivate *priv;
934 gboolean show_col_headers = FALSE;
935 ModestHeaderViewStyle old_style;
937 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
938 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
941 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
942 if (priv->style == style)
943 return TRUE; /* nothing to do */
946 case MODEST_HEADER_VIEW_STYLE_DETAILS:
947 show_col_headers = TRUE;
949 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
952 g_return_val_if_reached (FALSE);
954 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
955 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
957 old_style = priv->style;
964 ModestHeaderViewStyle
965 modest_header_view_get_style (ModestHeaderView *self)
967 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), FALSE);
969 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
972 /* This is used to automatically select the first header if the user
973 * has not selected any header yet.
976 modest_header_view_on_expose_event(GtkTreeView *header_view,
977 GdkEventExpose *event,
980 GtkTreeSelection *sel;
982 GtkTreeIter tree_iter;
983 ModestHeaderViewPrivate *priv;
985 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
986 model = gtk_tree_view_get_model(header_view);
991 #ifdef MODEST_TOOLKIT_HILDON2
994 sel = gtk_tree_view_get_selection(header_view);
995 if(!gtk_tree_selection_count_selected_rows(sel)) {
996 if (gtk_tree_model_get_iter_first(model, &tree_iter)) {
997 GtkTreePath *tree_iter_path;
998 /* Prevent the widget from getting the focus
999 when selecting the first item */
1000 tree_iter_path = gtk_tree_model_get_path (model, &tree_iter);
1001 g_object_set(header_view, "can-focus", FALSE, NULL);
1002 gtk_tree_selection_select_iter(sel, &tree_iter);
1003 gtk_tree_view_set_cursor (header_view, tree_iter_path, NULL, FALSE);
1004 g_object_set(header_view, "can-focus", TRUE, NULL);
1005 if (priv->autoselect_reference) {
1006 gtk_tree_row_reference_free (priv->autoselect_reference);
1008 priv->autoselect_reference = gtk_tree_row_reference_new (model, tree_iter_path);
1009 gtk_tree_path_free (tree_iter_path);
1012 if (priv->autoselect_reference != NULL) {
1013 gboolean moved_selection = FALSE;
1014 GtkTreePath * last_path;
1015 if (gtk_tree_selection_count_selected_rows (sel) != 1) {
1016 moved_selection = TRUE;
1020 rows = gtk_tree_selection_get_selected_rows (sel, NULL);
1021 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1022 if (gtk_tree_path_compare (last_path, (GtkTreePath *) rows->data) != 0)
1023 moved_selection = TRUE;
1024 g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
1026 gtk_tree_path_free (last_path);
1028 if (moved_selection) {
1029 gtk_tree_row_reference_free (priv->autoselect_reference);
1030 priv->autoselect_reference = NULL;
1033 if (gtk_tree_model_get_iter_first (model, &tree_iter)) {
1034 GtkTreePath *current_path;
1035 current_path = gtk_tree_model_get_path (model, &tree_iter);
1036 last_path = gtk_tree_row_reference_get_path (priv->autoselect_reference);
1037 if (gtk_tree_path_compare (current_path, last_path) != 0) {
1038 g_object_set(header_view, "can-focus", FALSE, NULL);
1039 gtk_tree_selection_unselect_all (sel);
1040 gtk_tree_selection_select_iter(sel, &tree_iter);
1041 gtk_tree_view_set_cursor (header_view, current_path, NULL, FALSE);
1042 g_object_set(header_view, "can-focus", TRUE, NULL);
1043 gtk_tree_row_reference_free (priv->autoselect_reference);
1044 priv->autoselect_reference = gtk_tree_row_reference_new (model, current_path);
1046 gtk_tree_path_free (current_path);
1047 gtk_tree_path_free (last_path);
1057 modest_header_view_get_folder (ModestHeaderView *self)
1059 ModestHeaderViewPrivate *priv;
1061 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), NULL);
1063 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1066 g_object_ref (priv->folder);
1068 return priv->folder;
1072 set_folder_intern_get_headers_async_cb (TnyFolder *folder,
1078 ModestHeaderView *self;
1079 ModestHeaderViewPrivate *priv;
1081 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1083 self = MODEST_HEADER_VIEW (user_data);
1084 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1086 if (cancelled || err)
1089 /* Add IDLE observer (monitor) and another folder observer for
1090 new messages (self) */
1091 g_mutex_lock (priv->observers_lock);
1092 if (priv->monitor) {
1093 tny_folder_monitor_stop (priv->monitor);
1094 g_object_unref (G_OBJECT (priv->monitor));
1096 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
1097 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
1098 tny_folder_monitor_start (priv->monitor);
1099 g_mutex_unlock (priv->observers_lock);
1103 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
1107 ModestHeaderViewPrivate *priv;
1108 GList *cols, *cursor;
1109 GtkTreeModel *filter_model, *sortable;
1111 GtkSortType sort_type;
1113 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1115 headers = TNY_LIST (tny_gtk_header_list_model_new ());
1117 /* Start the monitor in the callback of the
1118 tny_gtk_header_list_model_set_folder call. It's crucial to
1119 do it there and not just after the call because we want the
1120 monitor to observe only the headers returned by the
1121 tny_folder_get_headers_async call that it's inside the
1122 tny_gtk_header_list_model_set_folder call. This way the
1123 monitor infrastructure could successfully cope with
1124 duplicates. For example if a tny_folder_add_msg_async is
1125 happening while tny_gtk_header_list_model_set_folder is
1126 invoked, then the first call could add a header that will
1127 be added again by tny_gtk_header_list_model_set_folder, so
1128 we'd end up with duplicate headers. sergio */
1129 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
1131 set_folder_intern_get_headers_async_cb,
1134 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
1135 g_object_unref (G_OBJECT (headers));
1137 /* Init filter_row function to examine empty status */
1138 priv->status = HEADER_VIEW_INIT;
1140 /* Create a tree model filter to hide and show rows for cut operations */
1141 filter_model = gtk_tree_model_filter_new (sortable, NULL);
1142 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
1146 g_object_unref (G_OBJECT (sortable));
1148 /* install our special sorting functions */
1149 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
1151 /* Restore sort column id */
1153 type = modest_tny_folder_guess_folder_type (folder);
1154 if (type == TNY_FOLDER_TYPE_INVALID)
1155 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1157 sort_colid = modest_header_view_get_sort_column_id (self, type);
1158 sort_type = modest_header_view_get_sort_type (self, type);
1159 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1162 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1163 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
1164 (GtkTreeIterCompareFunc) cmp_rows,
1166 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
1167 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1168 (GtkTreeIterCompareFunc) cmp_subject_rows,
1173 gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
1174 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1175 tny_folder_get_id(folder));
1176 g_object_unref (G_OBJECT (filter_model));
1183 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1185 GtkSortType sort_type)
1187 ModestHeaderViewPrivate *priv = NULL;
1188 GtkTreeModel *tree_filter, *sortable = NULL;
1191 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1192 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1194 /* Get model and private data */
1195 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1196 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1197 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1198 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1200 /* Sort tree model */
1201 type = modest_tny_folder_guess_folder_type (priv->folder);
1202 if (type == TNY_FOLDER_TYPE_INVALID)
1203 g_warning ("%s: BUG: TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
1205 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1208 /* Store new sort parameters */
1209 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1214 modest_header_view_set_sort_params (ModestHeaderView *self,
1216 GtkSortType sort_type,
1219 ModestHeaderViewPrivate *priv;
1220 ModestHeaderViewStyle style;
1222 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1223 g_return_if_fail (sort_type == GTK_SORT_ASCENDING || sort_type == GTK_SORT_DESCENDING);
1224 g_return_if_fail (type != TNY_FOLDER_TYPE_INVALID);
1226 style = modest_header_view_get_style (self);
1227 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1229 priv->sort_colid[style][type] = sort_colid;
1230 priv->sort_type[style][type] = sort_type;
1234 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1237 ModestHeaderViewPrivate *priv;
1238 ModestHeaderViewStyle style;
1240 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), 0);
1241 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, 0);
1243 style = modest_header_view_get_style (self);
1244 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1246 return priv->sort_colid[style][type];
1250 modest_header_view_get_sort_type (ModestHeaderView *self,
1253 ModestHeaderViewPrivate *priv;
1254 ModestHeaderViewStyle style;
1256 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), GTK_SORT_DESCENDING);
1257 g_return_val_if_fail (type != TNY_FOLDER_TYPE_INVALID, GTK_SORT_DESCENDING);
1259 style = modest_header_view_get_style (self);
1260 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1262 return priv->sort_type[style][type];
1266 ModestHeaderView *header_view;
1267 RefreshAsyncUserCallback cb;
1272 folder_refreshed_cb (ModestMailOperation *mail_op,
1276 ModestHeaderViewPrivate *priv;
1277 SetFolderHelper *info;
1279 info = (SetFolderHelper*) user_data;
1281 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1285 info->cb (mail_op, folder, info->user_data);
1287 /* Start the folder count changes observer. We do not need it
1288 before the refresh. Note that the monitor could still be
1289 called for this refresh but now we know that the callback
1290 was previously called */
1291 g_mutex_lock (priv->observers_lock);
1292 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1293 g_mutex_unlock (priv->observers_lock);
1295 /* Notify the observers that the update is over */
1296 g_signal_emit (G_OBJECT (info->header_view),
1297 signals[UPDATING_MSG_LIST_SIGNAL], 0, FALSE, NULL);
1299 /* Allow filtering notifications from now on if the current
1300 folder is still the same (if not then the user has selected
1301 another one to refresh, we should wait until that refresh
1303 if (priv->folder == folder)
1304 priv->notify_status = TRUE;
1307 g_object_unref (info->header_view);
1312 refresh_folder_error_handler (ModestMailOperation *mail_op,
1315 const GError *error = modest_mail_operation_get_error (mail_op);
1317 if (error->code == TNY_SYSTEM_ERROR_MEMORY ||
1318 error->code == TNY_IO_ERROR_WRITE ||
1319 error->code == TNY_IO_ERROR_READ) {
1320 ModestMailOperationStatus st = modest_mail_operation_get_status (mail_op);
1321 /* If the mail op has been cancelled then it's not an error: don't show any message */
1322 if (st != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1323 modest_platform_information_banner (NULL, NULL,
1325 "cerm_device_memory_full"));
1331 modest_header_view_set_folder (ModestHeaderView *self,
1334 ModestWindow *progress_window,
1335 RefreshAsyncUserCallback callback,
1338 ModestHeaderViewPrivate *priv;
1340 g_return_if_fail (self);
1342 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1345 if (priv->status_timeout) {
1346 g_source_remove (priv->status_timeout);
1347 priv->status_timeout = 0;
1350 g_mutex_lock (priv->observers_lock);
1351 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1352 g_object_unref (priv->folder);
1353 priv->folder = NULL;
1354 g_mutex_unlock (priv->observers_lock);
1358 GtkTreeSelection *selection;
1359 SetFolderHelper *info;
1360 ModestMailOperation *mail_op = NULL;
1362 /* Set folder in the model */
1363 modest_header_view_set_folder_intern (self, folder);
1365 /* Pick my reference. Nothing to do with the mail operation */
1366 priv->folder = g_object_ref (folder);
1368 /* Do not notify about filterings until the refresh finishes */
1369 priv->notify_status = FALSE;
1371 /* Clear the selection if exists */
1372 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1373 gtk_tree_selection_unselect_all(selection);
1374 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1376 /* Notify the observers that the update begins */
1377 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1380 /* create the helper */
1381 info = g_malloc0 (sizeof (SetFolderHelper));
1382 info->header_view = g_object_ref (self);
1383 info->cb = callback;
1384 info->user_data = user_data;
1386 /* Create the mail operation (source will be the parent widget) */
1387 if (progress_window)
1388 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(progress_window),
1389 refresh_folder_error_handler,
1392 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1395 /* Refresh the folder asynchronously */
1396 modest_mail_operation_refresh_folder (mail_op,
1398 folder_refreshed_cb,
1401 folder_refreshed_cb (mail_op, folder, info);
1404 g_object_unref (mail_op);
1406 g_mutex_lock (priv->observers_lock);
1408 if (priv->monitor) {
1409 tny_folder_monitor_stop (priv->monitor);
1410 g_object_unref (G_OBJECT (priv->monitor));
1411 priv->monitor = NULL;
1414 if (priv->autoselect_reference) {
1415 gtk_tree_row_reference_free (priv->autoselect_reference);
1416 priv->autoselect_reference = NULL;
1419 gtk_tree_view_set_model (GTK_TREE_VIEW (self), NULL);
1421 modest_header_view_notify_observers(self, NULL, NULL);
1423 g_mutex_unlock (priv->observers_lock);
1425 /* Notify the observers that the update is over */
1426 g_signal_emit (G_OBJECT (self), signals[UPDATING_MSG_LIST_SIGNAL],
1432 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1433 GtkTreeViewColumn *column, gpointer userdata)
1435 ModestHeaderView *self = NULL;
1436 ModestHeaderViewPrivate *priv = NULL;
1438 GtkTreeModel *model = NULL;
1439 TnyHeader *header = NULL;
1440 TnyHeaderFlags flags;
1442 self = MODEST_HEADER_VIEW (treeview);
1443 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1445 model = gtk_tree_view_get_model (treeview);
1446 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1449 /* get the first selected item */
1450 gtk_tree_model_get (model, &iter,
1451 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1452 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1455 /* Dont open DELETED messages */
1456 if (flags & TNY_HEADER_FLAG_DELETED) {
1459 win = gtk_widget_get_ancestor (GTK_WIDGET (treeview), GTK_TYPE_WINDOW);
1460 msg = modest_ui_actions_get_msg_already_deleted_error_msg (MODEST_WINDOW (win));
1461 modest_platform_information_banner (NULL, NULL, msg);
1467 g_signal_emit (G_OBJECT(self),
1468 signals[HEADER_ACTIVATED_SIGNAL],
1474 g_object_unref (G_OBJECT (header));
1479 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1481 GtkTreeModel *model;
1482 TnyHeader *header = NULL;
1483 GtkTreePath *path = NULL;
1485 ModestHeaderView *self;
1486 ModestHeaderViewPrivate *priv;
1487 GList *selected = NULL;
1489 g_return_if_fail (sel);
1490 g_return_if_fail (user_data);
1492 self = MODEST_HEADER_VIEW (user_data);
1493 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1495 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1496 if (selected != NULL)
1497 path = (GtkTreePath *) selected->data;
1498 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1499 return; /* msg was _un_selected */
1501 gtk_tree_model_get (model, &iter,
1502 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1506 g_signal_emit (G_OBJECT(self),
1507 signals[HEADER_SELECTED_SIGNAL],
1510 g_object_unref (G_OBJECT (header));
1512 /* free all items in 'selected' */
1513 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1514 g_list_free (selected);
1518 /* PROTECTED method. It's useful when we want to force a given
1519 selection to reload a msg. For example if we have selected a header
1520 in offline mode, when Modest become online, we want to reload the
1521 message automatically without an user click over the header */
1523 _modest_header_view_change_selection (GtkTreeSelection *selection,
1526 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1527 g_return_if_fail (user_data && MODEST_IS_HEADER_VIEW (user_data));
1529 on_selection_changed (selection, user_data);
1533 compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1540 if (p1 == TNY_HEADER_FLAG_HIGH_PRIORITY)
1544 if (p1 == TNY_HEADER_FLAG_LOW_PRIORITY)
1548 if ((p1 == TNY_HEADER_FLAG_NORMAL_PRIORITY) && (p2 == TNY_HEADER_FLAG_HIGH_PRIORITY))
1556 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1563 /* static int counter = 0; */
1565 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1566 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1567 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1571 case TNY_HEADER_FLAG_ATTACHMENTS:
1573 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1574 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1575 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1576 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1578 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1579 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1581 return cmp ? cmp : t1 - t2;
1583 case TNY_HEADER_FLAG_PRIORITY_MASK: {
1584 TnyHeader *header1 = NULL, *header2 = NULL;
1586 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header1,
1587 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1588 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header2,
1589 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1591 /* This is for making priority values respect the intuitive sort relationship
1592 * as HIGH is 01, LOW is 10, and NORMAL is 00 */
1594 if (header1 && header2) {
1595 cmp = compare_priorities (tny_header_get_priority (header1),
1596 tny_header_get_priority (header2));
1597 g_object_unref (header1);
1598 g_object_unref (header2);
1600 return cmp ? cmp : t1 - t2;
1606 return &iter1 - &iter2; /* oughhhh */
1611 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1617 /* static int counter = 0; */
1619 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1621 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1622 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1623 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1624 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1626 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1627 val2 + modest_text_utils_get_subject_prefix_len(val2),
1634 /* Drag and drop stuff */
1636 drag_data_get_cb (GtkWidget *widget,
1637 GdkDragContext *context,
1638 GtkSelectionData *selection_data,
1643 ModestHeaderView *self = NULL;
1644 ModestHeaderViewPrivate *priv = NULL;
1646 self = MODEST_HEADER_VIEW (widget);
1647 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1649 /* Set the data. Do not use the current selection because it
1650 could be different than the selection at the beginning of
1652 modest_dnd_selection_data_set_paths (selection_data,
1653 priv->drag_begin_cached_selected_rows);
1657 * We're caching the selected rows at the beginning because the
1658 * selection could change between drag-begin and drag-data-get, for
1659 * example if we have a set of rows already selected, and then we
1660 * click in one of them (without SHIFT key pressed) and begin a drag,
1661 * the selection at that moment contains all the selected lines, but
1662 * after dropping the selection, the release event provokes that only
1663 * the row used to begin the drag is selected, so at the end the
1664 * drag&drop affects only one rows instead of all the selected ones.
1668 drag_begin_cb (GtkWidget *widget,
1669 GdkDragContext *context,
1672 ModestHeaderView *self = NULL;
1673 ModestHeaderViewPrivate *priv = NULL;
1674 GtkTreeSelection *selection;
1676 self = MODEST_HEADER_VIEW (widget);
1677 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1679 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1680 priv->drag_begin_cached_selected_rows =
1681 gtk_tree_selection_get_selected_rows (selection, NULL);
1685 * We use the drag-end signal to clear the cached selection, we use
1686 * this because this allways happens, whether or not the d&d was a
1690 drag_end_cb (GtkWidget *widget,
1694 ModestHeaderView *self = NULL;
1695 ModestHeaderViewPrivate *priv = NULL;
1697 self = MODEST_HEADER_VIEW (widget);
1698 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1700 /* Free cached data */
1701 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1702 g_list_free (priv->drag_begin_cached_selected_rows);
1703 priv->drag_begin_cached_selected_rows = NULL;
1706 /* Header view drag types */
1707 const GtkTargetEntry header_view_drag_types[] = {
1708 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1712 enable_drag_and_drop (GtkWidget *self)
1714 #ifdef MODEST_TOOLKIT_HILDON2
1717 gtk_drag_source_set (self, GDK_BUTTON1_MASK,
1718 header_view_drag_types,
1719 G_N_ELEMENTS (header_view_drag_types),
1720 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1724 disable_drag_and_drop (GtkWidget *self)
1726 #ifdef MODEST_TOOLKIT_HILDON2
1729 gtk_drag_source_unset (self);
1733 setup_drag_and_drop (GtkWidget *self)
1735 #ifdef MODEST_TOOLKIT_HILDON2
1738 enable_drag_and_drop(self);
1739 g_signal_connect(G_OBJECT (self), "drag_data_get",
1740 G_CALLBACK(drag_data_get_cb), NULL);
1742 g_signal_connect(G_OBJECT (self), "drag_begin",
1743 G_CALLBACK(drag_begin_cb), NULL);
1745 g_signal_connect(G_OBJECT (self), "drag_end",
1746 G_CALLBACK(drag_end_cb), NULL);
1749 static GtkTreePath *
1750 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1752 GtkTreePath *path = NULL;
1753 GtkTreeSelection *sel = NULL;
1756 sel = gtk_tree_view_get_selection(self);
1757 rows = gtk_tree_selection_get_selected_rows (sel, model);
1759 if ((rows == NULL) || (g_list_length(rows) != 1))
1762 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1767 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1773 #ifndef MODEST_TOOLKIT_HILDON2
1775 * This function moves the tree view scroll to the current selected
1776 * row when the widget grabs the focus
1779 on_focus_in (GtkWidget *self,
1780 GdkEventFocus *event,
1783 GtkTreeSelection *selection;
1784 GtkTreeModel *model;
1785 GList *selected = NULL;
1786 GtkTreePath *selected_path = NULL;
1788 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1792 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1793 /* If none selected yet, pick the first one */
1794 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1798 /* Return if the model is empty */
1799 if (!gtk_tree_model_get_iter_first (model, &iter))
1802 path = gtk_tree_model_get_path (model, &iter);
1803 gtk_tree_selection_select_path (selection, path);
1804 gtk_tree_path_free (path);
1807 /* Need to get the all the rows because is selection multiple */
1808 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1809 if (selected == NULL) return FALSE;
1810 selected_path = (GtkTreePath *) selected->data;
1813 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1814 g_list_free (selected);
1820 on_focus_out (GtkWidget *self,
1821 GdkEventFocus *event,
1825 if (!gtk_widget_is_focus (self)) {
1826 GtkTreeSelection *selection = NULL;
1827 GList *selected_rows = NULL;
1828 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1829 if (gtk_tree_selection_count_selected_rows (selection) > 1) {
1830 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1831 g_signal_handlers_block_by_func (selection, on_selection_changed, self);
1832 gtk_tree_selection_unselect_all (selection);
1833 gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
1834 g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
1835 g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
1836 g_list_free (selected_rows);
1844 on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1846 enable_drag_and_drop(self);
1851 on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
1853 GtkTreeSelection *selection = NULL;
1854 GtkTreePath *path = NULL;
1855 gboolean already_selected = FALSE, already_opened = FALSE;
1856 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
1858 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
1860 GtkTreeModel *model;
1862 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1863 already_selected = gtk_tree_selection_path_is_selected (selection, path);
1865 /* Get header from model */
1866 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1867 if (gtk_tree_model_get_iter (model, &iter, path)) {
1868 GValue value = {0,};
1871 gtk_tree_model_get_value (model, &iter,
1872 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1874 header = (TnyHeader *) g_value_get_object (&value);
1875 if (TNY_IS_HEADER (header)) {
1876 status = modest_tny_all_send_queues_get_msg_status (header);
1877 already_opened = modest_window_mgr_find_registered_header (modest_runtime_get_window_mgr (),
1880 g_value_unset (&value);
1884 /* Enable drag and drop only if the user clicks on a row that
1885 it's already selected. If not, let him select items using
1886 the pointer. If the message is in an OUTBOX and in sending
1887 status disable drag and drop as well */
1888 if (!already_selected ||
1889 status == MODEST_TNY_SEND_QUEUE_SENDING ||
1891 disable_drag_and_drop(self);
1894 gtk_tree_path_free(path);
1896 /* If it's already opened then do not let the button-press
1897 event go on because it'll perform a message open because
1898 we're clicking on to an already selected header */
1903 folder_monitor_update (TnyFolderObserver *self,
1904 TnyFolderChange *change)
1906 ModestHeaderViewPrivate *priv = NULL;
1907 TnyFolderChangeChanged changed;
1908 TnyFolder *folder = NULL;
1910 changed = tny_folder_change_get_changed (change);
1912 /* Do not notify the observers if the folder of the header
1913 view has changed before this call to the observer
1915 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1916 folder = tny_folder_change_get_folder (change);
1917 if (folder != priv->folder)
1920 MODEST_DEBUG_BLOCK (
1921 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
1922 g_print ("ADDED %d/%d (r/t) \n",
1923 tny_folder_change_get_new_unread_count (change),
1924 tny_folder_change_get_new_all_count (change));
1925 if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
1926 g_print ("ALL COUNT %d\n",
1927 tny_folder_change_get_new_all_count (change));
1928 if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
1929 g_print ("UNREAD COUNT %d\n",
1930 tny_folder_change_get_new_unread_count (change));
1931 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
1932 g_print ("EXPUNGED %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_FOLDER_RENAME)
1936 g_print ("FOLDER RENAME\n");
1937 if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
1938 g_print ("MSG RECEIVED %d/%d (r/t) \n",
1939 tny_folder_change_get_new_unread_count (change),
1940 tny_folder_change_get_new_all_count (change));
1941 g_print ("---------------------------------------------------\n");
1944 /* Check folder count */
1945 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1946 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1948 g_mutex_lock (priv->observers_lock);
1950 /* Emit signal to evaluate how headers changes affects
1951 to the window view */
1952 g_signal_emit (G_OBJECT(self),
1953 signals[MSG_COUNT_CHANGED_SIGNAL],
1956 /* Added or removed headers, so data stored on cliboard are invalid */
1957 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1958 modest_email_clipboard_clear (priv->clipboard);
1960 g_mutex_unlock (priv->observers_lock);
1966 g_object_unref (folder);
1970 modest_header_view_is_empty (ModestHeaderView *self)
1972 ModestHeaderViewPrivate *priv;
1974 g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
1976 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1978 return priv->status == HEADER_VIEW_EMPTY;
1982 modest_header_view_clear (ModestHeaderView *self)
1984 g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
1986 modest_header_view_set_folder (self, NULL, FALSE, NULL, NULL, NULL);
1990 modest_header_view_copy_selection (ModestHeaderView *header_view)
1992 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
1994 /* Copy selection */
1995 _clipboard_set_selected_data (header_view, FALSE);
1999 modest_header_view_cut_selection (ModestHeaderView *header_view)
2001 ModestHeaderViewPrivate *priv = NULL;
2002 const gchar **hidding = NULL;
2003 guint i, n_selected;
2005 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2007 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2009 /* Copy selection */
2010 _clipboard_set_selected_data (header_view, TRUE);
2012 /* Get hidding ids */
2013 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
2015 /* Clear hidding array created by previous cut operation */
2016 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
2018 /* Copy hidding array */
2019 priv->n_selected = n_selected;
2020 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
2021 for (i=0; i < n_selected; i++)
2022 priv->hidding_ids[i] = g_strdup(hidding[i]);
2024 /* Hide cut headers */
2025 modest_header_view_refilter (header_view);
2032 _clipboard_set_selected_data (ModestHeaderView *header_view,
2035 ModestHeaderViewPrivate *priv = NULL;
2036 TnyList *headers = NULL;
2038 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2039 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
2041 /* Set selected data on clipboard */
2042 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
2043 headers = modest_header_view_get_selected_headers (header_view);
2044 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
2047 g_object_unref (headers);
2051 ModestHeaderView *self;
2056 notify_filter_change (gpointer data)
2058 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2060 g_signal_emit (info->self,
2061 signals[MSG_COUNT_CHANGED_SIGNAL],
2062 0, info->folder, NULL);
2068 notify_filter_change_destroy (gpointer data)
2070 NotifyFilterInfo *info = (NotifyFilterInfo *) data;
2071 ModestHeaderViewPrivate *priv;
2073 priv = MODEST_HEADER_VIEW_GET_PRIVATE (info->self);
2074 priv->status_timeout = 0;
2076 g_object_unref (info->self);
2077 g_object_unref (info->folder);
2078 g_slice_free (NotifyFilterInfo, info);
2082 filter_row (GtkTreeModel *model,
2086 ModestHeaderViewPrivate *priv = NULL;
2087 TnyHeaderFlags flags;
2088 TnyHeader *header = NULL;
2091 gboolean visible = TRUE;
2092 gboolean found = FALSE;
2093 GValue value = {0,};
2094 HeaderViewStatus old_status;
2096 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
2097 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2099 /* Get header from model */
2100 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
2101 flags = (TnyHeaderFlags) g_value_get_int (&value);
2102 g_value_unset (&value);
2103 gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
2104 header = (TnyHeader *) g_value_get_object (&value);
2105 g_value_unset (&value);
2107 /* Hide deleted and mark as deleted heders */
2108 if (flags & TNY_HEADER_FLAG_DELETED ||
2109 flags & TNY_HEADER_FLAG_EXPUNGED) {
2114 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
2115 if (priv->is_outbox &&
2116 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2122 if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
2123 if (priv->is_outbox &&
2124 modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
2130 /* If no data on clipboard, return always TRUE */
2131 if (modest_email_clipboard_cleared(priv->clipboard)) {
2136 /* Get message id from header (ensure is a valid id) */
2143 if (priv->hidding_ids != NULL) {
2144 id = tny_header_dup_message_id (header);
2145 for (i=0; i < priv->n_selected && !found; i++)
2146 if (priv->hidding_ids[i] != NULL && id != NULL)
2147 found = (!strcmp (priv->hidding_ids[i], id));
2154 old_status = priv->status;
2155 priv->status = ((gboolean) priv->status) && !visible;
2156 if ((priv->notify_status) && (priv->status != old_status)) {
2157 NotifyFilterInfo *info;
2159 if (priv->status_timeout)
2160 g_source_remove (priv->status_timeout);
2162 info = g_slice_new0 (NotifyFilterInfo);
2163 info->self = g_object_ref (G_OBJECT (user_data));
2164 info->folder = tny_header_get_folder (header);
2165 priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
2166 notify_filter_change,
2168 notify_filter_change_destroy);
2175 _clear_hidding_filter (ModestHeaderView *header_view)
2177 ModestHeaderViewPrivate *priv = NULL;
2180 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
2181 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2183 if (priv->hidding_ids != NULL) {
2184 for (i=0; i < priv->n_selected; i++)
2185 g_free (priv->hidding_ids[i]);
2186 g_free(priv->hidding_ids);
2191 modest_header_view_refilter (ModestHeaderView *header_view)
2193 GtkTreeModel *model = NULL;
2194 ModestHeaderViewPrivate *priv = NULL;
2196 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
2197 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2199 /* Hide cut headers */
2200 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
2201 if (GTK_IS_TREE_MODEL_FILTER (model)) {
2202 priv->status = HEADER_VIEW_INIT;
2203 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
2208 * Called when an account is removed. If I'm showing a folder of the
2209 * account that has been removed then clear the view
2212 on_account_removed (TnyAccountStore *self,
2213 TnyAccount *account,
2216 ModestHeaderViewPrivate *priv = NULL;
2218 /* Ignore changes in transport accounts */
2219 if (TNY_IS_TRANSPORT_ACCOUNT (account))
2222 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
2225 TnyAccount *my_account;
2227 my_account = tny_folder_get_account (priv->folder);
2228 if (my_account == account)
2229 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
2230 g_object_unref (my_account);
2235 modest_header_view_add_observer(ModestHeaderView *header_view,
2236 ModestHeaderViewObserver *observer)
2238 ModestHeaderViewPrivate *priv;
2240 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2241 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2243 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2245 g_mutex_lock(priv->observer_list_lock);
2246 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
2247 g_mutex_unlock(priv->observer_list_lock);
2251 modest_header_view_remove_observer(ModestHeaderView *header_view,
2252 ModestHeaderViewObserver *observer)
2254 ModestHeaderViewPrivate *priv;
2256 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2257 g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
2259 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2261 g_mutex_lock(priv->observer_list_lock);
2262 priv->observer_list = g_slist_remove(priv->observer_list, observer);
2263 g_mutex_unlock(priv->observer_list_lock);
2267 modest_header_view_notify_observers(ModestHeaderView *header_view,
2268 GtkTreeModel *model,
2269 const gchar *tny_folder_id)
2271 ModestHeaderViewPrivate *priv = NULL;
2273 ModestHeaderViewObserver *observer;
2276 g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
2278 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
2280 g_mutex_lock(priv->observer_list_lock);
2281 iter = priv->observer_list;
2282 while(iter != NULL){
2283 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
2284 modest_header_view_observer_update(observer, model,
2286 iter = g_slist_next(iter);
2288 g_mutex_unlock(priv->observer_list_lock);
2292 _modest_header_view_get_display_date (ModestHeaderView *self, time_t date)
2294 ModestHeaderViewPrivate *priv = NULL;
2296 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
2297 return modest_datetime_formatter_display_datetime (priv->datetime_formatter, date);
2301 modest_header_view_set_filter (ModestHeaderView *self,
2302 ModestHeaderViewFilter filter)
2304 ModestHeaderViewPrivate *priv;
2305 GtkTreeModel *filter_model;
2307 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2308 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2310 priv->filter |= filter;
2312 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2313 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2314 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2319 modest_header_view_unset_filter (ModestHeaderView *self,
2320 ModestHeaderViewFilter filter)
2322 ModestHeaderViewPrivate *priv;
2323 GtkTreeModel *filter_model;
2325 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2326 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2328 priv->filter &= ~filter;
2330 filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
2331 if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
2332 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
2337 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
2339 if (strcmp ("style", spec->name) == 0) {
2340 update_style (MODEST_HEADER_VIEW (obj));
2341 gtk_widget_queue_draw (GTK_WIDGET (obj));
2346 update_style (ModestHeaderView *self)
2348 ModestHeaderViewPrivate *priv;
2349 GdkColor style_color;
2350 PangoAttrList *attr_list;
2352 PangoAttribute *attr;
2354 g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
2355 priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
2359 attr_list = pango_attr_list_new ();
2360 if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
2361 gdk_color_parse ("grey", &style_color);
2363 attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
2364 pango_attr_list_insert (attr_list, attr);
2367 style = gtk_rc_get_style_by_paths (gtk_widget_get_settings
2369 "SmallSystemFont", NULL,
2372 attr = pango_attr_font_desc_new (pango_font_description_copy
2373 (style->font_desc));
2374 pango_attr_list_insert (attr_list, attr);
2376 g_object_set (G_OBJECT (priv->renderer_address),
2377 "foreground-gdk", &style_color,
2378 "foreground-set", TRUE,
2379 "attributes", attr_list,
2381 g_object_set (G_OBJECT (priv->renderer_date_status),
2382 "foreground-gdk", &style_color,
2383 "foreground-set", TRUE,
2384 "attributes", attr_list,
2386 pango_attr_list_unref (attr_list);